PDA

View Full Version : HSERIN using EUSART RX interrupt - how does it really work?



flotulopex
- 23rd January 2021, 20:40
Hi all,

I've made this small program to learn how the EUSART RX interrupt works....and it works BUT :biggrin:

Yes, the code below works but I'm getting confused when I think about having unknown data length to be received. I'm not even sure that this code is really correct.

In this example, I'm waiting for 5 charaters to be received. As long as 5 characters have not arrived, nothing will be printed out.

And here is where I'm confused.

Since the HSERIN command is in the ISR, each time I send a character from the Serial Communiactor, I expect the ISR the receive "at least" one character so the LED should toggle. But no, it doesn't. It seems to wait until all 5 characters have arrived and then, only then, the LED toggles.

I don't get it....

How shall I handle this please? What is the correct approach?


' ====== FUSES ================================================== ==================================
' PIC 16F690 Fuses (MPASM)
@ __config _FCMEN_OFF &_IESO_OFF &_CPD_OFF &_WDT_OFF &_INTRC_OSC_NOCLKOUT &_BOR_OFF &_CP_OFF &_PWRTE_OFF &_MCLRE_OFF
@ ERRORLEVEL -306

' ====== REGISTERS ================================================== ===============================
OSCCON = %01110000 'Internal RC set to 8Mhz - Register not to be used with XTal
OPTION_REG = %10000000 'PORT A&B Pull-Ups (look WPUA & WPUB)
ANSEL = %00000000 'Select analog inputs Channels 0 to 7
ANSELH = %00000000 'Select analog inputs Channels 8 to 11
INTCON = %11000000 'INTERRUPT Control
PIE1 = %00100000 'Peripheral Interrupts

' ====== UART SETTINGS ================================================== ==========================
RCSTA = %10010000 ' Enable serial port & continuous receive
TXSTA = %00100000 ' Enable transmit, BRGH = 0
SPBRG = 12 ' 9600 Baud @ 8MHz, 0.16%

' ====== DEFINES ================================================== ================================
DEFINE OSC 8
DEFINE NO_CLRWDT 1 ' Don't waste cycles clearing WDT
DEFINE HSER_CLOERR 1 ' Automatic clear overrun error

' ====== VARIABLES ================================================== ==============================
LED VAR PORTB.6
SData VAR BYTE(5)
Print VAR BIT

'======= INITIALIZE ================================================== =============================
PORTB.5 = 1 ' set to avoid sending serial data garbage
Print = 0 ' send out serial data only if some is received

'======= PROGRAM ================================================== ================================
ON INTERRUPT GOTO ISR

MAIN:
IF PRINT THEN
Hserout [STR SData\5,13,10]
Print = 0
ENDIF
GOTO MAIN
END

' ====== INTERRUPT SERVICE ROUTINE ================================================== ==============
Disable
ISR:
HSerin [STR SData\5]
PRINT = 1
TOGGLE LED
RESUME
ENABLE

richard
- 23rd January 2021, 21:26
ON INTERRUPT GOTO ISR
i have great reservation that "on interrupt" will be successful @9600 with a 4mhz osc, use dt ints


HSerin [STR SData\5]
puts your 690 in a blocking loop until 5 characters have been received, no matter how long it takes
its not isr material







my take



' ====== FUSES ================================================== ==================================' PIC 16F690 Fuses (MPASM)
@ __config _FCMEN_OFF &_IESO_OFF &_CPD_OFF &_WDT_OFF &_INTRC_OSC_NOCLKOUT &_BOR_OFF &_CP_OFF &_PWRTE_OFF &_MCLRE_OFF
@ ERRORLEVEL -306


' ====== REGISTERS ================================================== ===============================
OSCCON = %01110000 'Internal RC set to 8Mhz - Register not to be used with XTal
OPTION_REG = %10000000 'PORT A&B Pull-Ups (look WPUA & WPUB)
ANSEL = %00000000 'Select analog inputs Channels 0 to 7
ANSELH = %00000000 'Select analog inputs Channels 8 to 11
INTCON = %11000000 'INTERRUPT Control
;PIE1 = %00100000 'Peripheral Interrupts


