打印
[应用相关]

STM32片上驱动 - TIMER驱动

[复制链接]
182|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
晓伍|  楼主 | 2025-3-11 07:19 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
一、TIMER概述
定时器分类:基本定时器、通用定时器(输出比较、输入捕获)、高级定时器(PWM互补输出、死区及刹车功能);

时基单元:

        计数器:16位计数器,从0计数累加到自动重装载计数值产生溢出,从0重新开始计数;

        预分频器:对时钟源进行分频,降低计数频率;

        自动重装载寄存器:定时器计数周期。

输出比较:

        强制输出模式:通用定时器输出直接由软件将输出比较信号强制设置为有效电平、无效电平或输出不变;

        比较输出模式:根据捕获/比较寄存器和计数器比较结果输出为有效电平、无效电平或者电平翻转;

        PWM输出模式:根据捕获/比较寄存器和计数器比较结果输出为有效电平、无效电平。

输入捕获:设置捕获寄存器,通过边沿中断判断状态值;

二、HWTIMER定时回调
1、CubeMX驱动生成
根据使用定时器选择TIMX:



2、使用HWTIMER设备驱动程序



注意:支持哪些定时器需要查看drivers/include/config/tim_config.h,查找设备时根据.name匹配:



确保HAL库hal_conf.h功能模块使能(CubeMX会自动解除注释):

#define HAL_TIM_MODULE_ENABLED

添加tim.c硬件驱动到drivers/board.c:

void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)

{

    if(tim_baseHandle->Instance==TIM3)

    {

        /* USER CODE BEGIN TIM3_MspInit 0 */

        /* USER CODE END TIM3_MspInit 0 */

        /* TIM3 clock enable */

        __HAL_RCC_TIM3_CLK_ENABLE();

        /* USER CODE BEGIN TIM3_MspInit 1 */

        /* USER CODE END TIM3_MspInit 1 */

    }

}

修改drivers/board.h:

#define BSP_USING_TIM

#ifdef BSP_USING_TIM

#define BSP_USING_TIM3

/*#define BSP_USING_TIM15*/

/*#define BSP_USING_TIM16*/

/*#define BSP_USING_TIM17*/

#endif

3、使用示例
#include <rtthread.h>
#include <rtdevice.h>

static rt_err_t timeoutCallBack(rt_device_t dev, rt_size_t size);
bool timerInit(char *devName, uint32_t sec, uint32_t usec)
{
    // 查找定时器设备
    rt_device_t devTimer = rt_device_find(devName);
    if (devTimer == RT_NULL) {
        rt_kprintf("rt_device_find[%s] failed!\n", devName);
        return false;
    }
    // 打开定时器设备
    rt_err_t ret = rt_device_open(devTimer, RT_DEVICE_OFLAG_RDWR);
    if (ret != RT_EOK) {
        rt_kprintf("rt_device_open[%s] failed!\n", devName);
        return false;
    }
    // 设置超时回调函数
    rt_device_set_rx_indicate(devTimer, timeoutCallBack);
    // 设置计数频率(默认1Mhz或支持的最小计数频率)
    rt_uint32_t freq = 10000;
    ret = rt_device_control(devTimer, HWTIMER_CTRL_FREQ_SET, &freq);
    if (ret != RT_EOK) {
        rt_kprintf("rt_device_control[%s] freq failed!\n", devName);
        return false;
    }
    // 设置模式为周期性定时器
    rt_hwtimer_mode_t mode = HWTIMER_MODE_PERIOD;
    ret = rt_device_control(devTimer, HWTIMER_CTRL_MODE_SET, &mode);
    if (ret != RT_EOK) {
        rt_kprintf("rt_device_control[%s] mode failed!\n", devName);
        return false;
    }
    // 设置定时器超时值并启动定时器
    rt_hwtimerval_t timeout_s;
    timeout_s.sec = sec;
    timeout_s.usec = usec;
    if (rt_device_write(devTimer, 0, &timeout_s, sizeof(timeout_s)) != sizeof(timeout_s)) {
        rt_kprintf("rt_device_write[%s] failed!\n", devName);
        return false;
    }
       
    rt_kprintf("timer init ok!\n");
       
    return true;
}

