UART RX buffer clear/dispose

Go To Last Post
9 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi all, \o.

Consider an AVR based modbus rtu chain. Master always call them by a 8 byte query each time. Therefore rx buffer size was set to 8 bytes in codewizard. But in response condition a slave might send 17, 33 or 69 bytes. Odd numbers cause rx_buffer[n] variable (made by codewizard) several times and lock to take further bytes therefore wrong-reading.

 

How can I dispose/clear the step of rx_buffer[n] variable and the value it contains in the RIGHT way?

 

My approach was to take first byte (slave address) {without comparing CRC checksums at the end of the stream} to check if the address is not mine, terminate the UART interrupt register:

...INSIDE OF CODEWIZARD UART RX INTERRUPT...
//TEMPORARY DISABLING USART FOR REDUCING INTERFERENCE
UCSR1B=(1<<RXCIE1) | (0<<TXCIE1) | (0<<UDRIE1) | (1<<RXEN1) | (1<<TXEN1) | (0<<UCSZ12) | (0<<RXB81) | (0<<TXB81);
// Clock value: 172.800 kHz
// Timer Period: 0.37926 s
// Timer1 Overflow Interrupt: On
TCCR1B=(0<<ICNC1) | (0<<ICES1) | (0<<WGM13) | (0<<WGM12) | (0<<CS12) | (1<<CS11) | (1<<CS10);
            

Then turn on a timer and after calculated milliseconds re-enable corresponding UART interrupt register:

//RE-ENABLE USART SETTINGS
interrupt [TIM1_OVF] void timer1_ovf_isr(void)
{
//ENABLE USART
UCSR1B=(0<<RXCIE1) | (0<<TXCIE1) | (0<<UDRIE1) | (1<<RXEN1) | (1<<TXEN1) | (0<<UCSZ12) | (0<<RXB81) | (0<<TXB81);
//DISABLE TIMER AGAIN
TCCR1A=(0<<COM1A1) | (0<<COM1A0) | (0<<COM1B1) | (0<<COM1B0) | (0<<COM1C1) | (0<<COM1C0) | (0<<WGM11) | (0<<WGM10);
TCCR1B=(0<<ICNC1) | (0<<ICES1) | (0<<WGM13) | (0<<WGM12) | (0<<CS12) | (0<<CS11) | (0<<CS10);
}

Long story short: Hang up the phone, then pick again when other communications finished...

