前一篇分析了前十個基礎實驗的代碼,從這裡開始分析後十個~ 一、PPI原理: PPI(Programmable Peripheral Interconnect),中文翻譯為可編程外設互連。 在nRF51822 內部設置了PPI 方式,可以通過任務和事件讓不同外設之間進行互連,而不需要CPU 進行參與。 ...
前一篇分析了前十個基礎實驗的代碼,從這裡開始分析後十個~
一、PPI原理:
PPI(Programmable Peripheral Interconnect),中文翻譯為可編程外設互連。
在nRF51822 內部設置了PPI 方式,可以通過任務和事件讓不同外設之間進行互連,而不需要CPU 進行參與。
PPI 通過通道讓任務和事件連接在一起。PPI 通道由兩個端點組成:
- 任務端點:Task End-Point (TEP)。
- 事件端點:Event End-Point (EEP)。
所謂的互聯就是將任務端點寫入需要連接的任務寄存器地址,事件端點寫入需要連接事件寄存器地址,之後,使能該PPI 通道,即實現了任務和事件的互聯。
可以通過如下兩種方式使能和關閉PPI 通道:
- 1) 通過獨立設置CHEN,CHENSET 和CHENCLR 寄存器。
- 2) 通過PPI 通道組的使能和關閉任務。使用這種方式,在觸發任務之前,需要先配置好哪些PPI 通道屬於哪個組。
二、運行邏輯:
實驗中,用到了3 個定時器:Timer 0、Timer 1 和Timer 2。
1) Timer 0 配置為計數器,在主迴圈中每100ms 被觸發一次,並通過串口列印出計數值。
2) Timer 1 每個偶數秒(2、4、6、8……)產生一次比較匹配事件,該事件通過PPI通道0 和Timer 0 的STOP Task 互聯,互聯後通過該事件觸發Timer 0 的STOP Task。
3) Timer 2 每個奇數秒(1、3、5、7……)產生一次比較匹配事件,該事件通過PPI通道1 和Timer 0 的START Task 互聯,互聯後通過該事件觸發Timer 0 的START Task。
實驗原理框圖如圖1 所示:
三、核心代碼分析
系統運行後,在迴圈中Timer 0 計數器的計數值每100ms 增加一次,在偶數秒時,Timer2 產生比較匹配事件,通過PPI 觸發Timer 0 的STOP Task,Timer 0 停止計數。此時,儘管主迴圈中每隔100ms 觸發一次Timer 0 計數,但是由於Timer 0 已經停止,所以,計數值不會增加。每個奇數秒,Timer2 產生比較匹配事件,通過PPI 觸發Timer 0 的START Task,Timer 0 恢復計數。
main函數部分:
1 int main(void) 2 { 3 timer0_init(); // Timer used to blink the LEDs. 4 timer1_init(); // Timer to generate events on even number of seconds. 5 timer2_init(); // Timer to generate events on odd number of seconds. 6 ppi_init(); // PPI to redirect the event to timer start/stop tasks. 7 串口初始化(略) 28 29 // Enabling constant latency as indicated by PAN 11 "HFCLK: Base current with HFCLK 30 // running is too high" found at Product Anomaly document found at 31 // https://www.nordicsemi.com/eng/Products/Bluetooth-R-low-energy/nRF51822/#Downloads 32 // 33 // @note This example does not go to low power mode therefore constant latency is not needed. 34 // However this setting will ensure correct behaviour when routing TIMER events through 35 // PPI (shown in this example) and low power mode simultaneously. 36 NRF_POWER->TASKS_CONSTLAT = 1; 37 38 // Start clock. 39 nrf_drv_timer_enable(&timer0); 40 nrf_drv_timer_enable(&timer1); 41 nrf_drv_timer_enable(&timer2); 42 43 // Loop and increment the timer count value and capture value into LEDs. @note counter is only incremented between TASK_START and TASK_STOP. 44 while (true) 45 { 46 47 printf("Current cout: %d\r\n", (int)nrf_drv_timer_capture(&timer0,NRF_TIMER_CC_CHANNEL0)); 48 49 /* increment the counter */ 50 nrf_drv_timer_increment(&timer0); 51 52 nrf_delay_ms(100); 53 } 54 }
定時器初始化部分:
1 // Timer even handler. Not used since timer is used only for PPI. 2 void timer_event_handler(nrf_timer_event_t event_type, void * p_context){} 3 4 /** @brief Function for Timer 0 initialization, which will be started and stopped by timer1 and timer2 using PPI. 5 */ 6 static void timer0_init(void) 7 { 8 ret_code_t err_code = nrf_drv_timer_init(&timer0, NULL, timer_event_handler); 9 APP_ERROR_CHECK(err_code); 10 } 11 12 /** @brief Function for Timer 1 initialization. 13 * @details Initializes Timer 1 peripheral, creates event and interrupt every 2 seconds, 14 * by configuring CC[0] to timer overflow value, we create events at even number of seconds 15 * for example, events are created at 2,4,6 ... seconds. This event can be used to stop Timer 0 16 * with Timer1->Event_Compare[0] triggering Timer 0 TASK_STOP through PPI. 17 */ 18 static void timer1_init(void) 19 { 20 // Configure Timer 1 to overflow every 2 seconds. Check TIMER1 configuration for details 21 // The overflow occurs every 0xFFFF/(SysClk/2^PRESCALER). 22 // = 65535/31250 = 2.097 sec 23 ret_code_t err_code = nrf_drv_timer_init(&timer1, NULL, timer_event_handler); 24 APP_ERROR_CHECK(err_code); 25 26 nrf_drv_timer_extended_compare(&timer1, NRF_TIMER_CC_CHANNEL0, 0xFFFFUL, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, false);//比較模式,Timer 1 每個偶數秒(2、4、6、8……)產生一次比較匹配事件,該事件通過PPI通道0 和Timer 0 的STOP Task 互聯,互聯後通過該事件觸發Timer 0 的STOP Task。 27 } 28 29 /** @brief Function for Timer 2 initialization. 30 * @details Initializes Timer 2 peripheral, creates event and interrupt every 2 seconds 31 * by configuring CC[0] to half of timer overflow value. Events are created at odd number of seconds. 32 * For example, events are created at 1,3,5,... seconds. This event can be used to start Timer 0 33 * with Timer2->Event_Compare[0] triggering Timer 0 TASK_START through PPI. 34 */ 35 static void timer2_init(void) 36 { 37 // Generate interrupt/event when half of time before the timer overflows has past, that is at 1,3,5,7... seconds from start. 38 // Check TIMER1 configuration for details 39 // now the overflow occurs every 0xFFFF/(SysClk/2^PRESCALER) 40 // = 65535/31250 = 2.097 sec */ 41 ret_code_t err_code = nrf_drv_timer_init(&timer2, NULL, timer_event_handler); 42 APP_ERROR_CHECK(err_code); 43 44 nrf_drv_timer_extended_compare(&timer2, NRF_TIMER_CC_CHANNEL0, 0x7FFFUL, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, false);//Timer 2 每個奇數秒(1、3、5、7……)產生一次比較匹配事件,該事件通過PPI通道1 和Timer 0 的START Task 互聯,互聯後通過該事件觸發Timer 0 的START Task。 45 }
PPI連接事件部分:
1 /** @brief Function for initializing the PPI peripheral. 2 */ 3 static void ppi_init(void) 4 { 5 uint32_t err_code = NRF_SUCCESS; 6 7 err_code = nrf_drv_ppi_init(); 8 APP_ERROR_CHECK(err_code); 9 10 // Configure 1st available PPI channel to stop TIMER0 counter on TIMER1 COMPARE[0] match, which is every even number of seconds. 11 err_code = nrf_drv_ppi_channel_alloc(&ppi_channel1); 12 APP_ERROR_CHECK(err_code); 13 err_code = nrf_drv_ppi_channel_assign(ppi_channel1,//PPI連接事件 14 nrf_drv_timer_event_address_get(&timer1, NRF_TIMER_EVENT_COMPARE0), 15 nrf_drv_timer_task_address_get(&timer0, NRF_TIMER_TASK_STOP)); 16 APP_ERROR_CHECK(err_code); 17 18 // Configure 2nd available PPI channel to start timer0 counter at TIMER2 COMPARE[0] match, which is every odd number of seconds. 19 err_code = nrf_drv_ppi_channel_alloc(&ppi_channel2); 20 APP_ERROR_CHECK(err_code); 21 err_code = nrf_drv_ppi_channel_assign(ppi_channel2, 22 nrf_drv_timer_event_address_get(&timer2, NRF_TIMER_EVENT_COMPARE0), 23 nrf_drv_timer_task_address_get(&timer0, NRF_TIMER_TASK_START)); 24 APP_ERROR_CHECK(err_code); 25 26 // Enable both configured PPI channels 27 err_code = nrf_drv_ppi_channel_enable(ppi_channel1); 28 APP_ERROR_CHECK(err_code); 29 err_code = nrf_drv_ppi_channel_enable(ppi_channel2); 30 APP_ERROR_CHECK(err_code); 31 }
@beautifulzzzz - 物聯網&普適計算實踐者
e-mail:[email protected]
i-blog:blog.beautifulzzzz.com