新唐的NuMicro M451的定时器控制器共包含4组32位定时器,TIMER0~TIMER3。定时器有很多功能,比如,延时,频率测量,外部输入管脚事件计数······
NuMicro M451定时器特性:(来自数据手册)
4 组 32-位定时器,带24位向上计数器和一个8位的预分频计数器
每个定时器都可以设置独立的时钟源
提供 one-shot, periodic, toggle 和 continuous 四种计数操作模式
通过CNT (TIMERx_CNT)可读取内部 24 位向上计数器的值
支持事件计数功能
通过CMPDAT (TIMERx_CMP)可读取24-bit 捕捉值
支持外部管脚捕捉功能,可用于脉宽测量
支持外部引脚事件计数,可用于复位24位向上定时器
如果定时器中断信号产生,支持芯片从空闲/掉电模式唤醒
支持Timer0 超时溢出中断来触发Touch-Key 扫描
支持Timer0 ~ Timer3 超时溢出中断或捕捉中断来触发PWM, EADC 和 DAC 功能
接着看看定时器控制器的框图:
通过框图结合上面介绍的特性更容易理解
定时器控制器的时钟源框图:
从上图可以看出:
TMR0~TMR3的时钟源可以由TMRxCKEN (CLK_APBCLK0)使能,通过寄存器TMR0SEL (CLK_CLKSEL1) (定时器0),TMR1SEL (CLK_CLKSEL1) (定时器1),TMR2SEL (CLK_CLKSEL1) (定时器2),TMR3SEL (CLK_CLKSEL1) (定时器3)来选择。
时钟源可以是HXT、LXT、HIRC、LIRC、PCLK1或T0~T3(指外部的脉冲输入)
NuMicro M451定时器控制器提供四种定时器计数模式: one-shot, periodic, toggle-output 和 continuous counting 计数模式。
本次主要讲解periodic计数模式:
如果定时器工作在周期 (periodic) 模式(TIMERx_CTL为01)且CNTEN (TIMERx_CTL)置1,则定时器的计数器开始向上计数。一旦CNT (TIMERx_CNT)计数器的值达到CMPDAT (TIMERx_CMP)的值时,TIF (TIMERx_INTSTS)标志将变为1,CNT的值将由定时器控制器自动清零,然后定时器重新计数。与此同时,如果INTEN (TIMERx_CTL)使能,则定时器中断信号产生并送到 NVIC 通知 CPU 。在该模式,定时器控制器周期性地操作计数和 与CMPDAT的值比较,直到CNTEN位由软件清0。
接着讲如何使用库函数来操作定时器
本次实验用到了扩展板上的7段数码管,关于数码管部分的原理图:
两个数码管的段选接一起,通过两个控制引脚来控制位选
首先,依然是系统时钟初始化,使能HIRC,选择HIRC作为HCLK的时钟源,使能HXT(后面作为定时器0的时钟源),配置时钟为72MHz。
使能TMR0的时钟并选择TMR0的时钟源为HXT
//Enable Timer0 clock and select Timer0 clock source CLK_EnableModuleClock(TMR0_MODULE); CLK_SetModuleClock(TMR0_MODULE, CLK_CLKSEL1_TMR0SEL_HXT, 0);
接着初始化定时器0,一条语句搞定
TIMER_Open(TIMER0, TIMER_PERIODIC_MODE, 2);
函数原型:
uint32_t TIMER_Open(TIMER_T *timer, uint32_t u32Mode, uint32_t u32Freq);
第一个参数,选择定时器(TIMER0, TIMER1, TIMER2, TIMER3)
第二个参数,选择定时器的模式,可选参数:
TIMER_ONESHOT_MODE
TIMER_PERIODIC_MODE
TIMER_TOGGLE_MODE
TIMER_CONTINUOUS_MODE
第三个参数,设置定时器的工作频率
相当方便!
接着,使能定时器中断
//Enable Timer0 interrupt TIMER_EnableInt(TIMER0); NVIC_EnableIRQ(TMR0_IRQn);
中断函数:
void TMR0_IRQHandler(void) { TimerCounter == 99 ? (TimerCounter = 0) : (TimerCounter++); // clear Timer0 interrupt flag TIMER_ClearIntFlag(TIMER0); }
主要是两个数码管计数到99清零,并清除中断标志位
接着初始化数码管,启动定时器
TIMER_Start(TIMER0);
接着就是让数码管不断扫描,显示了
while(1) { Segment_Display(TimerCounter / 10, 1); Delay_us(200); Segment_Display(TimerCounter % 10, 2); Delay_us(200); }
实验现象:
完整代码: