7
\$\begingroup\$

I'm trying to setup UART communication with the STM32F0 Discovery Board but I am having difficulty adapting the Rx side of things to my needs.

The STM32 will be receiving a message (4-6 bytes with no end character) from the UART device every few seconds and then must send a reply.

How should I be handling receiving messages so that I'm not limited to a fixed message size, so that I don't need to look for an end character (maybe using a timer) and so that glitches don't throw off my entire communications?

I'm debugging the below code with an FTDI USB-UART cable right now and the interrupt only triggers for the first character I send through terminal and then wont trigger again.

//This function handles USART1 global interrupt request.
void USART1_IRQHandler(void)
{
  if( USART_GetITStatus(PMIC_COM1, USART_IT_RXNE))
  {
    char t = USART_ReceiveData(PMIC_COM1);

    //Check if received character is end character
    if( (t != 'x') && (cnt < MAX_STRLEN) ){
      received_string[cnt] = t;
      cnt++;
    }
    else{ // otherwise reset the character counter and print the received string
      cnt = 0;
      printf("UART Message: %c", received_string);
    }
  }
}
\$\endgroup\$
3
  • 4
    \$\begingroup\$ It should be "%s", not "%c" in the printf() to print a string. "%c" will only print a single character. \$\endgroup\$ Commented Feb 17, 2014 at 20:57
  • 1
    \$\begingroup\$ Using printf() in your ISR is very risky for several reasons. Even if it works it may output the midst of some other output, but it may also corrupt the state of that output. Further, on a simple embedded system the implementation may well be blocking which means that you may be stuck there until that long message has clocked out over the wire, causing you to miss incoming data. Also consider the difference in character arrival times when the source is a terminal vs. a program, and remember that a USB serial converter's packetization can fairly arbitrarily distort the timing too. \$\endgroup\$ Commented Jun 27, 2016 at 16:46
  • \$\begingroup\$ Use an idle character as a message separator. You can enable the corresponding interrupt (IDLEIE) to be notified when the line is idle, i.e. a break in messages has been received. \$\endgroup\$
    – neoxic
    Commented Feb 19, 2023 at 23:19

5 Answers 5

7
\$\begingroup\$

Did you clear the interrupt bit? Try calling void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT) after you've processed the event.

If I were you I'd just include line feeds as the command separator, but you certainly could set a timer.

\$\endgroup\$
2
  • \$\begingroup\$ Also, on this platform at least, watch out for sticky UART error flags. \$\endgroup\$ Commented Feb 17, 2014 at 20:48
  • 1
    \$\begingroup\$ This is what I was missing to get the interrupt to re-trigger, but I ended up using USART_ClearFlag. \$\endgroup\$
    – spizzak
    Commented Feb 18, 2014 at 14:18
0
\$\begingroup\$

For message separation there are several things you can do. You've stated that you don't like a fixed length format and End-Of-Message termination characters so my next suggestion would be to make the first byte transmitted be the number of characters to follow. Alternatively, you can force a pause between messages such that if the receive line is idle for, say, 16 bit times after a character then that pause delimits the end of a message. Personally I'd combine the two: set the first byte as a length byte and then use that to determine how long it should take to receive the whole message. If that time expires and you haven't received the correct number of bytes then you've got a broken message and can just discard it.

\$\endgroup\$
0
\$\begingroup\$

Have a fixed size buffer, say 64 bytes, and read up to 64 bytes at a time, then feed them into a state machine. That way it doesn't matter if you read past the end of one message and only part of the next, for example.

\$\endgroup\$
-1
\$\begingroup\$

Sample :> :

////// The above comment is automatically generated by CoIDE ///////////////////

/* Includes ------------------------------------------------------------------*/
#include "stm32f10x_usart.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_gpio.h"
#include "misc.h"

#define NUM 10

int i,j;
char name[NUM+1] = {'\0'}; 

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
ErrorStatus HSEStartUpStatus;

/* Private function prototypes -----------------------------------------------*/
void NVIC_Configuration(void);
void GPIO_Configuration(void);
void USART_Configuration(void);
void USART1_IRQHandler(void);
void UARTSend(const unsigned char *pucBuffer, unsigned long ulCount);

/******************************************************************************/
/*            STM32F10x Peripherals Interrupt Handlers                        */
/******************************************************************************/

/**
  * @brief  This function handles USARTx global interrupt request.
  * @param  None
  * @retval None
  */
void USART1_IRQHandler(void)
{
    if ((USART1->SR & USART_FLAG_RXNE) != (u16)RESET)           
    {          
            i = USART_ReceiveData(USART1);
            if(j == NUM)
            {
                name[j] = i;
                j = 0;
            }
            else
            {
                name[j++] = i;
            }
            name[j] = '\0';
    }   
}

/* Private functions ---------------------------------------------------------*/

