一、我要解决的问题
我的需求
需要单片机几乎同时在A,B,C 三个IO 分别输出T1,T2,T3 时长的高电平,时间结束后,恢复低电平。
初步思路
面对这个需求,我第一时间想到的是用三个定时器,分别设置T1,T2,T3 时间的定时,定时结束之后,在中断处理函数,将对应的IO 拉低,但是这个做法有几点不好:
- 产生了很多中断。
- 如果程序有更高级的中断在响应,当前中断会得不到及时响应,影响时间的精度。
- 程序结构比较分散。
解决办法
重新研究了一次定时器的功能,发现定时器的pwm 单脉冲功能,能完美解决这个问题。
二、定时器 pwm
先来整理一下普通的 定时器 pwm 功能的相关知识点。下面默认以计数器向上计数为例说明。
2.1 定时器是如何产生pwm的?
图1
介绍一个pwm信号产生的过程
- 计时器从0 开始计数,在达到CCRx 值之前,输出口维持初始电平。
- 在计数值达到CCRx 时,电平翻转。
- 在计数值到达ARR 值时,电平再翻转(恢复初始电平),计数值复位为0。重新回到第1步。
上面流程循环执行,产生连续的pwm 波形。
2.2 几个重要的参数
2.2.1 预分频器(PSC)
值的范围是0 - 65535。定时器计数频率 = 定时器时钟频率/ ( PSC + 1)。
假设定时器的时钟频率的64M,PSC 设置为63,那么定时器计数频率是1MHZ,也就是每1us,计数值加1。
2.2.2 周期(Counter Period)
pwm 周期 = (ARR + 1)/ 定时器计数频率。
2.2.3 有效电平(CH Polarity)
可以设置为 High 或 Low。有效电平的影响,参考下面的PWM1 和PWM2 模式。
2.2.4 PWM1 和 PWM2 的区别
CNT < CCRx | CNT > CCRx | |
---|---|---|
PWM模式1 | 有效电平 | 无效电平 |
pwm 模式2 | 无效电平 | 有效电平 |
三、pwm 输出的特例:单脉冲输出
3.1 简介
单脉冲输出,就是只有一个输出周期的pwm。当计数器向上计数时,计数值到ARR时,计数器自动停止。当计数器向下计数时,计数值到0时,计数器自动停止。
图2
3.2 如何配置单脉冲模式
配置 TIMx_CR1 的OPM bit 为1,进入one-pulse mode。使用Cubemx 配置定时器时,勾选One Pulse Mode 前面的勾。
图3
3.3 如何使用单脉冲模式
单脉冲模式,分成两个阶段:Delay 阶段,脉冲输出阶段。
uint16_t pulse_delay = 50;
uint16_t pulse_width = 1000;
__HAL_TIM_SET_AUTORELOAD( &htim16, pulse_delay + pulse_width - 2 );
__HAL_TIM_SET_COMPARE( &htim16, TIM_CHANNEL_1, pulse_delay - 1 );
HAL_TIM_PWM_Start( &htim16, TIM_CHANNEL_1);
调用 __HAL_TIM_SET_AUTORELOAD 设置ARR 寄存器值,设置整个周期的长度。
调用 __HAL_TIM_SET_COMPARE 设置CCR1值,设置Delay 阶段的时间
四、我遇到的问题
4.1 问题描述
输出单脉冲信号之后,切换状态输出pwm,无法正常输出。步骤如下:
- 先在TIM_CHANNEL_1 输出 常规pwm 波形,pwm 输出正常。
- 然后在TIM_CHANNEL_1 输出单脉冲信号,单脉冲输出正常。
- 再在TIM_CHANNEL_1 输出常规pwm 波形,无法输出pwm 信号。
4.2 解决步骤
4.2.1 重新初始化定时器
每次输出pwm 和单脉冲信号时,都重新完整初始化一次定时器,问题依然存在。
4.2.2 清空单脉冲配置位
配置输出pwm 信号之前,调用以下函数,复位单脉冲配置的状态
HAL_TIM_OnePulse_DeInit( &htim16 );
HAL_TIM_PWM_DeInit( &htim16 );
4.2.3 逐个寄存器比较定时器的配置
每次配置输出pwm 之前,打印一次当前定时器的配置(htim16. Instance 里面的各个成员函数),通过比较,发现 htim16.Instance->DMAR 这个参数被修改了。执行如下代码,问题解决:
HAL_TIM_OnePulse_DeInit( &htim16 );
HAL_TIM_PWM_DeInit( &htim16 );
htim16.Instance->DMAR = 0x00;
检查了一遍代码,无论是配置单脉冲输出模式,还是pwm 输出模式,都没配置dma 功能。但是在输出单脉冲信号之后,切换到pwm 模式,这个DMAR 寄存器的值就被改变了,暂时没找到原因。