简体   繁体   中英

STM32 Timer Counter problem during sending data via UART

I have some problem with the handling of the timer in code (STM32F303RE). My goal is to send messages to UART port every 200ms (one packet) and every 1s (second packet). I implemented timer ( MX_TIM6_Init ) which increments 2 variables ( timer100ms and timer1000ms ) every 100ms ( HAL_TIM_PeriodElapsedCallback ).

What the code does?

Sends messages to the UART in normal operation. Timing and data are OK - everything works fine here. When the other device wants to send information to the STM32, an "ED" frame is sent first, which changes the flag ( flag_UART_SEND_DATA to FALSE ) and turns off all communication in main loop. Then it sends the relevant frame and waits for a response with the same frame (echo).

When the code have commented sections about checking the values of timers and data everything works good ( even when data is send every 100ms-120ms ):

 if (flag_UART_SEND_DATA == TRUE) {
        //  if (timer100ms >= 2) {

                 Print_CAN_Frame("Tx", IPC_Ligths.ID, IPC_Ligths.DLC, IPC_Ligths.CAN_Tx);
                 HAL_Delay(5);
                 Print_CAN_Frame("Tx", IPC_SpeedOdometerInfo.ID, IPC_SpeedOdometerInfo.DLC, IPC_SpeedOdometerInfo.CAN_Tx);
                 HAL_Delay(5);
                 Print_CAN_Frame("Tx", IPC_SpeedOdometerInfo.ID, IPC_SpeedOdometerInfo.DLC, IPC_SpeedOdometerInfo.CAN_Tx);
                 HAL_Delay(5);
                 Print_CAN_Frame("Tx", IPC_SpeedOdometerInfo.ID, IPC_SpeedOdometerInfo.DLC, IPC_SpeedOdometerInfo.CAN_Tx);
                 HAL_Delay(5);
                 timer100ms = 0;
        //    } else if (timer1000ms == 10) {
                  Print_CAN_Frame("Tx", IPC_StatusBCM.ID, IPC_StatusBCM.DLC, IPC_StatusBCM.CAN_Tx);
                  HAL_Delay(100);
                  timer100ms = 0;
                  timer1000ms = 0;

             //  }
         }

Where does the issue occur?

When the sections are uncommented as below. The breaks between sent frames are larger and despite this the code does not work properly. When any message is sent (eg by TeraTerm), only the first two characters of the sent message are received in response (eg by sending HELLO gives HE ). The only difference when there is a problem is that the sections in the main loop. The frame sent by UART to the device goes to entirely and full data is passed on to HAL_UART_TRANSMIT_IT (in HAL_UART_RxCpltCallback ) and HAL_UART_TRANSMIT_IT return HAL_OK -> but on console it sends only 2 characters. Do you have any ideas / suggestions what to check more / where is the issue?

 if (flag_UART_SEND_DATA == TRUE) {
          if (timer100ms >= 2) {

                 Print_CAN_Frame("Tx", IPC_Ligths.ID, IPC_Ligths.DLC, IPC_Ligths.CAN_Tx);
                 HAL_Delay(5);
                 Print_CAN_Frame("Tx", IPC_SpeedOdometerInfo.ID, IPC_SpeedOdometerInfo.DLC, IPC_SpeedOdometerInfo.CAN_Tx);
                 HAL_Delay(5);
                 Print_CAN_Frame("Tx", IPC_SpeedOdometerInfo.ID, IPC_SpeedOdometerInfo.DLC, IPC_SpeedOdometerInfo.CAN_Tx);
                 HAL_Delay(5);
                 Print_CAN_Frame("Tx", IPC_SpeedOdometerInfo.ID, IPC_SpeedOdometerInfo.DLC, IPC_SpeedOdometerInfo.CAN_Tx);
                 HAL_Delay(5);
                 timer100ms = 0;
            } else if (timer1000ms == 10) {
                  Print_CAN_Frame("Tx", IPC_StatusBCM.ID, IPC_StatusBCM.DLC, IPC_StatusBCM.CAN_Tx);
                  HAL_Delay(5);
                  timer100ms = 0;
                  timer1000ms = 0;

               }
         }

Full code:

#define TRUE 1
#define FALSE 0
#define FIFO_BUFFER g_rxFifo;
#define USART_HEADER_LENGTH 6
#define USART_ID_LENGTH 7
#define USART_DLC_LENGTH 1
#define USART_MAX_MESSAGE_LENGTH 17
#define USART_DLC_LOCATION 23
#define LENGTH_CARRIGE_RETURN_SIGN 1

