We're running this code on a PIC16F883 on it's own internal oscillator at 8Mhz. The code includes error checking, but not correction- i.e. if a message received is junk, it's simply discarded. To increase stability, it may be preferable to run the mcu off a crystal, to ensure accurate timing:
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 = PORTB.5
Symbol tx = PORTB.7
ConfigPin PORTC = Output
ConfigPin button = Input
ConfigPin data_pin = Input
Dim prev_pin_state As Bit
Dim received_bit As Bit
Dim last_received_bit As Bit
Dim received_byte As Byte
Dim bitflag As Bit
Dim bits_received As Byte
Dim bytes_received As Byte
Dim preamble_count As Byte
Dim receive_buffer(6) As Byte
Dim state As Byte
Dim k As Byte
init:
PORTC = 0
OPTION_REG.7 = 0 'enable pull ups on portb (RBPU)
'start up serial output
'(we'll use software serial for ease when running with
'the PicKit2 programmer)
ConfigPin tx = Output
High led_blue
WaitMs 255
WaitMs 255
Low led_blue
Gosub setuptimer1
Serout tx, 9600, "Ready to receive..."
'start up the state machine
state = 0
'our inputs have pull-ups on them so the prev_pin_state
'for the dataPin should be high be default
prev_pin_state = 0
loop:
Select Case state
'---------------------------------------------
'just idling to see if we've had any data yet
Case 0
'---------------------------------------------
If data_pin = 0 And prev_pin_state = 1 Then
'hey up! something's pulled the data pin low,
'it could be the start of a preamble
state = 1
bitflag = 0
received_byte = 0
bits_received = 0
bytes_received = 0
last_received_bit = 0
preamble_count = 0
Gosub ledsoff
Gosub starttimer1
Else
'I suppose we could see if the user has pressed a button
'for testing
Gosub getinput
If buttondown = 1 Then
Gosub flashled
Endif
Endif
prev_pin_state = data_pin
'----------------------------------------------------
'something's twitched on the data pin line, so better
'receive some data in
Case 1
'----------------------------------------------------
'this is handled with the timer1 interrupt handler
'--------------------------------------------
'end of transmission or data receive time out
Case 3
'--------------------------------------------
'validate the output
k = 0
k = k Xor receive_buffer(0)
k = k Xor receive_buffer(1)
k = k Xor receive_buffer(2)
If k = receive_buffer(3) Then
'cool, checksums add up
Serout tx, 9600, receive_buffer(0)
Serout tx, 9600, receive_buffer(1)
Serout tx, 9600, receive_buffer(2)
Serout tx, 9600, receive_buffer(3)
Else
High led_red
Endif
state = 4
Case 4
prev_pin_state = 0
state = 0
EndSelect
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 = 00001000b 'set bit zero to start the timer
INTCON.GIE = 1
INTCON.PEIE = 1
PIE1.TMR1IE = 1
PIR1.TMR1IF = 0
T1CON.TMR1ON = 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 = 0xf8
TMR1L = 0x2f
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
getbit:
If bitflag = 0 Then
'this is fine
prev_pin_state = data_pin
bitflag = 1
Else
'in the second half of any bit, there
'should be a transition on the data line
'(if there's not, we're out of sync)
received_bit = 0
If prev_pin_state <> data_pin Then
'this is awesome. We've had a transition
'on the second half of the bit, just as we expect
If data_pin = 0 Then
'this is a one bit
received_bit = 1
Endif
Else
'something has gone weird
'maybe we're just out of sync?
High led_red
Endif
prev_pin_state = data_pin
bitflag = 0
'now decode the bit that's just come in
If preamble_count < 32 Then
'--------------------------------------------------
'we're still waiting for 1010101 etc followed by 11
'--------------------------------------------------
If last_received_bit = 1 And received_bit = 1 And preamble_count > 8 Then
'here's our 11 at the end of the preamble!
preamble_count = 99
received_byte = 0
bits_received = 0
bytes_received = 0
High led_blue
Else
'we're looking for a transition from 01 or 10
If last_received_bit <> received_bit Then
preamble_count = preamble_count + 1
Else
'we've had two bits of the same value but not
'preceded by 010101 etc so reset the preamble count
preamble_count = 0
Endif
Endif
Else
'-------------------------------------------------------
'we're into the meaty stuff now, actually receiving data
'-------------------------------------------------------
If received_bit = 1 Then
'write this bit to the received byte value
received_byte = received_byte Or 0x01
Endif
bits_received = bits_received + 1
If bits_received >= 8 Then
'put the received_byte into the bytebuffer
receive_buffer(bytes_received) = received_byte
bytes_received = bytes_received + 1
received_byte = 0
bits_received = 0
If bytes_received >= 4 Then
Gosub stoptimer1
state = 3
Endif
Else
'prepare for the next incoming bit
'shunt the received_byte value along
'(whether we received a one or a zero)
received_byte = ShiftLeft(received_byte, 1)
Endif
Endif
last_received_bit = received_bit
Endif
Return
On Interrupt
Save System
If PIR1.TMR1IF = 1 Then
'clear the interrupt flag for timer1
PIR1.TMR1IF = 0
Gosub preloadtimer1
Gosub getbit
Endif
Resume
We've tested the receiver a few times and get consistently good results.
However, the RC internal oscillator in a PIC is accurate to about 1% but can drift over time and with changes in temperature. If you're likely to have the sender and receiver in two different places (we're planning on using these routines to send data via RF link between two controllers in different rooms in the house) it might be worthwhile adding a precision cut crystal, just to ensure exact timing between the two mcus.
No comments:
Post a Comment