' ====== UART SETTINGS ================================================== ==========================
RCSTA = %10010000 ' Enable serial port & continuous receive
TXSTA = %00100000 ' Enable transmit, BRGH = 0
SPBRG = 12 ' 9600 Baud @ 8MHz, 0.16%


' ====== DEFINES ================================================== ================================
DEFINE OSC 8
DEFINE NO_CLRWDT 1 ' Don't waste cycles clearing WDT
DEFINE HSER_CLOERR 1 ' Automatic clear overrun error


' ====== VARIABLES ================================================== ==============================
LED VAR PORTB.6
SData VAR BYTE(5)
rx_count var byte
rx_complete var bit
rx_hit var bit
;Print VAR BIT
clear
'======= INITIALIZE ================================================== =============================
PORTB.5 = 1 ' set to avoid sending serial data garbage
,Print = 0 ' send out serial data only if some is received

'======= PROGRAM ================================================== ================================
ON INTERRUPT GOTO ISR
gosub rx_start
MAIN:
IF rx_complete THEN
Hserout [STR SData\5,13,10]
gosub rx_start
ENDIF
IF rx_hit THEN
TOGGLE LED
rx_hit=0
endif
GOTO MAIN
END


rx_start ;resume /start rx
RCSTA.4=0 ;clear any rcsta error
RCSTA.4=1
rx_count=0
rx_complete=0
PIE1.5=1
return


' ====== INTERRUPT SERVICE ROUTINE ================================================== ==============
Disable
ISR:
sdata[rx_count]=RCREG
rx_hit=1
rx_count=rx_count+1
if rx_count==5 then ;stop rx for now
rx_complete=1
PIE1.5=0
endif
RESUME
ENABLE

richard
- 23rd January 2021, 21:34
since most chips have a rxbuffer you could also take advantage of



ISR:
rx_hit=1
while pir1.5
sdata[rx_count]=RCREG
rx_count=rx_count+1
if rx_count==5 then ;stop rx for now

rx_complete=1
PIE1.5=0
endif
wend

RESUME
ENABLE

flotulopex
- 24th January 2021, 10:22
Thank you Richard,

Actually, my program works on a 8MHz basis - it should be more than enough to handle 9600bps serial data, no?

I'd like to stick to the very basics since I'm not an ultra-programming-geek (just a curious guy). I often heard and read about DT's interrupts but it looks "too much" to me, a least, for my so small projects.



HSerin [STR SData\5]
puts your 690 in a blocking loop until 5 characters have been received, no matter how long it takes its not isr material Thanks a lot for explaining this.


