[AVR] UART RX-flag triggers one to many times. - Page 1
Forum section: electronics / general

author: Sudo_apt-get_install_yum
author_rank: Regular Contributor
timestamp: May 20, 2022, 08:51:36 am
content: Hello everyone!I’ve written a small routine that is contained in the RX-interrupt ISR. The gist of the code is that it is a blocking loop that fetches all transmitted bytes, not an optimal thing in an ISR but communication is priority, and I haven’t figured out any other "stable and reliable" solution. The biggest issue is that the received packet contains differing amounts of bytes which is why I loop inside the ISR to check if the transmission is stopped. The rest of the code relies upon interrupts which is why I don’t want to fill the buffer, exit ISR and jump back in when the next byte comes.The issue is that some of the PCB's I’ve made register one to many transmitted bytes while other PCBs from the same batch register the exact right amount. The code is the same for both.Example:Send 8-Bytes to the AVR.Board 1: Frame_Size = 8.Board 2: Frame_Size = 9.I’m posting this question since I want to be sure how many bytes the code should register, maybe I’ve written the code incorrectly or interpreted the AVR flags in an incorrect manor, I’m not sure.Below is the ADC ISR code.Any input is appreciated.Code:ISR(USART_RX_vect){// ISR for UART receive// RX_Frame = Buffer for receiver.uint16_t Time_Out = 50; //Time out = transmission time of 5 Bytes.Frame_Size = 0;TX_Flag = 0;for(uint8_t i = 0; i < Time_Out; i++){_delay_us(RX_BYTE_DELAY);// Delay = 1/10 of Byte transmission time.if(UCSR0A & (1 << RXC0)){// If receive buffer has unread data.RX_Frame[Frame_Size++] = UDR0;// Fill buffer.i = 0;// Reset "timer".}}TX_Flag = 1;return;}

author: PlainName
author_rank: Super Contributor
timestamp: May 20, 2022, 01:15:08 pm
content: Not sure of your actual issue, but if I might make a couple of observations...AFAICT, you loop reading in data until there hasn't been any data for the time taken to send 5 bytes. If you had, say, noise on the line then you could continue reading past the end of real data, and since there is no check for how big your receive buffer is, you run the risk of a buffer overflow. You should at least check whether the buffer is about to be burst and stop if so.This is the receive interrupt and receive buffer, etc. But you talk about transmission (and have a TX_flag that appears to do nothing). It's a bit confusing! So from what I can make out your problem is that something (test PC or something) sends 8 bytes and some boards see those 8 bytes while some boards see 9 bytes. OK?Have you verified the received data to determine that a) the 8-byte ones see the correct 8 bytes, and b) the 9-byte ones see 8 correct bytes and whether the extra byte is at the start or end (or middle)? That kind of info can quickly lead to the cause of the problem.Do you ensure the receive buffer is empty before enabling the interrupt? That is, might the ISR be triggered by existing data before the real data turns up?Do you check the UART status flags at any point to see if there are errors (frame error, overrun, etc)?You talk about other interrupt-driven processes ongoing. Could any of these override the USART interrupt? If so, do any of them touch the USART registers?

author: Sauli
author_rank: Contributor
timestamp: May 20, 2022, 02:43:04 pm
content: I don't know where the 8/9 byte difference comes from, but in my opinion you are spending an awful lot of time in the interrupt, blocking other interrupts.Are you certain that there is at least 5 character times idle period between blocks of data? I would use a timer here to detect end of data block. In the UART receive interrupt just read all received characters to the buffer as fast as possible. There might be two, but more likely just one. After that (re)start a hardware timer with that 5 character time to timeout before exiting the interrupt. When the timer times out, you know that the whole block has been received.

