PDA

View Full Version : PC to EUART on pic 16F688



docaberle
- 20th October 2008, 20:34
I'm transmitting 5 bytes of data at a time. Decimal 0,0,1,127,128. The data goes from a UART on a netburner card to the RX pin on a 16F688. About 50% of the time I get a USART Overrun error. In addition, the pic chip sometimes restarts up showing a buffer error.

I'm using the internal oscillator at 4MHz and running at 2400 baud. Wondering if anyone can identify an issue with my code. The data makes it through most of the time without trouble, but I'm not sending a lot of data and there are seconds between when I send another 5 bytes. Curious if my defines are ok also? The code is taken from the samples list online and modified slightly. The only odd things is I look through the buffer with each byte that is collected and look for the 0,0,1 bytes to be there. Then the next byte will be a steering value and the next a throttle value.

' PicBasic Pro program to demonstrate an interrupt-driven
' input buffer for Hserin using On Interrupt.
' Pin definitions compatible with LAB-X1 and PIC16F877

DEFINE HSER_RCSTA 90h 'SPEN=0, CREN=0
DEFINE HSER_TXSTA 20h 'BRGH=0, SYNC=0, TXEN=1
DEFINE HSER_BAUD 2400
SPBRG = 25 ' Set baud rate to 2400 for USART

Include "modedefs.bas"

RCIF VAR PIR1.5 ' Alias RCIF (USART Receive Interrupt Flag)
OERR VAR RCSTA.1 ' Alias OERR (USART Overrun Error Flag)
CREN VAR RCSTA.4 ' Alias CREN (USART Continuous Receive Enable)
LCD VAR PORTC.1 ' Alias for LCD

buffer_size CON 32 ' Sets the size of the ring buffer
buffer VAR BYTE[buffer_size] ' Array variable for holding received characters
index_in VAR BYTE ' Pointer - next empty location in buffer
index_out VAR BYTE ' Pointer - location of oldest character in buffer
bufchar VAR BYTE ' Stores the character retrieved from the buffer
i VAR BYTE ' loop counter
col VAR BYTE ' Stores location on LCD for text wrapping
errflag VAR BYTE ' Holds error flags
carsteering Var byte ' Car Steering
carthrottle Var byte ' Car Throttle

index_in = 0
index_out = 0
i = 0

SEROUT LCD,N9600,[$1b,$2a,$40] 'Set Backlight Brightness
SEROUT LCD,N9600,[$fe,1] ' Clear Screen

INTCON = %11000000 ' Enable interrupts
ON INTERRUPT GoTo serialin ' Declare interrupt handler routine
PIE1.5 = 1 ' Enable interrupt on USART

loop: ' Main program starts here - do nothing for now


display: ' dump the buffer to the LCD
IF errflag Then error ' Handle error if needed
IF index_in = index_out Then loop ' loop if nothing in buffer