As far as I read the DS, the RX buffer can store 2 characters. Starting this thread, my example was using a 5 characters but in fact, I will always have unknown data length (I'll read sms messages) - so let's forget the 5 characters story :wink:

Since you refer to it, why is there a buffer of "2" characters? What's its use?


This is my latest code I modified to receive 1 character at the time giving the chance of handling variable lenght incoming data:

' ====== FUSES ================================================== ==================================
' PIC 16F690 Fuses (MPASM)
@ __config _FCMEN_OFF &_IESO_OFF &_CPD_OFF &_WDT_OFF &_INTRC_OSC_NOCLKOUT &_BOR_OFF &_CP_OFF &_PWRTE_OFF &_MCLRE_OFF
@ ERRORLEVEL -306

' ====== REGISTERS ================================================== ===============================
OSCCON = %01110000 'Internal RC set to 8Mhz - Register not to be used with XTal
OPTION_REG = %10000000 'PORT A&B Pull-Ups (look WPUA & WPUB)
ADCON0 = %00000000 'A/D Module
ANSEL = %00000000 'Select analog inputs Channels 0 to 7
ANSELH = %00000000 'Select analog inputs Channels 8 to 11
INTCON = %11000000 'INTERRUPT Control
PIE1 = %00100000 'Peripheral Interrupts

' ====== UART SETTINGS ================================================== ==========================
RCSTA = %10010000 ' Enable serial port & continuous receive
TXSTA = %00100000 ' Enable transmit, BRGH = 0
SPBRG = 12 ' 9600 Baud @ 8MHz, 0.16%

' ====== DEFINES ================================================== ================================
DEFINE OSC 8
DEFINE NO_CLRWDT 1 ' Don't waste cycles clearing WDT
DEFINE HSER_CLOERR 1 ' Automatic clear overrun error

' ====== VARIABLES ================================================== ==============================
LED VAR PORTB.6
SData VAR BYTE(5)
Print VAR BIT

'======= INITIALIZE ================================================== =============================
PORTB.5 = 1 ' set to avoid sending serial data garbage
Print = 0

'======= PROGRAM ================================================== ================================
ON INTERRUPT GOTO ISR

MAIN:
IF PRINT THEN
HSEROUT [SData]
Print = 0
ENDIF
GOTO MAIN
END

' ====== INTERRUPT SERVICE ROUTINE ================================================== ==============
Disable
ISR:
PRINT = 1
HSerin [SData]
Resume
ENABLE


Looks like a silly question, but, can I trust this code? Will it work @ 100%? Could something make it hang or stop?

BTW, thanks for your code - I'm playing with it trying to understand it :D

HenrikOlsson
- 24th January 2021, 11:01
Since you refer to it, why is there a buffer of "2" characters? What's its use?
First, there's the actual shiftregister into which the data is shifted bit-by-bit as it's received. You don't have access to this register (AFAIK).
Once the correct number of bits have been shifted in they are copied to RCREG (where you can read it) and the interrupt flag is set. Now the user code needs to grab the byte from RCREG before a new byte is fully shifted in. So there's your two byte buffer.


Looks like a silly question, but, can I trust this code? Will it work @ 100%? Could something make it hang or stop?
Using ON INTERRUPT is tricky since all it does is poll the interrupt flag BETWEEN PBP statements. What that means is that if you have a PBP statement that takes considerable time the execution of the interrupt handler will be postponed for (up to) that amount of time. In your case, I don't see any issue but lets say that, instead of HSEROUT[SData] in the main loop, you had done HSEROUT["The received byte is: ", SData] then the execution time of that complete statement is longer than the time between two received characters and it would not work properly.

/Henrik.

richard
- 24th January 2021, 11:04
Actually, my program works on a 8MHz basis - it should be more than enough to handle 9600bps serial data, no?

probably not , on interrupt is not suitable for processing async data streams in any useful way without flow control
if there is a "real" foreground task being processed incurring data loss is inevitable


Since you refer to it, why is there a buffer of "2" characters? What's its use?

a simple fifo buffer gives you a little bit more time to process the rx data without loss
@9600 its about 100uS at best


I will always have unknown data length (I'll read sms messages)
to approach a task like that you need to have a good understanding of what the message data will look like
eg is there a start chr? is there and end chr eg /n or /r , does the message have its length encoded in it? is there a chksum or crc
what is the maximum and minimum length

there is zero benefit in having an isr capture one chr at a time then immediately pass it back to the foreground task.
your isr would ideally allow the foreground tasks proceed unhindered by the rx process until a message was
received and ready to process


dt ints is not difficult the forum abounds with examples

flotulopex
- 24th January 2021, 20:11
Thanks Henrik, thanks Richard.

I have some homework to do following your info :)

mpgmike
- 26th January 2021, 04:00
I assume you already found this, but several members compiled a book on using DT Interrupts:

http://www.picbasic.co.uk/forum/forumdisplay.php?f=41

Just in case you haven't seen it.