使用1个定时器作为多个串口的超时计数器

记录下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();
    
    // 这里可以添加协议解析、数据存储等操作
}

注意:

回调函数是在中断中处理的,不要进行耗时操作。只是用于演示,打印等操作不应在此处

完美

相关推荐
悠哉悠哉愿意2 小时前
【EDA学习笔记】电子技术基础知识:元件数据手册
笔记·单片机·嵌入式硬件·学习·eda
点灯小铭2 小时前
基于单片机的档案库房漏水检测报警labview上位机系统设计
单片机·嵌入式硬件·毕业设计·课程设计·labview·期末大作业
一个平凡而乐于分享的小比特3 小时前
STM32 GPIO 8种工作模式深入详解
stm32·单片机·嵌入式硬件·gpio
x县豆瓣酱3 小时前
STM32F1新建工程(基于STMCubeMX)
stm32·单片机·嵌入式硬件
Sean_woo19983 小时前
Zephyr rtos ESP32系列BSP提交流程指南
stm32·单片机·esp32·wsl·zephyr·立创开发板
polarislove02144 小时前
8.2 时钟树编程-嵌入式铁头山羊STM32笔记
笔记·stm32·嵌入式硬件
AI_56784 小时前
STM32开发的效率跃迁
stm32·单片机·嵌入式硬件
旧梦吟4 小时前
脚本网页 linux内核源码讲解
linux·前端·stm32·算法·html5
进阶的猪14 小时前
STM32 使用HAL库SPI读写FLASH(W25Q128JV)数据 Q&A
c语言·stm32·单片机
就是蠢啊18 小时前
51单片机——DAC数模转换实验(一)
单片机·嵌入式硬件·51单片机