输入捕获
在输入捕获模式下,定时器的计数值(TIMERn_CNT)可以被比较/捕获寄存器(TIMERn_CCx_CCV)获得。
在TIMERn_STATUS寄存器中CCPOL位指示了触发TIMERn_CCx_CCV寄存器(比较/捕获寄存器)捕获动作的边沿的极性。
上图中,TIMERn_CNT表示了定时器的计数,其中m和y分别记录了input脉冲的头两个上升沿的时间。
TIMERn_CCx_CCVB表示了TIMERn_CCx_CCV寄存器中的Buffer缓冲寄存器。
TIMERn_CCx_CCV表示捕获/比较寄存器。
由于缓冲寄存器的存在,可以在读出前记录两次捕获事件。
第一次捕获的时间总是可以从TIMERn_CCx_CCV中读出。读过该地址之后,该寄存器将从TIMERn_CCx_CCVB寄存器中载入下一个捕获时间值(如果缓冲寄存器中存在有效计数值)。
捕获计数值可以通过读TIMERn_CCx_CCVP寄存器获得,而不改变FIFO的内容。TIMERn_CCx_CCVB也可以不改变FIFO内容的情况下读出数值。
TIMERn_STATUS中ICV_flag指示了TIMERn_CCx_CCV中是否存在有效但是没有读出的捕获计数。
在输入捕获模式下,TIMERn_CCx_CCV是只读的。
如果在TIMERn_CCx_CCV和TIMER_CCx_CCVB都含有没有读出的捕获值时,一个新的捕获被触发,那么缓存溢出标志(位于TIMERn_IF中ICBOF)将被置位。
发生溢出后,新的捕获值将被写入TIMERn_CCx_CCVB,并覆盖原值。而TIMERn_CCx_CCV中的值将保持不变。
因此,TIMERn_CCx_CCV中总是保存了最早产生的一个未读出值,而TIMERn_CCx_CCVB中总是保存了最新读到的值。
注意:在输入捕获模式,定时器将在它运行时只触发中断。
周期/脉宽 捕获
周期和脉宽的捕获只能在通道0中使用,因为只有该通道能够开始和停止定时器。
开始和停止定时器通过设置TIMERn_CTRL寄存器中RISEA为Clear&Start。
对于周期捕获,比较/捕获通道应该被设置为捕获相同输入信号的上升沿。
为了捕获高脉冲的宽度,比较/捕获通道应该被设置为捕获输入信号的下降沿。(这里为什么呢?如何理解高脉冲?)
为了测量信号的脉宽,应该选择对立的极性。
脉宽调制
PWM模式,TIMERn_CCx_CCV被缓冲,以避免输出中的小故障。
比较输出动作配置位在PWM模式下被忽略。
PWM输出只支持向上计数和向上/向下计数两种模式。
向上计数(单斜坡)PWM
如果计数器被设置为向上计数并且比较/捕获通道处于PWM模式,那么可以产生单斜坡的PWM输出。
向上计数模式里,PWM周期是TOP+1次循环。PWM输出为高的次数等于TIMERn_CCx_CCV中的值。这意味着一个连续的高输出通过设置TIMERn_CCx_CCV为TOP+1或者更大来实现。(其实就是定时器计数和CCV中值做比较,大于CCV输出低,小于CCV输出高)
PWM分辨率如下计算:
Rpwm_up = log(TOP + 1) / log(2)
PWM频率如下计算:
fpwm_up = fHFPERCLK / (2 ^ PRESC * (TOP + 1))
高状态占空比如下计算:
DSup = CCVx / (TOP +1)
上下计数(双斜坡)PWM
如果设置为上下计数,比较/捕获通道以PWM方式输出,将产生双斜坡的PWM波形。
PWM分辨率如下计算:
Rpwm_up_down = log(TOP + 1) / log(2)
PWM频率如下计算:
fpwm_up_down = fHFPERCLK / (2 ^ PRESC * TOP)
高状态占空比如下计算:
DSup_down = CCVx / (TOP)
关键代码:
参考代码来自 https://github.com/SiliconLabs/peripheral_examples
PWM输出
对于PWM功能的实现,有以下几个关键点:
设置TOP寄存器:
TIMER_TopSet(TIMER0, CMU_ClockFreqGet(cmuClock_TIMER0) / PWM_FREQ);
这里在设置TOP寄存器是,用到了定时器自身的频率。这里的关系应该怎么理解呢?
设置CCV寄存器:
TIMER_CompareSet(TIMER0, 0, (TIMER_TopGet(TIMER0) * duty_cycle_percent) / 100);
对于Silabs的EFRBG还需要设置IO管脚的路由位置。这里原理还没有搞清楚,但不像早期接触的ST的芯片,每个IO有固定的功能复用。在silabs上对于同一个功能,可以通过IO路由映射到不同的管脚上。
上图中TIM0_CC0功能,可以分表route到不同的线上,同时对应了不同的管脚。测试代码里面选择了IO_PC10,所以需要路由到15号线。
TIMER0->ROUTELOC0 |= TIMER_ROUTELOC0_CC0LOC_LOC15;
TIMER0->ROUTEPEN |= TIMER_ROUTEPEN_CC0PEN;
在中断处理函数中,重新修改CCV寄存器的值,即可输出特定占空比的波形。手册上没有明确写,个人上图理解中断事件是由于TOP的溢出产生的。TIMER_IF_CC0是中断标识寄存器。
void TIMER0_IRQHandler(void) { // Get pending flags and clear TIMER_IntClear(TIMER0, TIMER_IF_CC0); // Update CCVB to alter duty cycle starting next period TIMER_CompareBufSet(TIMER0, 0, (TIMER_TopGet(TIMER0) * duty_cycle_percent) / 100); }
PWM一般用于电机的控制和呼吸灯的实现。我这里只是需要一个简单的脉冲输出,然后再通过输入捕获功能将其频率捕获回来即可。
下图是例程中输出的脉冲波形(频率1000,即周期1ms,占空比50%):
输入捕获
下面还需要通过输入捕获,采集到上面生成的波形的频率。