物联网BLE裸机程序开发 -- (6)nRF52840配置定时器和计数器

nRF52840配置定时器和计数器

几乎所有的单片机都有定时器或计数器这种外设,可以用来进行定时,高精度延时,计数等等,在大多数领域都能用到定时器功能。

定时器在处理器里面,是单独于CPU运行的一个硬件外围设备,通俗来说,就是把定时器配置好之后,定时器的加计数或减计数不需要CPU进行参与,并且可以通过软件控制,让定时器在合适的条件下产生中断,使用起来很灵活方便。

Nordic nRF52840芯片有5个32位的Timer外设,这些Timer外设可以工作在定时器模式或计数器模式,并且可以设置Timer的时钟频率和位宽。

先来了解一下定时器和计数器的区别:

定时器主要是通过对一个周期不变的系统时钟进行脉冲计数,每来一个时钟脉冲,定时器就进行计数一次,同时在定时器启动前,会预先设置一个比较值,当计数脉冲等于这个比较值的时候,说明定时的时间到了,如果设置了定时器中断,就会触发相应的中断事件。

计数器主要是对某一事件进行计数的,而不是对系统时钟脉冲进行计数。系统时钟脉冲计数是有规律的,但事件的发生没有规律,事件发生一次,计数器就计数一次,通过具体的计数值来反映事件的发生次数。

Timer外设的原理框图如下图所示:

物联网BLE裸机程序开发 -- (6)nRF52840配置定时器和计数器_第1张图片

从图中可以看出,Timer外设的时钟源主要来自PCLK1M或者PCLK16M,这两种时钟源是系统自动分配的,不需要用户编写程序去干预。主要是根据Timer需要的运行频率来配置,例如,当Timer需要的频率大于1MHz的时候,Timer模块就会为其分配PCLK16M作为时钟源,反之则使用PCLK1M作为时钟源。

Timer的时钟频率可以通过分频系数来进行设置,无论使用PCLK1M还是使用PCLK16M,Timer的时钟频率都是以16MHz进行计算。计算公式是:Ftimer = 16MHz / (2Pres)。其中,Pres是分频系数,取值范围是 0 – 9 。需要注意的是,在设置分频系数的时候,定时器需要处于停止运行的状态,否则可能会出现不可预知的后果。

Nordic的SDK提供了Timer外设的操作函数库,主要位于nrfx_timer.c这个文件里面,使用操作函数库,可以把Timer外设可以配置在定时器模式或者是计数器模式,以下分别用实验来验证这两种模式。

 

软件开发前准备:

开发板:Nordic官方开发板nRF52840-DK(PCA10056)

编译器:SEGGER Embedded Studio v4.22

SDK版本:nRF5_SDK_15.2.0_9412b96

 

定时器模式实验

1、在定时器模式下,应用程序需要设置定时器的捕获/比较寄存器的值,这个值就是需要定时的时间。定时器启动后,计数值会在每个时钟脉冲下进行累加计数,当计数值等于比较值的时候,定时时间到达,产生比较匹配事件,用户可以在比较事件的回调函数中进行任务处理。

 

2、我们基于前面构建的“005_log_debug”工程来进行软件开发。先复制一份工程,并重命名为“006_timer_test”,并且把Timer外设模块相关的源文件添加到工程,完成后打开工程,如下图所示。

物联网BLE裸机程序开发 -- (6)nRF52840配置定时器和计数器_第2张图片

物联网BLE裸机程序开发 -- (6)nRF52840配置定时器和计数器_第3张图片

 

3、新建一个timer.c和timer.h的文件,用来编写Timer外设的操作函数。头文件timer.h的内容如下图所示。

物联网BLE裸机程序开发 -- (6)nRF52840配置定时器和计数器_第4张图片

头文件的内容比较简单,主要是通过CURRENT_TEST_FUNC宏定义来进行两个实验的条件编译,分别是定时器实验和计数器实验。以及对一些可以被调用的外部函数进行声明。

 

