简体   繁体   English

通过UART发送数据期间的STM32 Timer Counter问题

[英]STM32 Timer Counter problem during sending data via UART

I have some problem with the handling of the timer in code (STM32F303RE).我在代码(STM32F303RE)中处理定时器有一些问题。 My goal is to send messages to UART port every 200ms (one packet) and every 1s (second packet).我的目标是每 200 毫秒(一个数据包)和每 1 秒(第二个数据包)向 UART 端口发送消息。 I implemented timer ( MX_TIM6_Init ) which increments 2 variables ( timer100ms and timer1000ms ) every 100ms ( HAL_TIM_PeriodElapsedCallback ).我实现了定时器( MX_TIM6_Init ),它每 100 毫秒( HAL_TIM_PeriodElapsedCallback )增加 2 个变量( timer100mstimer1000ms )。

What the code does?代码有什么作用?

Sends messages to the UART in normal operation.在正常操作中向 UART 发送消息。 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.当其他设备要向 STM32 发送信息时,首先发送一个“ED”帧,这会将标志( flag_UART_SEND_DATA 更改为FALSE )并关闭主循环中的所有通信。 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 ):当代码注释了有关检查计时器和数据值的部分时,一切正常(即使每 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 ).当任何消息被发送时(例如通过 TeraTerm),只有发送消息的前两个字符被接收作为响应(例如通过发送HELLO给出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. UART 发送到设备的帧完全进入,完整数据被传递到HAL_UART_TRANSMIT_IT (在HAL_UART_RxCpltCallback中)和 HAL_UART_TRANSMIT_IT 返回 HAL_OK -> 但在控制台上它只发送 2 个字符。 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.我找到了一个部分解决方案——我降低了定时器中断的优先级,以便 UART 具有更高的优先级。 For bigger times (500ms) it seems to work but this solution still doesn't satisfy me - its unstable.对于更大的时间(500 毫秒),它似乎可以工作,但这个解决方案仍然不能满足我 - 它不稳定。 And what's more, the 300ms timer only works well if the device is in debugging mode .更重要的是,300ms定时器只有在设备处于调试模式时才能正常工作 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关键字添加到在中断处理程序 (ISR) 中更改并从主循环读取的所有(全局)变量中,即:

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.这将导致问题,因为 ISR 对变量所做的部分或全部更改将被遗漏。 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.注意: volatile关键字不保证对这些变量的原子访问。 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.在 STM32 设备上,对uint8_tuint32_t等小类型的读取或写入访问是隐式原子的,因为 MCU 始终将这些类型作为一个整体进行读取/写入。 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).在读取-修改-写入操作或访问较大类型(如struct )的情况下,您需要采取额外措施以确保数据始终一致(即在主循环中读取时不会在中断内更改)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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