SDCC + PIC18 Assembly Manchester Encoding for Neutral RF DC Bias

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.

Leave a Reply

Your email address will not be published. Required fields are marked *

© Zillow, Inc., 2006-2016. Use is subject to Terms of Use
What's a Zestimate?