Tuesday, 11 February 2014

Manchester Encoded Transmission using Oshonsoft

We've been playing about with one-wire Manchester-encoded data again, only this time using (our preferred) Oshonsoft PIC compiler. We've been using pretty much the same techniques as before, only this time, in BASIC rather than C.

Here's the first part, transmitting manchester-encoded data over a single  wire. It's based on our earlier SouceBoost code which we've already proven to work.

This code has been set up to run on a 16F882 (it was just one of the PIC microcontrollers we had lying around) and the only unusal thing of note is that we're using the internal oscillator at 8Mhz, not the default 4Mhz (see the OSCCON command at the start of the code). Other than that, this code should be useable by pretty much any of the 16F range of PICs (you may need to change a few pin assignments



Define CONFIG = 0x20c4
Define CONFIG2 = 0x3eff
Define CLOCK_FREQUENCY = 8
OSCCON = OSCCON Or 01110000b
AllDigital

declarations:
     Dim buttondown As Bit
     Symbol button = PORTB.0
     Symbol led_red = PORTC.4
     Symbol led_green = PORTC.2
     Symbol led_blue = PORTC.3
     Symbol data_pin = PORTC.5
   
     ConfigPin PORTC = Output
     ConfigPin button = Input
   
     Dim testcount As Byte
     Dim heartbeat As Bit
     Dim databuffer(6) As Byte
   
     Dim byteindex As Byte
     Dim bytetosend As Byte
     Dim bitssent As Byte
     Dim bitflag As Bit
     Dim sendnextbyte As Bit
     Dim sending As Bit
     Dim pinstate As Bit
   
     Dim i As Byte
     Dim k As Byte
   
init:
     PORTC = 0
     OPTION_REG.7 = 0 'enable pull ups on portb (RBPU)
     Gosub setuptimer1
   
     High led_blue
     WaitMs 255
     WaitMs 255
     Low led_blue
   
loop:
     Gosub getinput
     If buttondown = 1 Then
           databuffer(2) = 0x26
           databuffer(3) = 0x11
           databuffer(4) = 0x75
           Gosub senddata
     Endif
   
Goto loop
End

getinput:
     buttondown = 0
     If button = 0 Then
           'debounce the button input
           WaitMs 10
           If button = 0 Then
                 While button = 0
                       'do nothing
                 Wend
                 buttondown = 1
           Endif
     Endif
Return

ledsoff:
     Low led_red
     Low led_green
     Low led_blue
Return

flashled:
     Gosub ledsoff
     WaitMs 50
     High led_red
     WaitMs 200

     Gosub ledsoff
     WaitMs 50
     High led_green
     WaitMs 200
   
     Gosub ledsoff
     WaitMs 50
     High led_blue
     WaitMs 200

     Gosub ledsoff
Return

setuptimer1:
     'T1CON = 0x30
     T1CON = 00001000b 'set bit zero to start the timer

     INTCON.GIE = 1
     INTCON.PEIE = 1

     PIE1.TMR1IE = 1
     PIR1.TMR1IF = 0
Return

preloadtimer1:
     'at 8mhz, fosc/4=2Mhz (2,000,000)
     'our radio modules are rated up to 4khz
     'so let's use about half that, just to be safe
     'so 2m/2k = 1,000
     'we want our counter to go up to 1,000 then
     'send another pulse
     '65535-2000 = 63535 (0xF82F)
     '65535-1000 = 64535 (0xFC17)
     TMR1H = 0xfc
     TMR1L = 0x17
Return

starttimer1:
     'in case the timer is already running, stop it
     T1CON.TMR1ON = 0
     'reset the timer1 interrupt flag
     PIR1.TMR1IF = 0
     'preload with the correct value
     Gosub preloadtimer1
     'start the timer
     T1CON.TMR1ON = 1
Return

stoptimer1:
     'stop timer1
     T1CON.TMR1ON = 0
     'reset the timer1 interrupt flag
     PIR1.TMR1IF = 0
Return

senddata:
     sending = 1
     testcount = 0
     High led_blue
   
     databuffer(0) = 10101010b
     databuffer(1) = 10101011b
   
     'databuffer 2,3,4 should already be populated
     'so create a checksum for the three values
     databuffer(5) = 0
     databuffer(5) = databuffer(5) Xor databuffer(2)
     databuffer(5) = databuffer(5) Xor databuffer(3)
     databuffer(5) = databuffer(5) Xor databuffer(4)

     'send the data: the interrupts will take care of
     'moving this (blocking) function along
     Low data_pin
   
     byteindex = 0
     bytetosend = databuffer(0)
     bitssent = 0
     bitflag = 0
     sendnextbyte = 0
   
     Gosub starttimer1

     'this is a blocking Function
     While sending = 1
           'do nothing
     Wend
     Low led_blue

Return

sendbit:
     'this is where we wiggle the pin
     If bitflag = 0 Then
           'this is the first half of sending:
           'work out which way the Bit should be sent
           '(start low For Bit value 1, start high For bit value zero)

           k = bytetosend And 10000000b
           If k = 0 Then pinstate = 0 Else pinstate = 1

           'set the pin according to the bitState value
           If pinstate = 0 Then Low data_pin Else High data_pin
         
           'change the bit-flag so we will invert the output on the
           'next timer1 interrupt
           bitflag = 1
         
     Else
   
           'it doesn't matter which way the data pin is, just toggle it
           pinstate = Not pinstate
           If pinstate = 0 Then Low data_pin Else High data_pin

           'keep track of the total number of bits sent
           bitssent = bitssent + 1
           If bitssent >= 8 Then
                 sendnextbyte = 1
           Else
                 'move the value in the byte to send buffer
                 '(shift right)
                 bytetosend = ShiftLeft(bytetosend, 1)
                 sendnextbyte = 0
           Endif
         
           'change the bit-flag for the next timer1 interrupt
           bitflag = 0

     Endif
   
     If sendnextbyte = 1 Then
           byteindex = byteindex + 1
           If byteindex >= 6 Then
                 'we've sent our six bytes from buffer(0-5)
                 'so chill out and have a brew now
                 Gosub endtransmission
           Else
                 'prepare the next byte for transmission
                 bytetosend = databuffer(byteindex)
                 bitssent = 0
                 bitflag = 0
           Endif
           sendnextbyte = 0
     Endif
         
Return

endtransmission:
     'first things first, kill the timer
     Gosub stoptimer1
   
     'now wait a timer-width before pulling the line low
     WaitMs 2
     Low data_pin
   
     'kill the sending flag
     sending = 0
   
Return

On Interrupt
     Save System

     If PIR1.TMR1IF = 1 Then
           'clear the interrupt flag for timer1
           PIR1.TMR1IF = 0
           Gosub preloadtimer1
           Gosub sendbit
     Endif
Resume

By running the clock at 8Mhz instead of 4Mhz, we've got about 1,000 clock instructions between Timer1 intervals. But even at the default 4Mhz, there would be 500 instructions between Timer1 intervals, which should be plenty to do the processing required between "ticks".

As before, we ran the single wire into pin4 of our PicKit2 programmer and ran the built-in Logic Analyser tool. This time, we wrote our data values in hex, so that they are easier to read back at the other end.



Amazingly, it worked first time!
Let's hope that converting the receiver code from SourceBoost C to Oshonsoft BASIC goes as smoothly!