4、在使用定时器库函数的时候,需要先定义一个库函数的实例,这个实例用来贯穿定时器的所有操作。定时器实例用nrfx_timer_t这个结构体来表示,这个结构体的内容,如下图所示。

物联网BLE裸机程序开发 -- (6)nRF52840配置定时器和计数器_第5张图片

结构体主要有3个成员变量,p_reg主要是指具体的Timer外设,范围是Timer0 – Timer4。instance_id主要是定时器对象的索引,这个索引是供SDK里面定时器驱动模块使用的。cc_channel_count主要用来表示该定时器的捕获/比较通道的数量。定时器的库函数里面,提供了NRF_DRV_TIMER_INSTANCE宏来进行初始化定时器实例,这个宏定义的输入参数id,表示定时器外设的编号,如id的值为0,则表示使用定时器0。

 

5、主要有三个可供外界调用的函数:定时器的初始化函数my_timer_init(),定时器的启动函数my_timer_start(),定时器的停止运行函数my_timer_stop()。这三个函数的实现,如下图所示。

物联网BLE裸机程序开发 -- (6)nRF52840配置定时器和计数器_第6张图片

 

6、初始化函数主要调用了nrf_drv_timer_init()函数来进行定时器初始化,这个函数实际上是调用了nrfx_timer_init()函数,nrfx_timer_init()函数的声明如下图所示。

物联网BLE裸机程序开发 -- (6)nRF52840配置定时器和计数器_第7张图片

这个函数主要有三个输入参数,p_instance主要是指向定时器实例,p_config主要是定时器的配置结构体,timer_event_handler主要是指定时器的事件回调函数。

 

7、定时器的配置结构体nrfx_timer_config_t,包含了定时器的所有配置参数,用户只需要设置定时器的时钟频率frequency,驱动程序会自动计算对应的分频系数,结构体的内容如下图所示。

物联网BLE裸机程序开发 -- (6)nRF52840配置定时器和计数器_第8张图片

对于这个结构体的默认参数配置,一般使用NRF_DRV_TIMER_DEFAULT_CONFIG宏进行,这个宏定义的一系列默认参数,可以在sdk_config.h配置文件中修改。对于一些用户想自定义的配置参数,可以先用该宏默认初始化一次,然后再根据某些参数进行细节修改,如初始化程序里面的timer_cfg.frequency = NRF_TIMER_FREQ_16MHz;

 

8、定时器的事件回调函数是在定时器初始化的时候,注册给驱动程序的,当某个定时器通道的计数值等于比较值的时候,就会触发定时事件,回调函数就会被调用,回调函数的实现,如下图所示。

物联网BLE裸机程序开发 -- (6)nRF52840配置定时器和计数器_第9张图片

定时器可以产生4个比较事件,在回调函数里面,对这些比较事件进行逐一处理。比较事件主要在nrf_timer_event_t中进行定义。

 

9、在完成一个定时器实例的初始化之后,这个定时器实例还不能运行,因为还没有进行定时时间的设置以及还没有进行比较通道的绑定。定时时间的设置主要对应定时器的滴答时钟数(ticks),定时器的ticks数量不需要我们进行手动计算,驱动程序已经提供了计算ticks的函数nrf_drv_timer_ms_to_ticks,这个函数的声明,如下图所示。

物联网BLE裸机程序开发 -- (6)nRF52840配置定时器和计数器_第10张图片

p_instance主要是指定时器的具体实例。time_ms主要是指需要定时器的时间,如需要定时器定时1000ms,则可以写成以下形式:nrf_drv_timer_ms_to_ticks(&my_timer,1000)。

 

10、完成定时器的定时值配置后,最后还需要进行通道绑定,指定使用哪个比较/捕获通道来进行定时计数,通道绑定可以使用nrf_drv_timer_extended_compare()进行,这个函数的声明如下图所示。

物联网BLE裸机程序开发 -- (6)nRF52840配置定时器和计数器_第11张图片

这个函数主要有5个输入参数,p_instance主要是指具体的定时器实例。cc_channel是指比较/捕获的通道号。cc_value是比较值。timer_short_mask是指快捷方式。enable_int是指开启或关闭比较中断。

