PDA

View Full Version : PIC16F88 HSERIN to LCD Problem



robbrownuk
- 2nd June 2009, 12:09
Hello,

I have been having problems with the HSERIN instruction on a 16F88 and after too many hours on Google I hope someone can help me.

I have written a program that will take serial data and output it to an LCD. The program works at 1200 baud but doesn't work at any speed above this. Ideally I would like the baud rate to be 9600 but 1200 is too slow.

Here is my code:


'-------------------------------------
' STARTUP CODE
'-------------------------------------

@ DEVICE PIC16F88,XT_OSC
@ DEVICE PIC16F88,PROTECT_OFF
@ DEVICE PIC16F88,WDT_OFF
@ DEVICE PIC16F88,PWRT_ON
@ DEVICE PIC16F88,MCLR_ON
@ DEVICE PIC16F88,BOD_OFF
@ DEVICE PIC16F88,LVP_OFF
@ DEVICE PIC16F88,CPD_OFF
@ DEVICE PIC16F88,DEBUG_OFF
@ DEVICE PIC16F88,CCPMX_OFF

' *THIS SPEED DOESN'T WORK??
'DEFINE HSER_RCSTA 90h ' Enable serial port & continuous receive
'DEFINE HSER_TXSTA 24h ' Enable transmit, BRGH = 1
'DEFINE HSER_SPBRG 25 ' 9600 Baud @ 0.16%
'DEFINE HSER_CLROERR 1 ' Clear overflow automatically

' *THIS SPEED DOES WORK
DEFINE HSER_RCSTA 90h ' Enable serial port & continuous receive
DEFINE HSER_TXSTA 20h ' Enable transmit, BRGH = 0
DEFINE HSER_SPBRG 51 ' 1200 Baud @ 0.17%
DEFINE HSER_CLROERR 1 ' Clear overflow automatically

'Define the LCD
Define LCD_DREG PORTA
DEFINE LCD_DBIT 0
DEFINE LCD_RSREG PORTB
DEFINE LCD_RSBIT 0
DEFINE LCD_EREG PORTA
DEFINE LCD_EBIT 4
DEFINE LCD_BITS 4
DEFINE LCD_LINES 2
'DEFINE LCD_COMMANDUS 1500
'DEFINE LCD_DATAUS 44

'Register setup
ADCON1 = %00000111 ' Disable A/D converter
ANSEL = %00000000 ' all analog pins to digital
CCP1CON = %00000000 ' Disable CCP Module
CMCON = %00000111 ' Turn off comparator
INTCON = %00000000 ' Interrupts disabled

'Clear the ports
TRISA = %00000000 'All O/P's
TRISB = %00001101 'All O/P's
PORTA = 0 'Zero PortA O/P's
PORTB = 0 'Zero PortB O/P's

'Var for RS232 data
RxData var byte

'-------------------------------------
' SPLASH SCREEN
'-------------------------------------
SplashScreen:

pause 60 'Power up delay

'Display a splash screen for 2 seconds
LCDOUT $FE,1,"RS232 to LCD"
LCDOUT $FE,$C0,"Display Test"
pause 2000

'Now ready for COMMS...
LCDOUT $FE,1,"Waiting for PC"
LCDOUT $FE,$C0,"to send data..."

'-------------------------------------
' RS232 TO LCD
'-------------------------------------
Main:


high PORTB.7 'This is just a test for diagnostics
hserin [RxData] 'Get the RS232 Data
low PORTB.7 'This is just a test for diagnostics

lcdout RxData 'Output the data to the LCD

goto Main 'Loop forever


I have looked at the RS232/LCD data with a digital logic probe and I have attached 2 screen shots from this. One shows 1200 baud and the other 9600 baud. At 1200 baud the timing looks ok but at 9600 it looks like there is not enought time to send the LCD data before another byte of data comes in.

Does anyone have any ideas how to resolve this issue?

Thanks,
robbrownuk

mister_e
- 2nd June 2009, 21:58
Yes, You change the way your receive your Bytes of data, and/or the way you transmit them.

Knowing how much data you want to display, and how often you receive/refresh them would help.

robbrownuk
- 3rd June 2009, 00:27
Hello mister_e,

Changing how the data is transmitted (by the PC) would be the easiest thing to do but i would like to fix the problem at the receiving end.

If the program could just handle 34 characters that would be good (16*2 lines and 2 bytes to move to the 2nd line). Currently if i transmit more that 2 bytes of data the LCD shows garbage.
To demonstrait this as an example: If i send 2 bytes of data [chr("0xFE") & chr("0x01")] this clears the display without any problems. If i send 3 bytes of data [chr("0xFE") & chr("0x01") & "H"] the display clears but instead of displaying the character "H" it displays a garbage character.

