I'm working with an ATmega168p and compiling with avr-gcc.
Specifically, I have an RS485 slave that receives bytes via UART and writes them to a buffer in an ISR. If an end character is received, a flag is set in the ISR. In my main loop this flag is checked and the input buffer is processed if necessary. However, there is the problem that some time can pass between the arrival of the end byte and the time when the handler in the main loop processes the input buffer, because of the other "stuff". This results in a latency which can be up to several milliseconds, because e.g. sensors are read in every n-th iterations.
ISR(UART_RX_vect) {
write_byte_to_buffer();
if (byte==endbyte) // return to <HERE>
}
void main(){
init();
for(;;){
// <HERE> I want my program to continue after the ISR received an end byte
handle_buffer();
do_stuff(); // "stuff" may take a while
}
I want to get rid of this latency, as it is the bottleneck for the higher-level system.
I would like that after the ISR received the end byte, the program returns to the beginning of my main loop, where the input buffer would be processed immediately. I could of course process the input buffer directly in the ISR, but I am aware that this is not a good practice. This would also overwrite packets when the ISR gets invoked while processing a packet.
So, is there a way to overwrite an ISR's return address? Does C include such a feature, maybe something like goto
?
Or am I completely on the wrong track?
Edit: Below is a reduced version of my code which also causes the described latency.
#define F_CPU 8000000UL
#define BAUD 38400
#define BUFFER_LENGTH 64
#include <util/setbaud.h>
#include <avr/interrupt.h>
#include <stdbool.h>
volatile char input_buffer[BUFFER_LENGTH + 1] = "";
volatile uint8_t input_pointer = 0;
volatile bool packet_started=false;
volatile bool packet_available = false;
ISR (USART_RX_vect) {
unsigned char nextChar;
nextChar = UDR0;
if (nextChar=='<') {
input_pointer=0;
packet_started=true;
}
else if (nextChar=='>' && packet_started) {
packet_started=false;
packet_available=true;
}
else {
if (input_pointer>=BUFFER_LENGTH) {
input_pointer=0;
packet_started=false;
packet_available=false;
}
else {
input_buffer[input_pointer++]=nextChar;
}
}
}
bool ADC_handler () {
ADCSRA = (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
ADCSRA |= (1<<ADSC);
while (ADCSRA & (1<<ADSC)); // this loop blocks and causes latency
// assigning conversion result to a variable (not shown)
}
void ADC_init(void) {
ADMUX = (1<<REFS1)|(1<<REFS0)|(1<<MUX3);
ADCSRA = (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
}
void process_buffer() {
// this function does something with the buffer
// but it takes "no" time and is not causing latency
return;
}
void UART_handler () {
if (packet_available) process_buffer();
}
void UART_init (void) {
UBRR0H = UBRRH_VALUE;
UBRR0L = UBRRL_VALUE;
UCSR0B |= (1<<RXCIE0)|(1<<RXEN0)|(1<<TXEN0);
UCSR0C |= (1<<UCSZ01)|(1<<UCSZ00);
}
int main(void){
UART_init();
ADC_init();
// initializing some other things
sei();
for(;;){
UART_handler();
ADC_handler();
// other handlers like the ADC_handler follow
}
return 0;
}
I'm aware that the latency is due to blocking code, in this case the while loop in the ADC_handler()
that waits for the conversion to finish. I could check for packet_available
in the ADC handler and make this funtion return if the flag is set or I could even retrieve the conversion result with an ADC interrupt. That's all nice because I'm the one who implements the ADC_handler()
. But if I wanted to use third party libraries (e.g. sensor libraries provided by manufacturers) I would depend on how those libraries are implemented. So what I'm looking for is a way to handle the problem "on my side"/in the UART implementation itself.
setjmp/longjmp
does. You'll have to check the documentation for your compiler to see if it's supported for interrupt handlers, though. – Nate Eldredgedo_stuff
function for the special condition set by the ISR, and handle it then and there (by calling a function of course). – Some programmer dudesetjmp/longjmp
looks like what I was looking for, I'll check my compiler! – Sim Sonsetjmp/longjmp
is for signal handlers in POSIX application programs. This will not work for HW ISRs. You may need to completely restructure your code so that most of the processing is done in the ISR as the bytes arrive rather than waiting until you have some end-of-packet sentinel byte [or not]. Maybe you need to communicate the bytes via a ring queue [or not]. Or, ... You may need to tell your H/W engineer that the processor is not fast enough to handle the load and the board needs a redesign with a faster model processor ... – Craig Estey