static rt_err_t timeoutCallBack(rt_device_t dev, rt_size_t size)
{

    return RT_EOK;
}

三、HWTIMER输出比较
1、输出比较种类
强制输出模式:

        强制输出模式是指通用定时器的输出直接由软件将输出比较信号(OxRef和OCx)强制设置为有效电平、无效电平或者输出不变。无需考虑捕获/比较寄存器(CCRx)和计数器(CNT)之间的任何比较结果;

比较输出模式:

        比较输出模式的输出结果和捕获/比较寄存器(CCRx)和计数器(CNT)之间比较结果相关,当这两个值相等的时候,输出结果为有效电平、无效电平或者电平翻转;

PWM输出模式:

        PWM输出模式是输出比较的一个特例,输出结果和捕获/比较寄存器(CCRx)和计数器(CNT)之间的比较值影响,输出结果为有效电平、无效电平。

注:有效电平和无效电平都可以是高电平也可以是低电平,但有效电平和无效电平两者一定是相反值。

2、输出比较框图



比较输出模式下,捕获/比较寄存器(CCRx)的值由用户自行设置,如果计数器(CNT)的值大于或等于捕获/比较寄存器(CCRx)中的值时,会产生对应的标志或者中断。

3、PWM输出模式概述
        PWM是脉冲宽度调制的简称,在输出频率不变的情况下,通过调节其占空比(高电平占整个脉冲周期的比值),从而达到调节输出的目的。

        通用定时器PWM模式可以产生一个信号,这个信号的频率周期是由自动重装载寄存器中的值决定,信号的占空比则由捕获/比较寄存器中的值决定。

PWM输出模式应用:

        控制灯光:七彩灯(RGB灯)、灯光亮度;

        控制电机:直流电机、步进电机、无刷电机;

        红外遥控:红外发射;

        电池充电:控制充电电流。

4、CubeMX驱动生成
查找手册查看GPIO支持复用定时器输出比较通道:



or:



注意:支持哪些定时器需要查看drivers/include/config/pwm_config.h,查找设备时根据.name匹配,如果默认没有则自定义添加:



自定义添加示例:

#ifdef BSP_USING_PWM14

#ifndef PWM14_CONFIG

#define PWM14_CONFIG                            \

    {                                           \

       .tim_handle.Instance     = TIM14,        \

       .name                    = "pwm14",      \

       .channel                 = 0             \

    }

#endif /* PWM14_CONFIG */

#endif /* BSP_USING_PWM14 */

如果.channel不指定且drivers/drv_pwm.c中未预定义通道,则不起作用(注意位移不要搞错):



5、使用PWM设备驱动程序



确保HAL库hal_conf.h功能模块使能(CubeMX会自动解除注释):

#define HAL_TIM_MODULE_ENABLED

添加tim.c硬件驱动到drivers/board.c:

void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)

{

    if(tim_baseHandle->Instance==TIM3)

    {

        /* USER CODE BEGIN TIM3_MspInit 0 */

        /* USER CODE END TIM3_MspInit 0 */

        /* TIM3 clock enable */

        __HAL_RCC_TIM3_CLK_ENABLE();

        /* USER CODE BEGIN TIM3_MspInit 1 */

        /* USER CODE END TIM3_MspInit 1 */

}

    else if(tim_baseHandle->Instance==TIM14)

    {

         /* USER CODE BEGIN TIM14_MspInit 0 */

         /* USER CODE END TIM14_MspInit 0 */

         /* TIM14 clock enable */

         __HAL_RCC_TIM14_CLK_ENABLE();

         /* USER CODE BEGIN TIM14_MspInit 1 */

         /* USER CODE END TIM14_MspInit 1 */

    }

}

