Issue relaying messages between multiple USART connections on the STM32F1 Blue pill

347 views Asked by At

I have a setup with a STM32F103C8T8 and two modules (SARA & ESP82) on USART connections. we are currently using the libopencm3 and FreeRTOS libraries.

The goal

I want the STM to send AT commands to the SARA module, receive the response and relay this message coming from the SARA module to the ESP module. All via USART connections.

Description of setup:

The STM is connected to the SARA R412 LTE-M/NB-IoT board on USART2 and the ESP8266 is connected on USART3.

The SARA R412 has PWR_ON and RST connected to A5 and A4 respectively. These are used for a power cycle to turn on the SARA module correctly.

The STM32 has some hardcoded commands which are sent to the SARA module on USART2 which in return should answer, this answer should then be relayed by the STM32 to the ESP8266 on USART3.

For testing purposes the ESP is not connected atm, we are just listening on the same USART port using TTL instead.

Below is an image of the TTL to STM connection: TTL to STM connection

Below is an image of the STM to SARA connection (RX and TX on the SARA board are switched): STM to SARA connection

The problem:

When relaying the message answer from the SARA to the ESP8266, something is not right. We are experiencing some connection, where the message is relayed correctly, however it is very inconsistent and most of the time it gets stuck on a single character.

What we have tried: We are using a TTL connector and minicom to listen to the USART connections and see where the problem arises. We observe that the SARA module correctly receives the command from the STM32 and also correctly answers with the appropriate message. The next thing that happens is that the STM32 should receive the message correctly, however when listening on USART3 (ESP usart connection), the message is sometimes correct, and sometimes not.

We have also tried lowering the baud rates, however with no difference in results.

The STM32 can send commands just fine on both USART2 and USART3, however the answer that should be relayed is sometimes not relayed correctly (or at all).

We suspect the problem lies within our usart_get_string method, which relays a message coming from one USART to another USART connection:

static void usart_get_string(uint32_t usartSrc, uint32_t usartDst, uint16_t str_max_size)
{
    uint8_t received = 'V';
    uint16_t itr = 0;
    uint8_t recvPrev;

    while (itr < str_max_size)
    {
        if (!((USART_SR(USART2) & USART_SR_RXNE) == 0))
        {
            received = usart_recv_blocking(usartSrc);
        }
        uart_putc(received, usartDst);

        if (recvPrev == 'O' && received == 'K')
        {
            break;
        }
        recvPrev = received;
        uart_putc_blocking(itr, usartDst); // Somehow doesn't work at all without this
        itr++;
    }
}

The method is quite naive, and received = usart_recv_blocking(usartSrc); part should probably be within the first if-statement, but nothing gets returned if we do so.

Included is the full code:

#include <FreeRTOS.h>
#include <task.h>
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/usart.h>

#define MSG_LENGTH 512

static void
uart_setup(void)
{

    // SARA SETUP
    rcc_periph_clock_enable(RCC_GPIOA);
    rcc_periph_clock_enable(RCC_USART2);

    gpio_set_mode(GPIOA,
                  GPIO_MODE_OUTPUT_50_MHZ,
                  GPIO_CNF_OUTPUT_ALTFN_PUSHPULL,
                  GPIO_USART2_TX);

    usart_set_baudrate(USART2, 115200);
    usart_set_databits(USART2, 8);
    usart_set_stopbits(USART2, USART_STOPBITS_1);
    usart_set_mode(USART2, USART_MODE_TX_RX);
    usart_set_parity(USART2, USART_PARITY_NONE);
    usart_set_flow_control(USART2, USART_FLOWCONTROL_NONE);
    usart_enable(USART2);

    // ESP SETUP
    rcc_periph_clock_enable(RCC_GPIOB);
    rcc_periph_clock_enable(RCC_USART3);

    gpio_set_mode(GPIOB,
                  GPIO_MODE_OUTPUT_50_MHZ,
                  GPIO_CNF_OUTPUT_ALTFN_PUSHPULL,
                  GPIO_USART3_TX);

    usart_set_baudrate(USART3, 115200);
    usart_set_databits(USART3, 8);
    usart_set_stopbits(USART3, USART_STOPBITS_1);
    usart_set_mode(USART3, USART_MODE_TX_RX);
    usart_set_parity(USART3, USART_PARITY_NONE);
    usart_set_flow_control(USART3, USART_FLOWCONTROL_NONE);
    usart_enable(USART3);
}

static inline void
uart_putc(uint8_t ch, uint32_t usart_port)
{
    usart_send(usart_port, ch);
}

static inline void
uart_putc_blocking(uint8_t ch, uint32_t usart_port)
{
    usart_send_blocking(usart_port, ch);
}

static inline void uart_puts(uint8_t *s, uint32_t usart_port)
{
    while (*s != '\0')
    {
        uart_putc_blocking(*s, usart_port);
        gpio_toggle(GPIOC, GPIO13);
        vTaskDelay(pdMS_TO_TICKS(100));
        s++;
    }
    uart_putc_blocking('\r', usart_port);
    uart_putc_blocking('\n', usart_port);
}


static void usart_get_string(uint32_t usartSrc, uint32_t usartDst, uint16_t str_max_size)
{
    uint8_t received = 'V';
    uint16_t itr = 0;
    uint8_t recvPrev;

    while (itr < str_max_size)
    {
        if (!((USART_SR(USART2) & USART_SR_RXNE) == 0))
        {
            received = usart_recv_blocking(usartSrc);
        }
        uart_putc(received, usartDst);

        if (recvPrev == 'O' && received == 'K')
        {
            break;
        }
        recvPrev = received;
        uart_putc_blocking(itr, usartDst); // Somehow doesn't work at all without this
        itr++;
    }
}

