nordic—RTC+PPI定时驱动某外设做非单次触发(本次测试为驱动GPIO口做电平翻转)

发布时间 2023-12-25 17:51:08作者: 星辰_stars

简介:在nordic的开发中使用到RTC时,对于比较通道0/1/2/3的中断来说如果不进行相关配置,会导致比较中断只进入一次,如果说是使用RTC+PPI+ADC进行采样或者RTC+PPI+GPIOTE做IO口翻转等,都会只翻转一次就停止了,不能做的无限触发,接下来我就进行一下相关配置,让其可以无限循环起来。

测试环境:

硬件:nrf52840DK

软件路径:nordic的SDK\examples\ble_peripheral\ble_app_uart\pca10056 (在官方的SDK中的ble_app_uart例子中加入相关测试代码)

环境:keil5

一、RTC和PPI加入

1、源文件加入

加入三个文件分别是RTC和PPI的驱动文件,如果你选择ADC,作为RTC触发后要执行的任务的外设,在选择的工程没有ADC的源文件时,需要你自己加入,因为我本次测试使用的是RTC+PPI+GPIOTE去做IO口输出控制,ble_app_uart中已经有GPIOTE相关的代码了,我就不在加入了。

 

sdk_config.h修改,由于RTC需要低速时钟,默认是外部低速晶振,且协议栈使用了RTC0,然后app timer使用了RTC1,所以我就只能使用RTC2来进行配置,实际中你也可以使用RTC1的其余通道(app timer没有用到的通道进行,这就不改了,有需要自己看源码进行配置就行),因此有一些截图的配置

 以上宏定义启动完成对于RTC和PPI来说就够了GPIOTE的我使用的例子中已经启用好了,如果你驱动的是其他外设,参考SDK中和外设相关的例子进行宏定义启动就可以了,所有外设的基础例子都在如下截图的SDK路径下:

头文件加入:

#include "nrf_drv_rtc.h"
#include "nrf_drv_clock.h"
#include "nrf_drv_ppi.h"
#include "nrf_drv_gpiote.h"

二、RTC带中断的无线循环模式

初始化RTC:

const nrf_drv_rtc_t rtc = NRF_DRV_RTC_INSTANCE(2);  //2就是初始化RTC2的意思
#define handle_tirgg_timer 1                 //RTC通道0的中断时间
#define GPIO_pin 16                             //需要翻转的GPIO口

在main中调用的代码

        uint32_t err_code;
        /*###############  时钟使能  ###############*/
    //ble中时钟已经初始化,不用再次初始化,否则会报错 ERROR 133 [NRF_ERROR_MODULE_ALREADY_INITIALIZED]
//    err_code = nrf_drv_clock_init();
//    APP_ERROR_CHECK(err_code);

    nrf_drv_clock_lfclk_request(NULL);

       /*################ RTC 初始化 #######################################*/ 
        
    //Initialize RTC instance
    nrf_drv_rtc_config_t config = NRF_DRV_RTC_DEFAULT_CONFIG;
    config.prescaler = 4095;
    err_code = nrf_drv_rtc_init(&rtc, &config, rtc_handler);
    APP_ERROR_CHECK(err_code);
    /*禁用tick和溢出,你也可以根据需要启用*/   
    nrfx_rtc_tick_disable(&rtc);
    nrfx_rtc_overflow_disable(&rtc);
        /* 启用CC通道使能 */
    err_code = nrf_drv_rtc_cc_set(&rtc,0,handle_tirgg_timer * 8,true);
    APP_ERROR_CHECK(err_code);

  nrf_drv_rtc_enable(&rtc);

回调:

static void rtc_handler(nrf_drv_rtc_int_type_t int_type)
{
    if (int_type == NRF_DRV_RTC_INT_COMPARE0)
    {
            uint32_t err_code;
       printf("NRF_DRV_RTC_INT_COMPARE0 \n");
            /* 再次设置溢出值 */
             err_code = nrf_drv_rtc_cc_set(&rtc,0,handle_tirgg_timer * 8,true);
            APP_ERROR_CHECK(err_code);
            /* 清除 */
            nrf_drv_rtc_counter_clear(&rtc);
    }
}

测试结果如下:

 注意如何你需要的是tick就不需要清除,tick配置如下:

nrfx_rtc_tick_enable(&rtc);

 

三、RTC+PPI+GPIOTE