void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle)

{

  GPIO_InitTypeDef GPIO_InitStruct = {0};

  if(timHandle->Instance==TIM3)

  {

  /* USER CODE BEGIN TIM3_MspPostInit 0 */

  /* USER CODE END TIM3_MspPostInit 0 */

    __HAL_RCC_GPIOB_CLK_ENABLE();

    /**TIM3 GPIO Configuration

    PB0     ------> TIM3_CH3

    */

    GPIO_InitStruct.Pin = GPIO_PIN_0;

    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;

    GPIO_InitStruct.Pull = GPIO_NOPULL;

    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;

    GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;

    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  /* USER CODE BEGIN TIM3_MspPostInit 1 */

  /* USER CODE END TIM3_MspPostInit 1 */

  }

... ...

}

修改drivers/board.h:

/*#define BSP_USING_PWM1*/

/*#define BSP_USING_PWM2*/

/*#define BSP_USING_PWM3*/

#define BSP_USING_PWM14

#define BSP_USING_PWM14_CH1

6、使用示例
#include <rtthread.h>
#include <rtdevice.h>

struct rt_device_pwm *pwmdev;
bool pwmInit(char *devName, int channel, rt_uint32_t period, rt_uint32_t pulse)
{
    pwmdev = (struct rt_device_pwm *)rt_device_find(devName);
    if (pwmdev == RT_NULL) {
        rt_kprintf("rt_device_find[%s] failed!\n", devName);
        return false;
    }
    rt_pwm_set(pwmdev, channel, period, pulse);
    rt_pwm_enable(pwmdev, channel);

    return true;
}

四、HWTIMER输入捕获
1、输入捕获概述
通用定时器输入捕获用于对输入信号脉冲宽度测量(测量时钟脉冲信号的周期以及时钟脉冲信号的占空比)。

输入捕获框图:





总结:通过检测TIMx_CHx边沿信号,在边沿信号发生跳变(比如上升沿/下降沿)时,将当前定时器的计数值(TIMx_CNT)保存到对应通道的捕获/比较寄存器,完成一次捕获。

2、CubeMX驱动生成
定时器通道配置:



中断使能:



3、 使用示例
#include <rtthread.h>
#include <rtdevice.h>

#include "tim.h"

#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>

// 中断服务函数
// 默认生成到cubemx/Src/stm32f4xx_it.c,由于该文件未添加到SConscript,所以单独摘出来
void TIM3_IRQHandler(void)
{
  /* USER CODE BEGIN TIM3_IRQn 0 */

  /* USER CODE END TIM3_IRQn 0 */
  HAL_TIM_IRQHandler(&htim3);
  /* USER CODE BEGIN TIM3_IRQn 1 */

  /* USER CODE END TIM3_IRQn 1 */
}

// 输入捕获中断回调函数
uint16_t last_capture = 0;
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
    // 周期计算 - 100Hz的方波意味着每个周期的时间应该是10ms(10000微秒)
    if (htim->Instance == TIM3 && htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) {
        uint16_t capture = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
        uint16_t diff = (capture >= last_capture) ? (capture - last_capture) : ((0xFFFF - last_capture) + capture + 1);
        last_capture = capture;

        rt_kprintf("Capture: %d, Diff: %d\n", capture, diff);
    }
}

int main(void)
{
    // TIM3初始化
    MX_TIM3_Init();
    // TIM3_CH1中断使能
    if (HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_1) != HAL_OK) {
        rt_kprintf("HAL_TIM_IC_Start_IT error!\n");
        // 启动错误处理
        Error_Handler();
    }

    while (1) {
        rt_kprintf("status: %d\n", 1);
        rt_thread_mdelay(1000);
    }

    return RT_EOK;
}




————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/dxf1971738522/article/details/146058720

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

80

主题

4243

帖子

1

粉丝