2014年3月22日

CC2530基础实验(3)—— 定时器

CC2530上共有T1~T4,4个定时器,另外还有诸如系统定时器、睡眠定时器、看门狗定时器等等。在不同的电源模式下可以使用不同速率的振荡器。晶振的精度高、能耗高、启动慢;RC振荡器的精度更低一些,能耗低,启动快。需要注意的是在射频传输时必须使用32M的晶振,而在要求低功耗又对精度要求不高的场景下可以使用RC振荡器。默认情况下,使用Active / Idel模式,32M晶振,振荡器速率为16M。

功耗模式高速振荡器低速振荡器稳压器
配置A. None
B. 32MHz 晶振
C. 16MHz RC
A. None
B. 32k 晶振
C. 16kHz RC
Active / Idle ModeB和C可同时使用B或C任选其一ON
PM1AB或C任选其一ON
PM2AB或C任选其一OFF
PM3AAOFF
定时器在不同电源模式下的可用情况

对于定时器,依然有轮询和中断两种方式,由于中断方式的优点,这里只讲中断方式。下面以T1计时器为例,配置并启动定时器,再用中断来处理定时器的中断请求有如下步骤:

  1. 选择晶振,待晶振稳定后设定速率。

    CC2430与CC2530的数据手册在时钟、睡眠相关的寄存器上是有差别的,有的只是换了个名字,有的被挪到别的寄存器了。根据[1]的博文,CC2530的数据手册在这里是错误的。

    cc2530数据手册上SLEEPCMD这个寄存器的第2位(从第0位开始计)说明是错误,该位的正确说明,应当看cc2430的数据手册SLEEP寄存器的第2位。当该位为0时,将两个高频晶振同时上电;该位为1时,将CLKCONCMD.OSC位没有选择的高频晶振关闭。同样的,cc2530数据手册SLEEPSTA这个寄存器的第6位和第5位说明是错误,这两位的正确说明,应当看cc2430的数据手册SLEEP寄存器的第6位和5位[1]

    根据数据手册,选择晶振或是RC,需要设置CLKCONCMD的第6位,1为使用RC,0为使用晶振。然后是等待振荡器报告其稳定,晶振和RC所检查的位各不同,当然按[1]的方式去确认稳定似乎也是可行的,但我发现下面这种方法也可以监测振荡器的稳定。

    while(!(SLEEPSTA & 0x40)); // 对应使用晶振
    while(!(SLEEPSTA & 0x20)); // 对应使用RC
    asm("NOP"); // 额外多等待一个安全时间,以确保其真的稳定了
    

    当振荡器稳定后可以设定其速率,CC2530可以选择8档不同的速率,比CC2430多出好几种选择。数据手册也提到,选择的速率不能超过振荡器的最大振荡频率。在CC2530中,设置振荡器速率需要操作CLKCONCMD的低3位,从000~111,对应32M~250k的8档不同速率。对连续多位的写操作,移位操作是最方便的。预先定义好这些速率常量,设置时将CLKCONCMD先右移3位再左移3位,再把这个速率加上(3位二进制数)就完成了。

    CLKCONCMD = (CLKCONCMD >> 3 << 3) + speed;
    
  2. 设置定时器的分频和工作模式

    对于T1来说,分频和工作模式使用的是T1CTL的低4位,同样使用移位操作,这里不再赘述,表示不同分频和工作模式的二进制数可以参阅数据手册。

  3. 开启必要的中断

    不使用T1提供的几个通道的情况下,一共需要开3个中断,分别是T1的中断、总中断、TIMIF.OVFIM(默认开启),所以实际上,只需要开2个即可。

    EA  = 1;  //开启中断
    TIMIF |= 0x40;
    IEN1  |= 0x02;
    
  4. 编写T1的中断请求代码

    中断请求都有固定的格式,copy、改,就行了。

    //定时器T1中断处理函数
    #pragma vector = T1_VECTOR 
    __interrupt void T1_Tick_IRQ(void){ 
     flashAllLeds(speed);  // 根据振荡器的速度来闪烁LED,
                           // 当前速度可从CLKCONSTA的低三位读出,
                           // 即speed = CLKCONSTA & 0x07
     
     // 清除中断标志
     T1STAT &= 0x20; //T1STAT.OVFIF=0
     T1IF =0; 
    }
    

我在实验中发现了一些令人沮丧的现象,比如,当断点加到那些移位操作的代码上,调试时会报IDataStack空间被占满,随后一定几率IDE崩溃;又比如用宏定义设定的一些常量在调试时发现它们居然还会变动!再比如,如果我声明一个变量并赋值为一个已经宏定义的常量,例如:int speed = OSC_SPEED_4M;,然后把这个speed作为入参,传进函数里,程序会罢工,断点一上,又是IDataStack耗尽,根本查不出原因。IAR的编辑和调试功能实在做得太糟糕了,一个2011年出的IDE,界面跟98年出的VC6相近,功能上还远不如VC6。但编译器和连接器至此一家,你也只能边骂边用了。

没有评论:

发表评论