I'm new to Embedded programming and multi-threading and I'm trying to understand how Interrupt handlers work in different contexts/scenarios. For the current question, I just want to know how a interrupt handler would work in the following scenario.
We have a data stream coming from a RS232 interface that is processed by some microcontroller. An interrupt handler(of void type) has a read() function which reads the incoming data bytes. If a character is detected then the interrupt handler invokes a function called detectString() which returns TRUE if the string matches the reference string which is "ON". If detectString() returns boolean TRUE it invokes a function called LED_ON() which should turn on an LED for 1 minute. If it returns false it should turn off the LED. Lets say the microcontroller has a clock frequency of 20MHz and an addition operation taken 5 clock cycles.
My questions are as follows
How do we approach this problem with an FSM?
The RS232 interface keeps transmitting data even after the LED is turned on. So am I correct in assuming that the interrupt handler should work with a one thread and the functions that it invokes should work from a different threads?
How would a skeletal program implementing this FSM look like? (a C pseudocode might really help to understand the backbone of the design)
If you are doing this in an interrupt handler, why would you need different threads? It shouldn't matter what else you're doing, as long as interrupts are enabled.
As for FSM, I wouldn't call a "detect_string". RS232 is going to give you one character at a time. It's possible your UART interrupts you only when you've received more than one, but there's usually a time component as well so it would be unwise to count on that. Make your FSM take one input character at a time. Your states would be something like:
=> new state = [Init] (turn LED off if on)
Init: (Get 'O') => new state = [GotO]
Init: (Get anything else) => new state = [Init]
Init: (Timer expires) => who cares? new state = [Init]
GotO: (Get 'N') => new state = [GotON] (turn on LED, set timer)
GotO: (Get anything else) => new state = [Init]
GotO: (Timer expires) => who cares? new state = [GotO]
GotON: (Get anything) => who cares? new state = [GotON]
GotON: (Timer expires) => turn LED off, new state = [Init]
Obviously lots of tinkering you could do with details, but that's the general idea.
A preemptive kernel will usually provide the ability for an interrupt to set an event that a higher priority thread is pending on.
As for the interrupts, one way of implementing something like a state machine is to use nested pointers to function, similar to an asynchronous callback, but with optional nesting: For example:
typedef void (*PFUN)(void);
/* ... */
PFUN pFunInt = UnexpectedInt; /* ptr to function for interrupt */
PFUN pFunIntSeqDone;
/* ... */
void DoSeq(void)
{
pFunIntSeqDone = IntSeqDone;
pFunInt = IntStep0;
/* enable interrupt, start I/O */
}
void IntStep0(void)
{
pFunInt = IntStep1;
/* handle interrupt */
}
void IntStep1(void)
{
pFunInt = IntStep2;
/* handle interrupt */
}
void IntStep2(void)
{
/* done with sequence, disable interrupt */
pFunInt = UnexpectedInt;
pFunIntSeqDone(); /* call end action handler */
}
void IntSeqDone(void)
{
/* interrupt sequence done handling code */
/* set event for pending thread */
}
void UnexpectedInt(void)
{
/* ... error handling code */
}
Related
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.
Don't try to use setjmp()/longjmp() to re-enter a main-level function from an ISR. This calls for disaster, because the ISR is never finished correctly. You might like to use assembly to work around, but this is really fragile. I'm not sure that this works at all on AVRs.
Since your baudrate is 38400, one byte needs at least some 250µs to transfer. Assumed that your message has a minimum of 4 bytes, the time to transfer a message is at least 1ms.
There are multiple possible solutions; your question might be closed because they are opinion-based...
However, here are some ideas:
Time-sliced main tasks
Since a message can arrive only once per millisecond or less, your application don't need to be much faster than that.
Divide your main tasks into separated steps, each running faster than 1 ms. You might like to use a state machine, for example to allow slower I/O to finish.
After each step, check for a completed message. Using a loop avoids code duplication.
Completely interrupt-based application
Use a timer interrupt to do the repeated work. Divide it in short tasks, a state machine does magic here, too.
Use an otherwise unused interrupt to signal the end of the message. Its ISR may run a bit longer, because it will not be called often. This ISR can handle the message and change the state of the application.
You need to think about interrupt priorities with much care.
The endless loop in main() will effectively be empty, like for (;;) {}.
I've read multiple times that it is usually good practice to minimize the amount of time to spend in a timer interrupt, and the advice of only raising a flag came up several times.
I am using a timer to run a bit of code (conversion of sensor data into usable data). It is important in my application to read and manipulate this data at fairly high-speed (8KHz).
Here's how I am approaching the problem:
I am using an STM32 H743
I am using RTOS with two threads, with slightly different priority levels
I am using 2 timers (TIM2 and TIM3) in my case
TIM2 is set to trigger a callback at 1KHz, and is started in my main thread (slightly higher priority than the secondary thread)
TIM3 is set to trigger a callback at 8KHz, and is started in the secondary thread
the HAL_TIM_PeriodElapsedCallback is used for both Timers and is looking like this:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
/* USER CODE BEGIN Callback 0 */
/* USER CODE END Callback 0 */
if (htim->Instance == TIM6) {
HAL_IncTick();
}
/* USER CODE BEGIN Callback 1 */
else if (htim->Instance == TIM2) {
TIM3_flag = 1;
}
else if (htim->Instance == TIM3) {
TIM3_flag = 1;
}
/* USER CODE END Callback 1 */
}
And then each of the 2 threads have a simple test on the flag, here's what it looks like for the secondary thread:
void StartSecondaryThread(void *argument)
{
/* USER CODE BEGIN StartSecondaryThread */
HAL_TIM_Base_Start_IT(&htim3);
/* Infinite loop */
for(;;)
{
if (TIM3_flag == 1) {
runCALC();
//MORE USER CODE HERE
TIM3_flag = 0;
}
}
/* USER CODE END StartSecondaryThread */
}
Per the autogenerated code from CubeMX, both the mainThread and secondaryThread infinite for(;;) loops had a osDelay(1).
am I supposed keep these days? outside of the if statement for the raised flag?
I have some concerns that if I don't it will crash the MCU because outside nothing to do when the flag isn't raised. And I am concerns that keeping the osDelay(1) will be "too long" (1ms vs 125 us). Is there a way to apply a shorter delay that would not slow down my 8KHz polling?
Of course the runCAL() stuff will take significantly less time than the 125 us period.
It would make sense for me to remove the delay all together but I have a feeling that it will crash severely.
What should I do?
cheers
Flags not are net very good way of the thread synchronisation when you use RTOS.
In this case use semaphores, mutexes or direct task notifications.
slightly higher priority than the secondary thread
It does not make any difference in code you shown The different priorities RTOS tasks are not preempted by the scheduler and context switch happens only when you pass the control yourself. The only task which will actually run all the time is the last started one, as your task does not pass the control to the RTOS and the ISR also does not. Your code is not actually the correct RTOS code.
You can have it in one task.
void StartSecondaryThread(void *argument)
{
/* USER CODE BEGIN StartSecondaryThread */
HAL_TIM_Base_Start_IT(&htim3);
HAL_TIM_Base_Start_IT(&htim2);
/* Infinite loop */
for(;;)
{
switch(ulTaskNotifyTake(pdTRUE, portMAX_DELAY))
{
case 3:
runCALC();
//MORE USER CODE HERE for timer 3
break;
case 2:
//MORE USER CODE HERE for timer 2
break;
default:
//MORE USER CODE HERE for other timers
break;
}
}
/* USER CODE END StartSecondaryThread */
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
BaseType_t xHigherPriorityTaskWoken = pdFalse;
switch((uint32_t)htim->Instance)
{
case (uint32_t)TIM6:
HAL_IncTick();
break;
case (uint32_t)TIM2:
xTaskNotifyFromISR( xThreadHndl, 2, eSetValueWithOverwrite, &xHigherPriorityTaskWoken );
break;
case (uint32_t)TIM3:
xTaskNotifyFromISR( xThreadHndl, 3, eSetValueWithOverwrite, &xHigherPriorityTaskWoken );
break;
}
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
My goal is to have a timer that triggers an interrupt at 1kHz and for that to call a few lines of code that starts a HAL_SPI_TransmitReceive_DMA call (or possibly more than one on different SPI buses). Once the SPI calls are made, it enters a while loop waiting for the SPI communication to finish, then it interprets the data received.
I've set up my timer (TIM3) and SPI (SPI1) peripheral using STM32CubeMX. The SPI bus and its DMA channel interrupts have NVIC priorities of 0 while the timer interrupt priority is set to 10. I added this code to the TIM3_IRQHandler function created in the STM32f4xx_it.c file by CubeMX.
'''
void TIM3_IRQHandler(void) {
/* USER CODE BEGIN TIM3_IRQn 0 */
imu_Tx[0] = FirstData_Reg | SPIReadMask;
while (completedSPI1 != 1);
//imu0 start
HAL_GPIO_WritePin(ICM0.CS_Port, ICM0.CS_Pin, GPIO_PIN_RESET);
completedSPI1 = 0;
HAL_SPI_TransmitReceive_DMA(&hspi1, imu_Tx, imu0_reading, 15);
//imu0 end
while (completedSPI1 != 1);
imu_reading_to_data(IMU0_data, imu0_reading);
imu_int_to_norm_float(IMU0_floats, IMU0_data, &ICM0);
/* USER CODE END TIM3_IRQn 0 */
HAL_TIM_IRQHandler(&htim3);
/* USER CODE BEGIN TIM3_IRQn 1 */
/* USER CODE END TIM3_IRQn 1 */
}
'''
I also added this function at the end of STM32f4xx_it.c:
'''
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi) {
if (hspi == &hspi1){
HAL_GPIO_WritePin(ICM0.CS_Port, ICM0.CS_Pin, GPIO_PIN_SET);
completedSPI1 = 1;
}
}
'''
"completedSPI1" is declared as a "volatile int" at the top of the file.
My problem is that the code never makes it to the SPI callback function because it gets locked up in the "while (completedSPI1 != 1);" loop (the second one, under "//imu0 end"). If I comment out that line, it does make it to the callback function (but of course that ruins the function of the code since I'm interpreting data that I haven't finished reading yet). I feel like I must have something configured incorrectly such that the timer interrupt is higher priority than the SPI interrupt even though I was sure to not do that in CubeMX.
The other thing that has got me extra confused is that this code is pretty much copy and pasted from one of my previous projects that reads data from the same sensor in the same way and that worked just fine. Though that project was based on an STM32F7, not an F4.
Please let me know if you've got any advice on how to fix this problem or ideas of other ways I could structure my code to avoid it in the first place while retaining the intended functionality. And of course let me know if there is some important information that I failed to share.
Thanks a lot!
-Ben
The code snippets below come from the manufacturer. I am completely confused as to what they are trying to do. In particular, anything to do with InterruptHandler.
Please comment on the code in the globals section. Does this look right?
Also the various APIs included all have sticking points for me. Are they trying to use a void pointer? ... Plenty to ask, please comment mainly on how they are suggesting the timer2 would work.
...
/**
Section: Global Variables Definitions
*/
void (*TMR2_InterruptHandler)(void);
...
/**
Section: TMR2 APIs
*/
void TMR2_Initialize(void)
{
// Set TMR2 to the options selected in the User Interface
// T2CKPS 1:2; T2OUTPS 1:2; TMR2ON off;
T2CON = 0x11;
// T2CS LFINTOSC;
T2CLKCON = 0x04;
// T2PSYNC Not Synchronized; T2MODE Software control One shot; T2CKPOL Rising Edge; T2CKSYNC Not Synchronized;
T2HLT = 0x08;
// T2RSEL TMR4_postscaled;
T2RST = 0x02;
// PR2 8;
T2PR = 0x08;
// TMR2 0;
T2TMR = 0x00;
// Clearing IF flag before enabling the interrupt.
PIR4bits.TMR2IF = 0;
// Enabling TMR2 interrupt.
PIE4bits.TMR2IE = 1;
// Set Default Interrupt Handler
TMR2_SetInterruptHandler(TMR2_DefaultInterruptHandler);
// Start TMR2
TMR2_Start();
}
void TMR2_ISR(void)
{
// clear the TMR2 interrupt flag
PIR4bits.TMR2IF = 0;
if(TMR2_InterruptHandler)
{
TMR2_InterruptHandler();
}
}
void TMR2_SetInterruptHandler(void* InterruptHandler)
{
TMR2_InterruptHandler = InterruptHandler;
}
void TMR2_DefaultInterruptHandler(void)
{
// add your TMR2 interrupt custom code
// or set custom function using TMR2_SetInterruptHandler()
}
/**
End of File
*/
Thanks,
Steve
edit:
The manufacturer code includes a routine (I can't seem to add it here) for managing high-level interrupts that calls TMR2_ISR if (PIE4bits.TMR2IE == 1 && PIR4bits.TMR2IF == 1) This would seem to negate any TMR2_DefaultInterruptHandler would it not?
Thanks again for your help.
The comments of Garr Godfrey and Martin James provided helpful hints. Thus, I will try to fill the possible gaps:
void (*TMR2_InterruptHandler)(void); is a global variable with a function pointer providing the current interrupt handler.
I would read TMR2_ISR() as "interrupt service routine". From the presented code, it's not clear from where it is called. Suspecting from it's name, it's reasonable to assume that it's called when the interrupt is triggered. It checks whether the global interrupt handler function pointer TMR2_InterruptHandler is not NULL and calls the pointed function in case of success.
If TMR2_Initialize() has been called before then TMR2_InterruptHandler points to function TMR2_DefaultInterruptHandler(). Thus, in this case, the latter is called from TMR2_ISR().
void TMR2_SetInterruptHandler(void* InterruptHandler) is used in TMR2_Initialize(). It sets the current interrupt handler. (Surprise.)
I'm a little bit puzzled because I would make it:
void TMR2_SetInterruptHandler(void (*InterruptHandler)())
This is because my daily work with C++ drilled me for "clean typing" but I remember that C is much more lax about this. (void* is compatible to any pointer.)
As Garr Godfrey already said: "put your code in TMR2_DefaultInterruptHandler"
where it is recommended by the comment
// add your TMR2 interrupt custom code
I am working on a project that requires USB communication.
I'm using a Nuvoton NUC123 which runs an ARM cortex M0 core, with a speed of 48MHz, 20kb RAM and 64kb flash memory.
The microcontroller implements a hardware interrupt whenever a USB endpoint gets data transmitted to it from the host, whether it's an Ack, Nak or a setup packet.
The sample code supplied by the manufacturer is rather dirty, it involves switch-case-ing the endpoint to which the interrupt belongs and if it is a setup packet containing a class-specific request, it make a switch-case for every interface or endpoint that may be the target of the request.
I figured I can make things prettier by defining an array of structure:
typedef void UsbEventCallback(uint32_t u32IntFlag, uint32_t u32EPSTS);
typedef uint32_t UsbClassReqCallback(void);
typedef struct
{
uint8_t ep_address;
uint32_t config;
uint32_t buff_offset;
UsbClassReqCallback *usb_classreq_cb;
UsbEventCallback *usb_event_cb;
} ATTR_PACKED EP_Config_Struct;
typedef struct
{
uint8_t interface_id;
UsbClassReqCallback *usb_classreq_cb;
} ATTR_PACKED Interface_Config_Struct;
extern const EP_Config_Struct EP_config_settings[TOTAL_NUM_ENDPOINTS];
extern const Interface_Config_Struct interfaces_config_settings[TOTAL_NUM_INTERFACES];
and then, in the interrupt callback I do:
switch( req_destination )
{
case 1: //interface
for ( uint8_t interface_index = 0 ; interface_index < TOTAL_NUM_INTERFACES ; interface_index++ )
{
if ( interfaces_config_settings[interface_index].interface_id == UsbDev.Setup.wIndex )
{
if ( interfaces_config_settings[interface_index].usb_classreq_cb == NULL )
return FALSE;
else
return (*interfaces_config_settings[interface_index].usb_classreq_cb)();
}
}
break;
case 2: //endpoint
for ( uint8_t ep_index = 0 ; ep_index < TOTAL_NUM_ENDPOINTS ; ep_index++ )
{
if ( EP_config_settings[ep_index].ep_address == UsbDev.Setup.wIndex )
{
if ( EP_config_settings[ep_index].usb_classreq_cb == NULL )
return FALSE;
else
return (*EP_config_settings[ep_index].usb_classreq_cb)();
}
}
break;
}
return FALSE;
My questions are:
Is it better to not actually make all these decisions and calling all these other functions in interrupt time? Am I better to just save the interrupt data and switch some flag on requiring the main thread to process the interrupt?
How important is it to return from the callback as soon as possible?
What do you think is the correct architecture for such a program?
Thank you
It is hard to say without knowing precisely your application, but your interrupt handler looks quite reasonable.
Generally for multi-tasks systems it is advised to do the least possible in interrupt handlers, because while an interrupt is being handled the different tasks on the systems are not being scheduled any more. This can be a lot more complicated than that, especially when using interrupt priorities and interrupt nesting, but still the general idea is to avoid staying too long in interrupt handlers.
For your USB driver, I would select the appropriate endpoint/interface in the interrupt handler, then write the data received in the appropriate queue/array and finally trigger a flag/semaphore to signal that some data has been received. I would then parse the data received in a normal task rather than directly in the interrupt handler, to keep the interrupt handler minimal.
Not sure whether it's critical to keep busy ISR in your project but in principle interrupt handler should return as soon as possible. If i were you I would do following regarless of the situation.
Parse protocol in ISR and then feed data to a ring buffer as a parsed packets. Ring buffer may require the ability of variable length data peek/push/pop according the protocol. Then proceed time consuming work in main.