/*******************************************************************************
* Function Name  : usart_rxtx
* Description    : Print "Welcome to CooCox!"  on Hyperterminal via USART1.
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void usart_init(void)
{
        const unsigned char menu[] = " Welcome to CooCox!\r\n";

        /* Enable USART1 and GPIOA clock */
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);

        /* NVIC Configuration */
        NVIC_Configuration();


        /* Configure the GPIOs */
        GPIO_Configuration();

        /* Configure the USART1 */
        USART_Configuration();

        /* Enable the USART1 Receive interrupt: this interrupt is generated when the
             USART1 receive data register is not empty */
        USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);

        /* print welcome information */
        UARTSend(menu, sizeof(menu));



        while(1)
        {
        }
}

/*******************************************************************************
* Function Name  : GPIO_Configuration
* Description    : Configures the different GPIO ports.
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void GPIO_Configuration(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;

  /* Configure USART1 Tx (PA.09) as alternate function push-pull */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOA, &GPIO_InitStructure);

  /* Configure USART1 Rx (PA.10) as input floating */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
}

/*******************************************************************************
* Function Name  : USART_Configuration
* Description    : Configures the USART1.
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void USART_Configuration(void)
{
  USART_InitTypeDef USART_InitStructure;

/* USART1 configuration ------------------------------------------------------*/
  /* USART1 configured as follow:
        - BaudRate = 115200 baud  
        - Word Length = 8 Bits
        - One Stop Bit
        - No parity
        - Hardware flow control disabled (RTS and CTS signals)
        - Receive and transmit enabled
        - USART Clock disabled
        - USART CPOL: Clock is active low
        - USART CPHA: Data is captured on the middle 
        - USART LastBit: The clock pulse of the last data bit is not output to 
                         the SCLK pin
  */
  USART_InitStructure.USART_BaudRate = 9600;
  USART_InitStructure.USART_WordLength = USART_WordLength_8b;
  USART_InitStructure.USART_StopBits = USART_StopBits_1;
  USART_InitStructure.USART_Parity = USART_Parity_No;
  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;

  USART_Init(USART1, &USART_InitStructure);

  /* Enable USART1 */
  USART_Cmd(USART1, ENABLE);
}

/**
  * @brief  Configures the nested vectored interrupt controller.
  * @param  None
  * @retval None
  */
void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;

  /* Enable the USARTx Interrupt */
  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}

/*******************************************************************************
* Function Name  : UARTSend
* Description    : Send a string to the UART.
* Input          : - pucBuffer: buffers to be printed.
*                : - ulCount  : buffer's length
* Output         : None
* Return         : None
*******************************************************************************/
void UARTSend(const unsigned char *pucBuffer, unsigned long ulCount)
{
    //
    // Loop while there are more characters to send.
    //
    while(ulCount--)
    {
        USART_SendData(USART1, *pucBuffer++);// Last Version USART_SendData(USART1,(uint16_t) *pucBuffer++);
        /* Loop until the end of transmission */
        while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)
        {
        }
    }
}
\$\endgroup\$
2
  • 2
    \$\begingroup\$ This looks like a useful example on the RX interrupt side of things but maybe adding a little bit of a description to what the code does. I think it's a useful starting point but for example it does't handle the variable packet sizes the question is asking about from what I can see. \$\endgroup\$
    – PeterJ
    Commented Jan 26, 2015 at 11:16
  • \$\begingroup\$ I think there is a is enough explanation of the code...? \$\endgroup\$
    – Hamed
    Commented Jan 27, 2015 at 14:09
-1
\$\begingroup\$

Certainly you use a specific baud rate. Set up a timer of 'n' milliseconds. Reset the timer after each byte received. the timer will trigger it's update. Interrupt after 'n' milliseconds have passed after the last byte received. (because after the last byte is received, there is no one who can reset the timer).

This way the timer will announce you when the message has ended . The 'n' milliseconds depends on what time window you can have between two messages. E.g. for 9600 bps you can use 5 msec. If no other byte came after 'n' milliseconds after the previous one , consider the message ended (all bytes which you have waited have arrived ).

\$\endgroup\$
2
  • \$\begingroup\$ This is a very bad idea - it introduces an entirely unnecessary dependence on the pacing of the transmission, so for example it will fail when the poster is typing at a terminal to test things. It might work with a program one day, but break the next if something slows the host, or with a different USB-serial converter that imposes different timing. If a certain sequence of characters defining a message is sought, then they should be collected and matched. A delimiter or reset character would be a good idea - if a system without is going to use a time based reset it needs to be long. \$\endgroup\$ Commented Jun 27, 2016 at 16:51
  • \$\begingroup\$ I corrected numerous grammar, spelling and syntax errors. Also split into two paragraphs for easier reading and commenting. In the future please pay attention to the quality of your typing. \$\endgroup\$
    – user105652
    Commented Jun 27, 2016 at 17:17

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.