在汽车电子系统中,经常需要通过编码器反馈机械结构的位置信息,微控制器芯片解析编码器的信号时,会使用到定时器的输入捕获功能测量编码器输出的脉冲宽度。绝大多数带有多通道输入输出的定时器,都设计了输入捕获功能。基本的硬件输入捕获功能只是对外部的触发事件(通道引脚上的上升沿、下降沿)进行响应,将定时器当前的计数值快照到通道的输入数据寄存器中,还需要配合少量软件和硬件中断服务进行计算,才能得到表示脉冲宽度的量化值。
本文以国产云途半导体的YTM32B1ME05微控制器为例,介绍使用硬件定时器eTMR外设的输入捕获功能,实现测量脉冲宽度的方法。特别地,eTMR外设在硬件上还设计了专门测量脉冲宽度的功能,以简化软件处理的过程。
在《YTM32的增强型定时器eTMR外设模块详解》一文中曾介绍过,硬件定时器功能的核心,是一个在选定时钟源驱动下的计数器。其中,若配置某个定时器的通道为输入捕获模式,软件可预设检测通道引脚上的信号变化边沿触发事件(上升沿、下降沿),当定时器硬件捕获到对应的触发信号时,会自动将定时器计数器当前的计数值写入到该通道对应的输入捕获数据寄存器中,记录该值作为预设触发信号的时间戳。
在YTM32B1ME05
微控制器芯片中集成的eTMR模块,最多支持8个通道,并且每个通道可以配置成输入捕获模式(设置寄存器eTMR_CHn_CTRL[CHMODE]
),并且可以检测通道引脚上的单独的上升沿、单独的下降沿或是任一边沿(设置寄存器eTMR_CHn_CTRL[CAPEDGE]
),每个通道设计有专门的eTMR_CHn_CVAL
寄存器,专用于存放最近一次捕获到的触发事件的时间戳。
图x中描述的信号时序,解释了eTMR通道的输入捕获模式的工作机制。其中:
clock
是驱动计数器的时钟源信号,counter
对每个时钟源的脉冲进行计数。valid input
是通道引脚上的电平信号。positive edge flag
和negative edge flag
分别为eTMR检测到valid input
信号上的上升沿和下降沿的事件标志。注意,这两个标志存在于芯片电路系统的内部,使用脉冲的方式传递信号。positive edge flag
信号时(上升沿),当前counter
的值CNT0
被载入到CHXCVAL
寄存器中,保持,直到下一个上升沿到来。在下降沿时不做更新。negative edge flag
信号时(下降沿),当前counter
的值CNT1
被载入到CHXCVAL
寄存器中,保持,直到下一个下降沿到来。在上升沿时不做更新。positive edge flag
或negative edge flag
任一信号时,当前counter
的值都会被载入到CHXCVAL
寄存器中。测量脉冲的宽度,具体就是分别捕获脉冲方波的上升沿和下降沿的时刻(若是测量负脉冲则先捕获下降沿再捕获上升沿),然后使用后一个时刻减去前一个时刻,即可得到脉冲的时间长度。这里面还有个情况需要考虑,输入捕获记录的一个周而复始的计数器,在未能限定输入脉冲宽度的最大范围的场景下,第二次捕获的时刻可能会出现在后续的计数“轮回”中,因此还需计入脉冲宽度中计数器绕圈的次数。
这里有计算公式如下:
脉冲宽度 = 计数器周期的时间长度 * 计数器周期的次数(从捕获第一边沿开始至捕获第二边沿结束中间)
+ 捕获第二边沿的时刻 - 捕获第一边沿的时刻
通常,定时器输入捕获通道都只设计了一个输入捕获数据寄存器,但软件可以配置在捕获上升沿或者下降沿的时刻。此时,若要测量脉冲宽度,可以在芯片外面(PCB板电路)将两个输入捕获的通道连在一起接入即将捕获的信号,然后分别配置两个输入捕获的通道事件,一个是上升沿,一个是下降沿。因为两个引脚的信号方向都是输入,不会相互影响。还要记得启用计数器溢出中断,并在中断程序中设计一个软件计数器,记录测量脉冲宽度中可能包含的溢出周期的个数pulse_width_period_count
。若正脉冲到来:
eTMR_CH0_CVAL
,同时触发本通道的中断,在服务程序中,将pulse_width_period_count
计数器清零。pulse_width_period_count
计数器将被累加。这里还可以加入超时判断,即软件设计判定,若pulse_width_period_count
计数器的值被递增至某个预设值时,即可为判定脉宽过长,然后向应用逻辑反馈。eTMR_CH1_CVAL
,同时触发本通道的中断,在服务程序中,将使用上述计算公式计算测量的脉冲宽度,然后向应用逻辑反馈。分析测量脉冲宽度的实现过程可知,程序运行过程对中断服务有比较多的依赖,测量一路脉冲宽度信号,需要配合三个中断服务程序。下文中可以看到,eTMR外设模块针对这种应用场景,专门设计了测量脉宽的功能,可以使用硬件自动记录有效的pulse_width_period_count
计数器,并自行完成计算脉冲宽度的过程。
细心的读者可能会顾虑,若测量一路脉冲宽度时,使用了公共的定时器溢出中断的资源,是不是别的测量通路就不能它来参与额外的脉宽测量了呢?实际上,这里可以通过软件实现多路共用同一个时基,即为多个测量脉冲宽度的通道各自设计一个pulse_width_period_count
计数器,在计数器溢出中断服务程序中将这些脉宽可能包含的溢出周期数都递增。
void eTMR0_IRQHandler(void)
{
...
for (uint32_t i = 0u; i < PULSE_WIDTH_NUM; i++)
{
pulse_width_period_count[i]++;
if (pulse_width_period_count > PULSE_WIDTH_TIMEOUT_PERIOD_COUNT)
{
pulse_width_period_timeout_flag = true; /* timeout event. */
}
}
}
void eTMR_CH0_IRQHandler(void)
{
...
pulse_width_period_count[0] = 0u; /* clear the period counter. */
}
void eTMR_CH1_IRQHandler(void)
{
/* calc the pulse width between the ch0 and ch1. */
pulse_width[0] = eTMR0->MOD * pulse_width_period_count[0] + eTMR0->CH[1].CVAL - eTMR0->CH[0].VAL;
}
定时器通道的输入捕获事件,可以是仅上升沿、仅下降沿,或者是上升沿或下降沿的任一事件。既然是捕获一对边沿事件,一些开发者就想把这个上升沿或下降沿的任一事件
利用起来,这样还能减少在芯片外面将两个引脚连在一起的奇怪设计
。。。中间的计算过程同上文相同,这里就不再赘述了。但要特别提示的是,如果使用这个二者均可
的触发模式,就需要硬件额外提供判定当前捕获的边沿是上升沿还是下降沿的机制,几种可能的实现方式,这里做一些推演。
当跳变沿到来(此时软件还不知道上升沿还是下降沿)时,在中断服务程序中,软件通过硬件提供的可用机制读输入通道的电平信号,若为高电平,则判定为上升沿,若为低电平,则判定为下降沿。例如,eTMR外设使用eTMR_STS
寄存器中的对应字段记录跳变沿事件到来时,同时还会在eTMR_IOSTS
寄存器中记录对应通道的电平状态,因此可用来判定边沿的方向。
void eTMR_CH0_IRQHandler(void)
{
...
if (eTMR0->STS & eTMR_STS_CH0F_MASK)
{
if (0u == (eTMR0->IOSTS | eTMR_IOSTS_CH0IO_MASK) )
{
/* falling edge. */
}
else
{
/* rising edge. */
}
}
...
}
如果定时器外设模块没有这种硬件机制,就得靠软件临时切换引脚的复用功能成GPIO的输入功能,利用GPIO外设模块读电平。记得读完电平后,还得将引脚的复用功能切换回定时器功能,否则就抓不到下个跳变沿了。
如果担心中间软件执行指令比较慢,那就外接一个专门的引脚配置成GPIO输入功能,不用动态切换引脚复用功能,专门用于测量脉冲信号的瞬时电平。但这就又变成两个引脚的方案的,只是不要求另一个引脚具有输入捕获的功能,可以使用宝贵的带输入捕获功能的引脚测量更多路脉冲信号。
如果定时器硬件能接受运行时改工作模式(重新写入寄存器),也可以有针对性地在特定时间段捕获特定边沿,相当于基于软件实现了一个检测信号模式的状态机,那么这个状态机中的各个状态将由软件接管,不再依赖于硬件已有的实现。例如,若要测量正脉冲,则先配置捕获通道的事件为仅上升沿,当上升沿信号到来之时,软件在通道的中断服务中,使用软件变量暂存当前捕获时刻的计数值后,再重新配置本通道捕获的通道事件为仅下降沿。之后,下降沿到来时,就可以使用之前缓存的前一个捕获的计数值配合当前的捕获值计算脉宽。
eTMR外设为计数器数据相关的寄存器设计了“影子寄存器”,需要特定的同步机制才能更改,但通道的配置寄存器eTMR_CHn_CTRL
不在其中,可以支持这种用法。
etmr_ch_input_edge_dir_t etmr_ch0_dir = eTRM_CH_INPUT_EDGE_FALLING_EDGE;
void eTMR0_CH0_IRQHandler(void)
{
...
eTMR0->CH[0].CTRL &= ~eTMR_CH_CTRL_CAPEDGE_MASK;
switch (etmr_ch0_dir)
{
case eTRM_CH_INPUT_EDGE_FALLING_EDGE:
/* falling edge event. */
eTMR0->CH[0].CTRL |= eTMR_CH_CTRL_CAPEDGE(1); /* next is rising edge. */
break;
case eTRM_CH_INPUT_EDGE_RISING_EDGE:
/* rising edge event. */
eTMR0->CH[0].CTRL |= eTMR_CH_CTRL_CAPEDGE(2); /* next is falling edge. */
break;
default:
break;
}
}
针对汽车电子系统中,MCU在一些关键阶段对计算实时性要求比较高,多个中断的协同配合对CPU的相应时序要求比较高。为了降低多线程相互配合完成计算的风险,YTM32集成在车规MCU的eTMR定时器模块,专门在芯片硬件上完全实现了测量脉冲的全部操作过程,不全程不需要软件介入,只需要在对应的通道中断服务程序中从硬件寄存器中读取自动计算的结果即可。
RM手册中提供给开发者的说明并不多,只是简单介绍了寄存器接口。本节可以作为手册的补充,扩展说明芯片内部的工作过程,便于读者理解芯片手册,使用合适的方式集成到自己的软件中。
RM手册中的eTMR外设寄存器清单中,列出了每个通道为测量脉冲宽度设计的4个寄存器,CH_PPCNTN
、CH_PPCNTV
、CH_NPCNTN
、CH_NPCNTV
,如图x所示。
这里先声明,所谓的正脉冲(Positive Pulse),是指由上升沿开始、下降沿结束形成的方波信号,对应地,负脉冲(Negative Pulse),是指由下降沿开始、上升沿结束形成的方波信号。如图x所示。
eTMR硬件为每个通道可能出现的正脉冲和负脉冲都各自配备了一组内部的寄存器。以测量正脉冲为例,在上升沿捕获到的数值记录在专属的buf0
寄存器中,下降沿捕获到的数值记录在buf1
寄存器中,用eTMR_CH_PPCNTN
寄存器对脉宽内部可能包含的定时器溢出周期进行计数。当下降沿(结束边沿)到来之时,硬件自动将buf1
减去buf0
的值,存入eTMR_CH_PPCNTV
寄存器中。如果最初的结果是负数,则硬件也会自动从eTMR_CH_PPCNTN
寄存器中借1位(数值也减1回存),确保存入eTMR_CH_PPCNTV
寄存器中的有效值是正数。类似地,eTMR_CH_NPCNTN
和eTMR_CH_NPCNTV
对应的是负脉冲测量数值。
在eTMR_STS
寄存器中,也专门为有效的脉冲捕获事件设计的标志位。如图x所示。
当前版本的eTMR并没有对应地专门为脉宽检测设计专门的中断事件,但可以借用边沿事件的中断,例如,使用输入捕获下降沿的中断服务程序读eTMR_CH_PPCNTN
和eTMR_CH_PPCNTV
寄存器并完成计算。
RM手册中提供了第一张关于输入捕获脉冲的时序图,如图x所示。这里也作一个补充说明,详细解释其中时序的含义。
clock
是计数器计数的时基。valid data
是引脚上实际的电平信号。counter
是计数器当前的计数值。CNT0
时,valid data
信号线上产生上升沿,对应positive edge flag
产生一个触发信号。CNT1
时,valid data
信号线上产生下降沿,对应nagative edge flag
产生一个触发信号。同时,之前在CNT0
产生的上升沿和当前产生的下降沿组合成一个正脉冲,对应positive pulse flag
信号线上产生一个触发信号,PPCNTV
和PPCVTN
寄存器也被硬件写入了经过基本处理的数值,可用于计算正脉冲的宽度。CNT2
时,valid data
信号线上产生上升沿,对应positive edge flag
产生一个触发信号。同时,之前在CNT1
产生的下降沿和当前产生的上升沿组合成一个负脉冲,对应negative pulse flag
信号线上产生一个触发信号,NPCNTV
和NPCNTN
寄存器也被硬件写入了经过基本处理的数值,可用于计算负脉冲的宽度。一些微控制器的多通道带有IO引脚的定时器具有输入捕获功能,可以通过记录通道引脚出现跳变沿的时刻,用以计算两个相反边沿组成的脉冲的宽度。除了需要配合定时器溢出中断对溢出周期计数之外,本文梳理了常用的实现测量包含两个边沿的脉冲的方法:
还特别介绍了YTM32的eTMR定时器中基于硬件实现的脉宽测量方法,实际上是将典型测量方法由硬件逻辑自动实现,开发者在使用这个功能时,需要了解其中的工作机制,以便于以合适的方式整合到具体的应用逻辑中。