浅浅的撅一下这个TIMER(二):定时器发生PWM
上回我们利用定时器完成了基本的定时操作,进行了周期性的中断调用。这回我们用它来完成定时器的另一个常见操作——PWM波的发生。
本文还是以GD32E230所配的TIMER13(通用L2)为例。
(还是因为它配置最寒碜(TIMER13:为什么受伤的总是我?))
旧事重提
没错,又是这个老图。
也没错,今天我们还是只需要其中的一部分功能,因此这个图又可以砍成这样:
根据上次我们所讲的,定时器输入时钟先经过预分频,然后作为计数器的计数时钟进行计数。在最常用的向上计数模式中,每次时钟脉冲会令计数器的值增加1。一旦计数器的值达到自动重载值(溢出事件),定时器就会将计数器的值重置为0,并重新开始计数。
定时器的PWM模式运行初探
那计数器和定时器发生PWM又有什么关系呢?
不妨考虑一下,定时器处在计数运行时,溢出事件的发生是固定周期性的,也就是说溢出事件发生的频率是固定的。
那么根据“从特殊到一般、从简单到复杂”的探索思维,设想一个最简化的情况,如果我们需要发生一个1kHz的对称方波信号(即占空比为50%),那么一个方波的周期即为1ms,也即高电平和低电平各占0.5ms。换句话说,电平翻转的频率是2kHz(一个方波周期内有两次翻转)。如果我们将定时器的溢出事件周期配置为2kHz,并开启对应的溢出中断,那么该中断函数就会被以2kHz的频率调用。如果我们在该函数内翻转某个IO输出电平,则在该IO上即会发生频率为2kHz的电平翻转,也就是产生了1kHz的方波信号。
图形示意如下:(每条青色竖线代表一次溢出中断)
如果我们需要产生一个1kHz、占空比为10%的PWM方波信号呢?那么我们可以类比上面的过程(实际上是将一个波形的周期等分为2份),将一个波形的周期时间等分为10份,在第1份的时间(0.1ms)内令其为高电平,而在其余9份的时间(0.9ms)内令其为低电平:
也就是在第1份时间的开始处将IO输出电平置为高,在第2份时间的开始处将IO输出电平置为低。为了在中断内确定“当前是第几份时间开始处的中断”,我们可以另外设置一个状态变量以记录,在每次中断内+1,若超过0-9的范围即归0。
可以看出,如果我们将一个周期的时间等分的份数越多,我们就可以产生越高分辨率的PWM方波信号。PWM占空比的最小分辨率与周期(T)、周期等分数(N)显然有以下关系:
$$
Resolution_{min}( \% )=(\frac{1}{N})*100 \%
$$
$$
Resolution_{min}=(\frac{T}{N})
$$
然后呢?
上面的方式有一个缺点:定时器的中断会反复打断CPU的运行,在中断内计数、判断的过程均会占用CPU时间。并且在进行一些对时序有高要求的操作时,若关中断会造成PWM信号错误,若不关中断又会造成该操作受到影响。
能不能想想办法,解决这个问题?
答案是有办法的——通道输出比较功能。
上面我们提到了“计数量”、“周期等分份数”和“第几份时间处电平翻转”的概念。在通道输出比较功能中,其对应关系如下:
- 计数量 对应 定时器计数值
- 周期等分份数 对应 定时器自动重载值
- 第几份时间处电平翻转 对应 定时器输出比较值
CHxCV
当定时器向上计数时,一旦计数值等于CHxCV
的值,输出比较控制器就会产生准备信号OxCPRE
的置位、清除或翻转(一般使用的PWM模式0是清除),并可以按照CHxP
和CHxE
的设置而控制对应输出通道的电平。
是不是像我们刚刚所说的“第几份时间处电平翻转”?
实际上,PWM模式就是输出比较功能的一个特例用法,它在定时器每个计数周期(也就是溢出事件的周期)内只发生一次计数值与CHxCV
的值比较成功(即相等),并在此处采取控制电平的操作。以较常用的PWM模式0为例,其在每个计数周期开始时将OxCPRE
置为有效,并在比较成功时将其置为无效。
如何根据需求计算定时器的各项参数呢?
我们采取逆向计算的思维。
首先确定所需要的PWM信号频率$f_{PWM}$和PWM分辨率点数$N$(即一共有多少级分辨率)。于是得到以下数据:
$$
f_{PSC\_CLK}=f_{PWM}*N
$$
$$
自动重载值CAR=N-1
$$
然后根据TIMER_CK
的时钟频率$f_{TIMER\_CK}$计算得到一个合适的PSC
值:
$$
f_{TIMER\_CK}=f_{PSC\_CLK}*(PSC+1)
$$
需要注意的是,$PSC$的取值范围是有限制的,其必须处于$[0,2^{B}-1]$的区间内(B为该定时器的PSC
寄存器宽度位数,也必须是一个整数。若计算得到的$PSC$为负数,说明该定时器的时钟频率过低,无法同时满足PWM信号频率与分辨率点数要求,需要提高时钟频率,或是降低频率/分辨率点数要求;若计算得到的$PSC$为小数,可就近约取一整数,并按照应用需求适当调节PWM信号频率$f_{PWM}$或PWM分辨率点数$N$。
示例代码
以下代码整理自“GD32E23x_Firmware_Library_V1.1.4/Examples/TIMER/TIMER2_pwmout”。
示例在PB1引脚上输出频率为1kHz、占空比为11.4%的PWM方波。
1 | #include "gd32e23x.h" |
浅浅的撅一下这个TIMER(二):定时器发生PWM