记录下1个定时器作为多个串口的超时计数器的方式
1 常规串口接收方式
使用定时器作为串口的超时计数。当有数据来时,重新复位计数器,等数据帧最后一个字节到来后,则必然会超时。在定时器中断中则代表接收了完整帧
2使用多个串口时
当有多个串口时,1个的串口对应1个定时器会相当麻烦,那能不能是使用1个定时器作为做个串口的计数器呢。根据DeepSeek,亲测试验可行
头文件
c
#define MAX_UART_NUM 3 // 最大串口数量
#define UART_TIMEOUT_MS 100 // 超时时间(ms)
typedef struct {
UART_HandleTypeDef *huart; // 串口句柄
uint8_t rx_buffer[256]; // 接收缓冲区
uint16_t rx_index; // 接收索引
uint32_t last_active_tick; // 最后活动时间戳
uint8_t data_ready; // 数据就绪标志
void (*callback)(uint8_t uart_id, uint8_t *data, uint16_t len); // 回调函数
} uart_timeout_t;
c文件
c
static TIM_HandleTypeDef htim_timeout;
uart_timeout_t g_uart_mgr[MAX_UART_NUM];
volatile uint32_t g_timer_ticks = 0;
/* TIM7 init function */
void MX_TIM7_Init(void) //1ms定时
{
/* USER CODE BEGIN TIM7_Init 0 */
/* USER CODE END TIM7_Init 0 */
TIM_MasterConfigTypeDef sMasterConfig = {0};
/* USER CODE BEGIN TIM7_Init 1 */
/* USER CODE END TIM7_Init 1 */
htim7.Instance = TIM7;
htim7.Init.Prescaler = 8999;
htim7.Init.CounterMode = TIM_COUNTERMODE_UP;
htim7.Init.Period = 9;
htim7.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_Base_Init(&htim7) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim7, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM7_Init 2 */
/* USER CODE END TIM7_Init 2 */
}
// 定时器更新中断回调
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM6)
{
HAL_IncTick();
}
if (htim->Instance == TIM7)
{
g_timer_ticks++;
// 检查所有串口的超时状态
for (int i = 0; i < MAX_UART_NUM; i++) {
if (g_uart_mgr[i].huart != NULL &&
g_uart_mgr[i].rx_index > 0) {
// 计算时间差
uint32_t time_diff = g_timer_ticks - g_uart_mgr[i].last_active_tick;
if (time_diff >= UART_TIMEOUT_MS) {
// 触发超时处理
g_uart_mgr[i].data_ready = 1;
// 调用回调函数
if (g_uart_mgr[i].callback != NULL) {
g_uart_mgr[i].callback(i, g_uart_mgr[i].rx_buffer, g_uart_mgr[i].rx_index);
}
// 重置接收状态
g_uart_mgr[i].rx_index = 0;
}
}
}
}
}
// 串口管理器初始化
void UART_Timeout_Manager_Init(void)
{
// 初始化定时器
MX_TIM7_Init();
// 启动定时器中断
HAL_TIM_Base_Start_IT(&htim7);
// 初始化所有串口管理结构
for (int i = 0; i < MAX_UART_NUM; i++) {
memset(&g_uart_mgr[i], 0, sizeof(uart_timeout_t));
g_uart_mgr[i].rx_index = 0;
g_uart_mgr[i].data_ready = 0;
g_uart_mgr[i].last_active_tick = 0;
}
}
// 注册串口到超时管理器
uint8_t UART_Register_Timeout(UART_HandleTypeDef *huart, uint8_t uart_id,
void (*callback)(uint8_t uart_id, uint8_t *data, uint16_t len))
{
if (uart_id >= MAX_UART_NUM) {
return 0;
}
g_uart_mgr[uart_id].huart = huart;
g_uart_mgr[uart_id].callback = callback;
g_uart_mgr[uart_id].rx_index = 0;
g_uart_mgr[uart_id].data_ready = 0;
g_uart_mgr[uart_id].last_active_tick = g_timer_ticks;
return 1;
}
使用
c
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_I2C2_Init();
MX_USART2_UART_Init();
MX_TIM7_Init();
/* USER CODE BEGIN 2 */
SendOver();
UART_Timeout_Manager_Init();
UART_Register_Timeout(&huart2, 1, User_UART_Data_Handler);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
回调函数
c
/* USER CODE END 0 */
// 用户定义的数据处理回调函数
void User_UART_Data_Handler(uint8_t uart_id, uint8_t *data, uint16_t len)
{
SendBefore();
switch(uart_id) {
case 0:
printf("UART1 Received %d bytes: ", len);
for (int i = 0; i < len; i++) {
printf("%02X ", data[i]);
}
printf("\r\n");
break;
case 1:
printf("UART2 Received %d bytes: ", len);
for (int i = 0; i < len; i++) {
printf("%02X ", data[i]);
}
printf("\r\n");
break;
case 2:
printf("UART3 Received %d bytes: ", len);
for (int i = 0; i < len; i++) {
printf("%02X ", data[i]);
}
printf("\r\n");
break;
}
SendOver();
// 这里可以添加协议解析、数据存储等操作
}
注意:
回调函数是在中断中处理的,不要进行耗时操作。只是用于演示,打印等操作不应在此处

完美