简体   繁体   English

STM32 UART传输问题(阻塞和中断模式)

[英]STM32 UART transmission problem (blocking and interrupt mode)

I have a problem with the HAL_UART_Transmit_IT function from the HAL library.我对 HAL 库中的HAL_UART_Transmit_IT function 有疑问。 I use this function in two places in the program:我在程序的两个地方使用了这个 function:

  1. In function HAL_UART_RxCpltCallback , where I write back to UART the data received on interruption (just an echo).在 function HAL_UART_RxCpltCallback中,我将中断时收到的数据写回UART(只是一个回声)。 Here everything works correctly.在这里一切正常。

  2. In Print_CAN_Frame function where I send every 3s message on UART (from main function).Print_CAN_Frame function 中,我在 UART 上每 3 秒发送一次消息(来自函数)。 Previously I used the blocking function there ( HAL_UART_Transmit ) and program prited data correctly.以前我在那里使用了阻塞 function ( HAL_UART_Transmit )并正确编程 prited 数据。 But when I changed it to innterrupt ( HAL_UART_Transmit_IT ) I get weird characters as in screen here: TeraTerm screen .但是,当我将其更改为中断( HAL_UART_Transmit_IT )时,我会在此处的屏幕中看到奇怪的字符: TeraTerm screen The most interesting thing is that both functions take the same arguments so they should not differ... I use Nucleo F303RE board.最有趣的是,这两个函数都采用相同的 arguments,所以它们应该没有区别……我使用的是 Nucleo F303RE 板。

#include "main.h"
#include "main.h"
#include "stm32f3xx_hal.h"
#include "stm32f303xe.h"
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <inttypes.h>

void SystemClockConfig(void);
void UART2_Init(void);
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);
void ClearArray(uint8_t array[], uint32_t size);
void Print_CAN_Frame(char CanFrameName[], uint32_t CanID, uint32_t CanDlc, uint8_t CANmsg[]);
void parseFromUART(char CanFrame[]);
uint8_t* convertToHex(char *string);
void saveDataToFrame(CAN_MessageTypeDef canBuffer);

#define TRUE 1
#define FALSE 0
#define FIFO_BUFFER g_rxFifo;

CAN_MessageTypeDef IPC_Ligths =
{
        0x2214000,                          // ID
        6,                                  // DLC
        {0x00,0x00,0x00,0x00,0x00,0x00},    // TX frame
        {0}                                 // RX frame initialization
};

CAN_MessageTypeDef canUartBuffer;

void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);

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;


int main(void)
{
 
  HAL_Init();

  SystemClock_Config();
  MX_GPIO_Init();
  MX_USART2_UART_Init();
  HAL_UART_Receive_IT(&huart2, &rcvd_data, 1);
 
  while (1)
  {
      Print_CAN_Frame("Tx", IPC_Ligths.ID, IPC_Ligths.DLC, IPC_Ligths.CAN_Tx);
      HAL_Delay(3000);

            while((flag_UART_RX_COMPLETED && flag_UART_TX_COMPLETED) == TRUE)
            {
                ClearArray(data_buffer, 70);
                count = 0;
                flag_UART_RX_COMPLETED = FALSE;
                flag_UART_TX_COMPLETED = FALSE;
            }
  }
}


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

  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_BYPASS;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSE;
  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_0) != 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();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pin : B1_Pin */
  GPIO_InitStruct.Pin = B1_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(B1_GPIO_Port, &GPIO_InitStruct);

  /*Configure GPIO pin : LD2_Pin */
  GPIO_InitStruct.Pin = LD2_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(LD2_GPIO_Port, &GPIO_InitStruct);

}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if (rcvd_data == '\r') {
        data_buffer[count++] = '\r';
        flag_UART_RX_COMPLETED = TRUE;
        if (HAL_UART_Transmit_IT(&huart2, data_buffer, count) != HAL_OK)
        {
            Error_Handler();
        }
    }
    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 ClearArray(uint8_t array[], uint32_t size)
{
    for (int i = 0; i < size; ++i)
    {
        array[i] = 0;
    }
}