I did not tested it yet, but it may not work due to checking-first-address-byte already occupied first buffer size and data collapses again. : (

Any clues?

 

Codevision 3.12 | Windows 11 | Atmega128AU

- Dear moderator, I didn't find a specific codevision dedicated forum so feel free to migrate the topic. -

Last Edited: Thu. Aug 4, 2022 - 02:36 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Aren't you keeping track of how many bytes received?  It's up to you to decide what to do if too many are received:

  • Reject them all, as if nothing arrived
  • Keep only the first ones and ignore (don't save) all the extras
  • Keep only the final n ones (thus overwriting the excess originals) 
  • Use a larger buffer, so extras become an allowed portion of the next round

 

Each option has various tradeoffs in performance, so you have to decide what is best for your needs.

 

Why is the slave sending the wrong number of bytes in the first place??  Hopefully that is only during some failure or glitch.  That might be your first step of investigation and planning.

 

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What is wrong with receiving the entire packet, performing a few checks (address, checksum etc.) then if you're not the addressee dump the whole thing ?

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

avrcandies wrote:

Why is the slave sending the wrong number of bytes in the first place?

It doesn't. They follow as instructions.

By the way those bullet items are seem very important functioning-gears the question is how can I do at least one of them??? I'm a visual studio pro, not microcontrollers. But the language is the same, so it is not a big deal...

 

N.Winterbottom wrote:

What is wrong with receiving the entire packet, performing a few checks (address, checksum etc.) then if you're not the addressee dump the whole thing ?

The problem is all slave nodes are listeners most of the time, therefore they listen to long data stream (for instance 17, 33 or maybe 69 bytes) in an 8 byte rx_buffer size which is not a divisible/partible number and next bytes will collapse while they're correct.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Pourkascheff wrote:
they listen to long data stream (for instance 17, 33 or maybe 69 bytes) in an 8 byte rx_buffer size which is not a divisible/partible number and next bytes will collapse while they're correct.

 

Then don't pull every character out of the ISR buffer. Your code instead should only read the correct number of bytes that constitute the Modbus Response Frame. Do not read/discard any bytes that remain over the frame length because they could be part of the next Request Frame.

 

Your Frame Parser could (or should) be implemented as a State Machine which would make the code fairly easy to write.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

N.Winterbottom wrote:
Your code instead should only read the correct number of bytes that constitute the Modbus Response Frame. Do not read/discard any bytes that remain over the frame length because they could be part of the next Request Frame.

Now, there are different flavours of Modbus, but OP did mention RTU in the opening post.

 

Also, my Modbus is a bit rusty, but the end of a frame has to do with quiet time on the bus; e.g.

It is the use of this silent period for detecting the end of a message frame that makes the use of Modbus RTU ...

 

Now, I've done it a few different ways, using bit times or byte times or a fixed interval, but I think that the standard is a fixed time about 2ms.

 

I've almost always done Modbus by letting the "engine" collect the whole frame, wait for the quiet time, and then signal "buffer ready to process".  This does in fact make many things in processing easier, as a circular buffer isn't needed and the first few bytes are in the same position.  It makes the checksum engine a little easier as well.  And no need to "clear the buffer"; always just start at the beginning.

 

Note the "almost" in the preceding paragraph.  If, say, one was expecting a particular response to a query one could "peek" at the command code and peek at the location of the answer.  Frame verification still needs to be done.

 

But IME the most important reason to "peek" is in a small micro implementation where there is no room in SRAM for a full max Modbus RTU message frame of a bit over 256 bytes.  In such a generic system [remember that it is a multi-drop protocol and slaves could be of any flavour] it can be useful to peek at the message length and then fork to an alternative receiver that discards everything until the quiet time, and then resets the buffer index to be ready for the next message.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:

Re the Silence Detection

Now, I've done it a few different ways, using bit times or byte times or a fixed interval, but I think that the standard is a fixed time about 2ms.

OP says he is using CodeVision and the CodeWizard UART code. This library uses a circular buffer (OP configured to 8-bytes) and limits somewhat the ability to perform the silence detection.

 

I'll guess OP is doing Request Frame detection by:

if (rx_counter == 8) {
    addr = getchar();
    etc. etc.
}

This of course fails for variable sized Response Frames from other slaves. Which is why I'm suggesting a State Machine to receive/discard the entire frame. An alternative is to write an ISR which itself performs Modbus Frame Detection and replaces the CodeWizard ISR..

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

N.Winterbottom wrote:
OP says he is using CodeVision and the CodeWizard UART code. This library uses a circular buffer (OP configured to 8-bytes) and limits somewhat the ability to perform the silence detection.

I also was a dyed-in-the-wool CV person.

 

But (IME/IMO) using a circular buffer for Modbus RTU isn't "the way" to do it, as I outlined/hinted above.

 

That said, while more difficult (e.g. have to watch wrapping every time indexing into the buffer [and the more I think about it one would end up extracting all the fields anyway]) and more burden on having enough resources, one could certainly index.

 

Now, on to silence detection -- in [almost] all of my USART AVR apps that do poll/response whether master or slave side, I always have timeout detection anyway.  Otherwise, what do you do when your protocol misses a character and the message never "completes"?  It is a lot easier to start a soft timer of some sort at the start of the transaction.  Depending on the protocol, you either set back the timeout counter each character received, or expect the full message to be gotten within a certain time from the start.  (that second option works well for shortish fixed length)

 

When a "transaction" is started but not completed within a reasonable time then it failed.  Buffer flush, clean up everything, take action.

 

IF YOU >>DON'T<< DO SOMETHING SIMILAR, HOW DO YOUR SYSTEMS GRACEFULLY FAIL WHEN A SINGLE BYTE IS LOST?!?

 

I still vote for fixed-buffer for Modbus RTU (consider the complexity of handling the two-byte checksum straddling the wrap, versus indexing it directly, and then extrapolate to all the Modbus fields) but it can be done with circular buffer.

 

As OP seemed to have a problem with odd message lengths, I'd guess that there is indeed a battle with what I mentioned.  Yes, we could work through OP's circular buffer case with each problem one-by-one.  But IMO/IME it is a lot easier to do fixed buffer.

 

It isn't entirely clear from the OP whether a master or slave is being built.

 

If a master, it is quite a bit easier.  As mentioned, the queries are probably fixed length and only one or a few transactions, and the same response is expected.

 

But a generic slave to work on a network with other arbitrary master(s) and slave(s) requires much more careful attention.  I've indirectly mentioned several above.  The best I did to make a minimal yet robust Modbus RTU slave is half the code space of a Mega8.  For a robust slave implementation, it is NOT an afternoon of time starting with the Wizard.  When experienced, you might be able to do that with a single-transaction master and take the shortcuts.  For a robust slave from scratch, a couple weeks.

 

[edit] >>IF<< this is a slave with only a single expected poll, one could take shortcuts and just ignore any other transaction and not respond.  That violates the spec, but hey ho.  You still need to make your state machine and get each byte as received, parse your fields, and then discard anything else.  That is pretty much the "peeking" I mentioned earlier. 

 

>> You still need to make a robust checksum routine for your responses.  Easier IME with a fixed buffer.

>> You still (IMO) need more than an 8-byte buffer for an 8-byte message string.

>> And yes, you can if your really care to use a smaller transmit buffer than your message size if you want to stall waiting for buffer space.  But I don't want my app to stall in one place for milliseconds.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

Last Edited: Thu. Aug 4, 2022 - 03:25 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Master always call them by a 8 byte query each time.

Instead of turning on/off uart, you could use the rx buffer index to indicate state along with a timer to take care of telling you when between frame idle time has passed. May not be better/worse than turning off uart rx, but it seems simpler to just leave the uart rx running. The rx buffer index will always indicate where you are- if at start of message (0), or in a message addressed to 'us', or a message that can be ignored (>=8). Since the other responses also send their address, they are seen but also ignored since not our address. If things go wrong, the timer will always get us back to square 1 with the reset of the the rx index.

 

isr(uart rx){
    uint8_t udata = UDR0; //always get data

    //(always) setup timer to timeout in 28bits
    //19,200baud = ~1.45ms

 

    //check if we are watching this frame
    //or if its the first byte and is our address

    //will need idle time before rx_idx is reset
    if( rx_idx >= 8 || (rx_idx == 0 && udata != OUR_ADDRESS) ){
        rx_idx = 8; //make next byte fail frame test
        return; //not our frame
        }

 

    //is our frame. store the data
    rx_buffer[rx_idx++] = udata;
    if( rx_idx < 8 ) return; //frame not complete

 

    //now checkout frame contents and if valid
    //do something

 

    //timer isr will indicate when idle time is reached (rx_idx == 0)
    //if we need to respond via tx (wait for idle before tx)

}

isr(timer){
    rx_idx = 0; //reset rx_buffer index
    //turn off this timer irq, uart rx isr will enable again
}

 

edit- probably no real need for the timer to have an isr, as you can simply check the ovf flag instead-

 

isr(uart rx){
    uint8_t udata = UDR0; //always get data

    if( timer overflow flag ) rx_idx = 0;

    //(always) setup timer to timeout in 28bits
    //19,200baud = ~1.45ms

    ...

    //timer will indicate when idle time is reached (timer overflow flag set)
    //if we need to respond via tx (wait for timer overflow flag set before tx)

Last Edited: Thu. Aug 4, 2022 - 10:44 PM