static void
task1(void *args __attribute__((unused)))
{

    uint8_t esp[] = "Hi ESP";
    uint8_t AT[] = "ATI";

    uart_puts(esp, USART3);

    // Power_on Start for SARA module
    vTaskDelay(pdMS_TO_TICKS(500));
    gpio_clear(GPIOA, GPIO5);
    vTaskDelay(pdMS_TO_TICKS(5000));
    gpio_set(GPIOA, GPIO5);

    gpio_set_mode(
        GPIOA,
        GPIO_MODE_OUTPUT_2_MHZ,
        GPIO_CNF_INPUT_FLOAT,
        GPIO4); //RESET_N
    gpio_set_mode(
        GPIOA,
        GPIO_MODE_OUTPUT_2_MHZ,
        GPIO_CNF_OUTPUT_PUSHPULL,
        GPIO5); //PWR_ON

    vTaskDelay(pdMS_TO_TICKS(10000));

    for (;;)
    {
        uart_puts(esp, USART3);
        vTaskDelay(pdMS_TO_TICKS(500));

        uart_puts(AT, USART2);

        usart_get_string(USART2, USART3, MSG_LENGTH);
        vTaskDelay(pdMS_TO_TICKS(10000));
    }
}

int main(void)
{

    rcc_clock_setup_in_hse_8mhz_out_72mhz(); // Blue pill
    // PC13:
    rcc_periph_clock_enable(RCC_GPIOC);
    gpio_set_mode(
        GPIOC,
        GPIO_MODE_OUTPUT_2_MHZ,
        GPIO_CNF_OUTPUT_PUSHPULL,
        GPIO13);

    uart_setup();

    xTaskCreate(task1, "task1", 100, NULL, configMAX_PRIORITIES - 1, NULL);
    vTaskStartScheduler();

    for (;;)
        ;
    return 0;
}

Below is an example of the output observed in minicom, when listening on USART3. enter image description here

We have verified that the wiring should be correct by interchanging with other modules such as other ESPs and the wiring should indeed be correct. The power is coming from the TTL (which is 3v3) and goes into the breadboard, where the STM32 and the SARA R412 board gets power from here.

EDIT:

I tested out FreeRTOS timers as suggested, but unfortunately wasn't able to solve my problem:

I created a FreeRTOS timer in the main method the following way:

    timerHndl1 = xTimerCreate(
      "timer1", /* name */
      pdMS_TO_TICKS(400), /* period/time */
      pdFALSE, /* auto reload */
      (void*)0, /* timer ID */
      vTimerCallback1SecExpired); /* callback */

And in the get_string() method I reset the timer as the first thing. The updated method is shown below:

static bool usart_get_string(uint32_t usartSrc, uint32_t usartDst, uint16_t str_max_size)
{
    uint8_t received = 'V';
    uint16_t itr = 0;
    uint8_t recvPrev = 'Q';
    bool timeoutflag;



    // TODO for you: check the UART for error conditions here (like 
    // overrun or framing errors, and clear the errors if any have occurred
    // before we start the receive

    //restart timer:
    if ( xTimerReset(timerHndl1, 10 ) != pdPASS )
    {
        timeoutflag = true;
    }
    else
    {
        timeoutflag = false;
    }

    while ((itr < str_max_size) && (!timeoutflag))
    {
        while ( (!((USART_SR(USART2) & USART_SR_RXNE) == 0)) &&
                (itr < str_max_size) )
        {
            received = usart_recv_blocking(usartSrc);
            uart_putc(received, usartDst);

            if (recvPrev == 'O' && received == 'K')
            {
                return;
            }
            recvPrev = received;
            itr++;
        }


        // if timer times out
        if ( xTimerIsTimerActive(timerHndl1) == pdFALSE )
        {
            timeoutflag = true;
        }
    }

    return !timeoutflag;
}
1

There are 1 answers

5
Armand Jordaan On

It looks like the condition (!((USART_SR(USART2) & USART_SR_RXNE) == 0)) is never true again. Remember that the characters are received in a finite time and you have to allow some time for them to be received.

I would consider modifying the usart_get_string function to look like this:

// timeout in ms
static bool usart_get_string(uint32_t usartSrc, uint32_t usartDst, uint16_t str_max_size, uint16_t timeout)
{
    uint8_t received = 'V';
    uint16_t itr = 0;
    uint8_t recvPrev;
    bool timeoutflag;

    // TODO for you: check the UART for error conditions here (like 
    // overrun or framing errors, and clear the errors if any have occurred
    // before we start the receive

    // add some code here
    // TODO: start a timer in the RTOS here (using timeout)
    // pseudo code
    Start a timer of "timeout" ms

    timeoutflag = false;

    while ((itr < str_max_size) && (!timeoutflag))
    {
        while ( (!((USART_SR(USART2) & USART_SR_RXNE) == 0)) &&
                (itr < str_max_size) )
        {
            received = usart_recv_blocking(usartSrc);
            uart_putc(received, usartDst);

            if (recvPrev == 'O' && received == 'K')
            {
                return;
            }
            recvPrev = received;
            itr++;
        }

        // psueudo code
        if (RTOS timer has timed out)
        {
            timeoutflag = true;
        }
    }

    return !timeoutflag;
}

I have also removed the calling of the uart_putc_blocking function, as it should now work. (I suspect this was forming some sort of delay just long enough for the next character to be received. I have also suggested having some sort of timeout to the usart_get_string function. The function will return false if a timeout occurred.

Another suggestion which you may consider: currently your check for the letters 'O' and 'K', but you might want to expand your usart_get_string routine to also the for the \r\n (or whatever end of line sequence the modem sends).