Forum - MCS Electronics

 

FAQFAQ SearchSearch RegisterRegister Log inLog in

Problem with serial port

 
Post new topic   Reply to topic    www.mcselec.com Forum Index -> BASCOM-AVR
View previous topic :: View next topic  
Author Message
matjazs

Bascom Member



Joined: 08 Nov 2016
Posts: 76

PostPosted: Wed May 24, 2023 9:20 pm    Post subject: Problem with serial port Reply with quote

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
View user's profile
O-Family

Bascom Expert



Joined: 23 May 2010
Posts: 317
Location: Japan

japan.gif
PostPosted: Thu May 25, 2023 1:41 am    Post subject: Reply with quote

Are the TWI bus SCL and SDA locked to [L] level when the AVR freezes?
Back to top
View user's profile Visit poster's website
MWS

Bascom Member



Joined: 22 Aug 2009
Posts: 2196

blank.gif
PostPosted: Thu May 25, 2023 10:40 am    Post subject: Re: Problem with serial port Reply with quote

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
View user's profile
albertsm

Administrator



Joined: 09 Apr 2004
Posts: 5813
Location: Holland

blank.gif
PostPosted: Fri May 26, 2023 9:25 am    Post subject: Reply with quote

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
View user's profile Visit poster's website
matjazs

Bascom Member



Joined: 08 Nov 2016
Posts: 76

PostPosted: Wed Jun 14, 2023 10:43 am    Post subject: Reply with quote

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
View user's profile
MWS

Bascom Member



Joined: 22 Aug 2009
Posts: 2196

blank.gif
PostPosted: Wed Jun 14, 2023 11:53 am    Post subject: Reply with quote

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
View user's profile
MWS

Bascom Member



Joined: 22 Aug 2009
Posts: 2196

blank.gif
PostPosted: Wed Jun 14, 2023 3:44 pm    Post subject: Reply with quote

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
View user's profile
MWS

Bascom Member



Joined: 22 Aug 2009
Posts: 2196

blank.gif
PostPosted: Wed Jun 14, 2023 6:18 pm    Post subject: Reply with quote

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
View user's profile
MWS

Bascom Member



Joined: 22 Aug 2009
Posts: 2196

blank.gif
PostPosted: Fri Jun 16, 2023 12:34 pm    Post subject: Reply with quote

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
View user's profile
matjazs

Bascom Member



Joined: 08 Nov 2016
Posts: 76

PostPosted: Fri Jun 16, 2023 12:52 pm    Post subject: Reply with quote

MWS thank you very much for these examples.

I will check this.
Back to top
View user's profile
MWS

Bascom Member



Joined: 22 Aug 2009
Posts: 2196

blank.gif
PostPosted: Fri Jun 16, 2023 1:06 pm    Post subject: Reply with quote

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
View user's profile
Display posts from previous:   
Post new topic   Reply to topic    www.mcselec.com Forum Index -> BASCOM-AVR All times are GMT + 1 Hour
Page 1 of 1

 
Jump to:  
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