1、eTimer结构图
eTimer模块提供:
- 六个相同的计数器/定时器通道
- 一个看门狗定时器功能(可能并非每个eTimer模块实例都具备)
2、eTimer通道结构图
eTimer中的每个计时器/计数器通道结构图如下图所示,每个16位计数器/定时器通道包含一个预分频器、一个计数器、一个加载寄存器、一个保持寄存器、两个排队捕获寄存器、两个比较寄存器、两个比较预加载寄存器以及四个控制寄存器。
3、功能特性
eTimer模块设计包含以下独特功能:
•16位计数器/定时器,共六个。
•支持正向和反向计数。
•计数器支持级联。
•改进的可编程正向/反向模数计数功能。
•对于外部时钟,最大计数速率等于外设时钟频率的一半;对于内部时钟,最大计数速率等于外设时钟频率。
•可选择单次或重复计数。
•计数器支持预加载。
•比较寄存器也支持预加载。
•计数器可以共享输入引脚。
•每个计数器都有独立的预分频器。
•每个计数器具备捕获和比较功能。
•提供连续和单次捕获模式,以提高速度测量的精度。
•支持捕获寄存器和比较寄存器的DMA操作。
•32位看门狗功能,用于检测四象限计数是否停滞(此功能可能并非每个eTimer模块都具备)。
•OFLAG比较功能,适用于安全关键应用。
•支持在调试模式和停止模式下进行可编程操作。
•提供可编程输入滤波器。
•计数器之间可以同步启动计数。
4、功能描述
每个通道有两种基本操作模式:可以计数内部或外部事件,也可以在外部输入信号被激活时计数内部时钟源,从而测量外部输入信号的持续时间。
•计数器可以计数选定输入引脚的上升沿、下降沿或同时计数这两个边沿。
•计数器能够解码并计数正交编码的输入信号。
•计数器可以使用双输入以“方向计数”模式进行加减计数。
•计数器的最终计数值(模数)是可编程的。
•在达到最终计数值后,可以编程加载新的值。
•计数器可以连续计数,也可以在完成一个计数周期后停止。
•计数器可以被编程设定为计数到预设值后立即重新初始化,或者计数至比较值,直到计数‘翻转’为零。
每个计数器/定时器的外部输入可在模块内的各个通道之间共享。这些外部输入可用作:
•计数命令
•定时命令
•可触发当前计数器值被“捕获”
•可用于生成中断请求
外部输入的极性可选。每个通道的主要输出是OFLAG信号。当计数器达到预设值时,OFLAG信号可以设置、清除或切换。此外,OFLAG信号可以输出到外部引脚,而不是作为定时器输入使用。OFLAG信号使每个计数器能够生成方波、PWM或脉冲流输出。OFLAG信号的极性可编程。任何通道都可以被指定为主通道。主通道的比较信号可以广播给模块内的其他通道。当主通道发生比较事件时,其他通道可以重新初始化其计数器和/或将其OFLAG输出信号强制为预设值。
4.1、模式
选定的外部信号以eTimer的基本时钟速率进行采样,随后通过一个转换检测器。使用外部信号时,最大计数率是eTimer基本时钟速率的一半。内部时钟源可以用来以eTimer的基本时钟速率驱动计数器。如果计数器被编程为计数到特定值后停止,当计数结束时,CTRL1寄存器中的CNTMODE字段将被清除。
停止模式
如果CNTMODE字段设置为“000”,计数器将处于非活动状态,不会进行计数。此外,停止模式还会禁用由选定输入引脚上的输入转换所引起的中断。
计数模式
当CNTMODE字段设置为‘001’时,计数器将对选定的时钟源的上升沿进行计数。此模式适用于生成定时中断或计数外部事件。
边沿计数模式
如果CNTMODE字段设置为“010”,计数器将同时对选定外部时钟源的两个边沿进行计数。此模式可用于对外部环境的变化进行计数,例如简单编码器轮。
门控计数模式
如果CNTMODE字段设置为‘011’,计数器将在选定的次级输入信号为高电平时进行计数。此模式用于测量外部事件的持续时间。如果通过设置SIPS位将选定的输入信号反转,则计数器将在选定的次级输入信号为低电平时进行计数。
方波计数模式
当CNTMODE字段设置为100时,计数器将主要和次要外部输入解码为正交编码信号。正交信号通常由旋转或线性传感器生成,用于监测电机轴或机械设备的运动。这些正交信号是相位相差90度的方波。通过解码正交信号,可以获取计数和方向信息。下图展示了正交增量位置编码器的基本工作原理的时间图。
signed计数模式
如果CNTMODE字段设置为“101”,计数器将对主时钟源进行计数,而选定的辅助源则提供选定的计数方向(向上/向下)。
触发计数模式
当CNTMODE字段设置为‘110’时,计数器将在次级输入发生正跳变(如果SIPS=1则为负跳变)后开始对主时钟源进行计数。计数将持续到发生比较事件或检测到另一个正跳变。随后的次级正跳变将继续重启和停止计数,直到发生比较事件。
单次模式
当CNTMODE字段设置为‘110’,计数器在比较事件(LENGTH =1)时重新初始化,并且OFLAG OUTMODE设置为‘0101’(初始化时清除,比较时设置),计数器将进入“单次模式”。外部事件触发计数器开始计数,当达到设定的终端计数时,输出被激活。这种“延迟”输出可用于提供定时延迟。
级联计数模式
当CNTMODE字段设置为‘111’时,计数器的输入将连接到另一个选定计数器的输出。当选定的源计数器发生比较事件时,计数器将进行加减计数。这种“级联”或“菊花链”模式允许多个计数器级联,从而生成更长的计数器长度。在级联模式下,模块间使用特殊的高速信号路径,而不是OFLAG输出信号。如果选定的源计数器正在加计数,并且发生比较事件,计数器将增加;如果选定的源计数器正在减计数,并且发生比较事件,计数器将减少。最多可以级联两个计数器,以创建一个32位宽的同步计数器。每当读取计数器模块中的任何计数器时,该模块内的所有计数器值都会被捕获到各自的HOLD寄存器中。这一操作支持级联计数器链的读取。首先读取级联计数器链中的任意一个计数器,然后读取链中其他计数器的HOLD寄存器。级联计数器模式是同步的。
脉冲输出模式
如果计数器设置为CNTMODE = 001,OFLAG OUTMODE设置为‘1111’(门控时钟输出),并且ONCE位被设置,则计数器将输出与选定时钟源频率相同的脉冲流,输出的脉冲数量等于比较值减去初始值。此模式适用于步进电机系统。
固定频率PWM模式
如果计数器设置为CNTMODE = 001,且经过翻转(长度= 0)、连续计数(ONCE = 0)以及OFLAG OUTMODE为‘0111’(比较时设置,计数器翻转时清除),则计数器输出的脉冲宽度调制信号(PWM)频率等于计数时钟频率除以65,536,脉冲宽度占空比等于比较值除以65,536。这种模式常用于驱动PWM放大器,以驱动电机和逆变器。
变频PWM模式
如果计数器设置为CNTMODE = 001,计数至比较(LENGTH = 1),连续计数(ONCE = 0)且OFLAG OUTMMODE为‘0100’(切换OFLAG并交替比较寄存器),则计数器输出的脉冲宽度调制(PWM)信号的频率和脉宽由编程到COMP1和COMP2寄存器的值以及输入时钟频率决定。这种方法生成的PWM具有几乎任何所需的PWM频率和/或恒定的开/关周期的优势。这种操作模式常用于驱动PWM放大器,以驱动电机和逆变器。CMPLD1和CMPLD2寄存器特别适用于此模式,因为它们允许程序员在PWM电流周期进行时计算下一个PWM周期的值。
4.2、其它特性
冗余OFLAG检查
这种模式允许用户将两个定时器功能组合起来,生成任意模式,以比较它们的输出信号(OFLAG行为)。冗余模式用于在线检查,以确保可靠性和功能安全。当两个相邻通道出现不匹配时,系统会通过中断通知核心,并将这两个输出置于非活动状态。错误将通过RCF标志进行标记。此功能可以通过使用通道的VAL和FORCE位强制转换一个OFLAG来测试。
回环检查
此模式始终可用,因为一个通道可以生成OFLAG,而另一个通道使用第一个通道的OFLAG作为其输入,以测量和验证是否符合预期。
输入捕获模式
用于测量脉冲宽度(通过捕获两个连续输入边沿的计数器值)或波形周期(通过捕获两个连续上升沿或两个连续下降沿的计数器值)。捕获寄存器在检测到输入边沿(正、负或同时)时,会存储计数器值的副本。每个电路捕获的边沿类型由CPT1MODE和CPT2MODE位决定,这些位的功能在CPT1MODE中列出。此外,控制捕获电路操作的是启动逻辑,它允许捕获以连续运行或单次触发的方式进行。在连续运行模式下,捕获序列将无限期地执行。如果两个捕获电路都启用,它们将以乒乓方式工作,一个电路的捕获事件将触发另一个电路的启动,反之亦然。在单次触发模式下,仅执行一个捕获序列。如果两个捕获电路都启用,首先启动捕获电路0,当捕获事件发生时,启动捕获电路1。一旦第二次捕获发生,进一步的捕获将被禁用,直到另一个捕获。
主从模式
任何计时器通道均可被指定为主(MSTR = 1)。主计数器的比较信号可以广播到模块内的其他通道。当发生主计数器比较事件时,其他计数器可以被配置为重新初始化(COINIT = 1)和/或强制其OFLAG输出信号(COFRC = 1)达到预设值。
看门狗定时器
该定时器用于监控通道0在正交计数模式下是否出现停滞。当启用看门狗定时器时,它会将超时值加载到一个递减计数器中。只要通道0保持在正交解码计数模式,递减计数器就会持续计数。如果递减计数器达到零,系统将触发中断。每当通道0的计数值发生变化时,递减计数器会被重新加载到超时值。如果通道0的计数值在两个值之间切换(表明编码器可能已停滞),则不会重新加载递减计数器。
4.3、复位
eTimer模块只能通过全系统复位来复位。这将强制所有寄存器恢复到复位状态,并清除已激活的OFLAG信号。计数器将在CTRL寄存器中的设置更改之前保持关闭状态。
4.4、时钟
每个定时器通道都会接收到一个独立的外设总线时钟副本。这使得通过关闭未使用的通道时钟来实现更有效的电源管理成为可能。如果实现了看门狗定时器,它也会有自己的时钟版本。ENBL寄存器的每一位都使用对应通道的时钟。DREQn寄存器使用通道0的时钟,但这些寄存器编程后,该时钟可以关闭。
4.5、中断
以下内容可在eTimer内生成中断。
•看门狗定时器(如果已实现)
•每个通道可从多个源生成中断
中断服务例程(ISR)必须检查相关的中断启用和中断标志,以确定中断的实际原因。
4.6、DMA
每个通道可为每个捕获寄存器请求DMA读取访问,为每个比较预加载寄存器请求DMA写入。DREQ寄存器从这32个DMA请求源中选择,生成4个顶级DMA请求输出。
5、示例程序分析
以下是对该 eTimer_Freq_Measurement_MPC5744P示例工程文件中代码的逐行中文注释与解析,包括头文件、全局变量、函数定义和主函数逻辑。
📌 一、头文件部分
#include "MPC5744P.h"
包含 MPC5744P 微控制器寄存器定义头文件,用于访问硬件寄存器。
#include "uart.h"
包含串口通信相关函数声明(如
TransmitData
,ReceiveData
)。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
标准C库头文件:
stdio.h
: 输入输出函数(本程序未使用标准printf
系列函数)string.h
: 字符串操作函数stdlib.h
: 常用工具函数(如atoi
,malloc
)stdbool.h
: 支持布尔类型bool
,true
,false
📌 二、宏定义
#define USE_TERMINAL 1
启用终端输出功能。若为 0,则不启用串口输出。
📌 三、外部声明
extern void xcptn_xmpl(void);
声明一个外部函数,用于配置异常处理向量(如中断入口等)。
📌 四、全局变量定义
uint32_t counts, edge1, edge2, edge3, edge4;
存储计数值及捕获边沿的时间戳。
uint32_t capture_ch0[4], capture_ch1[4];
捕获通道0和1的4个时间点值。
float freq, period, duty, pulseH, pulseL;
存储计算出的频率、周期、占空比、高电平脉宽、低电平脉宽。
📌 五、函数定义
✅ 函数:SysClk_Init()
void SysClk_Init(void)
{MC_CGM.AC3_SC.B.SELCTL = 0x01; //选择XOSC作为PLL0输入源MC_CGM.AC4_SC.B.SELCTL = 0x01; //选择XOSC作为PLL1输入源PLLDIG.PLL0DV.R = 0x3002100A; //设置PLL0参数,输出200MHzMC_ME.RUN0_MC.R = 0x00130070; //RUN0模式配置MC_ME.MCTL.R = 0x40005AF0; //切换到RUN0模式MC_ME.MCTL.R = 0x4000A50F; //反向Key确认切换while (MC_ME.GS.B.S_MTRANS) {}; //等待切换完成while(MC_ME.GS.B.S_CURRENT_MODE != 4) {}; //等待进入RUN0PLLDIG.PLL1DV.R = 0x00020010; //设置PLL1为160MHz//使能外设在RUN模式下运行MC_ME.RUN_PC[1].R = 0x000000FE;MC_ME.PCTL30.B.RUN_CFG = 1; //PIT_0MC_ME.PCTL36.B.RUN_CFG = 1; //DMAMUX_0MC_ME.PCTL247.B.RUN_CFG = 1; //eTimer_0MC_ME.PCTL91.B.RUN_CFG = 1; //LINFlexD_1MC_ME.RUN0_MC.R = 0x001300F2; //更新RUN0配置(包含PLL1)MC_CGM.SC_DC0.R = 0x80030000; //系统时钟分频为50MHz//再次切换回RUN0确保稳定MC_ME.MCTL.R = 0x40005AF0;MC_ME.MCTL.R = 0x4000A50F;while (MC_ME.GS.B.S_MTRANS) {};while(MC_ME.GS.B.S_CURRENT_MODE != 4) {};//设置辅助时钟MC_CGM.AC6_SC.R = 0x04000000; //选择PLL1作为CLKOUT0源MC_CGM.AC6_DC0.R = 0x80500000; //分频为2MHz供eTimer使用MC_CGM.AC0_SC.R = 0x02000000; //选择PLL0作为其他模块时钟MC_CGM.AC0_DC0.R = 0x80010000; //分频为100MHz
}
功能:初始化系统时钟,设置主频为200MHz,并为外设提供合适的时钟源。
✅ 函数:SUIL2_Init()
void SUIL2_Init(void)
{SIUL2.MSCR[22].R = 0x22800001; //PB6作为CLK_OUT输出SIUL2.MSCR[43].R = 0x32000000; //PC11作为GPIO(LED)SIUL2.MSCR[1].B.IBE = 1; //PA1使能输入功能SIUL2.IMCR[60].B.SSS = 2; //eTimer0 CH1连接到PA1
}
功能:配置 GPIO 引脚,包括 CLKOUT 输出、LED 控制以及 eTimer 输入信号引脚。
✅ 函数:PIT_Init()
void PIT_Init(void)
{PIT_0.MCR.B.MDIS = 0; //启用PIT模块PIT_0.TIMER[0].LDVAL.R = 50000000 - 1; //设置1秒定时(50MHz)PIT_0.TIMER[0].TCTRL.B.TEN = 1; //启动定时器
}
功能:配置 PIT 定时器,用于每秒发送一次测量结果到终端。
✅ 函数:eTimer_Init()
static void eTimer_Init(void)
{ETIMER_0.ENBL.R = 0x0; //关闭所有通道//CH0 配置为自由运行计数器ETIMER_0.CH[0].CTRL1.R = 0x3801;ETIMER_0.CH[0].COMP1.R = 0xFFFF;ETIMER_0.CH[0].CCCTRL.R = 0x0264;ETIMER_0.CH[0].CTRL3.R = 1;//CH1 配置为级联模式ETIMER_0.CH[1].CTRL1.R = 0xF001;ETIMER_0.CH[1].COMP1.R = 0xFFFF;ETIMER_0.CH[1].CCCTRL.R = 0x0264;ETIMER_0.CH[1].CTRL3.R = 1;ETIMER_0.ENBL.R = 0x0003; //启用CH0和CH1
}
功能:初始化 eTimer 模块,用于捕获输入信号的上升/下降沿。
✅ 函数:ftoa()
void ftoa(float number, uint8_t *buf, uint8_t precision)
{...
}
功能:将浮点数转换为ASCII字符串,用于在无
printf/sprintf
的环境中显示浮点数值。
📌 六、主函数:int main(void)
int main(void)
{xcptn_xmpl(); //异常初始化SysClk_Init(); //系统时钟初始化SUIL2_Init(); //GPIO初始化PIT_Init(); //PIT定时器初始化eTimer_Init(); //eTimer初始化LINFlexD_1_Init(); //串口初始化#if USE_TERMINAL// 发送欢迎信息uint8_t intro0[] = {"\f"};uint8_t intro1[] = {"eTimer - wide frequency measurement\n\r"};uint8_t intro2[] = {"Connect periodic pulse signal to the eTimer0 channel1 input\n\r"};uint8_t intro3[] = {"J5_3 on the XDEVKIT-MPC5744P EVB\n\r"};uint8_t intro4[] = {"press any key to continue...\r\n"};uint8_t intro5[] = {"\f"};uint8_t dummychar = 0;TransmitData((const char*)intro0, strlen((const char*)intro0));TransmitData((const char*)intro1, strlen((const char*)intro1));TransmitData((const char*)intro2, strlen((const char*)intro2));TransmitData((const char*)intro3, strlen((const char*)intro3));TransmitData((const char*)intro4, strlen((const char*)intro4));ReceiveData(&dummychar); //等待按键TransmitData((const char*)intro5, strlen((const char*)intro5));
#endif// 启动输入捕获ETIMER_0.CH[0].CCCTRL.B.ARM = 1;ETIMER_0.CH[1].CCCTRL.B.ARM = 1;while(1){// 等待捕获标志while(!(ETIMER_0.CH[1].STS.R & 0x0080)){}while(!(ETIMER_0.CH[0].STS.R & 0x0080)){}// 读取四个边沿时刻capture_ch0[0] = ETIMER_0.CH[0].CAPT1.R;capture_ch0[1] = ETIMER_0.CH[0].CAPT2.R;capture_ch0[2] = ETIMER_0.CH[0].CAPT1.R;capture_ch0[3] = ETIMER_0.CH[0].CAPT2.R;capture_ch1[0] = ETIMER_0.CH[1].CAPT1.R;capture_ch1[1] = ETIMER_0.CH[1].CAPT2.R;capture_ch1[2] = ETIMER_0.CH[1].CAPT1.R;capture_ch1[3] = ETIMER_0.CH[1].CAPT2.R;// 计算两个上升沿之间的周期edge1 = capture_ch1[0]*65536 + capture_ch0[0];edge3 = capture_ch1[2]*65536 + capture_ch0[2];if(edge3 > edge1){counts = edge3 - edge1;}else{counts = (0xFFFFFFFF - edge1 +1) + edge3;}freq = 100000000.0 / counts;period = counts * 1000000.0 / 100000.0;// 计算高电平脉宽edge2 = capture_ch1[1]*65536 + capture_ch0[1];if(edge2 > edge1){counts = edge2 - edge1;}else{counts = (0xFFFFFFFF - edge1 +1) + edge2;}pulseH = counts * 1000000.0 / 100000.0;pulseL = period - pulseH;duty = pulseH / period * 100;#if USE_TERMINALif(PIT_0.TIMER[0].TFLG.B.TIF){// 转换并发送数据到终端ftoa(period, period_ascii, 2);TransmitData(message0_0, ...);// 类似地发送频率、脉宽、占空比等信息PIT_0.TIMER[0].TFLG.B.TIF = 1;}
#endif// 清除捕获标志ETIMER_0.CH[1].STS.R = 0x00C0;ETIMER_0.CH[0].STS.R = 0x00C0;// LED翻转SIUL2.GPDO[43].R ^= 0x1;// 再次提示用户按任意键继续TransmitData(intro4, ...);ReceiveData(&dummychar);}return 0;
}
✅ 总体功能说明:
该程序是一个基于 NXP MPC5744P 微控制器 的 输入信号频率测量示例,主要实现以下功能:
功能 | 描述 |
---|---|
系统时钟初始化 | 设置主频为200MHz |
GPIO配置 | 设置CLKOUT输出、LED控制、eTimer输入引脚 |
eTimer捕获 | 捕获输入信号的上升/下降沿,计算周期、频率、占空比 |
PIT定时器 | 每秒触发一次,用于刷新结果显示 |
串口通信 | 将测量结果通过串口打印到终端 |
浮点转字符串 | 自定义 ftoa() 实现浮点数显示 |
🔧 应用场景:
适用于需要对 外部方波信号进行实时测量 的嵌入式应用,如:
- 工业传感器信号采集
- 电机转速检测
- PWM信号分析
- 教学实验平台