需要单片机几乎同时在A,B,C 三个IO 分别输出T1,T2,T3 时长的高电平,时间结束后,恢复低电平。
面对这个需求,我第一时间想到的是用三个定时器,分别设置T1,T2,T3 时间的定时,定时结束之后,在中断处理函数,将对应的IO 拉低,但是这个做法有几点不好:
重新研究了一次定时器的功能,发现定时器的pwm 单脉冲功能,能完美解决这个问题。
先来整理一下普通的 定时器 pwm 功能的相关知识点。下面默认以计数器向上计数为例说明。

图1
介绍一个pwm信号产生的过程
上面流程循环执行,产生连续的pwm 波形。
值的范围是0 - 65535。定时器计数频率 = 定时器时钟频率/ ( PSC + 1)。
假设定时器的时钟频率的64M,PSC 设置为63,那么定时器计数频率是1MHZ,也就是每1us,计数值加1。
pwm 周期 = (ARR + 1)/ 定时器计数频率。
可以设置为 High 或 Low。有效电平的影响,参考下面的PWM1 和PWM2 模式。
| CNT < CCRx | CNT > CCRx | |
|---|---|---|
| PWM模式1 | 有效电平 | 无效电平 |
| pwm 模式2 | 无效电平 | 有效电平 |
单脉冲输出,就是只有一个输出周期的pwm。当计数器向上计数时,计数值到ARR时,计数器自动停止。当计数器向下计数时,计数值到0时,计数器自动停止。

图2
配置 TIMx_CR1 的OPM bit 为1,进入one-pulse mode。使用Cubemx 配置定时器时,勾选One Pulse Mode 前面的勾。

图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 阶段的时间
输出单脉冲信号之后,切换状态输出pwm,无法正常输出。步骤如下:
每次输出pwm 和单脉冲信号时,都重新完整初始化一次定时器,问题依然存在。
配置输出pwm 信号之前,调用以下函数,复位单脉冲配置的状态
HAL_TIM_OnePulse_DeInit( &htim16 );
HAL_TIM_PWM_DeInit( &htim16 );
每次配置输出pwm 之前,打印一次当前定时器的配置(htim16. Instance 里面的各个成员函数),通过比较,发现 htim16.Instance->DMAR 这个参数被修改了。执行如下代码,问题解决:
HAL_TIM_OnePulse_DeInit( &htim16 );
HAL_TIM_PWM_DeInit( &htim16 );
htim16.Instance->DMAR = 0x00;
检查了一遍代码,无论是配置单脉冲输出模式,还是pwm 输出模式,都没配置dma 功能。但是在输出单脉冲信号之后,切换到pwm 模式,这个DMAR 寄存器的值就被改变了,暂时没找到原因。