GoSub getbuf ' Get a character from buffer
If (buffer[index_out-4] = 0) then
if (buffer[index_out-3] = 0) then
if (buffer[index_out-2] = 1) then
carsteering = buffer[index_out-1]
carthrottle = buffer[index_out]
SEROUT LCD,N9600,[$fe,1] ' Clear Screen
SEROUT LCD,N9600,["Car=1"]
SEROUT LCD,N9600,[$fe,$c0] ' Move to beginning of 2nd line
SEROUT LCD,N9600,["Steering=",#carsteering]
SEROUT LCD,N9600,[$fe,$94] ' Move to beginning of 3rd line
SEROUT LCD,N9600,["Throttle=",#carthrottle]
Endif
Endif
endif

GoTo display ' Check for more characters in buffer

' Subroutines

Disable ' Don't check for interrupts in this section

getbuf: ' move the next character in buffer to bufchar
index_out = (index_out + 1) ' Increment index_out pointer (0 to 63)
IF index_out > (buffer_size-1) Then index_out = 0 ' Reset pointer if outside of buffer
bufchar = buffer[index_out] ' Read buffer location
Return


error: ' Display error message if buffer has overrun
SEROUT LCD,N9600,[$fe,$d4] ' Move to beginning of 4th line
IF errflag.1 Then ' Determine the error
SEROUT LCD,N9600,["Buffer Overrun"]
Else
SEROUT LCD,N9600,["USART Overrun"]
EndIF

errflag = 0 ' Reset the error flag
CREN = 0 ' Disable continuous receive to clear overrun flag
CREN = 1 ' Enable continuous receive

GoTo display ' Carry on


' Interrupt handler

serialin: ' Buffer the character received
IF OERR Then usart_error ' Check for USART errors
index_in = (index_in + 1) ' Increment index_in pointer (0 to 63)
IF index_in > (buffer_size-1) Then index_in = 0 'Reset pointer if outside of buffer
IF index_in = index_out Then buffer_error ' Check for buffer overrun
HSerin [buffer[index_in]] ' Read USART and store character to next empty location
IF RCIF Then serialin ' Check for another character while we're here

Resume ' Return to program

buffer_error:
errflag.1 = 1 ' Set the error flag for software
' Move pointer back to avoid corrupting the buffer. MIN insures that it ends up within the buffer.
index_in = (index_in - 1) MIN (buffer_size - 1)
HSerin [buffer[index_in]] ' Overwrite the last character stored (resets the interrupt flag)
usart_error:
errflag.0 = 1 ' Set the error flag for hardware

Resume ' Return to program


End

skimask
- 20th October 2008, 20:43
I'm using the internal oscillator at 4MHz and running at 2400 baud.
That's most likely your problem right there. Go with external crystal, resonator, oscillator, something...
But since you said you're having overrun errors, look at the 'F688 datasheet in the OSCTUN section and dial down your internal clock a couple of notches and see what happens.
Problem is, this will only work at that particular battery voltage, that temperature, that day of the week, when the moon is in that particular phase, etc.etc.etc...
Just save the trouble and go with an external clock source...
Could it be something else causing the problem?
Sure...trying to SEROUT while at the same time receiving serial data in the hardware...bad idea... I'd also be willing to bet if you took all of those SEROUT statement and made it so that each SEROUT statement only did one, two, maybe three characters at a time, you're overrun errors might go away, since it'll give the program/hardware a chance to interrupt and empty out the incoming hardware serial buffer.

Emil G
- 20th October 2008, 21:40
"SPBRG = 25 ' Set baud rate to 2400 for USART" ????

SPBRG = 25 ' Set baud rate to 9600 for USART !!!!!

Archangel
- 20th October 2008, 22:57
"SPBRG = 25 ' Set baud rate to 2400 for USART" ????

SPBRG = 25 ' Set baud rate to 9600 for USART !!!!!

???? SPBRG = 25 is 9600 at 16 mhz it is 2400 at 4mhz

docaberle
- 23rd October 2008, 22:46
I haven't tried the crystal yet, but after really simplifying the serout strings, everything works fine. No errors. Then when I added back the code that I took out to simplify things I start having problems again. It's the pauseus commands that give me problems since the pauses are between 1ms and 2ms to control two servos. I can't see any other way of receiving UART data and controlling two servos. I'm trying like crazy to avoid real interrupts and I'd prefer not to have to have another microcontroller. Any suggestions? The deletions are included here. It's the code in the main loop that I added back.

' PicBasic Pro program to demonstrate an interrupt-driven
' input buffer for Hserin using On Interrupt.
' Pin definitions compatible with LAB-X1 and PIC16F877

DEFINE HSER_RCSTA 90h 'SPEN=0, CREN=0
DEFINE HSER_TXSTA 20h 'BRGH=0, SYNC=0, TXEN=1
DEFINE HSER_BAUD 2400
SPBRG = 25 ' Set baud rate to 2400 for USART

Include "modedefs.bas"

RCIF VAR PIR1.5 ' Alias RCIF (USART Receive Interrupt Flag)
OERR VAR RCSTA.1 ' Alias OERR (USART Overrun Error Flag)
CREN VAR RCSTA.4 ' Alias CREN (USART Continuous Receive Enable)
LED VAR PORTC.2 ' Alias LED
LCD VAR PORTC.1 ' Alias for LCD
SERVO1 VAR PORTC.3 ' Alias Servo Port
SERVO2 VAR PORTC.0 ' Alias Servo Port

buffer_size CON 32 ' Sets the size of the ring buffer
buffer VAR BYTE[buffer_size] ' Array variable for holding received characters
index_in VAR BYTE ' Pointer - next empty location in buffer
index_out VAR BYTE ' Pointer - location of oldest character in buffer
bufchar VAR BYTE ' Stores the character retrieved from the buffer
i VAR BYTE ' loop counter
col VAR BYTE ' Stores location on LCD for text wrapping
errflag VAR BYTE ' Holds error flags
pw VAR WORD ' Pulse Width Variable
direction Var Byte ' Direction Variable
carnumber Var Byte ' Car # 1 through 8
carsteering Var byte ' Car Steering
carthrottle Var byte ' Car Throttle

index_in = 0
index_out = 0
i = 0
col = 1
pw = 1200 ' start servo at extreme left
direction = 1
LOW SERVO1
LOW SERVO2

SEROUT LCD,N9600,[$1b,$2a,$40] 'Set Backlight Brightness
SEROUT LCD,N9600,[$fe,1] ' Clear Screen

INTCON = %11000000 ' Enable interrupts
ON INTERRUPT GoTo serialin ' Declare interrupt handler routine
PIE1.5 = 1 ' Enable interrupt on USART

' Main program starts here - blink an LED at 1Hz

loop: Disable
HIGH SERVO1
Pauseus pw
LOW SERVO1

HIGH SERVO2
Pauseus pw
LOW SERVO2
Enable
For i = 0 to 16 ' Delay for 16ms (16*1mS)
Pause 1 ' Use a short pause within a loop
Next i ' instead of one long pause
pause 16 ' set frequency to about 50 Hz
If direction=0 THEN
pw = pw - 5
ELSE
pw = pw + 5
ENDIF
if pw > 1800 then direction=0 ' at extreme right, turn CCW. 0 is for left
if pw < 1200 then direction=1 ' at extreme left, turn CW. 1 is for right


display: ' dump the buffer to the LCD
IF errflag Then error ' Handle error if needed
IF index_in = index_out Then loop ' loop if nothing in buffer

GoSub getbuf ' Get a character from buffer
'SEROUT LCD,N9600,[#bufchar] ' Clear Screen
If (buffer[index_out-4] = 0) then
if (buffer[index_out-3] = 0) then
if (buffer[index_out-2] = 1) then
carsteering = buffer[index_out-1]
carthrottle = buffer[index_out]
SEROUT LCD,N9600,[$fe,1] ' Clear Screen
'SEROUT LCD,N9600,[#carnumber]
'SEROUT LCD,N9600,[$fe,$c0] ' Move to beginning of 2nd line
'SEROUT LCD,N9600,[#carsteering]
'SEROUT LCD,N9600,[$fe,$94] ' Move to beginning of 3rd line
'SEROUT LCD,N9600,[#carthrottle]
'SEROUT LCD,N9600,[$fe,$d4] ' Move to beginning of 4th line
'SEROUT LCD,N9600,["End of Data Stream"]
Endif
Endif
endif

GoTo display ' Check for more characters in buffer



' Subroutines

Disable ' Don't check for interrupts in this section

getbuf: ' move the next character in buffer to bufchar
index_out = (index_out + 1) ' Increment index_out pointer (0 to 63)
IF index_out > (buffer_size-1) Then index_out = 0 ' Reset pointer if outside of buffer
bufchar = buffer[index_out] ' Read buffer location
Return


error: ' Display error message if buffer has overrun
SEROUT LCD,N9600,[$fe,$d4] ' Move to beginning of 4th line
IF errflag.1 Then ' Determine the error
SEROUT LCD,N9600,["B"]
Else
SEROUT LCD,N9600,["U"]
EndIF

errflag = 0 ' Reset the error flag
CREN = 0 ' Disable continuous receive to clear overrun flag
CREN = 1 ' Enable continuous receive

GoTo display ' Carry on


' Interrupt handler

serialin: ' Buffer the character received
IF OERR Then usart_error ' Check for USART errors
index_in = (index_in + 1) ' Increment index_in pointer (0 to 63)
IF index_in > (buffer_size-1) Then index_in = 0 'Reset pointer if outside of buffer
IF index_in = index_out Then buffer_error ' Check for buffer overrun
HSerin [buffer[index_in]] ' Read USART and store character to next empty location
IF RCIF Then serialin ' Check for another character while we're here

Resume ' Return to program

buffer_error:
errflag.1 = 1 ' Set the error flag for software
' Move pointer back to avoid corrupting the buffer. MIN insures that it ends up within the buffer.
index_in = (index_in - 1) MIN (buffer_size - 1)
HSerin [buffer[index_in]] ' Overwrite the last character stored (resets the interrupt flag)
usart_error:
errflag.0 = 1 ' Set the error flag for hardware

Resume ' Return to program


End

skimask
- 24th October 2008, 03:35
I haven't tried the crystal yet, but after really simplifying the serout strings, everything works fine.
You'd still be better off in the long run using a crystal/resonator...


It's the pauseus commands that give me problems since the pauses are between 1ms and 2ms to control two servos.
Then don't use 1ms-2ms pause's. Give the serial interrupt routine a chance to grab the byte even when there's a pause going on.



DEFINE HSER_RCSTA 90h 'SPEN=0, CREN=0
DEFINE HSER_TXSTA 20h 'BRGH=0, SYNC=0, TXEN=1
DEFINE HSER_BAUD 2400
SPBRG = 25 ' Set baud rate to 2400 for USART
Include "modedefs.bas"
RCIF VAR PIR1.5 ' Alias RCIF (USART Receive Interrupt Flag)
OERR VAR RCSTA.1 ' Alias OERR (USART Overrun Error Flag)
CREN VAR RCSTA.4 ' Alias CREN (USART Continuous Receive Enable)
LED VAR PORTC.2 ' Alias LED
LCD VAR PORTC.1 ' Alias for LCD
SERVO1 VAR PORTC.3 ' Alias Servo Port
SERVO2 VAR PORTC.0 ' Alias Servo Port
buffer_size CON 32 ' Sets the size of the ring buffer
buffer VAR BYTE ' Array variable for holding received characters
index_in VAR BYTE ' Pointer - next empty location in buffer
index_out VAR BYTE ' Pointer - location of oldest character in buffer
bufchar VAR BYTE ' Stores the character retrieved from the buffer
i VAR BYTE ' loop counter
col VAR BYTE ' Stores location on LCD for text wrapping
errflag VAR BYTE ' Holds error flags
pw VAR WORD ' Pulse Width Variable
direction Var Byte ' Direction Variable
carnumber Var Byte ' Car # 1 through 8
carsteering Var byte ' Car Steering
carthrottle Var byte ' Car Throttle
index_in = 0
index_out = 0
i = 0
col = 1
pw = 1200 ' start servo at extreme left
direction = 1
LOW SERVO1
LOW SERVO2
SEROUT LCD,N9600,[$1b,$2a,$40] 'Set Backlight Brightness
SEROUT LCD,N9600,[$fe,1] ' Clear Screen
INTCON = %11000000 ' Enable interrupts
ON INTERRUPT GoTo serialin ' Declare interrupt handler routine
PIE1.5 = 1 ' Enable interrupt on USART
' Main program starts here - blink an LED at 1Hz
loop: Disable
HIGH SERVO1
[B] Pauseus (pw>>2
Pauseus (pw>>2)

LOW SERVO1
HIGH SERVO2
Pauseus (pw>>2
Pauseus (pw>>2)

LOW SERVO2
Enable
For i = 0 to 1600 ' Delay for 16ms (16*1mS)
Pauseus 10 ' Use a short pause within a loop
Next i ' instead of one long pause

' pause 16 ' set frequency to about 50 Hz
If direction=0 THEN
pw = pw - 5
ELSE
pw = pw + 5
ENDIF
if pw > 1800 then direction=0 ' at extreme right, turn CCW. 0 is for left
if pw < 1200 then direction=1 ' at extreme left, turn CW. 1 is for right
display: ' dump the buffer to the LCD
IF errflag Then error ' Handle error if needed
IF index_in = index_out Then loop ' loop if nothing in buffer
GoSub getbuf ' Get a character from buffer
'SEROUT LCD,N9600,[#bufchar] ' Clear Screen
If (buffer[index_out-4] = 0) then
if (buffer[index_out-3] = 0) then
if (buffer[index_out-2] = 1) then
carsteering = buffer[index_out-1]
carthrottle = buffer[index_out]
SEROUT LCD,N9600,[$fe,1] ' Clear Screen
'SEROUT LCD,N9600,[#carnumber]
'SEROUT LCD,N9600,[$fe,$c0] ' Move to beginning of 2nd line
'SEROUT LCD,N9600,[#carsteering]
'SEROUT LCD,N9600,[$fe,$94] ' Move to beginning of 3rd line
'SEROUT LCD,N9600,[#carthrottle]
'SEROUT LCD,N9600,[$fe,$d4] ' Move to beginning of 4th line
'SEROUT LCD,N9600,["End of Data Stream"]
Endif
Endif
endif
GoTo display ' Check for more characters in buffer
' Subroutines
Disable ' Don't check for interrupts in this section
getbuf: ' move the next character in buffer to bufchar
index_out = (index_out + 1) ' Increment index_out pointer (0 to 63)
IF index_out > (buffer_size-1) Then index_out = 0 ' Reset pointer if outside of buffer
bufchar = buffer[index_out] ' Read buffer location
Return
error: ' Display error message if buffer has overrun
SEROUT LCD,N9600,[$fe,$d4] ' Move to beginning of 4th line
IF errflag.1 Then ' Determine the error
SEROUT LCD,N9600,["B"]
Else
SEROUT LCD,N9600,["U"]
EndIF
errflag = 0 ' Reset the error flag
CREN = 0 ' Disable continuous receive to clear overrun flag
CREN = 1 ' Enable continuous receive
GoTo display ' Carry on
' Interrupt handler
serialin: ' Buffer the character received
IF OERR Then usart_error ' Check for USART errors
index_in = (index_in + 1) ' Increment index_in pointer (0 to 63)
IF index_in > (buffer_size-1) Then index_in = 0 'Reset pointer if outside of buffer
IF index_in = index_out Then buffer_error ' Check for buffer overrun
HSerin [buffer[index_in]] ' Read USART and store character to next empty location
IF RCIF Then serialin ' Check for another character while we're here
Resume ' Return to program
buffer_error:
errflag.1 = 1 ' Set the error flag for software
' Move pointer back to avoid corrupting the buffer. MIN insures that it ends up within the buffer.
index_in = (index_in - 1) MIN (buffer_size - 1)
HSerin [buffer[index_in]] ' Overwrite the last character stored (resets the interrupt flag)
usart_error:
errflag.0 = 1 ' Set the error flag for hardware
Resume ' Return to program
End

Archangel
- 24th October 2008, 04:03
I'm transmitting 5 bytes of data at a time. Decimal 0,0,1,127,128. The data goes from a UART on a netburner card to the RX pin on a 16F688. About 50% of the time I get a USART Overrun error. In addition, the pic chip sometimes restarts up showing a buffer error.



2 things come to mind about these problems, I have experienced with serial backpack using very much the same code. Send your data Open True not driven true and make sure there is a pullup resistor on the Hserin Rx pin so it is in a logic High state at boot.

docaberle
- 24th October 2008, 15:27
I don't understand what:
Pauseus (pw>>2)
Pauseus (pw>>2)
does? I can't find anything like this in the manual. Also why is it in there twice for each servo? Do the disable and enable commands negate this anyway if this code is somehow allowing interrupts to happen?

To Joe S.:
In other words, my netburner card isn't pulling high properly so I should make it open collector output and put a pull up resistor on the data line and that should correct some problems?

mackrackit
- 24th October 2008, 15:44
pw is a variable
>>2 shifts right two places

Look at the Math Operators section.

skimask
- 24th October 2008, 15:47
I don't understand what:
Pauseus (pw>>2)
Pauseus (pw>>2)
does? I can't find anything like this in the manual. Also why is it in there twice for each servo? Do the disable and enable commands negate this anyway if this code is somehow allowing interrupts to happen?

I screwed up anyways. They're supposed to be Pauseus (pw >> 1 )
>> is right-shift, same as divide-by a power of two (>>1=/2, >>2=/4, >>3=/8 and so on). If you right-shift a number by one bit, it's the same as divide-by-2. Same thing for a left-shift, << , multiplies a value by a power of 2.
I put that in there to split-up the long pause to give the serial port interrupt a chance to kick in.
At 2400 baud, a byte takes about 4.16ms to arrive. While that is a longer time frame than the pause for the servo, it was just an off-the-head change I thought might help. Might help to split the servo pause into more 'chunks', might not, might help to totally rewrite the code, then again, maybe what you've got is sufficient.

And DISABLE and ENABLE only disable and enable the PBP interrupt trapping and jumping to the PBP interrupt routine. The interrupts still happen, the bits still get set, but PBP doesn't do anything about them. For instance, if you DISABLE the interrupts, but manually check the individual interrupt flag bits in your code, you can still figure out if an interrupt hit or not.

Archangel
- 25th October 2008, 06:33
To Joe S.:
In other words, my netburner card isn't pulling high properly so I should make it open collector output and put a pull up resistor on the data line and that should correct some problems?
I do not know what your netburner is doing. In my use, herserin works better if held high with a resistor and hooked up to ??? which idles high and sends data Open True. Here is a really quick test, pull the serial line from the netburner and hook it to +, boot and after boot hook it back to netburner (quickly), if that solves the problem then my suggestion should work.