注意:

在这里配置的时候需要注意几个点:

  • GPIO要配置为GPIOTE的out功能,否则无法用PPI去触发输出
  • RTC初始化时,需要禁用回调功能,否则也只能执行一次
  • 需要启用PPI的fork机制,用于RTC的清除,否则无法触发循环,只能执行一次。

头文件和相关变量定义:

#include "nrf_drv_rtc.h"
#include "nrf_drv_clock.h"
#include "nrf_drv_ppi.h"
#include "nrf_drv_gpiote.h"

const nrf_drv_rtc_t rtc = NRF_DRV_RTC_INSTANCE(2); //2就是初始化RTC2的意思
#define handle_tirgg_timer 1     //RTC通道0的中断时间
#define GPIO_pin 16                     //需要翻转的GPIO口

1、RTC配置

RTC要工作需要启动低频时钟,代码如下

uint32_t err_code;
/*###############  时钟使能  ###############*/
    //ble中时钟已经初始化,不用再次初始化,否则会报错 ERROR 133 [NRF_ERROR_MODULE_ALREADY_INITIALIZED]
//    err_code = nrf_drv_clock_init();
//    APP_ERROR_CHECK(err_code);

 nrf_drv_clock_lfclk_request(NULL);
/*################ RTC 初始化 #######################################*/ 
    //Initialize RTC instance
    nrf_drv_rtc_config_t config = NRF_DRV_RTC_DEFAULT_CONFIG;
    config.prescaler = 4095;
    err_code = nrf_drv_rtc_init(&rtc, &config, NULL);
    APP_ERROR_CHECK(err_code);
    /*禁用tick和overflow降低功耗 */    
        nrfx_rtc_tick_disable(&rtc);
        nrfx_rtc_overflow_disable(&rtc);
   /* 启用CC通道使能 32768/(4095+1)=0.125ms,那么1s定时为0.125*8,所以设置定时器值是乘以8了,可以自由设置 */
        err_code = nrf_drv_rtc_cc_set(&rtc,0,handle_tirgg_timer * 8,false);
    APP_ERROR_CHECK(err_code);

2、GPIO口配置为GPIOTE的OUT模式

代码如下:

uint32_t err_code;
/*
################ GPIO 口初始化 ############################*/ /* 前面已经有初始化了,不用再次初始化 */ // err_code = nrf_drv_gpiote_init(); // APP_ERROR_CHECK(err_code); nrf_drv_gpiote_out_config_t out_config = GPIOTE_CONFIG_OUT_TASK_TOGGLE(true); //绑定输出端口 err_code = nrf_drv_gpiote_out_init(GPIO_pin, &out_config); APP_ERROR_CHECK(err_code); nrf_drv_gpiote_out_task_enable(GPIO_pin);

3、PPI的配置(需要PPI的fork机制)

代码如下:

  uint32_t err_code;  
/* ############### PPI 初始化 ####################*/ err_code = nrf_drv_ppi_init(); APP_ERROR_CHECK(err_code); /* 触发的事件地址*/ uint32_t rtc_event_addr = nrf_drv_rtc_event_address_get(&rtc,NRF_RTC_EVENT_COMPARE_0); /* 执行的任务地址 */ uint32_t gpio_tick_addr = nrfx_gpiote_out_task_addr_get(GPIO_pin); /* 执行二次任务的地址 */ uint32_t rtc_clear_tick_addr = nrf_drv_rtc_task_address_get(&rtc,NRF_RTC_TASK_CLEAR); err_code = nrf_drv_ppi_channel_alloc(&m_ppi_channel); APP_ERROR_CHECK(err_code); /* 关联事件和任务*/ err_code = nrf_drv_ppi_channel_assign(m_ppi_channel, rtc_event_addr, gpio_tick_addr); APP_ERROR_CHECK(err_code); /* 关联二次任务 */ err_code = nrf_drv_ppi_channel_fork_assign( m_ppi_channel, rtc_clear_tick_addr); APP_ERROR_CHECK(err_code); /* 启动PPI*/ err_code = nrf_drv_ppi_channel_enable(m_ppi_channel); APP_ERROR_CHECK(err_code);

4、使能RTC

最后使能RTC:

//Power on RTC instance
    nrf_drv_rtc_enable(&rtc);

结果:你会看到GPIO口16以1s的频率进行翻转。