author: Sudo_apt-get_install_yum
author_rank: Regular Contributor
timestamp: May 20, 2022, 02:55:33 pm
content: To clarify the issue.AFAICT, you loop reading in data until there hasn't been any data for the time taken to send 5 bytes.Definitively an issue and will add a check for that. I have lots of extra space in the processor, so the buffer is roughly 3 times larger than the largest allowed packet.it's a bit confusing! So, from what I can make out your problem is that something (test PC or something) sends 8 bytes and some boards see those 8 bytes while some boards see 9 bytes. OK?Have you verified the received data to determine that a) the 8-byte ones see the correct 8 bytes, and b) the 9-byte ones see 8 correct bytes and whether the extra byte is at the start or end (or middle)? That kind of info can quickly lead to the cause of the problem.Yes, that is exactly the issue. The AVR is connected to a PC over RS485, the AVR uses a RS232 to RS485 transceiver and the PC uses a RS485 to RS232 to USB-COM.I can control the exact amount of data being sent from the PC and can confirm it with a logic analyzer.The extra byte is always at the end of the transmission and always contains zeros.Do you ensure the receive buffer is empty before enabling the interrupt?The buffer index variable is always reset when entering the ISR, the entire packet is received within the ISR.That is, might the ISR be triggered by existing data before the real data turns up?Not sure, the receive buffer is only written to in the RX ISR and is separate from the transmission buffer.I have verified this by using "Data breakpoints" to see if the buffer is written to when not intended.The RX buffer is also parsed in a separate function separate from the ISR to ensure that the packet is good.Do you check the UART status flags at any point to see if there are errors (frame error, overrun, etc)?I have previously had error checking where I would abort the interrupt if any of the error flags were set. The Frame Error (FE) flag would be set almost all the time anywhere in the packet on some boards even though all bytes were received without any problems. This was a while ago and I completely forgot about it until now, might be reflections in the RS485 line? The signal looks relatively good on the scope which is why I’m asking about the software.You talk about other interrupt-driven processes ongoing. Could any of these override the USART interrupt? If so, do any of them touch the USART registers?Not complete sure what you mean, the AVR can que up to two interrupts if I remember correctly and all other interrupts are executed incredibly quick so I doubt that they would effect the RX interrupt. I also disable some interrupts when entering the RX ISR and reset other asynchronous counters to avoid any ISR conflict.Thanks for the input!

author: Sudo_apt-get_install_yum
author_rank: Regular Contributor
timestamp: May 20, 2022, 03:06:36 pm
content: Quote from: Sauli on May 20, 2022, 02:43:04 pmThe 8/9 bytes was just an example but I see why it can be confusing. In reality it can be any number of bytes between 6-24 bytes.I was completely out of timers/counters when I wrote the code. The AVR only has 2 8-bit T/C's and 1 16-bit, I recently changed some hardware and rewrote the code and freed up one 8-bit counter so moving the blocking busy wait loop to T/C based would be a better solution. Will definitely implement it.The 5-character idle period is slightly arbitrary, it stems from roughly 3 character idle/variation in data transmission, I simply added 2 extra bytes of wait to be sure everything would be captured. It’s not a good solution so moving it to an asynchronous timer would be better!

