分类: 嵌入式
2016-03-01 14:42:41
nordic nrf51822 BLE 交流群498676838
Programmable Peripheral
Interconnect即可编程外设互联 系统,该模块是51822 提供的一个特性。
目的是为了让51822 的外围模块可以不通过处理器而自动相互作用。
工作原理很简单。
可以将PPI看做是一通道。
该通道有两个端点,一个叫event end-point,另一个为task end-point. 通过将具体的 event寄存器和 task寄存器
分别赋值到 ppi通道的event end-point和task end-point中。
那么当 event定义的事件发生时。 PPI另一端被赋值的对应的task就会自动被触发。
举个简单的例子。
按键翻转LED灯状态的实现。一般需要使用 I/O中断。在按键按下后cpu在中断处理函数中判断是否按键按下,然后再改变LED引脚状态。
这种实现就需要CPU参与其中才能工作。
而利用PPI, 则只需定义 按键按下后产生event,同时定义task为翻转引脚状态。然后分别将event和task寄存器绑定到PPI通道两端的event end-point和task end-point中。再使能PPI,之后按键按下和LED翻转状态就完全是自动进行。不需要CPU参与运算处理。 这样就在一定程度上释放了CPU,提高了工作效率。
综上,PPI的使用 就是配置下两端的event end-point和task end-point。然后使能PPI就可以了。
那么什么值可以赋值给 PPI的event end-point和task end-point呢? 这就要用到51822每个模块中定义的event和task寄存器。 只要将这些寄存器的地址赋值给PPI的两个端点就可以了。
比如GPIOTE模块中的
对于PPI模块的寄存器。简单介绍下。
CH[n].EEP和CH[n].TEP
这两个寄存器就是配置某个通道两个端点的。 将形如上面截图的GPIOTE中的模块的event和task寄存器地址对应赋值给CH[n].EEP和CH[n].TEP 就完成了通道的配置。
然后通过CHEN或者CHENSET寄存器使能自己使用的通道就可以了。
下面新建一个工程来通过PPI实现按键点灯功能。
新建工程选择自己板子使用的芯片型号:
教程中为了更直接的理解模块的使用。不使用sdk中提供的库函数,而直接操作寄存器来实现。
所以运行时环境勾选下必要的CMSIS下的CORE,Device下的Startup。因为用了gpio的函数 勾选一下nRF_Drivers下的nrf_gpio 就可以了。
然后配置jlink的设置(我的板子使用的是jlink的sw方式下载程序)。
创建main.c文件,然后添加到工程中
下面介绍main.c代码细节。
上面说过PPI的配置 就是配置 两端的event end-point和task 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引脚,设task为toggle,即task被执行时led改变状态,引脚初始电平为高(led灭)
GPIOTE的Event和task配置好后。就需要将其绑定到PPI上。
在GPIOTE中:
因为event产生(按键按下时)时, 产生event[1]会被置位(BUTTON使用的是GPIOTE通道1)
而
而 OUT[0](LED使用的是GPIOTE通道0)被置位时,上面定义的task(led翻转)就会执行。
所以在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_GPIOTE和NRF_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通道 1和0 已经分别绑定到button和led,并且对应设置为了 event和task。
//所以当button被按下(产生下降沿)的时候,会产生event事件输入给 ppi通道0的 事件输入端,然后ppi 0的task端就会自动被触发,即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;
}