4 years, 5 months ago.

Help with USART serial communication STM32F401 (Basic)

Hello everyone. So, I am a beginner in this and I am trying to understand and use the USARTS. I am using the STM32F401 and currently having a problem which I think may be very stupid, but I am completely stuck. I am writing a code that uses the same USART to transmit and receive a char or int (I don't really care), with interrupts. As far as I know, the interrupt flag for the transmitter is reset when something is written into the port, in this case, I am using putc, and for the receiver is getc, right?

My code is the following:

<<code>>

  1. include "mbed.h"
  2. include "C12832.h"

UART6_RX = PA_12 UART6_TX = PA_11 C12832 lcd(D11,D13,D12,D7,D10); RawSerial usart6(PA_11,PA_12);Creates a raw serial port connected to the specified transmit and receive pins.

void TXCallBack () { char c = 0; I have tried with both int and char usart6.putc(c); As far as I know, this is supposed to reset the interrupt flag. c++; wait(1.0); }

void RXCallBack () { int j = 0; j= usart6.getc(); Read the serial port to get the value and reset the interrupt flag. I dont know if this should be here lcd.locate(0,2); lcd.printf(" "); lcd.locate(0,2); lcd.printf("Receiving: %d", j);

}

int main() { usart6.baud(115200); I really dont know if this matters because I am transmitting and receiving with the same usart, but for this board, I have seen people using this value of baud rate. usart6.attach(&TXCallBack, Serial::TxIrq); This is supposed to be executed every time the USART is available to write right? lcd.printf("Test"); Check if this statements is reached usart6.attach(&RXCallBack, Serial::RxIrq); This is supposed to be executed every time the USART is available to read right? lcd.printf("Testt"); Check if this statements is reached

while (1)

} <</code>>

The problem is that none of the two test statements is being reached, and by testing, I found that the transmission is never ending. It's like the program stays on that function because the interrupts is never reset. What may be wrong? I already tried different solutions nothing... Please, I would appreciate any help.

Thanks.

1 Answer

4 years, 5 months ago.

I see a couple problems. First rule for interrupts is Get in and get out ASAP. Do not call wait() inside of an ISR. Do not use printf in an interrupt. Normal printf is guarded with a mutex and mutexes are not allowed in ISR. The rawserial has a printf implemented without mutex. But it’s still a bad idea because printf is going to sit there and block the rest of your program while it slowly putc()’s characters out.

Put a wait statement in your main while loop – nearly any length will do. You want to release main to the scheduler to let other stuff run.

I would focus on getting the RX callback working first and not worry about TX. Do your TX printfs in main code. RX is always an issue because bytes could come in at any time. TX you control so there is less of a reason to do that via interrupt.

In RX interrupt what you want to do is the getc() and save the character. Getc() clears the interrupt flag. Then you want to signal another part of the program that there is a new char or message to handle. A bool flag in rx could work, be sure to use type volatile. If a char is received, set it and get out. Now in main(), which is not doing anything, you can rapidly check for the receive flag, and then handle the new char. Clear receive flag in main when you’re done with it.

It is common inside RX to push chars onto a buffer, and only signal when you have a complete message. A complete message could be as simple as ascii text that ends with a newline char. You seem to be using integers, so you can encode those as text so 123 becomes “123\n”.

Your main is free, so you can just put the lcd stuff in there and you are good to go. If main were busy with something else, a good pattern would be to put your lcd printf stuff in a new thread with a mailbox. Rxcallback then puts the new char or message into lcd thread. RX ISR then exists and the rtos will see the mail and call your lcd thread. This way you are deferring work out of ISR context back into the main program.

Have not tested, but something like this. This will only be able to handle single characters every so often because new_char is shared between ISR and main. When you start doing printf's in main it's a race to finish before a new char arrives in ISR. Packing into messages helps. Having request/response design with the sender helps.

include "mbed.h"
include "C12832.h"

UART6_RX = PA_12 
UART6_TX = PA_11 

C12832 lcd(D11,D13,D12,D7,D10); 
RawSerial usart6(PA_11,PA_12);

volatile bool received = false;
volatile char new_char = false;

void RXCallBack () { 
    new_char = usart6.getc(); 
    received = true;
}

int main() { 

    usart6.baud(115200); 
    usart6.attach(&RXCallBack, Serial::RxIrq); 

    lcd.printf("Test"); 

    char c = 0; 

    while (1) {
        if (received) {
            lcd.locate(0,2); 
            lcd.printf(" "); 
            lcd.locate(0,2); 
            lcd.printf("Receiving: %d", new_char);
            received = false;
        }
        usart6.putc(c); 
        c++;
        wait(0.01);
    }
}

Accepted Answer

Thank you very much! Now everything is more clear to me. Many many thanks.

Faruk F.

posted by Faruk Fakih Safadi 16 Nov 2019