So I wrote peripheral drivers on STM32 and wrote a simple application to read the user input via terminal and respond accordingly. I am using interrupts for both UART and I2C but the way I previously did was more towards "polling" than just interrupts, as shown in the example below.
// uart.c
void UART_session(USART_Handle *usart, I2C_Handle *i2c) {
char tempBuffer[usart->rxSize];
memset(tempBuffer, 0, rxBufferSize);
while(true)
{
ReceiveSerialData(usart);
ParseSerialData(usart, tempBuffer);
bool status = ReadTemp(usart, tempBuffer, I2C_Handle);
WriteUart(usart, tempBuffer);
}
}
// main.c
int main(void) {
HAL_init();
I2C_init();
UART_init();
UART_session(); // loop
while (true);
}
But I modified the code to call a callback function after i'm done receiving the data (using \r
as an indication) for processing the data, executing it accordingly, and continue what it was doing before... but when I do so, I don't receive I2C interrupts anymore. I also made sure the static i2cHandle
does have the same address as the original i2c
from main
.
// uart_hal.c
void USART2_IRQHandler(void)
{
// ....
if (ptrUSARTx->pUSARTx->SR & USART_SR_TC) // transmission complete
{
if (ptrUSARTx->USART_State == USART_TX_BUSY)
{
if (!ptrUSARTx->txLength) // no more bytes to send
{
USART_DisableTransmission();
}
}
else if (ptrUSARTx->USART_State == USART_RX_BUSY)
{
if (!ptrUSARTx->rxLength || endLine) // no more bytes to receive OR user pressed enter
{
USART_DisableTransmission();
USART_AppCallback(pUSART); // ** ADDED THIS CALLBACK **
}
}
}
}
// uart.c
void USART_AppCallback(USART_Handle_t *usart) {
ParseSerialData(usart, tempBuffer);
bool status = ReadTemp(usart, tempBuffer, i2cHandle); // ** NO I2C INTERRUPTS **
}
static I2C_Handle_t *i2cHandle;
void USART_receive(USART_Handle_t *usart, I2C_Handle_t *i2c)
{
i2cHandle = i2c;
USART_EnableInterrupts(USART_RX_BUSY);
}
// main.c
int main(void) {
HAL_init();
I2C_init();
USART_init();
USART_receive(&usart, &i2c);
while (true);
}```
1 Answer 1
The callback runs in the context of UART interrupt.
Therefore it blocks further UART interrupts, and all other lower priority interrupts, until the callback returns so the UART interrupt exits and normal program flow and other interrupts can continue. You can't even receive and buffer further UART characters for the next line during a lengthy operation.
This is exactly the thing almost all embedded programming tutorials warn about, do not do anything in an interrupt you don't have to do, especially don't do lengthy and complex operations, like UART parsing or executing arbitrary operations from there.
-
\$\begingroup\$ right, so all subsequent interrupts would be blocked until after ISR is done being executed. This leads me to a question: what are callbacks used for in ISRs? if i don't have a callback, how do I make use of interrupts such that I only get interrupts for handling a specific set of bytes received over uart, and then once done, continue what was previously being done? also, is uart parsing considered something complex? \$\endgroup\$MKD– MKD2020年05月30日 18:42:22 +00:00Commented May 30, 2020 at 18:42
-
\$\begingroup\$ First of all, not the parsing itself is complex, but imagine you have a command you type in and that takes long to execute, like blink_led_for_10_seconds(), or even worse, command that needs interrupts to work, it won't be able to do anything useful unless you rearrange interrupt priorities so that timer and I2C interrupts get executed even during UART. The callback is just a model how to separate driver code from the application that uses the driver. Instead of callback you could just directly set a flag that X happened, or with the callback, let application set a flag that X happened. \$\endgroup\$Justme– Justme2020年05月30日 18:59:01 +00:00Commented May 30, 2020 at 18:59
-
\$\begingroup\$ yeah like in my case, if I need to set up, say, I2C peripheral interrupt within an uart's ISR, I would have to increase the i2c's interrupt priority to more than that of uart for it work, yes? i still couldn't think of a way to get what I want without a callback, cause assuming the program is stuck in main's while loop, and then each for byte received over uart, ISR is triggered which populates the rx buffer until certain point when you want to process it, which is when I need an indication as to "okay, data is ready to be processed" without me having to explicitly call any function \$\endgroup\$MKD– MKD2020年05月30日 19:42:11 +00:00Commented May 30, 2020 at 19:42
-
\$\begingroup\$ Uh, but calling the callback is an explicit call of function. And if your main while loop is stuck, then fix that so it isn't stuck, why it would be stuck anyway? Of course you can trigger/start an I2C operation to initiate from the UART callback, which waits for almost nothing and takes almost no time to return. But you can't wait for I2C completion in the UART interrupt. \$\endgroup\$Justme– Justme2020年05月30日 20:39:58 +00:00Commented May 30, 2020 at 20:39
-
\$\begingroup\$ okay about main while loop being stuck, I mean the application is doing something. ideally instead of a loop, i would be doing something else. So you're saying I should make I2C read operation nonblocking if I were to use inside UART ISR? how would a nonblocking I2C read function look like? cause right now, it transmits the address bytes, and then reads the data off that address, and returns the processed bytes \$\endgroup\$MKD– MKD2020年05月30日 20:48:58 +00:00Commented May 30, 2020 at 20:48
\r
), and then repeat. I thought about using circular buffers but i want to do step by step; just making a small change is causing me enough trouble but yeah I do plan on using it only after I figure this issue out. And i'm not sure if I fully followed you.. \$\endgroup\$