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.