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

注意:

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

完美

相关推荐
三佛科技-187366133971 小时前
BP8501CH是什么芯片?BP8501CH(输出功率说明及典型电路图)
单片机·嵌入式硬件
芯联智造2 小时前
【stm32简单外设篇】- 水银开关
c语言·stm32·单片机·嵌入式硬件
Geek__19922 小时前
STM32F103开发板上移植Agile Modbus库的详细指南
stm32·嵌入式硬件·敏捷流程
沐欣工作室_lvyiyi2 小时前
基于单片机的用电器功率监测报警系统设计(论文+源码)
单片机·嵌入式硬件·功率监测
贝塔实验室2 小时前
Altium Designer原理图编辑基础
单片机·嵌入式硬件·硬件工程·信息与通信·射频工程·基带工程·嵌入式实时数据库
hazy1k2 小时前
MSPM0L1306 从零到入门:第二章 GPIO 从入门到精通 —— 点亮你的第一颗LED
stm32·单片机·嵌入式硬件·esp32·ti·mspm0
d111111111d2 小时前
STM32外设学习--PWR电源控制
笔记·stm32·单片机·嵌入式硬件·学习
MounRiver_Studio2 小时前
RISC-V IDE MRS2使用笔记(二): 编译后Memory分析
ide·笔记·单片机·嵌入式·risc-v
小柯博客3 小时前
从零开始打造 OpenSTLinux 6.6 Yocto 系统 - STM32MP2(基于STM32CubeMX)(三)
stm32·嵌入式硬件·开源·嵌入式·yocto·st·stm32mp2