PDA

View Full Version : Interrupt driven Buffered Serial Input



johnmiller
- 10th May 2007, 18:04
Hi, I'm going mad for the past few days here since my interrupts are not working. I am trying to make a interrupt driven buffered serial input but without success since my program doesn't do anything when it should deal with the interrupts (it just stays on that "While CounterA <= 70 : WEND" forever) (nothing is printed out when I send something to it's serial port). Here is my code (part of it at least that deals with the serial part)



DEFINE OSC 20

DEFINE HSER_TXSTA 24h
DEFINE HSER_RCSTA 90h
DEFINE HSER_BAUD 19200
DEFINE HSER_SPBRG 64 ' 19200 Bauds
DEFINE HSER_CLROERR 1

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 PORTD.0 ' Alias LED to PORTD.0

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
errflag VAR BYTE ' Holds error flags

CMCON=%00000111 ' Disable Comparators
TRISA=%00000000 ' PORTA all set to Output
TRISB=%11111111 ' PORTB all set to Input
T0CON=%00000111 ' OFF,16BIT,PRESCALE=256
ADCON0 = 0
CMCON=7
INTCON.5=1 'T0IE : TMR0 Overflow Interrupt Enable bit

ON interrupt GOTO GestIntrerupts ' Deal with interrupts

ReadSerial:
DataRec var byte[5] : DataRec = 0
PIE1.5 = 1 ' Enable interrupt on USART
While CounterA <= 70 : WEND
PIE1.5 = 0 ' Disable interrupt on USART

FOR COUNTERB=0 to COUNTERA
SEROUT2 SerWRT,16416,[SerDATA(COUNTERB)]
NEXT COUNTERB

Goto ReadReadyLoop


' 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
errflag = 0 ' Reset the error flag
CREN = 0 ' Disable continuous receive to clear overrun flag
CREN = 1 ' Enable continuous receive
GoTo display ' Carry on

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

GestIntrerupts:
IF INTCON.2 = 1 then
T0CON.0 = 0 ' Disable timer
TMR0L = 0 ' Reset timer
TMR0H = 0 ' Reset timer
TimeCount = TimeCount + 1
T0CON.0 = 1 ' Enabled timer
INTCON.2 = 0
ELSE : IF RCIF = 1 THEN
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
SEROUT2 SerWRT,16416,[buffer(index_in)]
SerData(CounterA)=buffer(index_in)
CounterA = CounterA + 1
IF RCIF Then GestIntrerupts ' Check for another character while we're here
ENDIF : ENDIF
resume
ENABLE


Any input is appreciated!

skimask
- 10th May 2007, 18:28
DISABLE before here
ENABLE after your interrupt subroutine
Jump over your interrupt subroutine with a Goto Main or something
ON interrupt GOTO GestIntrerupts ' Deal with interrupts
ReadSerial:
DataRec var byte[5] : DataRec = 0 : PIE1.5 = 1
While CounterA <= 70 : WEND
PIE1.5 = 0 ' Disable interrupt on USART
FOR COUNTERB=0 to COUNTERA:SEROUT2 SerWRT,16416,[SerDATA(COUNTERB)]:NEXT COUNTERB
Goto ReadReadyLoop
Not really a good idea inside an interrupt subroutine
' Subroutines
Disable ' Don't check for interrupts in this section
getbuf: ' move the next character in buffer to bufchar
index_out = (index_out + 1):IF index_out > (buffer_size-1) Then index_out = 0
bufchar = buffer[index_out]:Return
error: errflag = 0:CREN = 0:CREN = 1:GoTo display ' Carry on
buffer_error:
errflag.1 = 1:index_in = (index_in - 1) MIN (buffer_size - 1):HSerin ]
usart_error: errflag.0 = 1 ' Set the error flag for hardware
Resume ' Return to program
GestIntrerupts:
IF INTCON.2 = 1 then
T0CON.0 = 0:TMR0L = 0:TMR0H = 0:TimeCount = TimeCount + 1:T0CON.0 = 1:INTCON.2 = 0
ELSE[B] COLON isn't needed but IF RCIF = 1 THEN moved to next line is needed : IF RCIF = 1 THEN
IF OERR Then usart_error ' Check for USART errors
index_in = (index_in + 1):IF index_in > (buffer_size-1) Then index_in = 0
IF index_in = index_out Then buffer_error ' Check for buffer overrun
HSerin [buffer[index_in]]:SEROUT2 SerWRT,16416,[buffer(index_in)]:SerData(CounterA)=buffer(index_in)
CounterA = CounterA + 1
IF RCIF Then GestIntrerupts ' Check for another character while we're here
ENDIF : ENDIF
resume
ENABLE