快捷方式timer_short_mask主要有以下两个选项:

比较事件产生时停止定时器:这个主要用在定时器单次触发模式,定时器的定时时间到达后,就停止定时器运行。

比较事件产生时清零定时器:这个主要用在定时器的重复运行状态下,定时器的定时时间到达后,程序自动清零计数器,定时器重新反复运行。

 

11、完成以上定时器的配置操作后,定时器还没有开始运行,可以调用定时器的启动运行函数nrf_drv_timer_enable(),让定时器启动。如需关闭定时器的运行,可以调用定时器的关闭运行函数nrf_drv_timer_disable()。需要注意的是,定时器的关闭运行函数,是彻底关闭定时器的,而不是停止定时器,关闭定时器可以降低芯片的功耗。关闭定时器是指,下次启动的时候从零开始计数。停止定时器是指,下次启动的时候,从停止前的计数值开始计数。

 

12、以上代码就是Timer外设的定时器工作模式,代码编写完成后,还需要配置sdk_config.h文件,打开TIMER_ENABLED宏,如下图所示。

物联网BLE裸机程序开发 -- (6)nRF52840配置定时器和计数器_第12张图片

 

13、连接好开发板,点击“Debug->Go”或按F5,即可编译程序并且把程序下载到开发板运行。程序每隔一秒在终端打印出相应的信息,如下图所示。

物联网BLE裸机程序开发 -- (6)nRF52840配置定时器和计数器_第13张图片

 

计数器模式实验

14、在计数器模式下,同样需要定义一个Timer外设的实例对象,也需要一个计数器的初始化函数,初始化函数的实现,如下图所示。

物联网BLE裸机程序开发 -- (6)nRF52840配置定时器和计数器_第14张图片

在计数器的初始化函数里面,同样是使用了NRF_DRV_TIMER_DEFAULT_CONFIG宏进行参数初始化,然后把timer_cfg.mode修改为计数器模式。在计数器模式下,不需要绑定具体的比较/捕获通道。

 

15、由于在计数器模式下,不会产生比较事件,因此并不需要回调函数处理任务。但初始化函数的事件参数,不能设置为NULL,因此,我们可以提供一个空的回调函数,这个函数并不处理任何事情。如下图所示。

 

16、在计数器模式下,主要是触发计数任务来增加计数值的。触发计数任务,主要有以下两种方式:程序主动调用nrfx_timer_increment()函数,或者使用PPI(可编程外设互联)进行触发。在本实验中,主要是通过调用nrfx_timer_increment()函数进行触发计数。

 

17、nRF52840计数器的计数值,是保存在芯片内部的计数器中的,但并没有对外提供相应的寄存器供程序员读取。所以,要想读取计数器的计数值,只能通过芯片内部的比较/捕获寄存器进行操作。程序员可以通过nrfx_timer_capture()函数进行计数读取。

 

18、我们修改一下gpiote.c里面的按键输入事件处理函数,当用户每按下一次按键,就触发一次nrfx_timer_increment()函数,进行计数值加1,程序如下图所示。

物联网BLE裸机程序开发 -- (6)nRF52840配置定时器和计数器_第15张图片

 

19、在主函数里面,每隔两秒读取一次计数器的值,并打印出来。主函数的实现代码如下图所示。

物联网BLE裸机程序开发 -- (6)nRF52840配置定时器和计数器_第16张图片

 

20、连接好开发板,点击“Debug->Go”或按F5,即可编译程序并且把程序下载到开发板运行。用户每按一次按键,程序每隔两秒在终端打印出相应的信息,如下图所示。

物联网BLE裸机程序开发 -- (6)nRF52840配置定时器和计数器_第17张图片

 

21、源码下载链接:https://github.com/embediot/bluetooth_low_energy

 

物联网BLE裸机程序开发 -- (6)nRF52840配置定时器和计数器_第18张图片

 

 

 

你可能感兴趣的:(物联网,BLE,应用开发)