16位计数器的使用
PMS132B内置一个16位硬件计数器,我一般都是用来做定时器中断
下面这个是数据手册上面介绍的
只能向上计数,计数初始值可以通过stt16指令来设置,待会看代码里面,用来做中断就是当计数溢出是触发中断,具体的看例程吧。
/************************************************************************************/ /* 16位计数定时器T16 */ /************************************************************************************/ #define USER_TIMER_CONFIG()$ T16M IHRC, /16, BIT10 //对t16寄存器进行配置 时钟:IHRC 分频:/16 中断源:BIT10 #define ENABLE_TIMER()SET1 INTEN.2 //$ INTEN T16 //开定时器中断 #define DISENABLE_TIMER()SET0 INTEN.2 //关定时器中断 #define INIT_TIMER_VALUE(n)STT16 n//装载定时器计数值 #define EA_INT()ENGINT//开总中断 #define DIS_INT()DISGINT//关总中断 #define CONST_TIME_VALUE 0//0x10E//224// (BIT - CONST_TIME_VALUE) / (IHRC / 分频) = us word T16val; /************************************************************************************/ //一次中断的时间计算公式 (BIT - CONST_TIME_VALUE) / (IHRC / 分频) = us // (2^10 - 0) / (16 / 4) = 1024us void Set_User_T16(void) { USER_TIMER_CONFIG(); T16val = CONST_TIME_VALUE; INIT_TIMER_VALUE(T16val); INTRQ = 0;//清除INTRQ EA_INT(); ENABLE_TIMER(); } voidFPPA0 (void) { .ADJUST_ICSYSCLK=IHRC/4, IHRC=16MHz//SYSCLK=IHRC/2 // SetPortA(); // SetPortB(); Set_User_T16(); while (1) { //... //wdreset; //主函数功能实现区 } } voidInterrupt (void) { pushaf; if (Intrq.T16) {//T16 Trig //User can add code Intrq.T16=0; T16val = CONST_TIME_VALUE; INIT_TIMER_VALUE(T16val); //中断执行区 } popaf; }
有关其他的外部IO引脚中断目前正在研究中,没弄明白怎么回事,这个计数器中断时可以直接使用的,数据手册里面的计算公式跟我们实际测量出来的不一样,上面那个公式是我自己测量总结的,可行,大家也可以这样配置然后在中断里面通过IO口翻转看一下进一次中断的时间,下面那个是编译器里面使用手册对T16的计算方法。
有关16位计数器的就分享到这里,下次更新8位计数器和11位计数器的使用。
今天看到了官方的DEMO,16位定时器的中断时间还是跟我们实际测量到的不一样,先看一下官方给的DEMO 吧,`BIT p_Key_In : PA.0; // At ICE, has the circuit, you can try it.
BIT p_LED_Out : PA.1; //
void FPPA0 (void)
{
.ADJUST_OTP_IHRCR 4MIPS // IHRC/4 = 4MIPS, WatchDog Disable, RAM 0,1 temporary be used
$p_LED_OutOut, Low; $p_Key_InIn;//, Pull; PAPH=_FIELD(p_Key_In); $ T16MIHRC, /1, BIT11;//16MHz / 1 = 16MHz : the time base of T16. //这里是官方的对16位计数器寄存器的配置 BYTEKey_Flag; BITf_Key_In:Key_Flag._BIT(p_Key_In); Key_Flag=_FIELD(p_Key_In); BYTESys_Flag=0; BITf_Key_Trig:Sys_Flag.0; BITf_LED_On:Sys_Flag.1; BITt16_10ms:Sys_Flag.2; BITt16_1s:Sys_Flag.3; BITt16_over2:Sys_Flag.4; BITt16_over3:Sys_Flag.5;
// pmode Program_Mode;
// fppen = 0xFF;
BYTEt16_flag; BYTEcount1, count2, count3; BYTEcnt_Key_10ms=4;//Key debounce time = 40 mS while (1) { if (INTRQ.T16) { INTRQ.T16=0; If (--count1 == 0)//DZSN count { count1=39;//256uS * 39 = 9,984 uS ≤ 10 mS t16_10ms=1; } }
//
WORD T16_Cnt, T_100mS;
ldt16 T16_Cnt;
if (T16_Cnt.15)
{
t16_over3 = 1;
}
else if (t16_over3)
{
t16_over3 = 0;
if (–count3 == 0)
{
count3 = 244; // 4,096uS * 244 = 999,424 uS ≤ 1S
t16_1s = 1;
}
}
//*
if (T16_Cnt.12)
{
t16_over2 = 1;
}
else if (t16_over2)
{
t16_over2 = 0;
if (–count2 == 0)
{
count2 = 195; // 512uS * 195 = 99,840 uS ≤ 100 mS
T_100mS++;
}
}
//
A = (t16_flag ^ T16_Cnt$1) & 0x20; // Another way for calucate 100 mS
if (! ZF) // it use t16_flag.5 ^ T15_Cnt.13
{ // the code is more little,
t16_flag ^= A; // but, not everyone like it.
if (–count2 == 0)
{
count2 = 195;
T_100mS++;
}
}
///
while (t16_10ms) { t16_10ms=0; A=(PA ^ Key_Flag) & _FIELD(p_Key_In);//only check the bit of p_Key_In. if (! ZF) {//if is not same, if (--cnt_Key_10ms == 0) {//and over debounce time. Key_flag^=_FIELD(p_Key_In); f_Key_Trig=1;//so Trigger, when stable at 30 mS. } elsebreak; } cnt_Key_10ms=4;break; } if (f_Key_Trig) { f_Key_Trig=0; if (! f_Key_In) { if (p_LED_Out) {//3 )if next key in, then LED off f_LED_On=0; p_LED_Out=0; } else {//1 )when first key press, then LED on p_LED_Out=1; } } else { if (p_LED_Out) {//2 )when first key release, then start to count f_LED_On=1; T_100mS=0; } else {//4 )nothing to do } } } if (f_LED_On) { if (T_100mS >= 50) {//Over 5 S, then close f_LED_On=0; p_LED_Out=0; } } }
}
`
官方这个DEMO直接在大循环里面判断的中断,实际建项目的时候,会出来一个单独的中断函数,下面看一下实际建项目的源代码,是我自己写的,然后用逻辑分析仪抓取出了波形。
#include"extern.h" //.outfile %S_%T_%x.PDK typedef byte u8; typedef word u16; typedef EWORD u24; typedef DWORD u32; #define SetPortA() paph=0x18;pac=0x10;pa=0x00 #define SetPortB() pbph=0x00;pbc=0x00 #define LED_ON() PA.4 = 1 #define LED_OFF() PA.4 = 0 u16 T16val; u8 gCnt; void sys_init(void) { T16val = 0; gCnt = 0; } /************************************************************************************/ /* 16位计数定时器T16 */ /************************************************************************************/ #define USER_TIMER_CONFIG()$ T16M IHRC, /1, BIT11 //对t16寄存器进行配置 时钟:IHRC 分频:/16 中断源:BIT10 #define ENABLE_TIMER()SET1 INTEN.2 //$ INTEN T16 //开定时器中断 #define DISENABLE_TIMER()SET0 INTEN.2 //关定时器中断 #define INIT_TIMER_VALUE(n)STT16 n//装载定时器计数值 #define EA_INT()ENGINT//开总中断 #define DIS_INT()DISGINT//关总中断 #define CONST_TIME_VALUE 0//0x10E//224// (BIT - CONST_TIME_VALUE) / (IHRC / 分频) = us /************************************************************************************/ //一次中断的时间计算公式 (BIT - CONST_TIME_VALUE) / (IHRC / 分频) = us // (2^10 - 0) / (16 / 4) = 1024us void Set_User_T16(void) { USER_TIMER_CONFIG(); T16val = CONST_TIME_VALUE; INIT_TIMER_VALUE(T16val); INTRQ = 0;//清除INTRQ EA_INT(); ENABLE_TIMER(); } voidFPPA0 (void) //主函数 { .ADJUST_ICSYSCLK=IHRC/4, IHRC=16MHz//SYSCLK=IHRC/2 SetPortA(); SetPortB(); sys_init(); Set_User_T16(); while (1) { //... //wdreset; //主函数功能实现区 } } voidInterrupt (void) //中断函数 { pushaf; if (Intrq.T16) {//T16 Trig //User can add code Intrq.T16=0; T16val = CONST_TIME_VALUE; INIT_TIMER_VALUE(T16val); //中断执行区 if (gCnt) { // 利用IO口翻转看中断时间 LED_OFF(); gCnt = 0; } else { LED_ON(); gCnt = 1; } } popaf; }
16位计数器的中断源是BIT11,/1分频,时钟IHRC16M,跟官方例程里面的一样,下面是我测量出来的波形,翻转一次的时间是130us,根据我上面程序里面的公式(2^11-0)/(16/1) = 128us,测量出来多了一点点,是运行程序锁消耗的时间,为什么官方给出的数据跟实际测量的不一样我就不得而知了。