使用RT-Thread Studio + CubeMX开发项目(三):使用timer做超时检测

摘要:接着PWM之后,本文将继续增加 Timer,用来做串口数据的分包。

第一步、利用 RT-Thread Studio 配置 TIMER 相关代码(接着 PWM 项目进行)

首先在 board.h 中官方给出了如何在 rtt 中使用 HWTIMER。

  • STEP 1:RT-Thread Settings 中打开 hwtimer 驱动支持。
  • STEP 2:在 board.h 中定义相关宏,这里注意 “BSP_USING_PWM1_CH1 ”,这个也是需要的,可以看。

    /** if you want to use hardware timer you can use the following instructions.
     *
     * STEP 1, open hwtimer driver framework support in the RT-Thread Settings file
     *
     * STEP 2, define macro related to the hwtimer
     *                 such as     #define BSP_USING_TIM  and
     *                             #define BSP_USING_TIM1
     *
     * STEP 3, copy your hardwire timer init function from stm32xxxx_hal_msp.c generated by stm32cubemx to the end of board.c file
     *                 such as     void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* htim_base)
     *
     * STEP 4, modify your stm32xxxx_hal_config.h file to support hardwere timer peripherals. define macro related to the peripherals
     *                 such as     #define HAL_TIM_MODULE_ENABLED
     *
     */
    
    #define BSP_USING_TIM
    #ifdef BSP_USING_TIM
    #define BSP_USING_TIM2
    /*#define BSP_USING_TIM16*/
    /*#define BSP_USING_TIM17*/
    #endif
    
  • STEP 3:使用 cubemx 生成 hwtimer 的初始化相关代码 HAL_TIM_Base_MspInit (只需要修改红色标记就可以,其他的我们使用 rtt 中提供的 API 来修改就好,timer_init 会处理。),并拷贝 HAL_TIM_Base_MspInit 到 board.c 中。

/* 主要是下边的函数中对定时器的设置 */
static void timer_init(struct rt_hwtimer_device *timer, rt_uint32_t state)
{
    uint32_t prescaler_value = 0;
    TIM_HandleTypeDef *tim = RT_NULL;
    struct stm32_hwtimer *tim_device = RT_NULL;

    RT_ASSERT(timer != RT_NULL);
    if (state)
    {
        tim = (TIM_HandleTypeDef *)timer->parent.user_data;
        tim_device = (struct stm32_hwtimer *)timer;

        /* time init */
			
				... ...
       
        if (HAL_TIM_Base_Init(tim) != HAL_OK)
        {
            LOG_E("%s init failed", tim_device->name);
            return;
        }
        else
        {
            /* set the TIMx priority */
            HAL_NVIC_SetPriority(tim_device->tim_irqn, 3, 0);

            /* enable the TIMx global Interrupt */
            HAL_NVIC_EnableIRQ(tim_device->tim_irqn);

            /* clear update flag */
            __HAL_TIM_CLEAR_FLAG(tim, TIM_FLAG_UPDATE);
            /* enable update request source */
            __HAL_TIM_URS_ENABLE(tim);

            LOG_D("%s init success", tim_device->name);
        }
    }
}
/**
* @brief TIM_Base MSP Initialization
* This function configures the hardware resources used in this example
* @param htim_base: TIM_Base handle pointer
* @retval None
*/
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* htim_base)
{
  if(htim_base->Instance==TIM1)
  {
  /* USER CODE BEGIN TIM1_MspInit 0 */

  /* USER CODE END TIM1_MspInit 0 */
    /* Peripheral clock enable */
    __HAL_RCC_TIM1_CLK_ENABLE();
  /* USER CODE BEGIN TIM1_MspInit 1 */

  /* USER CODE END TIM1_MspInit 1 */
  }
  else if(htim_base->Instance==TIM2)
  {
  /* USER CODE BEGIN TIM2_MspInit 0 */

  /* USER CODE END TIM2_MspInit 0 */
    /* Peripheral clock enable */
    __HAL_RCC_TIM2_CLK_ENABLE();
  /* USER CODE BEGIN TIM2_MspInit 1 */

  /* USER CODE END TIM2_MspInit 1 */
  }

}
  • STEP 4:修改 stm32xxxx_hal_config.h 文件,打开 HAL 对相关功能的支持。

第二步、完善对 PWM 初始化及测试的代码

RTT 定时器设备使用流程:

  1. 以读写方式打开设备
  2. 设置超时回调函数(如果需要)
  3. 根据需要设置定时模式(单次/周期)
  4. 设置计数频率(可选)
  5. 写入超时值,定时器随即启动
  6. 停止定时器(可选)
  7. 关闭设备(如果需要)