author: Sudo_apt-get_install_yum
author_rank: Regular Contributor
timestamp: May 20, 2022, 03:40:00 pm
content: Quote from: Sauli on May 20, 2022, 02:43:04 pmAdjusted the RX ISR to utilize Timer Counter 2 for detection of the end of transmission, seems to work fine. The code is below.This still doesn’t fix the issue with receiving one extra byte but will probably use the this code henceforth.ISRCode:ISR(USART_RX_vect){// ISR for UART receiveTCCR2B = (1 << CS22) | (1 << CS20);// Enable TC2 and set CLK/128TCNT2 = 0;// Reset the transmission complete check.RX_Frame[Frame_Size++] = UDR0;// Fill buffer.return;}ISR(TIMER2_COMPA_vect){// Check for UART RX end of transmission.TCCR2B = 0;//Disable Timer 2 counter.TX_Flag = 1; //Full packet received.return;}ConfigurationCode://Timer 2 - CTC (Used for byte delay in the USART RX ISR)TCCR2A = (1 << WGM21);// CTCTIMSK2 = (1 << OCIE2A);// Enable interrupts.OCR2A = 0xE5;

author: PlainName
author_rank: Super Contributor
timestamp: May 20, 2022, 05:18:53 pm
content: Quote from: Sudo_apt-get_install_yum on May 20, 2022, 02:55:33 pmI meant the USART buffer. I think there is a 2-byte hardware buffer (actually two registers acting as a circular buffer), so your ISR could signal more than one character available as soon as you enable it.QuoteI think this could be worth eliminating. Framing could signify the wrong number of character bits, and if the data isn't back-to-back then the extra stop bits would let it recover before corrupting the next byte. Data overrun is unlikely here but worth checking.Are the bad boards always bad, and the good boards always good?QuoteI was thinking that another interrupt could interrupt the Rx ISR, but I think the AVR doesn't allow that kind of thing.One thing that just struck me (and should have before) is that this is RS485 so half-duplex and shared data line. What's the TX_Flag thing in the ISR - it seems to serve no purpose so perhaps it is signalling something outside of the ISR? The external circuitry could be important also.

author: Sauli
author_rank: Contributor
timestamp: May 21, 2022, 05:07:17 pm
content: Framing errors make me think that the AVR and PC baudrates don't quite match.Since you have a logic analyzer, what does it tell when measuring at the AVR end?

author: Sudo_apt-get_install_yum
author_rank: Regular Contributor
timestamp: May 25, 2022, 09:25:23 am
content: Quote from: Sauli on May 21, 2022, 05:07:17 pmI’m sure that the baud rates match, I’m using a crystal that is evenly divided for the AVR USART presale and the clocks seem fine.I’ve been a bit tight on time so have only performed a handful of tests on one of the boards that records one to many bytes.I sent the data with the typical program I use, used a "port sniffer" to capture what was sent by the PC and used the logic analyzer to capture what the microcontroller sees on its input pins.Both captures are from the same transmission. You can see a "FE" frame error in the end of each transmission.I’ve attached the logic analyzer capture as native file for DSView/Sigrok and an image with all information.The native Sigrok file extension is ".dsl" which is not allowed to be attached. I’ve changed it to ".txt", change it back to ".dsl" if you want to open it in the logic analyzer software.

author: jeremy
author_rank: Super Contributor
timestamp: May 25, 2022, 11:03:07 am
content: Quote from: Sudo_apt-get_install_yum on May 25, 2022, 09:25:23 amI would still check the UART output clock manually to make sure. All sorts of weird configuration errors can happen and be hard to diagnose.Also, I would try to look at the waveform with an oscilloscope if possible, just to make sure there isn't any ringing or slow edges or something like that. The logic analyser can make your signal look way cleaner than it actually is.Looking at the logic analyser traces, your PC transmission appears to be inverted. UART should be high when idle.Edit: or maybe this is just a quirk of your RS485 transmitter and the signal isn't inverted, it just doesn't go high in idle for some reason.

author: Psi
author_rank: Super Contributor
timestamp: May 25, 2022, 11:32:33 am
content: I assume RX_Frame[], Frame_Size and TX_Flag are all declared volatile?

author: Sudo_apt-get_install_yum
author_rank: Regular Contributor
timestamp: May 25, 2022, 03:04:42 pm
content: Quote from: jeremy on May 25, 2022, 11:03:07 amMeasured the signals with the scope and see the same thing as with the logic analyzer. Most of the transmission seems good but there is a "blip" at the end of each transmission that the RS485 transceiver seems to pick up.I believe that I’ve sort of solved the issue by simply not incrementing the buffer counter when a frame error is detected by the AVR, this is however not an ideal solution…Below are screenshots from the scope.Complete transmissionFrom PCFrom AVREnd of transmissionEnd of transmission zoomedEnd of transmission even more zoomedQuote from: Psi on May 25, 2022, 11:32:33 amYour assumptions are correct.

author: jeremy
author_rank: Super Contributor
timestamp: May 25, 2022, 11:30:20 pm
content: It seems like your issue is that this blip is being generated by the USB to RS485 converter (or RS485 transceiver), and the AVR is detecting this as a start bit. However, you don’t get the subsequent stop bit and so end up with a frame error. The pulse is probably short enough that it is only sometimes caught by the oversampling in the UART peripheral as valid, perhaps even to the point where some devices catch it and some don’t due to manufacturing tolerances or drift.You could solve the idle state by sending from the PC with an inverted UART signal rather than this strange idle low but still not inverted signal, and then use a NOT gate to flip it back or use the invert bits if your device has them:https://www.avrfreaks.net/forum/inverted-uartPerhaps try to add pull downs to the RS485 bus in the differential lines to see if you are just catching residual charge on the bus when going from TX to RX. RS485 isn’t supposed to use pull downs, but just for testing it should be fine.See alsohttps://www.avrfreaks.net/forum/rs485-framing-error-when-toggling-re-pinThere are tons of posts on avrfreaks about this problem.

author: Sauli
author_rank: Contributor
timestamp: May 26, 2022, 12:35:44 pm
content: QuoteThis somewhat contradicts with the oscilloscope trace. You obviously have already (correctly) received the 8 bytes frame and have responded with a 23 bytes frame, and only then does the blip occur.This should result in the extra zero byte being the first byte of the next frame. Unless you reset theFrame_Sizeindex only some time after sending the response. If this is the case you could just ignore the extra received byte.