Where is your main loop?
You're using HSerIn with interrupts. HSerIn will wait for the NEXT character into the USART receive register after the interrupt trips.
Lots of little problems with this code...rework it a bit, straighten it out, actually READ thru it from top to bottom and see how it executes, repost, and we'll work it from there...

johnmiller
- 10th May 2007, 19:27
Well how would you suggest me to do it? Should I use serin2 instead of hserin?
I need it to be a interrupt driven buffer serial read since if I send the data too quickly it just discards some data. I tried to read the date with serin, serin2 and now hserin. My only problem now is

PIE1.5 = 1 ' Enable interrupt on USART
While CounterA <= 70 : WEND
PIE1.5 = 0 ' Disable interrupt on USART

skimask
- 10th May 2007, 21:33
Well how would you suggest me to do it? Should I use serin2 instead of hserin?

Any one of the SerIn functions can catch data very quickly, even at 19,200 and much higher. It mostly depends on how much time you actually need to process that data once received. Like I said, re-read thru your code, figure out what you really NEED vs. what you want to do. Write down a 'quick-n-dirty' flowchart of how you want/need things to work.
What are you buffering the serial port for?
Is this a FIFO or a LIFO type buffer (probably FIFO type)?
What is the main function of the program going to be? What is this program going to do for you?

mister_e
- 11th May 2007, 03:11
1. i don't see any PIC #
2. i don't see any config fuses.
3. I don't see any INTCON and other interrupt related registers

Fast reading of your code but it may work. now just comment out your whole ISR and toggle a led in and then clear RCIF falg by reading RCREG untill it's empty.

Is the thing work or not? If not, your baudrate, config fuses and or hardware is faulty.

Easier way, HSERIN/HSEROUT to echo your incomming data to you PC.

Is this working or not?

johnmiller
- 11th May 2007, 18:18
Well my program needs to read the data from a serial connection and than it should save that data into a string. The only problem that I have is that if the date comes too quickly the PIC just discards that data. Any ideea why is that?

Darrel Taylor
- 11th May 2007, 18:31
Possibly because you are sending anything received back out at the same speed within the interrupt handler.

You should get in and out of the handler as quick as possible.
Then let the mainloop empty the buffer when it has time.
Preferably at a higher speed.

Also, T0CON is different than T1CON.
TMR0ON is bit 7, and bit 0 is part of the prescaler setting.
<br>

johnmiller
- 11th May 2007, 19:25
I tried this code:



ReadSerial:
DataRec var byte[5] : DataRec = 0
KP var byte : KP = 0

While CounterA <= 70
IF index_in > (buffer_size-1) Then index_in = 0 'Reset pointer if outside of buffer
HSerin [buffer[index_in]] ' Read USART and store character to next empty location

if buffer(index_in) = $20 then
index_in = 0
for CounterB=0 to buffer_size
buffer(CounterB) = 0
next CounterB
KP = 1
ENDIF

if ((KP = 1) AND (index_in=4)) THEN
SerData(CounterA)=buffer(4)
CounterA = CounterA + 1
KP = 1
ENDIF
index_in = index_in + 1
WEND


and it still doesn't work any ideea why.

PS: Is it better than the old one?

skimask
- 11th May 2007, 20:13
@ DEVICE PIC16F628A , HS_OSC , WDT_OFF , PWRT_ON , MCLR_ON , BOD_ON , LVP_OFF , PROTECT_OFF
'HS 20mhz external, watchdog off, powerup timer on, mclr external, brown out detect on, low volt program off , code protect off

resetplaceholder: 'wordpad, arial black, size 8, reg, 1600x1200 screen, 16f628A code
DEFINE OSC 20 '20mhz

serdata var byte : serialbuffer var byte[255] : bufferpointer var byte
bufferdata var byte : temp var byte

startupholder: goto skipsubs 'skip over all the commonly used subroutines

