Chinaunix首页 | 论坛 | 博客
  • 博客访问: 401102
  • 博文数量: 165
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1301
  • 用 户 组: 普通用户
  • 注册时间: 2013-10-23 22:57
个人简介

我本仁慈,奈何苍天不许

文章分类

全部博文(165)

文章存档

2018年(1)

2016年(33)

2015年(5)

2014年(34)

2013年(92)

分类: 嵌入式

2016-07-21 14:13:35

原文地址:nrf51822裸机教程-PPI 作者:ifndef


nordic nrf51822 BLE 交流群498676838


Programmable Peripheral Interconnect即可编程外设互联 系统,该模块是51822 提供的一个特性。 目的是为了让51822 的外围模块可以不通过处理器而自动相互作用。 工作原理很简单。 可以将PPI看做是一通道。 该通道有两个端点,一个叫event end-point,另一个为task end-point. 通过将具体的 event寄存器和 task寄存器 分别赋值到 ppi通道的event end-pointtask end-point中。 那么当 event定义的事件发生时。 PPI另一端被赋值的对应的task就会自动被触发。

 

举个简单的例子。

按键翻转LED灯状态的实现。一般需要使用 I/O中断。在按键按下后cpu在中断处理函数中判断是否按键按下,然后再改变LED引脚状态。

这种实现就需要CPU参与其中才能工作。

而利用PPI, 则只需定义 按键按下后产生event,同时定义task为翻转引脚状态。然后分别将eventtask寄存器绑定到PPI通道两端的event end-pointtask end-point中。再使能PPI,之后按键按下和LED翻转状态就完全是自动进行。不需要CPU参与运算处理。 这样就在一定程度上释放了CPU,提高了工作效率。

 

综上,PPI的使用 就是配置下两端的event end-pointtask end-point。然后使能PPI就可以了。

那么什么值可以赋值给 PPIevent end-pointtask end-point呢? 这就要用到51822每个模块中定义的eventtask寄存器。 只要将这些寄存器的地址赋值给PPI的两个端点就可以了。

比如GPIOTE模块中的




对于PPI模块的寄存器。简单介绍下。

 

CH[n].EEPCH[n].TEP

这两个寄存器就是配置某个通道两个端点的。 将形如上面截图的GPIOTE中的模块的eventtask寄存器地址对应赋值给CH[n].EEP和CH[n].TEP 就完成了通道的配置。

然后通过CHEN或者CHENSET寄存器使能自己使用的通道就可以了。

 

下面新建一个工程来通过PPI实现按键点灯功能。

 

新建工程选择自己板子使用的芯片型号:




教程中为了更直接的理解模块的使用。不使用sdk中提供的库函数,而直接操作寄存器来实现。

所以运行时环境勾选下必要的CMSIS下的COREDevice下的Startup。因为用了gpio的函数 勾选一下nRF_Drivers下的nrf_gpio 就可以了。




然后配置jlink的设置(我的板子使用的是jlinksw方式下载程序)



创建main.c文件,然后添加到工程中




下面介绍main.c代码细节。

 

上面说过PPI的配置 就是配置 两端的event end-pointtask end-point

现在我们要实现按键点灯。

所以首先要配置  :按键 产生 一个event 和 一个翻转电平的task

 

这需要使用到 GPIOTE 部分。细节参考 GPIOTE教程。

 

配置BUTTON的按下事件。 

    NRF_GPIOTE->CONFIG[1] = ( 1 << 0 )  //作为event

                         | ( BUTTON_PIN << 8)    //设置button引脚产生event

                         | ( 2 << 16 );         //button引脚高到低变化产生event

 

该配置使用 GPIOTE 通道1 作为event,并绑定BUTTON引脚,设置event触发事件为高到低电平的跳变。即按键按下触发event

 

 

配置 翻转电平的 task

    NRF_GPIOTE->CONFIG[0] = ( 3 << 0 )     //作为task

                         | ( LED_PIN << 8)   

                         | ( 3 << 16 )     //task为翻转电平

                         | ( 1 << 20);     //初始电平为高

改配置 使用 GPIOTE 通道0 作为task, 并绑定 led引脚,设tasktoggle,即task被执行时led改变状态,引脚初始电平为高(led)

 

 

GPIOTE的Eventtask配置好后。就需要将其绑定到PPI上。

 

GPIOTE中:

因为event产生(按键按下时)时, 产生event[1]会被置位(BUTTON使用的是GPIOTE通道1

OUT[0]LED使用的是GPIOTE通道0)被置位时,上面定义的taskled翻转)就会执行。

 

所以在PPI两端绑定的就是这两个寄存器地址的值。

    NRF_PPI->CH[0].EEP = (uint32_t)(&NRF_GPIOTE->EVENTS_IN[1]);

    NRF_PPI->CH[0].TEP = (uint32_t)(&NRF_GPIOTE->TASKS_OUT[0]);   

 

最后使能一下PPI通道0 就可以了。

下面是完整代码

 

 

程序中都是直接使用NRF_GPIOTENRF_PPI来操作模块。这两个宏是在nrf51.h文件中定义的

 

#include "nrf51.h"

#include "nrf_gpio.h"

 

#define LED       (22)

#define BUTTON    (18)

 

int main(void){

 

    nrf_gpio_cfg_input(BUTTON, NRF_GPIO_PIN_PULLUP);

   

    NRF_GPIOTE->CONFIG[0] = ( 3 << 0 )

                         | ( LED << 8)

                         | ( 3 << 16 )

                         | ( 1 << 20);

 

    NRF_GPIOTE->CONFIG[1] = ( 1 << 0 )

                         | ( BUTTON << 8)

                         | ( 2 << 16 );

    //将 GPIOTE通道 1 绑定到 PPI 通道 0 event 输入端。

    //将 GPIOTE通道 0 绑定到 PPI 通道 0 task

    //因为 GPIOTE通道 10 已经分别绑定到buttonled,并且对应设置为了 eventtask

    //所以当button被按下(产生下降沿)的时候,会产生event事件输入给 ppi通道0的 事件输入端,然后ppi 0task端就会自动被触发,即led电平翻转

    NRF_PPI->CH[0].EEP = (uint32_t)(&NRF_GPIOTE->EVENTS_IN[1]);

    NRF_PPI->CH[0].TEP = (uint32_t)(&NRF_GPIOTE->TASKS_OUT[0]); //注意,这里赋值要取地址   

   

    //使能PPI通道 0

    NRF_PPI->CHENSET = 0x01;

    while(1);

 

    return 0;

}

       

阅读(1504) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~