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

注意:

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

完美

相关推荐
清风6666665 小时前
基于单片机的多路温湿度采集与WIFI智能报警控制系统设计
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
爱吃番茄鼠骗5 小时前
回顾ESP32S3系列---基础篇(Bootloader)
单片机·嵌入式硬件
青椒*^_^*凤爪爪5 小时前
Vscode下调试STM32N6系列单片机的方法
vscode·单片机·stm32n6·stm32n647
余生皆假期-5 小时前
永磁同步电机下桥三电阻采样方式的电机电流重构方法
单片机·嵌入式硬件·嵌入式
蓬荜生灰6 小时前
STM32(12)-- GPIO输入,按键检测
stm32·单片机·嵌入式硬件
DLGXY6 小时前
STM32——ADC、多通道转换(十三)
stm32·单片机·嵌入式硬件
日更嵌入式的打工仔7 小时前
嵌入式MPU、MCU与SoC的本质区别
单片机·嵌入式硬件
__万波__7 小时前
STM32L475看门狗
stm32·单片机·嵌入式硬件
wanglong37137 小时前
51单片机STC8G1K08输出PWM
单片机·嵌入式硬件·51单片机
时光找茬18 小时前
【瑞萨AI挑战赛-FPB-RA6E2】+ 从零开始:FPB-RA6E2 开箱测评与 e2 studio 环境配置
c++·单片机·边缘计算