您好,欢迎进入 官方网站!
关于定时器 相信很多人都不会陌生,无论是51还是32,任何微控制器,都会有定时器
定时器控制器包含 4 组 32-位定时器,TIMER0~TIMER3,提供用户便捷的计数定时功能。定时器可执行很多功能,如频率测量,时间延迟,时钟发生,外部输入管脚事件计数和外部捕捉管脚脉宽测量等。
特性
4 组 32-位定时器,带24位向上计数器和一个8位的预分频计数器
每个定时器都可以设置独立的时钟源
提供 one-shot, periodic, toggle 和 continuous 四种计数操作模式
通过CNT (TIMERx_CNT[23:0])可读取内部 24 位向上计数器的值
支持事件计数功能
通过CAPDAT (TIMERx_CAP[23:0])可读取24-bit 捕捉值
支持外部管脚捕捉功能,可用于脉宽测量
支持外部引脚事件计数,可用于复位24位向上定时器
如果定时器中断信号产生,支持芯片从空闲/掉电模式唤醒
支持Timer0 超时溢出中断来触发Touch-Key 扫描
支持Timer0 ~ Timer3 超时溢出中断或捕捉中断来触发PWM, EADC 和 DAC 功能
/****************************************************************************** * @file main.c * @version V1.00 * $Revision: 4 $ * $Date: 15/09/02 10:03a $ * @brief NuEdu Basic01 Timer Sample Code * @note * Copyright (C) 2014~2015 Nuvoton Technology Corp. All rights reserved. *****************************************************************************/#include#include"M451Series.h"#include"NuEdu-Basic01.h"/*---------------------------------------------------------------------------------------------------------*//*TMR0 IRQ handler*//*---------------------------------------------------------------------------------------------------------*/uint32_tvolatileTimerCounter =0;voidTMR0_IRQHandler(void) { TimerCounter==99? (TimerCounter =0) : (TimerCounter++);//clear Timer0 interrupt flagTIMER_ClearIntFlag(TIMER0);//清空中断标志 }/*---------------------------------------------------------------------------------------------------------*//*MAIN function*//*---------------------------------------------------------------------------------------------------------*/intmain(void) {//Initial SystemSYS_Init();//Enable Timer0 clock and select Timer0 clock sourceCLK_EnableModuleClock(TMR0_MODULE);//使能模块 CLK_SetModuleClock(TMR0_MODULE, CLK_CLKSEL1_TMR0SEL_HXT,0);//选取时钟源,和进行分频//Initial Timer0 to periodic mode with 2HzTIMER_Open(TIMER0, TIMER_PERIODIC_MODE,2);//Enable Timer0 interruptTIMER_EnableInt(TIMER0); NVIC_EnableIRQ(TMR0_IRQn);//Initial 7-SegmentOpen_Seven_Segment();//Start Timer0TIMER_Start(TIMER0);while(1) { Show_Seven_Segment(TimerCounter/10,1); CLK_SysTickDelay(200); Show_Seven_Segment(TimerCounter%10,2); CLK_SysTickDelay(200); } }/*** (C) COPYRIGHT 2014~2015 Nuvoton Technology Corp. ***/
TIMER_Open(TIMER0, TIMER_PERIODIC_MODE, 2);定时器的模式有以下几种:
#defineTIMER_ONESHOT_MODE (0UL << TIMER_CTL_OPMODE_Pos) /*!< Timer working in one-shot mode */#defineTIMER_PERIODIC_MODE (1UL << TIMER_CTL_OPMODE_Pos) /*!< Timer working in periodic mode */#defineTIMER_TOGGLE_MODE (2UL << TIMER_CTL_OPMODE_Pos) /*!< Timer working in toggle-output mode */#defineTIMER_CONTINUOUS_MODE (3UL << TIMER_CTL_OPMODE_Pos) /*!< Timer working in continuous counting mode */#defineTIMER_TOUT_PIN_FROM_TX (0UL << TIMER_CTL_TGLPINSEL_Pos) /*!< Timer toggle-output pin is from Tx pin */#defineTIMER_TOUT_PIN_FROM_TX_EXT (1UL << TIMER_CTL_TGLPINSEL_Pos) /*!< Timer toggle-output pin is from Tx_EXT pin */#defineTIMER_CAPTURE_FREE_COUNTING_MODE (0UL << TIMER_EXTCTL_CAPFUNCS_Pos) /*!< Timer capture event to get timer counter value */#defineTIMER_CAPTURE_COUNTER_RESET_MODE (1UL << TIMER_EXTCTL_CAPFUNCS_Pos) /*!< Timer capture event to reset timer counter */#defineTIMER_CAPTURE_FALLING_EDGE (0UL << TIMER_EXTCTL_CAPEDGE_Pos) /*!< Falling edge detection to trigger timer capture */#defineTIMER_CAPTURE_RISING_EDGE (1UL << TIMER_EXTCTL_CAPEDGE_Pos) /*!< Rising edge detection to trigger timer capture */#defineTIMER_CAPTURE_FALLING_AND_RISING_EDGE (2UL << TIMER_EXTCTL_CAPEDGE_Pos) /*!< Both falling and rising edge detection to trigger timer capture */#defineTIMER_COUNTER_FALLING_EDGE (0UL << TIMER_EXTCTL_CNTPHASE_Pos) /*!< Counter increase on falling edge detection */#defineTIMER_COUNTER_RISING_EDGE (1UL << TIMER_EXTCTL_CNTPHASE_Pos) /*!< Counter increase on rising edge detection */
定时器计数模式
定时器控制器提供四种定时器计数模式: one-shot, periodic, toggle-output 和 continuous counting 计数模式
One–shot模式
如果定时器工作在单周期 (one-shot) 模式(TIMERx_CTL[28:27]为00,且CNTEN (TIMERx_CTL[30])置1),则定时器的计数器开始计数。一旦CNT (TIMERx_CNT[23:0])计数器的值达到CMPDAT (TIMERx_CMP[23:0])的值时,TIF (TIMERx_INTSTS[0])标志将变为1,CNT的值和 CNTEN位将由定时器控制器自动清零,然后定时器计数操作停止。与此同时,如果INTEN (TIMERx_CTL[29])位使能,则定时器中断信号产生并送到 NVIC通知CPU。
Periodic模式
如果定时器工作在周期 (periodic) 模式(TIMERx_CTL[28:27]为01)且CNTEN (TIMERx_CTL[30])置1,则定时器的计数器开始向上计数。一旦CNT (TIMERx_CNT[23:0])计数器的值达到CMPDAT (TIMERx_CMP[23:0])的值时,TIF (TIMERx_INTSTS[0])标志将变为1,CNT的值将由定时器控制器自动清零,然后定时器重新计数。与此同时,如果INTEN (TIMERx_CTL[29])使能,则定时器中断信号产生并送到 NVIC 通知 CPU 。在该模式,定时器控制器周期性地操作计数和 与CMPDAT的值比较,直到CNTEN位由软件清0
Toggle-Output模式
如果定时器工作在触发输出 (toggle-out) 模式(TIMERx_CTL[28:27]为10)且CNTEN (TIMERx_CTL[30])位置1,则定时器的计数器开始计数。toggle-out 模式的计数操做大部分与周期模式是一样的,除了该模式当TIF (TIMERx_INTSTS[0])位设置时,有相关的T0 ~ T3管脚来输出信号,因此,管脚T0 ~ T3上的触发输出信号以 50% 的占空周期反复改变。
Continuous Counting模式
如果定时器工作在连续计数 (continuous counting) 模式(TIMERx_CTL[28:27]为11)且CNTEN (TIMERx_CTL[30])位置1,则定时器的计数器开始计数。一旦CNT (TIMERx_CNT[23:0])的值达到CMPDAT (TIMERx_CMP[23:0])的值时,TIF (TIMERx_INTSTS[0])标志将变为1,但CNT的值继续保持向上计数。与此同时,如果INTEN (TIMERx_CTL[29])使能,则定时器中断信号产生并送到 NVIC 通知 CPU 。在该模式,用户可以立刻改变不同的CMPDAT值,而不需要停止定时器计数和重新开始定时器计数。
例如,CMPDAT的值设置为 80。当CNT 达到 80时,TIF标志将被置1,定时器计数器继续计数,而且CNT的值将不回到0,而是继续计数,81, 82, 83,˙˙˙ 到 (224 -1),然后再一次 0, 1, 2, 3, ˙˙˙ 到 224 -1,如此往复。接下来,如果软件改变CMPDAT的值为200并且清除TIF标志位,当CNT的值达到200时,TIF标志将再次变为1,。最后,软件改变CMPDAT的值为500并且清除TIF标志,当CNT的值达到500时,TIF标志将再次变为1。
在该模式,计数器计数是连续的。所以该操作模式叫做连续计数模式。
事件计数模式
定时器控制器也提供这样的应用,能对输入事件(来自管脚Tx x=0~3)计数并将事件的次数反应到
CNT (TIMERx_CNT[23:0]) 的值。也可以称为事件计数功能。该功能下, EXTCNTEN
(TIMERx_CTL[24])位需置位并且定时器外设时钟源必须设为HCLKPCLKx (x= 0~1)。
软件可以通过CNTDBEN (TIMERx_EXTCTL[7])位来使能或关闭Tx管脚消抖电路。如果Tx管脚的消
抖电路关闭,输入事件频率必须少于1/3 PCLKxHCLK,如果消抖电路打开,输入事件的频率须小
于1/8 PCLKxHCLK , 以保证CNT 的值是正确的。软件也可以通过设置CNTPHASE
(TIMERx_EXTCTL[0])来选择边沿检测Tx管脚的相位。
事件计数模式下,定时器计数操作模式可以设置为单次,周期,和连续计数模式来计算来自Tx管脚
的输入事件CNT (TIMERx_CNT[23:0])的值。
外部捕捉模式
事件捕捉功能是当检测到Tx_EXT管脚(x=0~3)边沿电平有变化时,CNT (TIMERx_CNT[23:0])会送
到CAPDAT (TIMERx_CAP[23:0])。在该模式下,需把CAPFUNCS (TIMERx_EXTCTL[4])位设置为
0,用来选择Tx_EXT变化时用作事件捕捉功能,而且定时器外设时钟源必须设为PCLKx (x=
0~1)HCLK。
软件可以通过CAPDBEN (TIMERx_EXTCTL[6])位来使能或关闭Tx_EXT管脚消抖电路。在Tx_EXT
的消抖电路关闭时,Tx_EXT管脚的转变频率必须少于1/3 PCLKxHCLK,在Tx_EXT的消抖电路打
开时,Tx_EXT管脚的转变频率必须少于1/8 PCLKxHCLK,以保证捕捉功能能够正常工作。软件也
可以通过设置CAPEDGE (TIMERx_EXTCTL[2:1])位来选择Tx_EXT管脚的边沿转变检测方式。.
在事件捕捉模式,软件不用考虑定时器计数器工作模式的选择,只有当检测到Tx_EXT管脚有边沿
变化时捕捉事件才会发生。
如果CPU不清除CAPIF状态标志,用户应知道此时的定时器会保持TIMERx_CAP寄存器的值不变,
且不会保存新的捕捉值。
外部复位计数模式
当检测到Tx_EXT管脚(x=0~3)有边沿转变时,定时器同样提供事件复位计数器功能来复位CNT
(TIMERx_CNT[23:0]) 的值。在该模式, 大部分设置与事件捕捉功能相同, 除了CAPFUNCS
(TIMERx_EXTCTL[4])位必须设置为1来选择Tx_EXT转变时用作为事件复位计数器。
定时器触发功能
定时器控制器提供定时器超时溢出中断或捕捉中断来触发PWM,DAC和EADC. 如果TRGSSEL (TIMERx_CTL[18])为0, 超时溢出中断信号用于触发PWM, EADC和DAC. 如果TRGSSEL (TIMERx_CTL[18]) 为 1, 捕捉中断信号用于触发PWM, EADC 和 DAC.
当TRGPWM (TIMERx_CTL[19])被置1, 如果定时中断信号产生, 定时器控制器将产生一个触发脉冲作为PWM外部时钟源。
当TRGDAC (TIMERx_CTL[20]) 被置1, 如果定时中断信号产生, 定时器控制器将触发DAC开始转换。
当TRGEADC (TIMERx_CTL[21]) 被置1, 如果定时中断信号产生, 定时器控制器将触发EADC开始转换。
Timer0定时器控制器也提供定时器超时中断来触发触摸按键(Touch-Key)的扫描,当WKTKEN (TIMER0_CTL[17])被置1且芯片在Power-down模式,如果Timer0超时中断信号产生, Timer0 控制器也会触发触摸按键(Touch-Key)的扫描. 在此功能模式下, timer0模块时钟源应设置为内部低速 内部低速 RC 振 荡器 (LIRC)的 10 kHz 时钟 或者外部低速晶体 外部低速晶体 外部低速晶体 (LXT) 的32.768 kHz时
这一段时间真的太安逸了,思维都懒惰了,想当初在大学时利用定时器就可以做个时钟出来,现在居然一心只为了完成任务,没有了原有的项目热情,以后一定要努力,绝不放过任何提升自己的机会。
最上面的程序是利用数码管来进行0到99的计数,我觉得着呢有必要讲一讲数码管的知识
数码管的一种是半导体发光器件,数码管可分为七段数码管和八段数码管,区别在于八段数码管比七段数码管多一个用于显示小数点的发光二极管单元DP(decimal point),其基本单元是发光二极管。
共阴极和共阳极的表示方法看下文:
void Show_Seven_Segment(unsigned char no, unsigned char number)
{
SEG_A_OFF;
SEG_B_OFF;
SEG_C_OFF;
SEG_D_OFF;
SEG_E_OFF;
SEG_F_OFF;
SEG_G_OFF;
SEG_H_OFF;
SEG_CONTROL1_OFF;
SEG_CONTROL2_OFF;
switch(no)
{
//show 0
case 0:
SEG_A_ON;
SEG_B_ON;
SEG_C_ON;
SEG_D_ON;
SEG_E_ON;
SEG_F_ON;
break;
//show 1
case 1:
SEG_B_ON;
SEG_C_ON;
break;
//show 2
case 2:
SEG_A_ON;
SEG_B_ON;
SEG_G_ON;
SEG_E_ON;
SEG_D_ON;
break;
//show 3
case 3:
SEG_A_ON;
SEG_B_ON;
SEG_G_ON;
SEG_C_ON;
SEG_D_ON;
break;
//show 4
case 4:
SEG_F_ON;
SEG_B_ON;
SEG_G_ON;
SEG_C_ON;
break;
//show 5
case 5:
SEG_A_ON;
SEG_F_ON;
SEG_G_ON;
SEG_C_ON;
SEG_D_ON;
break;
//show 6
case 6:
SEG_A_ON;
SEG_F_ON;
SEG_E_ON;
SEG_G_ON;
SEG_C_ON;
SEG_D_ON;
break;
//show 7
case 7:
SEG_A_ON;
SEG_B_ON;
SEG_C_ON;
SEG_F_ON;
break;
//show 8
case 8:
SEG_A_ON;
SEG_B_ON;
SEG_C_ON;
SEG_D_ON;
SEG_E_ON;
SEG_F_ON;
SEG_G_ON;
break;
//show 9
case 9:
SEG_A_ON;
SEG_B_ON;
SEG_C_ON;
SEG_F_ON;
SEG_G_ON;
break;
}
switch(number)
{
case 1:
SEG_CONTROL1_ON;
break;
//show 1
case 2:
SEG_CONTROL2_ON;
break;
}
}
M451则是这么表示的,其实是一样的。
根据M451的定时器,我们可以计算出它的一些特点,
uint32_t TIMER_Open(TIMER_T *timer, uint32_t u32Mode, uint32_t u32Freq) { uint32_t u32Clk=TIMER_GetModuleClock(timer); uint32_t u32Cmpr=0, u32Prescale =0;//Fastest possible timer working freq is (u32Clk / 2). While cmpr = 2, pre-scale = 0. //最快可能的定时器工作频率是(u32Clk / 2)。 当cmpr = 2时,预标度= 0。if(u32Freq > (u32Clk /2)) { u32Cmpr=2; }else{if(u32Clk >64000000) { u32Prescale=7;//real prescaler value is 8u32Clk >>=3; }elseif(u32Clk >32000000) { u32Prescale=3;//real prescaler value is 4u32Clk >>=2; }elseif(u32Clk >16000000) { u32Prescale=1;//real prescaler value is 2u32Clk >>=1; } u32Cmpr= u32Clk /u32Freq; } timer->CTL = u32Mode |u32Prescale; timer->CMP =u32Cmpr;return(u32Clk / (u32Cmpr * (u32Prescale +1))); }
uint32_t TIMER_GetModuleClock(TIMER_T *timer) { uint32_t u32Src;constuint32_t au32Clk[] = {__HXT, __LXT,0,0,0, __LIRC,0, __HIRC};if(timer ==TIMER0) u32Src= (CLK->CLKSEL1 & CLK_CLKSEL1_TMR0SEL_Msk) >>CLK_CLKSEL1_TMR0SEL_Pos;elseif(timer ==TIMER1) u32Src= (CLK->CLKSEL1 & CLK_CLKSEL1_TMR1SEL_Msk) >>CLK_CLKSEL1_TMR1SEL_Pos;elseif(timer ==TIMER2) u32Src= (CLK->CLKSEL1 & CLK_CLKSEL1_TMR2SEL_Msk) >>CLK_CLKSEL1_TMR2SEL_Pos;else//Timer 3u32Src = (CLK->CLKSEL1 & CLK_CLKSEL1_TMR3SEL_Msk) >>CLK_CLKSEL1_TMR3SEL_Pos;if(u32Src ==2) {return(SystemCoreClock); }return(au32Clk[u32Src]); } 它是自动选取时钟源的。 不明白的话看一下寄存器是怎么写的,对照一下。
printf("# Timer0 Settings:\n"); printf("- Clock source is HXT \n"); printf("- Time-out frequency is 1 Hz\n"); printf("- Periodic mode \n"); printf("- Interrupt enable \n"); printf("# Timer1 Settings:\n"); printf("- Clock source is HCLK \n"); printf("- Time-out frequency is 2 Hz\n"); printf("- Periodic mode \n"); printf("- Interrupt enable \n"); printf("# Timer2 Settings:\n"); printf("- Clock source is HIRC \n"); printf("- Time-out frequency is 4 Hz\n"); printf("- Periodic mode \n"); printf("- Interrupt enable \n"); printf("# Timer3 Settings:\n"); printf("- Clock source is HXT \n"); printf("- Time-out frequency is 8 Hz\n"); printf("- Periodic mode \n"); printf("- Interrupt enable \n"); printf("# Check Timer0 ~ Timer3 interrupt counts are reasonable or not.\n\n");/*Open Timer0 in periodic mode, enable interrupt and 1 interrupt tick per second*/TIMER0->CMP =__HXT; TIMER0->CTL = TIMER_CTL_INTEN_Msk |TIMER_PERIODIC_MODE; TIMER_SET_PRESCALE_VALUE(TIMER0,0);/*Open Timer1 in periodic mode, enable interrupt and 2 interrupt ticks per second*/TIMER1->CMP = ((SystemCoreClock /4) /2); TIMER1->CTL = TIMER_CTL_INTEN_Msk |TIMER_PERIODIC_MODE; TIMER_SET_PRESCALE_VALUE(TIMER1,3);/*Open Timer2 in periodic mode, enable interrupt and 4 interrupt ticks per second*/TIMER2->CMP = ((__HIRC /1) /4); TIMER2->CTL = TIMER_CTL_INTEN_Msk |TIMER_PERIODIC_MODE; TIMER_SET_PRESCALE_VALUE(TIMER2,0);/*Open Timer3 in periodic mode, enable interrupt and 8 interrupt ticks per second*/TIMER3->CMP = ((__HXT /1) /8); TIMER3->CTL = TIMER_CTL_INTEN_Msk |TIMER_PERIODIC_MODE; TIMER_SET_PRESCALE_VALUE(TIMER3,0);/*Enable Timer0 ~ Timer3 NVIC*/NVIC_EnableIRQ(TMR0_IRQn); NVIC_EnableIRQ(TMR1_IRQn); NVIC_EnableIRQ(TMR2_IRQn); NVIC_EnableIRQ(TMR3_IRQn);/*Clear Timer0 ~ Timer3 interrupt counts to 0*/g_au32TMRINTCount[0] = g_au32TMRINTCount[1] = g_au32TMRINTCount[2] = g_au32TMRINTCount[3] =0; u32InitCount= g_au32TMRINTCount[0];/*Start Timer0 ~ Timer3 counting*/TIMER_Start(TIMER0); TIMER_Start(TIMER1); TIMER_Start(TIMER2); TIMER_Start(TIMER3);/*Check Timer0 ~ Timer3 interrupt counts*/printf("# Timer interrupt counts :\n");
附一张时钟源的选择
/****************************************************************************** * @file main.c * @version V1.00 * $Revision: 4 $ * $Date: 15/09/02 10:03a $ * @brief NuEdu Basic01 Timer Sample Code * @note * Copyright (C) 2014~2015 Nuvoton Technology Corp. All rights reserved. *****************************************************************************/#include#include"M451Series.h"#include"NuEdu-Basic01.h"/*---------------------------------------------------------------------------------------------------------*//*TMR0 IRQ handler*//*---------------------------------------------------------------------------------------------------------*/uint32_tvolatileTimerCounter =0;voidGPD_IRQHandler(void) {/*To check if PC.5 interrupt occurred*/if(GPIO_GET_INT_FLAG(PD, BIT3)) { Write_Buzzer(0,0,0); }//else//{///* Un-expected interrupt. Just clear all PC interrupts *///PC->INTSRC = PC->INTSRC;//printf("Un-expected interrupts.\n");//}}voidTMR0_IRQHandler(void) { TimerCounter==99? (TimerCounter =0) : (TimerCounter++);//clear Timer0 interrupt flagif(Get_Key_Input()&0x01) { TimerCounter++; Show_Seven_Segment(TimerCounter/10,1);//CLK_SysTickDelay(200);Show_Seven_Segment(TimerCounter %10,2);//CLK_SysTickDelay(200);}if(Get_Key_Input()&0x02) { TimerCounter--; Show_Seven_Segment(TimerCounter/10,1);//CLK_SysTickDelay(200);Show_Seven_Segment(TimerCounter %10,2);//CLK_SysTickDelay(200);} TIMER_ClearIntFlag(TIMER0); }/*---------------------------------------------------------------------------------------------------------*//*MAIN function*//*---------------------------------------------------------------------------------------------------------*/intmain(void) {inti=125;//Initial SystemSYS_Init();/*Configure PD.3 as Quasi-bidirection mode and enable interrupt by falling edge trigger*/PD->MODE = (PD->MODE & (~GPIO_MODE_MODE3_Msk)) | (GPIO_MODE_QUASI <<GPIO_MODE_MODE3_Pos);//Enable Timer0 clock and select Timer0 clock sourceCLK_EnableModuleClock(TMR0_MODULE); CLK_SetModuleClock(TMR0_MODULE, CLK_CLKSEL1_TMR0SEL_HXT,0); PD->INTTYPE |= (GPIO_INTTYPE_LEVEL <<GPIO_INTTYPE_TYPE3_Pos); PD->INTEN |=GPIO_INTEN_FLIEN3_Msk; NVIC_EnableIRQ(GPD_IRQn);//Initial Timer0 to periodic mode with 2HzTIMER_Open(TIMER0, TIMER_PERIODIC_MODE,2);//Enable Timer0 interruptTIMER_EnableInt(TIMER0); NVIC_EnableIRQ(TMR0_IRQn); Open_Buzzer();//Initial 7-SegmentOpen_Seven_Segment(); Initial_Key_Input();//Start Timer0TIMER_Start(TIMER0);while(1) {/*Configure PC.5 as Quasi-bidirection mode and enable interrupt by falling edge trigger*/if(Get_Key_Input()&0x04) { Write_Buzzer(1,250,i++); }if(Get_Key_Input()&0x08) { Write_Buzzer(1,250,i--); } Show_Seven_Segment(TimerCounter/10,1); CLK_SysTickDelay(200); Show_Seven_Segment(TimerCounter%10,2); CLK_SysTickDelay(200);if(TimerCounter==50) { Write_Buzzer(1,250,70); } } }/*** (C) COPYRIGHT 2014~2015 Nuvoton Technology Corp. ***/
这部分需要我认真调试,观察,有一些不对的地方
上一篇:C语言基础之水仙花数
下一篇:C语言while语句