该讲介绍51822的Timer/Counter模块工作在timer模式下(定时器模式,还可以工作为计数器模式) 如何操作
51822的Timer/Counter结构如下图所示
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
新建一个工程选择自己的芯片型号
下面的代码我们不使用nrf提供的库函数,而按照上面说明的顺序直接设置寄存器来使用timer0,因为没用别的功能。所以下面勾选一个core和startup。因为用到了点灯所以勾选一下nrf_drivers下的Nrf_gpio
下面是源代码,为了更好理解模块,都是直接操作寄存器
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);
}
}