ON INTERRUPT GOTO INTHANDLER
DISABLE INTERRUPT
INTHANDLER: if pir1.5 = 1 then 'if serial data rx'd
serdata = rcreg 'save serial port data right away
pir1.5 = 0 'reset the RX flag
if ( rcsta.1 = 1 ) or ( rcsta.2 = 1 ) then 'check for err, if frame/overrun error,
rcsta.4 = 0 : rcsta.4 = 1 : serdata = rcreg : serdata = 0
else
if bufferpointer = 255 then goto intfinish 'buffer is full
serialbuffer[bufferpointer] = serdata
: bufferpointer = bufferpointer + 1 'save data in buffer, bump pointer
goto INTHANDLER 're-check serial port just in case another character came in while saving data
'...not likely
endif
endif

intfinish: RESUME
ENABLE INTERRUPT

getcharacterfrombuffer:
if bufferpointer = 0 then return 'no data in buffer in the first place!
buffereddata = serialbuffer[0] 'get oldest data from buffer
for temp = 0 to 254 : serialbuffer[temp] = serialbuffer[temp+1] : next temp 'shift data down one byte
serialbuffer[255]=0 'set last byte in buffer to zero since it's meaningless
bufferpointer = bufferpointer - 1 'one less byte in buffer
return

skipsubs: option_reg=8 : pie1=32 : trisa=0 : porta=0 : trisb=$ef : portb=16 : t1con=0 : t2con=0
cmcon=7 : ccp1con=0 : vrcon=0 : txsta=0 : rcsta=$90 : pir1.5=0 : spbrg=33 : intcon=$e0 '9600 baud
for temp = 0 to 255 : serialbuffer[temp] = 0 : next temp

mainloop: goto mainloop 'do it all over again

END


I modified one of my old programs. This works for me, always has.
I've left the character handling and the modifications of the program to you to make work for your PIC.

johnmiller
- 12th May 2007, 08:15
I tried your code, and now I tried the one above and still does not work


ReadSerial:
DataRec var byte[5] : DataRec = 0

While CounterA <= 55
HSERIN [serdata2] 'save serial port data right away
if serdata2 = $13 THEN
i = 0
KP = 1
ENDIF
if KP = 1 then
IF i=5 THEN
SerData(CounterA) = serdata2
CounterA = CounterA + 1
KP = 0
endif
i = i + 1
endif
wend

FOR COUNTERB=42 to COUNTERA
SEROUT2 SerWRT,16416,[hex2 SerDATA(COUNTERB), 10, 13]
NEXT COUNTERB
Goto ReadReadyLoop

I tried this one as well and it doesn't work either:


SERIN2 [WAIT($20), SKIP 4, serdata2]


PS: it doesn't work it's like the pic is not reading the data fast enough. It is supposed to read the data from a keyboard that than sends data over a serial link at 19200 (not inverted).

All the above codes work if I press a key once per second or so, but they don't work any faster. Anybody can tell me why

skimask
- 12th May 2007, 08:38
I think I can reasonably assume that what you posted isn't all of your code that you are trying to make work.
And if it is...well, where's the beef?
Post the whole thing if you want any useful help...

johnmiller
- 12th May 2007, 12:06
Well this is the only part where I have problems and I don't think pasting 80 pages will do any good :D
I really don't understand what's wrong with my code

skimask
- 12th May 2007, 18:33
I don't think pasting 80 pages will do any good :D
I really don't understand what's wrong with my code

And I guess neither will we...

mister_e
- 12th May 2007, 18:36
here's the plan... shrink the whole thing and test with a simple program... simple main loop (led blinking or else) + USART INT. Then post the whole new thing.

johnmiller
- 13th May 2007, 01:33
Ok. I managed to do my work with this ;) (after a lot of struggle) but it works. Hope this code will help somebody in the future

PS: Thanks guys for the help



ReadSerial:
DataRec var byte[6]
for counterB=0 to 5
DataRec(counterB)=0
next counterB

While CounterA <= 254
HSERIN [serdata2]
DataRec(0)=DataRec(1)
DataRec(1)=DataRec(2)
DataRec(2)=DataRec(3)
DataRec(3)=DataRec(4)
DataRec(4)=DataRec(5)
DataRec(5)=serdata2

IF DataRec(0)=$20 AND DataRec(2)="K" AND DataRec(3) = "E" AND DataRec(4) = "Y" THEN
' Parse serial data (command entered)
SerData(CounterA) = serdata2
CounterA = CounterA + 1
endif
wend

FOR COUNTERB=0 to COUNTERA
SEROUT2 SerWRT,16416,[SerDATA(COUNTERB), 10, 13]
NEXT COUNTERB
Goto MainLoop

mister_e
- 13th May 2007, 01:42
;) While / Wend was in the bug list right? ;)

Seems good to me.