凑在一起讲的原因,实在是因为他们关联密切,这个算是nRF5系特殊外设了.
说起这个东西,特别需要注意几个概念.
EVENT和TASK概念:
- EVENT即外设到PPI,TASK即PPI到外设,PPI全称Programmable peripheral interconnect,可以让多个外设不依赖CPU之间通信,区别于DMA,DMA是资料转移,他是事务驱动.
- 用现在很火热的云计算概念说,DMA叫多地同步,异地迁移,而PPI叫事务触发,完全不是一种东西.
- EVENT和TASK依赖特殊寄存器,这个寄存器比较宽,32Bit只为了一个功能,GPIOTE是针对GPIO特殊分离的外设,比如nRF52840可以控制8组.
- PPI接收EVENT,操作TASK,PPI没有特殊的能力,就是用来干这个的.
- GPIOTE除了产生EVENT,操作TASK之外,还能感知GPIO变化或者GPIO PORT变化(两者不是一回事)
我就拿官方例子说事,定时器EVENT驱动GPIO TASK,完整的自己去看examplesperipheralgpiote,这里只是抽取重点和添加一些注释.
static nrf_drv_timer_t timer = NRF_DRV_TIMER_INSTANCE(0); void timer_dummy_handler(nrf_timer_event_t event_type, void * p_context){} static void led_blinking_setup() { uint32_t compare_evt_addr; uint32_t gpiote_task_addr; nrf_ppi_channel_t ppi_channel; nrf_drv_gpiote_out_config_t config = GPIOTE_CONFIG_OUT_TASK_TOGGLE(false); /* GPIOTE OUT就是TASK,声明一个TASK,config传递的是默认状态,比如这里false指的是低电平. */ nrf_drv_gpiote_out_init(13, &config); /* 设置定时器比较通道参数 */ nrf_drv_timer_extended_compare(&timer, (nrf_timer_cc_channel_t)0, 500 * 1000UL, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, false); nrf_drv_ppi_channel_alloc(&ppi_channel); /* 比较通道0事件,作为PPI输入. */ compare_evt_addr = nrf_drv_timer_event_address_get(&timer, NRF_TIMER_EVENT_COMPARE0); /* GPIO连接GPUOTE OUT. */ gpiote_task_addr = nrf_drv_gpiote_out_task_addr_get(13); /* 连接这个EVENT到TASK,然后使能. */ nrf_drv_ppi_channel_assign(ppi_channel, compare_evt_addr, gpiote_task_addr); nrf_drv_ppi_channel_enable(ppi_channel); /* 使用GPIOTE里面的TASK配置. */ nrf_drv_gpiote_out_task_enable(13); } int main(void) { nrf_drv_ppi_init(); nrf_drv_gpiote_init(); /* 定时器初始化第三个参数不能为NULL,所以创建个空函数,接受各种事件回调. */ nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG; nrf_drv_timer_init(&timer, &timer_cfg, timer_dummy_handler); /* 创建PPI连接 */ led_blinking_setup(); /* 使能定时器后PPI生效 */ nrf_drv_timer_enable(&timer); while (true) { /* 什么都不需要做,PPI自动操作LED. */ } }
主要就是COMPORE0输出事件,会导致GPIO的TASK执行,然后GPIO就会按照一定的规律,进行翻转.这个规律,取决于COMPORE0的情况,后面还有ppi和pin_change_int的各种例子,可以参考.