NUC140之定时器
来源:互联网 发布:mac jdk 默认安装路径 编辑:程序博客网 时间:2024/05/17 03:46
相信用过芯塘NUC1xx系列单片机的朋友们对NUC1xx 固件库函数一定不陌生.
本人用的固件库版本是V1.05.001,在用定时器的库函数的时候发现了一个问题:
本人用定时器监控一个信号,这个信号平时是高电平,当它变为低电平的时候,启动定时器,开始计时,假设计时时间为120S,
如果这个信号保持低电平120S,则会触发中断函数,从而使这个信号不再起作用,
如果在这120S内,这个信号恢复高电平,则重置计数器,在下次这个信号变为低电平的时候,从新计数。
以下是本人设置的函数:
/**************************************************
*函数名: initialize_timer *
*功能: 初始化timer必须调用的函数 *
*出口参数:无 *
*入口参数:无 *
*************************************************/
void initialize_timer (void)
{
DrvTIMER_Init (); //初始化timer必须调用的函数
}
/**************************************************
*函数名: initialize_timer0 *
*功能: 初始化timer0 *
*出口参数:无 *
*入口参数:无 *
*************************************************/
void initialize_timer0 (void)
{
DrvSYS_UnlockProtectedReg (); //解锁受保护的系统寄存器
DrvSYS_SelectIPClockSource (E_SYS_TMR0_CLKSRC, 0x00); //设置定时器0的时钟源为外部12M晶振
DrvSYS_LockProtectedReg (); //对系统寄存器上锁
delay (50); //延迟使时钟稳定
DrvTIMER_Open (E_TMR0, //使能定时器0
1, //每秒计数1次
E_PERIODIC_MODE); //单周期模式
}
/**************************************************
*函数名: setup_ptt_limit_time *
*功能: 调整发定时时间 *
*出口参数:无 *
*入口参数:无 *
*************************************************/
void setup_ptt_limit_time (int32_t PTT_limit_time_) //参数最后加"_"是为了区别全局变量
{
DrvTIMER_SetTimerEvent (E_TMR0, //定时器0
PTT_limit_time_, //发定时时间,单位为秒
PTT_callback, //发定时回调函数,回调函数类型必须为typedef void (*TIMER_CALLBACK)(uint32_t data); /* function pointer */,否则应像例子那样用强制转换类型符
0); //传给回调函数的参数,暂无用
}
/**************************************************
*函数名: PTT_callback *
*功能: PTT定时回调函数,用于停发 *
*出口参数:无 *
*入口参数:有,暂时无用 *
*************************************************/
void PTT_callback (uint32_t callback_data)
{
DrvTIMER_DisableInt(E_TMR0); //关定时器0中断,发定时一直起作用的时候无需多次进入该中断中
DrvTIMER_ClearIntFlag(E_TMR0); //清空定时器0中断标志位
PTT_time_off = 1; //发定时到
}
简单的讲:就是先设定1秒中断几次,假如说想要200ms的中断,就设置1秒计数5次,计数1次就进入中断,
如果想要的是400ms,就设置1秒计数5次,计数两次进入中断
本人发现用定时器的one-shot模式,根本进不到中断函数中,只好用单周期模式,不知何解。
严归正传以上的函数是用以初始化定时器,并设置用户的中断服务函数,其实我们之后可以看到
用户设置的中断函数,并不是真的中断部分函数,而是在厂家的固件库函数里,通过函数指针来调用用户的中断函数。
笔者用以上的几个函数来初始化定时器中断
之后是笔者用来启动定时器和清除定时计数次数的函数
if ((enable_PTT_limit == 1) && (active_PTT_limit == 0)) //如果使能发定时并且没有使能定时器0中断
{
DrvTIMER_EnableInt (E_TMR0); //使能定时器0中断
DrvTIMER_Start (E_TMR0); //定时器0开始计时
active_PTT_limit = 1; //发定时已激活
}
else if ((PTT_mic == 0) && (PTT_remote == 0))
{
DrvTIMER_Stop(E_TMR0); //暂停定时0并清空当前计时
PTT_time_off = 0; //清除发定时到时标志
PTT_start = 0; //PTT未发起
active_PTT_limit = 0; //清除发定时已使能标志,使得定时器可以在PTT_on中再次被激活
}
其中DrvTIMER_Stop(E_TMR0); 这个函数是笔者自己写的
这样就能完成笔者所要的目的了,其中使能中断和定时器开始计数,只不过是对TCSR这个寄存器的几个控制位进行操作,在此就不贴出了
之后我们来看固件库函数,笔者一共用到的固件库函数如下:大家只用注意看有中文注释的部分即可
另外大家要对TIMER的寄存器有所了解 具体可参看NUC130-140数据手册
还有注意看红体字的部分那是很重要的
由于笔者只用定时器0,而且其他定时器的函数都是重复的,建议大家只看定时器0即可
/*---------------------------------------------------------------------------------------------------------*/
/* Function: DrvTIMER_Open */
/* */
/* Parameters: */
/* ch - [in] */
/* E_TIMER_CHANNEL, it could be E_TMR0/E_TMR1/E_TMR2/E_TMR3 */
/* uTicksPerSecond - [in] */
/* This value means how many timer interrupt ticks in one second */
/* op_mode - [in] */
/* E_TIMER_OPMODE, E_ONESHOT_MODE/E_PERIODIC_MODE/E_TOGGLE_MODE/E_CONTINUOUS_MODE */
/* Returns: */
/* E_SUCCESS Operation successful */
/* E_DRVTIMER_CHANNEL Invalid Timer channel */
/* E_DRVTIMER_CLOCK_RATE Calculate initial value fail */
/* Description: */
/* Open the specified timer channel with specified operation mode. */
/*---------------------------------------------------------------------------------------------------------*/
int32_t DrvTIMER_Open(E_TIMER_CHANNEL ch, uint32_t uTicksPerSecond, E_TIMER_OPMODE op_mode)
{
uint32_t i;
uint32_t uRegTcmpr, uRegTcr = 0x0;
switch (ch)
{
case E_TMR0:
{
if ((bIsTimer0Initial != TRUE) || (bIsTimer0Used != FALSE))
return E_DRVTIMER_EIO;
bIsTimer0Used = TRUE; //定时器0正在使用标志
SYSCLK->APBCLK.TMR0_EN = 1; //打开定时器0的APB时钟
outpw((uint32_t)&TIMER0->TCSR, 0); /* disable timer,这个很巧妙,是将TCSR整个32位全部赋0,注意看强制转换uint32_t并且按位与 */
TIMER0->TISR.TIF = 1; /* write 1 to clear for safty ,清定时器0中断标志位*/
for (i=0; i<TIMER_EVENT_COUNT; i++)
{
tTime0Event[i].active = FALSE; //设置定时器0事件未激活,给下一个事件设置函数做准备
}
uTimer0Tick = 0; //定时器0 Tick设置为0
_sys_uTimer0TickPerSecond = uTicksPerSecond; //将用户设置的1秒内Tick数传递给_sys_uTimer0TickPerSecond
uRegTcmpr = CalTimerInitValue(GetTimerClock(E_TMR0), uTicksPerSecond); //进行计算
if (uRegTcmpr == (uint32_t)-1)
{
return E_DRVTIMER_CLOCK_RATE;
}
TIMER0->TCMPR = (uRegTcmpr << 8) >> 8; //对24位定时器比较寄存器赋值,注意移位运算
outpw((uint32_t)&TIMER0->TCSR, (uRegTcr|(uRegTcmpr>>24))|(op_mode<<27)); //初始化TCSR
break;
}
case E_TMR1:
{
if ((bIsTimer1Initial != TRUE) || (bIsTimer1Used != FALSE))
return E_DRVTIMER_EIO;
bIsTimer1Used = TRUE;
SYSCLK->APBCLK.TMR1_EN = 1;
outpw((uint32_t)&TIMER1->TCSR, 0); /* disable timer */
TIMER1->TISR.TIF = 1; /* write 1 to clear for safty */
for (i=0; i<TIMER_EVENT_COUNT; i++)
{
tTime1Event[i].active = FALSE;
}
uTimer1Tick = 0;
_sys_uTimer1TickPerSecond = uTicksPerSecond;
uRegTcmpr = CalTimerInitValue(GetTimerClock(E_TMR1), uTicksPerSecond);
if(uRegTcmpr == (uint32_t)-1)
{
return E_DRVTIMER_CLOCK_RATE;
}
TIMER1->TCMPR = (uRegTcmpr << 8) >> 8;
outpw((uint32_t)&TIMER1->TCSR, (uRegTcr|(uRegTcmpr>>24))|(op_mode<<27));
break;
}
case E_TMR2:
{
if ((bIsTimer2Initial != TRUE) || (bIsTimer2Used != FALSE))
return E_DRVTIMER_EIO;
bIsTimer2Used = TRUE;
SYSCLK->APBCLK.TMR2_EN =1;
outpw((uint32_t)&TIMER2->TCSR, 0); /* disable timer */
TIMER2->TISR.TIF = 1; /* write 1 to clear for safty */
for (i=0; i<TIMER_EVENT_COUNT; i++)
{
tTime2Event[i].active = FALSE;
}
uTimer2Tick = 0;
_sys_uTimer2TickPerSecond = uTicksPerSecond;
uRegTcmpr = CalTimerInitValue(GetTimerClock(E_TMR2), uTicksPerSecond);
if(uRegTcmpr == (uint32_t)-1)
{
return E_DRVTIMER_CLOCK_RATE;
}
TIMER2->TCMPR = (uRegTcmpr << 8) >> 8;
outpw((uint32_t)&TIMER2->TCSR, (uRegTcr|(uRegTcmpr>>24))|(op_mode<<27));
break;
}
case E_TMR3:
{
if ((bIsTimer3Initial != TRUE) || (bIsTimer3Used != FALSE))
return E_DRVTIMER_EIO;
bIsTimer3Used = TRUE;
SYSCLK->APBCLK.TMR3_EN = 1;
outpw((uint32_t)&TIMER3->TCSR, 0); /* disable timer */
TIMER3->TISR.TIF = 1; /* write 1 to clear for safty */
for (i=0; i<TIMER_EVENT_COUNT; i++)
{
tTime3Event[i].active = FALSE;
}
uTimer3Tick = 0;
_sys_uTimer3TickPerSecond = uTicksPerSecond;
uRegTcmpr = CalTimerInitValue(GetTimerClock(E_TMR3), uTicksPerSecond);
if(uRegTcmpr == (uint32_t)-1)
{
return E_DRVTIMER_CLOCK_RATE;
}
TIMER3->TCMPR = (uRegTcmpr << 8) >> 8;
outpw((uint32_t)&TIMER3->TCSR, (uRegTcr|(uRegTcmpr>>24))|(op_mode<<27));
break;
}
default:
{
return E_DRVTIMER_CHANNEL ;
}
}
if (op_mode == E_TOGGLE_MODE)
{
switch (ch)
{
case E_TMR0:
case E_TMR1:
case E_TMR2:
case E_TMR3:
{
DrvGPIO_InitFunction((E_DRVGPIO_FUNC)((uint32_t)E_FUNC_TMR0 + (uint32_t)ch));
}
default:
{
return E_DRVTIMER_CHANNEL ;
}
}
}
return E_SUCCESS;
}
/*---------------------------------------------------------------------------------------------------------*/
/* Function: DrvTIMER_SetTimerEvent */
/* */
/* Parameters: */
/* ch - [in] */
/* E_TIMER_CHANNEL, it could be E_TMR0/E_TMR1/E_TMR2/E_TMR3 */
/* uInterruptTicks - [in] */
/* Number of timer interrupt occurred */
/* pTimerCallback - [in] */
/* The function pointer of the interrupt callback function */
/* parameter - [in] */
/* A parameter of the callback function */
/* */
/* Returns: */
/* uTimerEventNo The timer event number */
/* E_DRVTIMER_EVENT_FULL The timer event is full */
/* Description: */
/* Install the interrupt callback function of the specified timer channel. */
/* And trigger timer callback functuion when interrupt occur specified times. */
/*---------------------------------------------------------------------------------------------------------*/
int32_t DrvTIMER_SetTimerEvent(E_TIMER_CHANNEL ch, uint32_t uInterruptTicks, TIMER_CALLBACK pTimerCallback, uint32_t parameter)
{
volatile int32_t i;
int32_t uTimerEventNo = 0;
switch (ch)
{
case E_TMR0:
{
if (uTime0EventCount >= TIMER_EVENT_COUNT) //如果已经设置事件了
return E_DRVTIMER_EVENT_FULL; //返回事件已满
bIsSetTime0Event = TRUE; //有事件
uTime0EventCount++; //设置事件计数器加1
for (i=0; i<TIMER_EVENT_COUNT; i++)
{
if (tTime0Event[i].active == FALSE) //如果定时器0事件激活状态为0
{
tTime0Event[i].active = TRUE; //设置激活状态为1
tTime0Event[i].initTick = uInterruptTicks; //将传入的多少次Tick进入用户中断赋值给初始化Tick及当前Tick两个变量
tTime0Event[i].curTick = uInterruptTicks; //当前Tick数
tTime0Event[i].funPtr = (TIMER_CALLBACK)pTimerCallback; //用户自定义的回调函数
tTime0Event[i].transParam = parameter; //用户自定义的回调函数的参数
uTimerEventNo = i;
break;
}
}
break;
}
case E_TMR1:
{
if (uTime1EventCount >= TIMER_EVENT_COUNT)
return E_DRVTIMER_EVENT_FULL;
bIsSetTime1Event = TRUE;
uTime1EventCount++;
for (i=0; i<TIMER_EVENT_COUNT; i++)
{
if (tTime1Event[i].active == FALSE)
{
tTime1Event[i].active = TRUE;
tTime1Event[i].initTick = uInterruptTicks;
tTime1Event[i].curTick = uInterruptTicks;
tTime1Event[i].funPtr = (TIMER_CALLBACK)pTimerCallback;
tTime1Event[i].transParam = parameter;
uTimerEventNo = i;
break;
}
}
break;
}
case E_TMR2:
{
if (uTime2EventCount >= TIMER_EVENT_COUNT)
return E_DRVTIMER_EVENT_FULL;
bIsSetTime2Event = TRUE;
uTime2EventCount++;
for (i=0; i<TIMER_EVENT_COUNT; i++)
{
if (tTime2Event[i].active == FALSE)
{
tTime2Event[i].active = TRUE;
tTime2Event[i].initTick = uInterruptTicks;
tTime2Event[i].curTick = uInterruptTicks;
tTime2Event[i].funPtr = (TIMER_CALLBACK)pTimerCallback;
tTime2Event[i].transParam = parameter;
uTimerEventNo = i;
break;
}
}
break;
}
case E_TMR3:
{
if (uTime3EventCount >= TIMER_EVENT_COUNT)
return E_DRVTIMER_EVENT_FULL;
bIsSetTime3Event = TRUE;
uTime3EventCount++;
for (i=0; i<TIMER_EVENT_COUNT; i++)
{
if (tTime3Event[i].active == FALSE)
{
tTime3Event[i].active = TRUE;
tTime3Event[i].initTick = uInterruptTicks;
tTime3Event[i].curTick = uInterruptTicks;
tTime3Event[i].funPtr = (TIMER_CALLBACK)pTimerCallback;
tTime3Event[i].transParam = parameter;
uTimerEventNo = i;
break;
}
}
break;
}
default:
{
break;
}
}
return uTimerEventNo;
}
固件库里给了两个函数,看名字貌似是获得当前计数次数和清除当前计数次数我们来看函数:
/*---------------------------------------------------------------------------------------------------------*/
/* Function: DrvTIMER_GetIntTicks */
/* */
/* Parameters: */
/* ch - [in] */
/* E_TIMER_CHANNEL, it could be E_TMR0/E_TMR1/E_TMR2/E_TMR3 */
/* Returns: */
/* uTimerTick Return the interrupt ticks */
/* E_DRVTIMER_CHANNEL Invalid Timer channel */
/* Description: */
/* This function is used to get the number of interrupt occurred */
/* after the timer interrupt function is enabled. . */
/* Thus DrvTIMER_EnableInt(ch) must been called in advance. */
/*---------------------------------------------------------------------------------------------------------*/
uint32_t DrvTIMER_GetIntTicks(E_TIMER_CHANNEL ch)
{
switch (ch)
{
case E_TMR0:
{
return uTimer0Tick;
}
case E_TMR1:
{
return uTimer1Tick;
}
case E_TMR2:
{
return uTimer2Tick;
}
case E_TMR3:
{
return uTimer3Tick;
}
default:
{
return E_DRVTIMER_CHANNEL;
}
}
}
/*---------------------------------------------------------------------------------------------------------*/
/* Function: DrvTIMER_ResetIntTicks */
/* */
/* Parameters: */
/* ch - [in] */
/* E_TIMER_CHANNEL, it could be E_TMR0/E_TMR1/E_TMR2/E_TMR3 */
/* Returns: */
/* E_SUCCESS Operation successful */
/* E_DRVTIMER_CHANNEL Invalid Timer channel */
/* Description: */
/* This function is used to clear interrupt ticks to 0. */
/*---------------------------------------------------------------------------------------------------------*/
int32_t DrvTIMER_ResetIntTicks(E_TIMER_CHANNEL ch)
{
switch (ch)
{
case E_TMR0:
{
uTimer0Tick = 0;
break;
}
case E_TMR1:
{
uTimer1Tick = 0;
break;
}
case E_TMR2:
{
uTimer2Tick = 0;
break;
}
case E_TMR3:
{
uTimer3Tick = 0;
break;
}
default:
{
return E_DRVTIMER_CHANNEL;
}
}
return E_SUCCESS;
}
我们可以看到,所谓的获取计数次数和重置计数次数不过是操作了 uTimer2Tick这个全局变量而已。
接下来我们来看最重要的部分:
/*---------------------------------------------------------------------------------------------------------*/
/* Function: TMR0_IRQHandler */
/* */
/* Parameters: */
/* None */
/* Returns: */
/* None */
/* Description: */
/* The TIMER0 default IRQ, declared in startup_NUC1xx.s */
/*---------------------------------------------------------------------------------------------------------*/
void TMR0_IRQHandler(void)
{
int32_t i;
if ((TIMER0->TCSR.IE == 1) && (TIMER0->TISR.TIF == 1)) //如果使能了中断且中断标志为1
TIMER0->TISR.TIF = 1; //写1清中断标志
if ((TIMER0->TEXCON.TEXEN == 1) && (TIMER0->TEXCON.TEXIEN == 1) && (TIMER0->TEXISR.TEXIF == 1))
TIMER0->TEXISR.TEXIF = 1;
uTimer0Tick++; //定时器0 Tick++,这个Tick数只不过是给Get_Tick和Reset_Tick这两个函数用的而已,清除它根本不能使定时器从新计时
//要想让定时器从新计时,必须清除tTime0Event[i].curTick这个,由于他是递减运算,所以采用这句
//tTime0Event[i].curTick = tTime0Event[i].initTick; //重新装载用户设置的多少次Tick进入用户中断
if (bIsSetTime0Event) /* Timer Event Handle ,如果已经设置了事件*/
{
for (i=0; i<TIMER_EVENT_COUNT; i++)
{
if (tTime0Event[i].active)
{
tTime0Event[i].curTick--; //定时器0当前Tick计数器,找到了关键所在
if (tTime0Event[i].curTick == 0) //如果定时器当前计数器Tick值为0
{
(*tTime0Event[i].funPtr)(tTime0Event[i].transParam); //调用用户中断函数,在此用了函数指针间接调用了用户函数
tTime0Event[i].curTick = tTime0Event[i].initTick; //重新装载用户设置的多少次Tick进入用户中断
}
}
}
}
}
这才是真正的问题所在,笔者曾经试验过很多种方法,就像前面所说的,当控制信号为高电平的时候,笔者希望定时器从新计数,
想让定时器不计数很简单,我们可以设置寄存器TCSRDE 的CEN位为0让它暂停计数,
也可以禁止中断,但是禁止中断后,定时器仍然会继续计数,当它溢出后,又会从新开始,
但是由于我们用的是固件库函数,我们并非直接操作的是寄存器,所以笔者在此推荐,
哪里出现的问题,从哪里找办法解决,固件库很强大,但是也不可能是面面俱到,我们可以修改库函数来满足自己的要求。
笔者其实就是想找一个函数能够清除当前计数次数。
如果用DrvTIMER_ResetIntTicks 函数会出现什么情况呢?
假如我设置的是1秒计数1次,120次计数进入中断,那么,无论我使控制信号为高为低多少次,
定时器一定会忠诚地,每120秒打断我一次,控制信号为高并不能重置计数器的次数
我相信我的注释已经非常详细了,不需要再解释什么了吧?
下面给出我自己写的暂停计数器和重置计数器计数次数函数
/*---------------------------------------------------------------------------------------------------------*/
/* Function: DrvTIMER_Stop */
/* */
/* Parameters: */
/* ch - [in] */
/* E_TIMER_CHANNEL, it could be E_TMR0/E_TMR1/E_TMR2/E_TMR3 */
/* Returns: */
/* E_SUCCESS Operation successful */
/* E_DRVTIMER_CHANNEL Invalid Timer channel */
/* Description: */
/* 暂停对应的计时器通道,并重置当前计数次数Tick */
/*---------------------------------------------------------------------------------------------------------*/
int32_t DrvTIMER_Stop (E_TIMER_CHANNEL ch)
{
TIMER_T * tTMR;
switch (ch)
{
case E_TMR0:
case E_TMR1:
case E_TMR2:
case E_TMR3:
{
tTMR = (TIMER_T *)((uint32_t)TIMER0 + CH_OFFSET[ch]);
tTMR->TCSR.CEN = 0; //暂停计时器
tTime0Event[ch].curTick = tTime0Event[ch].initTick; //重新装载用户设置的多少次Tick进入用户中断
return E_SUCCESS ;
}
default:
{
return E_DRVTIMER_CHANNEL ;
}
}
}
其实我只是修改了DrvTIMER_Start函数红字的两行,这样,由于基本上还是用的是库函数,BUG少,用着也放心。
到此就完成了这次库函数TIMER篇的一个小问题的讲解。
- NUC140之定时器
- NUC140之定时器2
- NUC140之设置主频
- NUC140之看门狗
- NUC140之ADC
- 网页之定时器详解
- javascript 之 定时器setInterval
- 线程池之定时器
- MFC 定时器之妙用
- android之定时器AlarmManager
- android之定时器AlarmManager
- android之定时器AlarmManager .
- linux编程之定时器
- LPC1768之定时器TIMER0
- stm32之滴答定时器
- stm32f407之通用定时器
- TMS320F28027之CPU定时器
- android之定时器AlarmManager
- 我来设计(二):测试数据生成工具
- oralce存储过程的基本语法
- 杭电OJ——1198 Farm Irrigation (并查集)
- python计算组合数和阶乘
- 在tq2440上搭建 韦东山 视频的环境
- NUC140之定时器
- 说说大型高并发高负载网站的系统架构
- cxDBPivotGrid 动态增加field
- Query接口
- const 和static 变量
- AppBar与Panel
- 设计模式之观察者模式
- 关于inline函数
- 数组与Object的关系及其反射类型