To me this problem seems to be caused by the Rx buffer overflowing, but shouldnt this just make some of the characters disappear. If i sent the text "Hello World" would it not display something like "Heo rl" ???

Thanks,
robbrownuk

Bruce
- 3rd June 2009, 01:14
The BIG problem with your particular app is that LCDOUT is just too darn slow to keep up with a continuous incoming stream of serial data at 9600 bps. And were's why...

At 1200 baud it takes longer for the hardware USART FIFO buffer to fill. This gives you more time for LCDOUT to finish before the 3rd byte is clocked into the hardware USART, which causes a buffer overrun error, and the USART simply stops receiving data.

At 9600 baud it takes less time to overrun - so you end up with missed characters at the higher baud rate.

So - if your inbound serial data stream takes < time than it takes to output the string to your LCD, it's just not going to work.

A couple of options might be to wait until a serial data input array is filled, then send whatever's in the buffer to your LCD, or setup an interrupt to receive characters in the background, then output to the LCD.

Either way - you'll still want some control over the sending device so you have time to handle sending everything to your LCD, which is going to be a good deal slower than receiving serial data at 9600 bps.

Check the Receive Overrun Error section of the data sheet for a full explanation.

Archangel
- 3rd June 2009, 02:11
Ah Say, Ugh, Lookie Here !
http://www.picbasic.co.uk/forum/showthread.php?t=4972 Darrel's post 16, you alter it to fit that 88 chip and you got it. Use a pullup resistor on the Rx line. Ring Buffer up to 64 bytes.

Bruce
- 3rd June 2009, 02:22
Yep. The priority event with the hardware USART is to read or empty the FIFO buffer before it overruns. LCDOUT can handle being interrupted and never appear to miss a beat if you keep the int handler short & sweet.

And, of course, DT ints make it all relatively painless for the newbie....;o}

Archangel
- 3rd June 2009, 02:31
And, of course, DT ints make it all relatively painless for the newbie....;o}
Even for newbies into their 3rd year as a newbie . . .:D

Bruce
- 3rd June 2009, 02:50
Even for newbies into their 3rd year as a newbie . . .
Heck yes. I'm still a newbie myself....;o}

robbrownuk
- 3rd June 2009, 07:20
Ah Say, Ugh, Lookie Here !
http://www.picbasic.co.uk/forum/showthread.php?t=4972 Darrel's post 16, you alter it to fit that 88 chip and you got it. Use a pullup resistor on the Rx line. Ring Buffer up to 64 bytes.

Thanks very much Joe,

That code looks promising on "post 16", I’ll give it a try and let you know how it goes.

Thanks,
robbrownuk

robbrownuk
- 6th June 2009, 22:32
Thanks again Joe. That code worked very well and of course thanks to Darrel Taylor for his DT_INTS code.

The final code is posted here should anyone need it:


'-------------------------------------
' STARTUP CODE
'-------------------------------------

'MPASM generates an warning because of tabs around the macro (this prevents it)
@ errorlevel -207

'Config settings for MPASM
@ __CONFIG _CONFIG1, _HS_OSC & _CP_OFF & _WDT_OFF & _PWRTE_ON & _MCLR_ON & _BODEN_OFF & _LVP_OFF & _CPD_OFF & _DEBUG_OFF & _CCP1_RB0

'Using a 20Mhz XTAL
define OSC 20

'Register setup
ADCON1 = %00000111 ' Disable A/D converter
ANSEL = %00000000 ' all analog pins to digital
CCP1CON = %00000000 ' Disable CCP Module
CMCON = %00000111 ' Turn off comparator
INTCON = %00000000 ' Interrupts disabled

'Setup & Clear the ports
TRISA = %00000000 'All O/P's
TRISB = %00001101 'All O/P's
PORTA = 0 'Zero PortA O/P's
PORTB = 0 'Zero PortB O/P's

'-----------------------------------------------------------------------------
INCLUDE "DT_INTS-14.bas"
INCLUDE "ReEnterPBP.bas" ; Include if using PBP interrupts

ASM
INT_LIST macro ; IntSource, Label, Type, ResetFlag?
INT_Handler RX_INT, _INT_Serin, PBP, no
endm
INT_CREATE ; Creates the interrupt processor
ENDASM

@ INT_ENABLE RX_INT ; enable external (INT) interrupts
'-----------------------------------------------------------------------------

