Building on my previous post regarding inclusion of assembly language files in MPLABX projects that use SDCC for PIC18, I thought I would present a real-world example covering Manchester encoding of data to achieve neutral DC bias for RF applications. This seemed like the perfect example to use as the encoding of serial data is, most likely, a time-critical function that can be implemented more efficiently using assembly language as opposed to C. Still, the ability to reference these subroutines from SDCC C is a bonus if not a requirement. If you would like to read more about encoding data for neutral DC bias, I would recommend starting with the Manchester Code page at Wikipedia.
The following assembly file was added to my project and includes two [non-optimized] functions for encoding and decoding of byte data:
;----------------------------------------------------------------------------- ; File: encode.asm ; Purpose: Implementation of manchester encode/decode assembly ; subroutines ;Includes #include <p18f2620.inc> ;Declare relocatable data .encode.data udata _decodedByte res .1 _encodedBytes res .2 __loop res .2 ;Code section .encode.code code ;----------------------------------------------------------------------------- ; Function: _encode() ; Purpose: Encodes the byte of data stored in _decodedByte and stores ; the neutral-DC biased result in the two bytes at ; _encodedBytes. ; _encode: movlw .4 ;Load loop counter for first nibble movwf __loop _encode_nextBitN1 rrcf _decodedByte, f ;Rotate LSb into carry flag btfss STATUS,C ;Check bit in carry and goto _encode_clearN1 ; jump to clear of not set bcf STATUS,C ;Handle MARK by encoding transition rrcf _encodedBytes+1, f ; from 0 to 1 (two bits) in encoded bsf STATUS,C ; byte file registers rrcf _encodedBytes+1, f goto _encode_loopN1 ;Jump to loop for this nibble _encode_clearN1 bsf STATUS,C ;Handle space by encoding transition rrcf _encodedBytes+1, f ; from 1 to 0 (two bits) in encoded bcf STATUS,C ; byte file registers rrcf _encodedBytes+1, f _encode_loopN1 decfsz __loop, f ;Check for nibble complete goto _encode_nextBitN1 ; Still encoding, loop movlw .4 ;Load loop counter for second nibble movwf __loop _encode_nextBitN2 rrcf _decodedByte, f btfss STATUS,C goto _encode_clearN2 bcf STATUS,C rrcf _encodedBytes, f bsf STATUS,C rrcf _encodedBytes, f goto _encode_loopN2 _encode_clearN2 bsf STATUS,C rrcf _encodedBytes, f bcf STATUS,C rrcf _encodedBytes, f _encode_loopN2 decfsz __loop, f goto _encode_nextBitN2 return ;----------------------------------------------------------------------------- ; Function: _decode() ; Purpose: decodes a manchester encoded byte stored in _encodedBytes ; and writes the value to _decodedByte. ; _decode: movlw .4 movwf __loop _decode_byte1 rrcf _encodedBytes+1, f btfss STATUS,C goto _decode_setByte1 ;If the first bit is 0 then mark bcf STATUS,C ;Space in decoded byte rrcf _decodedByte ;Rotate carry into byte goto _decode_byte1Next _decode_setByte1 bsf STATUS,C ;Mark in decoded byte rrcf _decodedByte ;Rotate carry into byte _decode_byte1Next rrcf _encodedBytes+1, f ;Discard paired bit, we only need first decfsz __loop, f ;Decrement loop counter for first byte goto _decode_byte1 movlw .4 movwf __loop _decode_byte2 rrcf _encodedBytes, f btfss STATUS,C goto _decode_setByte2 ;If the first bit is 0 then mark bcf STATUS,C ;Space in decoded byte rrcf _decodedByte ;Rotate carry into byte goto _decode_byte2Next _decode_setByte2 bsf STATUS,C ;Mark in decoded byte rrcf _decodedByte ;Rotate carry into byte _decode_byte2Next rrcf _encodedBytes, f ;Discard paired bit, we only need first decfsz __loop, f ;Decrement loop counter for first byte goto _decode_byte2 return ;Declare global data and functions global _decodedByte,_encodedBytes global _encode, _decode end
The corrisponding C file that calls these routines contains a quick and dirty check to be sure that the decoded byte matches the data encoded
EDIT – I received an offline question regarding accessing the encoded data from C and changed the example below to demonstrate how I would accomplish this using a union:
//Processor definition #ifndef __SDCC_PIC18F2620 #define __SDCC_PIC18F2620 #endif //Library includes #include <pic18fregs.h> #include <delay.h> //Local includes //Configuration registers #pragma config OSC=INTIO67, BOREN=OFF, PWRT=ON, WDT=OFF, PBADEN=ON #pragma config STVREN=OFF, LVP=OFF, XINST=OFF //#pragma config MCLRE=OFF, DEBUG=OFF #pragma config MCLRE=ON, DEBUG=OFF //Module constant definitions #define LED_LAT LATCbits.LATC0 #define LED_TRIS TRISCbits.TRISC0 //Union typedef for encoded bytes typedef union { unsigned int data; unsigned char array[2]; } encodedBytes_t; //External references extern unsigned char decodedByte; extern encodedBytes_t encodedBytes; extern void encode(); extern void decode(); /* * */ int main(int argc, char** argv) { unsigned char encodedByte1, encodedByte2; argc; argv; //Quick oscillator configuration OSCCON = 0b01110000; //Disable analog to digital converters ADCON0 = 0b00000000; ADCON1 = 0b00000000; //Encode and decode a value decodedByte = 0xB2; encode(); //Get encoded byte data encodedByte1 = encodedBytes.array[0]; encodedByte2 = encodedBytes.array[1]; //Transmit data here //Sanity check decode(); if(decodedByte != 0xB2) { return -1; } return 0; }
To-do’s here would include optimizing both functions as well as instruction counting to assure that any path through the logic uses the same number of instruction cycles. This usually includes adding some strategically placed nop’s.
If you’re looking for a manchester encoding algorithm for PIC18 assembly, feel free to use the one above. Comments and suggestions are, of course, welcome.