创建路径 “RttDemo\applications\service\timer” 并创建文件 “Timer2.h” “Timer2.c”。

  • “Timer2.h” 内容如下:

    /*
     * Copyright (c) 2006-2020, Linfeng Electronic Technology Co., Ltd
     *
     * Change Logs:
     * Date           Author       Notes
     * 2020-10-23     ZhangFan     first version
     */
    
    #ifndef _TIMER_2_H_
    #define _TIMER_2_H_
    
    #include <rtdevice.h>
    
    typedef rt_err_t (*timeoutfunc)(rt_uint32_t cnt);
    
    rt_bool_t timer2_init();
    
    /* 超时事件注册器,注:事件函数中不应该有耗时操作 */
    rt_bool_t timer2_timeout_event_register(timeoutfunc func);
    
    #endif //_TIMER_2_H_
    
  • “Timer2.c” 内容如下:

    #include "Timer2.h"
    #include <string.h>
    
    /* log */
    #define DBG_TAG    "Timer2"
    #define DBG_LVL    DBG_LOG
    #include <rtdbg.h>
    
    #define HWTIMER_DEV_NAME   "timer2"                             /* 定时器名称 */
    
    #define FUNC_MAX_COUNT 10
    typedef struct _timeoutfunclist
    {
        timeoutfunc timeoutfuncs[FUNC_MAX_COUNT];                   /* 超时函数列表 */
        rt_int8_t count;                                            /* 超时函数列表实际个数 */
    }timeoutfunclist;
    
    rt_device_t hw_dev = RT_NULL;                                   /* 定时器设备句柄 */
    rt_int32_t timerticks = 0;                                      /* 定时计数器 */
    timeoutfunclist funclist;                                       /* 超时回调函数列表 */
    
    /* 定时器超时回调函数 */
    static rt_err_t timeout_cb(rt_device_t dev, rt_size_t size)
    {
        timerticks++;
    
        for(rt_int8_t i = 0; i < funclist.count; i++)
        {
            funclist.timeoutfuncs[funclist.count](timerticks);
        }
    
        return 0;
    }
    
    /* 设置定时器特性 */
    static rt_bool_t timer2_default_set()
    {
        rt_err_t ret = RT_EOK;
        rt_hwtimerval_t timeout_s;              /* 定时器超时值 */
        rt_hwtimer_mode_t mode;                 /* 定时器模式 */
    
        /* 设置模式为周期性定时器 */
        mode = HWTIMER_MODE_PERIOD;
        ret = rt_device_control(hw_dev, HWTIMER_CTRL_MODE_SET, &mode);
        if (ret != RT_EOK)
        {
            LOG_D("set mode failed! ret is :%d\n", ret);
            return ret;
        }
    
        /* 设置定时器超时值为5s并启动定时器 */
        timeout_s.sec = 0;      /* 秒 */
        timeout_s.usec = 2 * 100000;     /* 微秒 */
        if (rt_device_write(hw_dev, 0, &timeout_s, sizeof(timeout_s))
                != sizeof(timeout_s))
        {
            LOG_D("set timeout value failed\n");
            return RT_ERROR;
        }
    
        /* 延时3500ms */
        rt_thread_mdelay(3500);
    
        /* 读取定时器当前值 */
        rt_device_read(hw_dev, 0, &timeout_s, sizeof(timeout_s));
    
        LOG_D("Read: Sec = %d, Usec = %d\n", timeout_s.sec, timeout_s.usec);
    
        return RT_TRUE;
    }
    
    rt_bool_t timer2_init()
    {
        rt_err_t ret = RT_EOK;
        memset(&funclist, 0, sizeof(funclist));
    
        /* 查找定时器设备 */
        hw_dev = rt_device_find(HWTIMER_DEV_NAME);
        if (hw_dev == RT_NULL)
        {
            LOG_D("hwtimer sample run failed! can't find %s device!\n",
                    HWTIMER_DEV_NAME);
            return RT_FALSE;
        }
    
        /* 以读写方式打开设备 */
        ret = rt_device_open(hw_dev, RT_DEVICE_OFLAG_RDWR);
        if (ret != RT_EOK)
        {
            LOG_D("open %s device failed!\n", HWTIMER_DEV_NAME);
            return RT_FALSE;
        }
    
        /* 设置超时回调函数 */
        ret = rt_device_set_rx_indicate(hw_dev, timeout_cb);
        if (ret != RT_EOK)
        {
            LOG_D("%s set timeout callback failed!\n", HWTIMER_DEV_NAME);
            return RT_FALSE;
        }
    
        /* 定时器默认设置 */
        if(!timer2_default_set())
        {
            LOG_D("%s set failed!\n", HWTIMER_DEV_NAME);
            return RT_FALSE;
        }
    
        return RT_TRUE;
    }
    
    /* 超时事件注册器,注:事件函数中不应该有耗时操作 */
    rt_bool_t timer2_timeout_event_register(timeoutfunc func)
    {
        if(funclist.count >= FUNC_MAX_COUNT)
        {
            return RT_FALSE;
        }
    
        funclist.timeoutfuncs[funclist.count] = func;
        funclist.count++;
    
        return RT_TRUE;
    }
    
  • “main.c”函数内容:

    #include <rtthread.h>
    
    #define DBG_TAG "main"
    #define DBG_LVL DBG_LOG
    #include <rtdbg.h>
    
    #include "SerialUart2.h"
    #include "PwmChanel1.h"
    #include "Timer2.h"
    
    int main(void)
    {
        int count = 1;
    
        serial2_init(); /* 串口2初始化 */
        pwm_ch1_init(); /* PWM 初始化 */
        timer2_init();  /* TIMER初始化 */
    
        while (count++)
        {
            //LOG_D("Hello RT-Thread!");
            serial2_write("Hello RT-Thread!\r\n", sizeof("Hello RT-Thread!\r\n") - 1);
            rt_thread_mdelay(1000);
        }
    
        return RT_EOK;
    }
    

第三步、烧写并测试

定时器回调函数被触发,证明程序工作正常,后边我们只需要通过 “timer2_timeout_event_register” 函数注册用户自己的定时任务就可以,这里写的比较简单,因为我这边主要后期用于检测 uart 接收超时分包。

参考文档

HWTIMER设备 - RT-Thread 文档中心