nrf51822裸机教程-硬件timer

时间:2021-01-20 20:06:28

该讲介绍51822的Timer/Counter模块工作在timer模式下(定时器模式,还可以工作为计数器模式) 如何操作


51822的Timer/Counter结构如下图所示
nrf51822裸机教程-硬件timer

Timer模块从PCLK16M/PCLK1M 处获得时钟源,然后经分频后得到的时钟作为timer模块的时钟 ( 上图Ftimer)。当timer模块选择为timer模式时,Counter会在Ftimer的每个tick 计数一次当计数值与cc[n](n为0,1,2,3)寄存器中的值相等时就会触发对应的Compare[n]事件,如果我们设置了compare[n]事件产生时触发中断(关于事件与中断看前一篇GPIOTE),那么就可以在counter计数到与 cc[n]寄存器中的值相等时触发中断,也就能实现我们需要的定时器功能了

所以根据上面的模块结构图和说明想要实现定时器,我们需要做如下几个步骤:

1    选择Timer/Countermo模块为timer模式,并设置bitmode(8,16,,2,32位)

2    通过设置分屏来设置timer的时钟

3    设置cc[n](后面我们的例子选择使用cc0),来设置计数到多少产生compares[n]事件(当计数值技术到cc[n]的值时对应产生compare[n]事件)

4    设置compare事件产生时触发中断。

5     通过NVIC函数启动MCU 的timer中断

6    最后启动timer就可以了。



通过查看 数据手册看以看到

1

设置模式 通过 寄存器MODE 设置 0为timer模式 1为counter模式。


2

设置timer的时钟  通过以下公式设置

fTIMER = 16 MHz / (2^PRESCALER)

这里可能有个疑问,上面的图解中不是有两个时钟源 16M和1M吗,怎么这个公式只能通过16M来分频获得timer时钟。 这是因为51822为了降低功耗内部自动 做了时钟源切换,当 Ftimer <= 1M时会自动切换成1M时钟源


举两个例子解释下

如果需要timer的时钟为 4M,那么4 = Ftimer = 16M/2^2

即我们只需设置分频寄存器 PRESCALER为2,就能或得4M的时钟给timer了


当需要timer的时钟为500Khz时,根据公式 我们设置PRESCALER寄存器的值为5, 500kHZ = Ftimer = 16M/2^5。 这个时候Ftimer<=1M,所以51882内部会自动切换成1M的时钟源然后分频后获得500K的timer时钟。 不过这些都是51822自动切换的了


也就是说设置timer时钟只要根据上面的公式设置就可以了,时钟源的切换是51822自动完成的


3

设置cc[n]寄存器的值,定时就是通过这个值来设置的。 下面的例子会做一个一秒定时亮灯/灭灯的程序, 我们设置timer时钟为1M,即分频寄存器PRESCALER写值为4。 1M的时钟源则一个tick为1us,所以要定时1s,则cc[0]的值我们填入 1000000就行了。(这里也可以选择cc[1],cc[2],或cc[3],只要下面对应的compare事件产生中断设置成对应的就可以了)

4

通过寄存器INTENSET 第16bit位设置compare[0]事件产生时触发中断。

5

通过NVIC的功能函数NVIC_EnableIRQ 来使能MCU的Timer0中断

6

最后通过Timer/Counter模块的 START 启动timer。

 

 

 

注意:这里裸板例子代码我们用的是timer0,如果跑了协议栈就不能用timer0,可以使用timer1和timer2


新建一个工程选择自己的芯片型号

 nrf51822裸机教程-硬件timer

下面的代码我们不使用nrf提供的库函数,而按照上面说明的顺序直接设置寄存器来使用timer0,因为没用别的功能。所以下面勾选一个core和startup。因为用到了点灯所以勾选一下nrf_drivers下的Nrf_gpio


nrf51822裸机教程-硬件timer

下面是源代码,为了更好理解模块,都是直接操作寄存器


Main.c


#include "nrf51.h"

#include "nrf_gpio.h"


//定义自己板子上的LED灯

#define LED  22


int main(){


    nrf_gpio_cfg_output(LED);

    //NRF_TIMER0定义在nrf51.h中,该指针指向timer0中的寄存器组


    NRF_TIMER0->PRESCALER  = 4;     //2^4   16分频得到1M timer时钟

    NRF_TIMER0->MODE = 0;           //timer模式

    NRF_TIMER0->BITMODE = 3;        // 设置32bit

    NRF_TIMER0->CC[0] = 1000000;    //一个tick是1us,1000000代表1s

    NRF_TIMER0->INTENSET = 1<<16;//设置compare[0]事件产生时触发中断


  //该设置使timer模块中的conter计数到cc[0]值时会自动清零,以带到重//新计数的目的

NRF_TIMER0->SHORTS = 1;


//启动timer模块

    NRF_TIMER0->TASKS_START = 1;


//开启MCU的timer0中断 

    NVIC_SetPriority(TIMER0_IRQn, 3);

    NVIC_ClearPendingIRQ(TIMER0_IRQn);

    NVIC_EnableIRQ(TIMER0_IRQn);


    while(1);


    return 0;

}



//中断函数中翻转灯状态

void TIMER0_IRQHandler(){

    if(NRF_TIMER0->EVENTS_COMPARE[0] == 1){

       NRF_TIMER0->EVENTS_COMPARE[0] = 0;     //清除事件,不然会导致一

                                              //直产生中断

       nrf_gpio_pin_toggle(LED);                

    }

}