void Print_CAN_Frame(char CanFrameName[], uint32_t CanID, uint32_t CanDlc, uint8_t CANmsg[])
{
    char buffer[100] = {0};
    sprintf(buffer,"CAN_%s| ID:0x%02X| DLC:%d| FRAME: ",CanFrameName,(unsigned int)CanID,(unsigned int)CanDlc);
    for (uint8_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"); // add in the end of each frame new line and ....
// Problem in here:
    HAL_UART_Transmit_IT(&huart2, (uint8_t*)buffer, strlen(buffer));
//  HAL_UART_Transmit(&huart2, (uint8_t*)buffer, strlen(buffer),HAL_MAX_DELAY);

}

void Error_Handler(void)
{
 
    while (1)
        ;
}

Caveat: Not a total solution but some observations.警告:不是一个完整的解决方案,而是一些观察结果。


Race condition比赛条件

It appears that you have some race conditions between ISRs and main function.看来您在 ISR 和主要 function 之间存在一些竞争条件。 When you do something at task level that changes variables that are used in an ISR, the task level has to wrap such access to common variables in (eg):当您在任务级别执行更改 ISR 中使用的变量的操作时,任务级别必须将此类对公共变量的访问包装在(例如)中:

disable_interrupts();

// change variables used by ISR ...

enable_interrupts();

Likewise for any variables changed by an ISR that are read/used by task level.对于由任务级别读取/使用的 ISR 更改的任何变量也是如此。 They should also be wrapped (eg):它们也应该被包裹(例如):

disable_interrupts();

int saved_value_1 = global_variable_1;
int saved_value_2 = global_variable_2;

enable_interrupts();

// do stuff with the saved_value_* variables ...

Your Print_CAN_Frame is broken.你的Print_CAN_Frame坏了。

In either version, buffer is function scoped (ie) it is on the stack.在任一版本中, buffer都是function范围(即)它在堆栈上。 When the function returns the buffer goes out of scope (ie It is no longer usable).当 function 返回时,缓冲区超出 scope(即不再可用)。

HAL_UART_Transmit_IT is non-blocking. HAL_UART_Transmit_IT是非阻塞的。 When it returns, nothing has been transmitted.当它返回时,什么都没有传输。 It just sets the buffer address/count into the control struct.它只是将缓冲区地址/计数设置到控制结构中。 It expects that the buffer will remain stable until all chars have been sent [under interrupt control at some later time].它预计缓冲区将保持稳定,直到所有字符都已发送[在稍后的中断控制下]。

As you have it, after return from your function, the control struct is pointing to stack memory that can have arbitrary values [because other functions may be called and overwrite the stack frame with unrelated values].正如你所拥有的,在从 function 返回后,控制结构指向堆栈 memory 可以具有任意值[因为可能会调用其他函数并用不相关的值覆盖堆栈帧]。

Make buffer in Print_CAN_Frame a static variable.Print_CAN_Frame中的buffer设为static变量。 Or, better yet, change the buffer name to (eg) print_can_buffer and make it global/file scope as you have done for data_buffer或者,更好的是,将缓冲区名称更改为(例如) print_can_buffer并使其成为全局/文件 scope,就像对data_buffer所做的那样


You're not checking error/return values from the HAL_* primitives.没有检查HAL_*原语的错误/返回值。


You may need to design an "event ring queue" for debugging.您可能需要设计一个“事件环队列”进行调试。 I've done this many times in the past and find it extremely useful.我过去做过很多次,发现它非常有用。

At key points in your code, call an event_add function that stores in a ring queue of structs that have a timestamp, event type, and a type specific value (eg):在代码中的关键点,调用event_add function 存储在具有时间戳、事件类型和类型特定值(例如)的结构的环形队列中:

event_add(EVENT_TYPE_TXSEND,'\r');

After all is done, you can dump out this ring queue to see what happened at critical times, so you have a history similar to a logic analyzer trace [similar to Sun's dtrace ].完成后,您可以转储此环队列以查看在关键时刻发生了什么,因此您拥有类似于逻辑分析仪跟踪的历史记录 [类似于 Sun 的dtrace ]。

The ring queue primitives [notably, event_add ] should be wrapped in cli/sti pairs as mentioned above.如上所述,环形队列原语 [特别是event_add ] 应该包装在cli/sti对中。

I think I found a solution.我想我找到了解决办法。 Data in Print_Can_Frame function after typecasting to uint8 array and sending the array in this form started to work properly: Print_Can_Frame function 中的数据在类型转换为 uint8 数组并以这种形式发送数组后开始正常工作:

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();
    }

The solution is not pretty, but it works解决方案并不漂亮,但它有效

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

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