0
\$\begingroup\$

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);
}```

\$\endgroup\$
10
  • \$\begingroup\$ This looks a bit confused. ISRs need to do as little as possible, they should never try to call major chunks of application code and especially not ones which then use other peripherals in a non-trivial way, enter loops, or wait. Really there's no reason to be enabling and disabling your UART and interrupts, just leave it running and handle the foreground vs. ISR interchange with appropriately managed circular buffers. You may find it's easier to do everything but the UART receive in the foreground anyway (ie, busy-wait on the UART transmit and the I2C presuming you are the I2C master). \$\endgroup\$ Commented May 30, 2020 at 17:45
  • \$\begingroup\$ I don't think the callback is doing some significant amount of work here. The idea that I had in mind was to have interrupts enabled, have ISR fire for each byte received, and process them for each set of data bytes I get (till \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\$
    – MKD
    Commented May 30, 2020 at 17:57
  • \$\begingroup\$ Well, you don't think it does, but it does. \$\endgroup\$
    – Justme
    Commented May 30, 2020 at 18:06
  • \$\begingroup\$ Specifically you are trying to do an I2C operation from within the UART ISR. That you hid it from yourself in an application callback means nothing to the processor. Don't do that. Parse your UART data in the foreground, and if you find something that implies you should do an I2C operation then start that from the foreground, too. Step by step construction isn't a bad idea, but the problem is that you've taken a step down a path that leads to only trouble. Your UART ISR should basically just be a larger software FIFO around the tiny hardware one to give your code time to catch up. \$\endgroup\$ Commented May 30, 2020 at 19:10
  • \$\begingroup\$ by foreground, I guess you mean the application and not ISR, yeah? like I said in the description, I had the working code where I was parsing the UART data in the application, and doing I2C read but for that, I was explicitly calling a function to do so, which didn't seem the correct usage of interrupts to me. I'd imagine it work something like, you have your application running, and everytime you receive a byte from uart, ISR is triggered, and once you know enough data is received to be parsed, your application gets interrupted which parses, and does I2C read, and you continue where u left.. \$\endgroup\$
    – MKD
    Commented May 30, 2020 at 19:50

1 Answer 1

1
\$\begingroup\$

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.

\$\endgroup\$
14
  • \$\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
    Commented 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
    Commented 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
    Commented 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
    Commented 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
    Commented May 30, 2020 at 20:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.