TIM_HandleTypeDef htim6;
UART_HandleTypeDef huart2;

void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
static void MX_TIM6_Init(void);
void SystemClockConfig(void);
void UART2_Init(void);
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);
void Clear_Array(uint8_t array[], uint16_t size);
void Print_CAN_Frame(char CanFrameName[], uint32_t CanID, uint32_t CanDlc, uint8_t CANmsg[]);
void Parse_From_UART(char CanFrame[]);
uint8_t* Convert_To_HEX(char *string);
void Save_Data_To_CAN_Frame(CAN_MessageTypeDef canBuffer);
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);

CAN_MessageTypeDef canUartBuffer;
uint32_t timer100ms = 0;
uint32_t timer1000ms = 0;
uint8_t uint_8Buffer[70];
uint8_t data_buffer[70];
uint32_t count = 0;
uint8_t rcvd_data;
uint8_t flag_UART_TX_COMPLETED = FALSE;
uint8_t flag_UART_RX_COMPLETED = FALSE;
uint8_t flag_UART_SEND_DATA = TRUE;

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_USART2_UART_Init();
  MX_TIM6_Init();
  HAL_UART_Receive_IT(&huart2, &rcvd_data, 1);
  HAL_TIM_Base_Start_IT(&htim6);

  while (1)
  {
      if (flag_UART_SEND_DATA == TRUE) {
        //  if (timer100ms >= 2) {

                 Print_CAN_Frame("Tx", IPC_Ligths.ID, IPC_Ligths.DLC, IPC_Ligths.CAN_Tx);
                 HAL_Delay(5);
                 Print_CAN_Frame("Tx", IPC_SpeedOdometerInfo.ID, IPC_SpeedOdometerInfo.DLC, IPC_SpeedOdometerInfo.CAN_Tx);
                 HAL_Delay(5);
                 Print_CAN_Frame("Tx", IPC_SpeedOdometerInfo.ID, IPC_SpeedOdometerInfo.DLC, IPC_SpeedOdometerInfo.CAN_Tx);
                 HAL_Delay(5);
                 Print_CAN_Frame("Tx", IPC_SpeedOdometerInfo.ID, IPC_SpeedOdometerInfo.DLC, IPC_SpeedOdometerInfo.CAN_Tx);
                 HAL_Delay(5);
                 timer100ms = 0;
        //    } else if (timer1000ms == 10) {
                  Print_CAN_Frame("Tx", IPC_StatusBCM.ID, IPC_StatusBCM.DLC, IPC_StatusBCM.CAN_Tx);
                  HAL_Delay(100);
                  timer100ms = 0;
                  timer1000ms = 0;

             //  }
         }

            while((flag_UART_RX_COMPLETED && flag_UART_TX_COMPLETED))
            {
                if (data_buffer[0] == 'S' || data_buffer[0] =='E') // if received data is START / END tx Transmission:
                {
                    Clear_Array(data_buffer, strlen(data_buffer));
                    count = 0;
                    flag_UART_RX_COMPLETED = FALSE;
                    flag_UART_TX_COMPLETED = FALSE;
                    HAL_UART_Receive_IT(&huart2, &rcvd_data, 1);
                } else {
                count = 0;
                //Parse_From_UART(data_buffer);
                //Save_Data_To_CAN_Frame(canUartBuffer);
                Clear_Array(data_buffer, 70);
                flag_UART_RX_COMPLETED = FALSE;
                flag_UART_TX_COMPLETED = FALSE;
                }
            }

  }
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if (rcvd_data == '\r') {
        data_buffer[count++] = '\r';
        flag_UART_RX_COMPLETED = TRUE;
        if (data_buffer[0] == 'S' && data_buffer[1] == 'T') {
            flag_UART_SEND_DATA = TRUE;
        }
        else if ((data_buffer[0] == 'E' && data_buffer[1] == 'D') || (data_buffer[0] == 'E' && data_buffer[1] == 'E')) {
            flag_UART_SEND_DATA = FALSE;
            HAL_UART_Transmit_IT(&huart2, data_buffer, count);
            while (HAL_UART_GetState(&huart2) != HAL_UART_STATE_BUSY_TX);
            count = 0;
            HAL_UART_Receive_IT(&huart2, &rcvd_data, 1);
            return;
        }

        if (HAL_UART_Transmit_IT(&huart2, data_buffer, count) != HAL_OK)
        {
            Error_Handler(); // if HAL_ERROR
        }
        count = 0;
    }
    else
    {
        data_buffer[count++] = rcvd_data;
    }
    HAL_UART_Receive_IT(&huart2, &rcvd_data, 1);
}

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
        flag_UART_TX_COMPLETED = TRUE;
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    timer100ms=timer100ms+1;
    timer1000ms=timer1000ms+1;

}

