View previous topic :: View next topic |
Author |
Message |
matjazs
Joined: 08 Nov 2016 Posts: 76
|
Posted: Wed May 24, 2023 9:20 pm Post subject: Problem with serial port |
|
|
I'm having trouble using the serial interface. My program froze occasionally, sometimes after receiving a few bytes (after 50 seconds), sometimes once a day. I've noticed the same situation on my last few projects (it doesn't depend on the type of microcontroller - ATMEGA ATXMEGA). I solved the problems with Watchdog - that is, in this situation, the system reboots. Can someone explain to me what I'm doing wrong. Attached is my latest example where I read HEX data and represent it in a string using a serial buffer.
Code: | ' Serial Reader
$regfile = "m8def.dat"
$crystal = 7372800
$baud = 9600
$hwstack = 50
$swstack = 50
$framesize = 60
'---Serial buffer ----
Config Serialin = Buffered , Bytematch = 2 , Size = 30
'-I2C -----------------------------------------------------------------------------
Config Scl = Portc.5
Config Sda = Portc.4
'----------------------------------------------------------------------------------
Config Twi = 400000 ' i2c speed
I2cinit : Waitms 5
$lib "i2c_twi.lbx"
$lib "glcdSSD1306-I2C_V2.lib" ' override the default lib with this special one
'#if _build < 20784
'Dim ___lcdrow As Byte , ___lcdcol As Byte ' dim these for older compiler versions
'#endif
Dim _contrast As Byte
Const Ssd1306_rotate = 0 ' Display rotation. [connection cable is lower side = 0, upper side = 1] rotate display 180 degrees.
Config Graphlcd = Custom , Cols = 128 , Rows = 64 , Lcdname = "SSD1306"
Cls
'-For SSD1306-128x32------- Program --------------------------------------
I2cstart '128x32
I2cwbyte &H78
I2cwbyte &H00
I2cwbyte &HDA 'Enable COM Left/Right remap
I2cwbyte &H22
I2cstop
Cls
Setfont Andale10x16
Lcdat 1 , 1 , "Serial READER"
Wait 3
Cls
'-Alias--------------------------------------------------------------------------
Config Portb.2 = Output
Rele1 Alias Portb.2
Config Portd.3 = Output
Dere Alias Portd.3
Set Dere
'-Variables----------------------------------------------------------------------
Dim Data0 As Byte
Dim Data1 As Byte
Dim Data2 As Byte
Dim Data3 As Byte
Dim Data4 As Byte
Dim Data5 As Byte
Dim Data6 As Byte
Dim Data7 As Byte
Dim Data8 As Byte
Dim Data9 As Byte
Dim Ser0 As String * 2
Dim Ser1 As String * 2
Dim Ser2 As String * 2
Dim Ser3 As String * 2
Dim Ser4 As String * 2
Dim Ser5 As String * 2
Dim Ser6 As String * 2
Dim Ser7 As String * 2
Dim Ser8 As String * 2
Dim Ser9 As String * 2
Dim Card_nr As String * 17
Dim Card_nrx As String * 17
Dim Card_nry As String * 17
Enable Interrupts
'-Main Loop--------------------------------------------------------------------
Main:
Do
Loop
End
'-Serial Read------------------------------------------------------------------
Serial0charmatch:
Gosub Ser_read
Return
'-Read data--------------------------------------------------------------------
Ser_read:
Inputbin Data0 , Data1 , Data2 , Data3 , Data4 , Data5 , Data6 , Data7
Clear Serialin
Disable Interrupts
'Representation HEX to STRING
Ser0 = Hex(data0)
Ser1 = Hex(data1)
Ser2 = Hex(data2)
Ser3 = Hex(data3)
Ser4 = Hex(data4)
Ser5 = Hex(data5)
Ser6 = Hex(data6)
Ser7 = Hex(data7)
'Ser8 = Hex(data8)
'Ser9 = Hex(data9)
Card_nr = ">" + Ser1
Card_nr = Card_nr + Ser2
Card_nr = Card_nr + Ser3
Card_nr = Card_nr + Ser4
Card_nr = Card_nr + Ser5
Card_nr = Card_nr + Ser6
Card_nr = Card_nr + Ser7
'Card_nr = Card_nr + Ser8
'Card_nr = Card_nr + Ser9
Card_nrx = Card_nr
Card_nry = Left(card_nrx , 13)
Print Card_nry
Delchars Card_nry , ">"
Setfont Andale10x16
Lcdat 1 , 1 , Card_nry
Lcdat 3 , 1 , "> Input "
Card_nr = ""
Ser0 = ""
Ser1 = ""
Ser2 = ""
Ser3 = ""
Ser4 = ""
Ser5 = ""
Ser6 = ""
Ser7 = ""
'Ser8 = ""
'Ser9 = ""
Enable Interrupts
Return
'-GLCD---------------------------------------------------------------
$include "andale10x16.font" |
(BASCOM-AVR version : 2.0.8.5 ) |
|
Back to top |
|
 |
O-Family
Joined: 23 May 2010 Posts: 317 Location: Japan

|
Posted: Thu May 25, 2023 1:41 am Post subject: |
|
|
Are the TWI bus SCL and SDA locked to [L] level when the AVR freezes? |
|
Back to top |
|
 |
MWS
Joined: 22 Aug 2009 Posts: 2196

|
Posted: Thu May 25, 2023 10:40 am Post subject: Re: Problem with serial port |
|
|
matjazs wrote: | I'm having trouble using the serial interface. |
Understandable, looking at the code.
An always possible culprit is TWI-communication, a non-responding device may block the complete program.
Next, the Serial0charmatch is an extension of the RXC-interrupt handler, which does all the serial input buffering.
Interrupt service routines should be always as short as possible, while you stuff tons of code into it.
From entry into ISR extension till the Setfont command it takes a whooping 102000 cycles.
Code: | Serial0charmatch:
Gosub Ser_read
'...
Setfont my12_16 |
After the Setfont command the processor calls LCD-routines, thus enters glcdSSD1306-I2C_V2.lib, which I can not simulate.
Usually display routines eat up lots of cycles.
While within the ISR and its Serial0charmatch-extension, interrupts are disabled by default, which means while all of these cycles, no further interrupts are executed.
Next one: No Disable/Enable Interrupt should be issued within an ISR, beside one knows exactly what one does.
It means that
Code: | Enable Interrupts
Return |
Enable Interrupts in front of Return is at minimum superfluous, as the return opcode of an interrupt (RETI) is what globally enables interrupts again.
RETI as the last opcode of an ISR follows after previously saved processor registers are freed, freeing of registers in turn relieves the hardware stack.
If in contrary user-code re-enable interrupts before ISR exit-code is executed, any pending interrupt will cut in immediately.
Saved registers are not freed until the interrupting code returns, this leads to usage of more hardware stack.
And here we have one more culprit, the command
Code: | Inputbin Data0 , ... |
re-enables interrupts globally after it fetched Data0.
As soon an interrupt is pending, it is executed at the very moment fetching Data0 is finished.
This may happen in the same after Data1, Data2, a.s.o.
A pending interrupt may be as well the serial buffering interrupt RXC, in this case the Serial0charmatch-ISR has the potential to interrupt itself and executes twice with all sorts of interesting results.
Such blows up stack usage and messes up functionality of user code.
You may notice, that Serial0charmatch is executed only in course of a bytematch.
Thus your code will show problems only then, if a bytematch follows shortly after another bytematch.
Which, if you are lucky, will never occur from the sender's side.
On the other hand, who wants to have such a potential booby-trap within code?
My suggestion is to shorten Serial0charmatch as far as possible, only set a flag for pending data and do all the other cycle consuming stuff within the main loop.
If you want to keep the graphic lib within the Serial0charmatch-extension, you need to check the V2-version whether interrupts are enabled therein. |
|
Back to top |
|
 |
albertsm
Joined: 09 Apr 2004 Posts: 5813 Location: Holland

|
Posted: Fri May 26, 2023 9:25 am Post subject: |
|
|
i want to add that this is indeed not how in envisioned the usage. but that aside, when using inputbin or any other serial input routine that expects data, how can you be sure that data exist?
Lets say your sending device send "abc" + chr(2) and you react on this chr(2). when you first start the processor, then the sender, it will work. but what if you do not empty the buffer fast enough and you get this chr(2) but not the other data you expect? then inputbin is waiting till the data arrives.
I would make a simple state machine that set a flag when data is ready. and only after reading it continues with a next read.
then MWS also pointed out a potential problem to me. the buffered serial is intended to be used with waitkey, inputbin, etc. but all in the main code.
the idea is that in order to let the buffers work, you need the global int set. and since a global counter is used for the buffer it need to be protected. this is done by disabling ints and re enabling them when done. when used in the main that works fine, but not inside an ISR since it could cause other ints to run, something you only want when programmed that way.
Since reading the buffer in the ISR should be possible i will change that. But despite that, you best rewrite your code as suggested. _________________ Mark |
|
Back to top |
|
 |
matjazs
Joined: 08 Nov 2016 Posts: 76
|
Posted: Wed Jun 14, 2023 10:43 am Post subject: |
|
|
Thank you very much for the detailed explanation.
In my case I am reading binary data f.e. (HEX format) STX(02H) 0345E5F0 ETX(03H) and converting it to string.
Another example:
When I use serial interface with strings
f.e.
Code: | config serialin()
Main:
Do
If Ischarwaiting() = 1 Then 'Data
Gosub Rec_data
End If
Loop
End |
I noticed a similar problem.
Especially when ischarwaiting is used in combination with a timer interrupt and/or graphic LCD, sometimes the receive buffer does not have the correct data.
In one of my projects it happened that the program froze (stopped for about 20 seconds) about every 24 hours. During this time, the receive buffer does not accept received data. |
|
Back to top |
|
 |
MWS
Joined: 22 Aug 2009 Posts: 2196

|
Posted: Wed Jun 14, 2023 11:53 am Post subject: |
|
|
matjazs wrote: | In my case I am reading binary data f.e. (HEX format) STX(02H) 0345E5F0 ETX(03H) and converting it to string. |
Why you call Serial0charmatch at start of the telegram and not the end?
It makes no sense to use buffered serial and bypass the buffer's functionality.
Code: | Config Serialin = Buffered , Bytematch = 2 , Size = 30 |
You need to trigger on 3, ETX, as at that moment the buffer contains your complete telegram.
The first char you read from Serialin, start of the telegram, is expected to be STX, 2.
Quote: | sometimes the receive buffer does not have the correct data. |
Therefor your code must be hardened against such.
If you correctly get STX as first char, copy the telegram to a temporary buffer, flush Serialin and set a flag that data is ready.
Main code looks for the data-ready flag and does conversion of the temporary buffer into a string.
If the first char is not STX or the telegram exceeds its defined size then simply flush Serialin, return and do not set the flag.
Your main interest has to be to exit Serial0charmatch as fast as possible, because as long you dawdle there, you block other things.
Edit:
After reconsidering your approach to use buffered serial, I'd say that your whole approach is flawed.
You can't make sure, that the payload data between STX and ETX does not contain the value 2 or 3, which is misinterpreted as start or end of transmission.
This cuts in both ways, assume a transmission fault and STX is missed, then your original code triggers on 2 of incoming data, thus eight chars of data won't come in, especially as you block proper serial buffering.
My suggestion as above may fail also, as the ETX value of 3 may exist within the payload.
Do you have control over the sender, i.e. is it possible the sender shifts the values of the payload up?
In this case a payload of 0 3 4 5 E 5 F 0 would become for example 4 7 8 9 18 9 19 4, never colliding with STX/EDX.
If this is not possible, I would suggest to create a state machine around the RXC-interrupt of ATMega8.
You need to remove buffered serial input, as this uses the same interrupt vector. |
|
Back to top |
|
 |
MWS
Joined: 22 Aug 2009 Posts: 2196

|
Posted: Wed Jun 14, 2023 3:44 pm Post subject: |
|
|
State machine:
Code: | $Regfile="m8def.dat"
$crystal = 7372800
$hwstack=40
$swstack=16
$framesize=32
$baud = 9600
Config Base = 0
Const STX = &h02
Const ETX = &h03
Const state_waiting_STX = 0
Const state_rcv_DATA = 1
Const state_waiting_ETX = 2
Dim RX_state As Byte
Dim char_in As Byte
Dim char_cnt As Byte
Dim PL_ready As Byte SAFE
Dim payload As String * 8
Dim payload_ovl(8) As Byte At payload Overlay
On URXC get_data
PL_ready = 0
Enable URXC
Enable Interrupts
Do
If PL_ready = 1 Then
PL_ready = 0
Print payload
End If
Loop
get_data:
If PL_ready = 0 Then
char_in = UDR
Select Case RX_state
Case state_waiting_STX:
If char_in = STX Then
char_cnt = 0
RX_state = state_rcv_DATA
End If
Case state_rcv_DATA:
If char_in < 16 Then
If char_in < 10 Then
payload_ovl(char_cnt) = char_in + "0"
Else
char_in = char_in - 10
payload_ovl(char_cnt) = char_in + "A"
End If
char_cnt = char_cnt + 1
If char_cnt = 8 Then RX_state = state_waiting_ETX
Else
RX_state = state_waiting_STX
End If
Case state_waiting_ETX:
If char_in = ETX Then
PL_ready = 1
RX_state = state_waiting_STX
Else
RX_state = state_waiting_STX
End If
Case Else :
RX_state = state_waiting_STX
End Select
End If
Return |
Still not perfect, as it also may sync on data.
But, it does not block, it simply does not return anything if serial income is faulty.
The very problem however is the wrong protocol for this type of hex-data, STX means "Start of Text", and any value of received ASCII-text is far above and would not collide with values for STX/ETX. |
|
Back to top |
|
 |
MWS
Joined: 22 Aug 2009 Posts: 2196

|
Posted: Wed Jun 14, 2023 6:18 pm Post subject: |
|
|
Circular buffer:
Code: | $Regfile="m8def.dat"
$crystal = 7372800
$hwstack=40
$swstack=16
$framesize=32
$baud = 9600
Config Base = 0
Const STX = &h02
Const ETX = &h03
Dim circ_buffer(10) As Byte
Dim char_in As Byte
Dim CB_ptr As Byte
Dim STX_ptr As Byte
Dim ETX_ptr As Byte
Dim Tfr As Byte
Dim PL_ptr As Byte
Dim PL_data As Byte
Dim PL_ready As Byte SAFE
Dim payload As String * 8
Dim payload_ovl(8) As Byte At payload Overlay
On URXC get_data
PL_ready = 0
CB_ptr = 0
Enable URXC
Enable Interrupts
Do
If PL_ready = 1 Then
PL_ptr = STX_ptr + 1
If PL_ptr > 9 Then PL_ptr = 0
For Tfr = 0 To 7
PL_data = circ_buffer(PL_ptr)
If PL_data < 10 Then
payload_ovl(Tfr) = PL_data + "0"
Else
PL_data = PL_data - 10
payload_ovl(Tfr) = PL_data + "A"
End If
PL_ptr = PL_ptr + 1
If PL_ptr > 9 Then PL_ptr = 0
Next Tfr
PL_ready = 0
Print payload
End If
Loop
get_data:
char_in = UDR
If PL_ready = 0 Then
If CB_ptr > 9 Then CB_ptr = 0
circ_buffer(CB_ptr) = char_in
ETX_ptr = CB_ptr
STX_ptr = CB_ptr +1
If STX_ptr > 9 Then STX_ptr = 0
If circ_buffer(ETX_ptr) = ETX And circ_buffer(STX_ptr) = STX Then PL_ready = 1
CB_ptr = CB_ptr + 1
End If
Return |
Edit:
Moved PL_ready, as it unlocked next processing of data too early.
Code: | PL_ready = 0
Print payload |
Edit II:
Added code to prevent out of array access:
Code: | 'Do
' If PL_ready = 1 Then
' PL_ptr = STX_ptr + 1
If PL_ptr > 9 Then PL_ptr = 0 |
|
|
Back to top |
|
 |
MWS
Joined: 22 Aug 2009 Posts: 2196

|
Posted: Fri Jun 16, 2023 12:34 pm Post subject: |
|
|
Misusing SerialIn as circular buffer:
Code: | $Regfile="m8def.dat"
$crystal = 7372800
$hwstack=64
$swstack=32
$framesize=32
$baud = 9600
Const STX = &h02
Const ETX = &h03
Const buf0_size = 30
Const telegr_size = 10
Const PL_size = telegr_size - 2
Dim STX_ptr As Byte
Dim ETX_ptr As Byte
Dim PL_ptr As Byte
Dim PL_ctr As Byte
Dim PL_data As Byte
Dim PL_ready As Byte SAFE
Dim dummy As Byte
Dim payload As String * 8
Dim payload_ovl(8) As Byte At payload Overlay
Config Serialin = Buffered , Bytematch = ALL , Size = buf0_size
PL_ready = 0
Enable Interrupts
Do
If PL_ready = 1 Then
Print payload
PL_ready = 0
End If
Loop
Serial0bytereceived:
PUSHALL
dummy = Inkey()
If PL_ready = 0 Then
If dummy = ETX Then ' got match for ETX?
STX_ptr = _RS_TAIL_PTR0 + 1 ' yes
If STX_ptr < telegr_size Then STX_ptr = STX_ptr + buf0_size
STX_ptr = STX_ptr - telegr_size
If _RS232INBUF0(STX_ptr) = STX Then ' got match for both STX/ETX
PL_ptr = STX_ptr + 1
If PL_ptr > buf0_size Then PL_ptr = 1
For PL_ctr = 1 to PL_size
PL_data = _RS232INBUF0(PL_ptr)
If PL_data < 10 Then ' conversion into ASCII hex and store to string
payload_ovl(PL_ctr) = PL_data + "0"
Else
PL_data = PL_data - 10
payload_ovl(PL_ctr) = PL_data + "A"
End If
PL_ptr = PL_ptr + 1
If PL_ptr > buf0_size Then PL_ptr = 1
Next PL_ctr
PL_ready = 1
End If
End If
End If
POPALL
Return |
|
|
Back to top |
|
 |
matjazs
Joined: 08 Nov 2016 Posts: 76
|
Posted: Fri Jun 16, 2023 12:52 pm Post subject: |
|
|
MWS thank you very much for these examples.
I will check this. |
|
Back to top |
|
 |
MWS
Joined: 22 Aug 2009 Posts: 2196

|
Posted: Fri Jun 16, 2023 1:06 pm Post subject: |
|
|
matjazs wrote: | MWS thank you very much for these examples. |
You're welcome, I had fun doing them.
If something is hard to understand, feel free to ask. |
|
Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum You cannot attach files in this forum You cannot download files in this forum
|
|