'Setup the hardware USART for 9600 baud
DEFINE HSER_RCSTA 90h ' Enable serial port & continuous receive
DEFINE HSER_TXSTA 24h ' Enable transmit, BRGH = 1
DEFINE HSER_SPBRG 129 ' 9600 Baud @ 0.16%
DEFINE HSER_CLROERR 1 ' Clear overflow automatically

'Define the LCD
Define LCD_DREG PORTA
DEFINE LCD_DBIT 0
DEFINE LCD_RSREG PORTB
DEFINE LCD_RSBIT 0
DEFINE LCD_EREG PORTA
DEFINE LCD_EBIT 4
DEFINE LCD_BITS 4
DEFINE LCD_LINES 2
DEFINE LCD_COMMANDUS 1500
DEFINE LCD_DATAUS 50

'Alias & Vars
RCIF VAR PIR1.5 ' Receive interrupt flag (1=full , 0=empty)
TXIF VAR PIR1.4 ' Transmit interrupt flag (1=empty, 0=full)
OERR VAR RCSTA.1 ' Alias OERR (USART Overrun Error Flag)
CREN VAR RCSTA.4 ' Alias CREN (USART Continuous Receive Enable)
BufMax CON 64 ' Sets the size of the ring buffer, set up from 32
Buffer VAR BYTE[BufMax] ' Array variable for holding received characters
Ptr_in VAR BYTE ' Pointer - next empty location in buffer
Ptr_out VAR BYTE ' Pointer - location of oldest character in buffer
Bufchar VAR BYTE ' Stores the character retrieved from the buffer
ErrFlag VAR BYTE ' Holds error flags
ptr_in = 0 ' Clear buffer pointer (in)
ptr_out = 0 ' Clear buffer pointer (out)
Errflag = 0 ' Clear error flag

'Skip over the interupt handler & subroutines
goto ShowSplash

'-------------------------------------
' INTERUPT HANDLER
'-------------------------------------

INT_Serin: ' Serial Interrupt routine

IF OERR Then usart_error ' Check for USART errors
ptr_in = (ptr_in + 1) ' Increment ptr_in pointer (0 to 63)
IF ptr_in > (BufMax-1) Then ptr_in = 0 'Reset pointer if outside of buffer
IF ptr_in = ptr_out Then Buffer_Error ' Check for buffer overrun
HSerin [buffer[ptr_in]] ' Read USART and store data in next empty location
IF RCIF Then INT_Serin ' Check for another character while we're here
@ INT_RETURN ; Return to program

Buffer_Error: ' Error - Run out of buffer space

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.
ptr_in = (ptr_in - 1) MIN (BufMax - 1)
HSerin [buffer[ptr_in]] ' Overwrite the last data stored (clear the int.)

USART_Error: ' Error - USART has overrun (data too fast?)

errflag.0 = 1 ' Set the error flag for hardware
@ INT_RETURN ; Return to program


'-------------------------------------
' Subroutines
'-------------------------------------
GetBuf: ' move the next character in buffer to bufchar

@ INT_DISABLE RX_INT ; Dont want to interupt this, Disable interupts

ptr_out = (ptr_out + 1) ' Increment ptr_out pointer (0 to 63)
IF ptr_out > (BufMax-1) Then ptr_out = 0 ' Reset pointer if outside of buffer
bufchar = buffer[ptr_out] ' Read buffer location

@ INT_ENABLE RX_INT ; All done, Enable interupts
Return


'-------------------------------------
' SPLASH SCREEN
'-------------------------------------
ShowSplash:

pause 60 'Power up delay

'Display a splash screen for 2 seconds
LCDOUT $FE,1,"RS232 to LCD"
LCDOUT $FE,$C0,"Display Test"
pause 2000

'Now ready for COMMS...
LCDOUT $FE,1,"Waiting for PC"
LCDOUT $FE,$C0,"to send data..."

'-------------------------------------
' RS232 TO LCD
'-------------------------------------
MainLoop:

IF errflag Then ErrorHandler ' Handle error if needed
IF ptr_in = ptr_out Then MainLoop ' loop if nothing in buffer

GoSub getbuf ' Get a character from buffer (stored in bufchar)
LCDOut bufchar ' Send the character to LCD

goto MainLoop 'Loop forever

'-------------------------------------
' Error Handler
'-------------------------------------
ErrorHandler:

' Display error message if buffer has overrun
IF errflag.1 Then ' Determine the error
LCDOut $FE,$1,"Buffer Overflow" ' Display buffer error
Else
LCDOut $FE,$1,"USART Overrun" ' Display usart error
EndIF

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

GoTo MainLoop ' Errors cleared, time to work.