static void MX_TIM6_Init(void)
{
  htim6.Instance = TIM6;
  htim6.Init.Prescaler = 119;
  htim6.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim6.Init.Period = (59999);
  if (HAL_TIM_Base_Init(&htim6) != HAL_OK)
  {
    Error_Handler();
  }
}

void Print_CAN_Frame(char CanFrameName[], uint32_t CanID, uint32_t CanDlc, uint8_t CANmsg[])
{
    char buffer[70] = {0};
    sprintf(buffer,"CAN_%s| ID:0x%02X| DLC:%d| FRAME: ",CanFrameName,(unsigned int)CanID,(unsigned int)CanDlc);
    for (uint16_t i = 0; i<CanDlc; i++)
    {
        sprintf(buffer+strlen(buffer),"%02X ",*(CANmsg+i)); // print all DATA elements one by one
    }
    sprintf(buffer+strlen(buffer),"\n\r");
    
    Clear_Array(uint_8Buffer, 70);
    for (uint8_t i = 0U; i<strlen(buffer);i++)
    {
        uint_8Buffer[i] = (uint8_t)buffer[i];
    }

     if (HAL_UART_Transmit_IT(&huart2, uint_8Buffer, strlen(buffer)) != HAL_OK)
            {
                Error_Handler();
            }

}

void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};

  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_BYPASS;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  RCC_OscInitStruct.PLL.PREDIV = RCC_PREDIV_DIV1;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART2;
  PeriphClkInit.Usart2ClockSelection = RCC_USART2CLKSOURCE_PCLK1;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
  {
    Error_Handler();
  }
}

static void MX_USART2_UART_Init(void)
{
  huart2.Instance = USART2;
  huart2.Init.BaudRate = 115200;
  huart2.Init.WordLength = UART_WORDLENGTH_8B;
  huart2.Init.StopBits = UART_STOPBITS_1;
  huart2.Init.Parity = UART_PARITY_NONE;
  huart2.Init.Mode = UART_MODE_TX_RX;
  huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart2.Init.OverSampling = UART_OVERSAMPLING_16;
  huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  if (HAL_UART_Init(&huart2) != HAL_OK)
  {
    Error_Handler();
  }
}

static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOF_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

}

@update

I found a partial solution - I reduced the priority of timer interruptions so that the UART has higher. For bigger times (500ms) it seems to work but this solution still doesn't satisfy me - its unstable. And what's more, the 300ms timer only works well if the device is in debugging mode . After turning off the debugger the device does not work properly.

First of all, you need to add the volatile keyword to all (global) variables that are changed in interrupt handlers (ISR) and read from the main loop, ie:

volatile uint32_t timer100ms = 0;
volatile uint32_t timer1000ms = 0;
volatile uint32_t count = 0;
volatile uint8_t flag_UART_TX_COMPLETED = FALSE;
volatile uint8_t flag_UART_RX_COMPLETED = FALSE;
volatile uint8_t flag_UART_SEND_DATA = TRUE;

This tells the compiler, that the value of a variable might change at any time (ie by interrupt). Otherwise the compiler might optimize the code by reading the value of such a variable only once at the first time it is accessed. This will then lead to issues, since some or all changes to the variable done by the ISR will be missed. This can cause unexpected or undefined behavior. Since the optimization level is usually reduced in debug mode, such code might work for debug builds but not for release builds.

NOTE: The volatile keyword does not guarantee atomic access to these variables. On STM32 devices read or write accesses to small types like uint8_t and uint32_t are implicitly atomic since the MCU reads/writes these types always as a whole. In case of a read-modify-write operation or when accessing larger types (like a struct ) you need to take additional measures to assure that data is always consistent (ie not changed within interrupt just while it is read in the main loop).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM