如何理解nRF5芯片外設PPI


PPI,英文全稱Programmable Peripheral Interconnect,是Nordic獨有的外設,其設計目的是讓CPU處於idle模式下外設與外設之間也能完成相應通信,從而降低系統功耗。為此,很多人會把PPI類比成DMA,的確PPI和DMA兩者在設計最終目的上有一定的相似性,但兩者的功能和原理完全不相同。講解PPI原理之前,先大概闡述一下Nordic芯片一個獨特的設計理念。

Nordic芯片每個外設都可以看做一個狀態機,所以每個外設都有輸入(task),輸出(event)以及狀態。比較常見的task比如啟動外設,清0寄存器等,常見的event比如數據發送完畢,外設關閉等。實現上,每個task和event都是一個獨立的32-bit寄存器,這跟傳統的芯片不一樣,傳統的芯片都是用寄存器的某一個bit來啟動,或者某一個bit來顯示狀態。換句話說,Nordic把傳統芯片1bit要做的事情換成一個完整的32-bit寄存器來實現。這樣做的好處是,每個task和event都是一個寄存器,他們都可以用一個獨立而唯一的地址來標識,這就為PPI打下了堅實的基礎。比如Timer模塊的寄存器列表如下所示,里面就包含了各種task和event寄存器:

 

 

再比如ADC模塊的寄存器列表如下所示,里面也包含了各種task和event寄存器:

 

 

現在開始講PPI,首先PPI也是一個外設,因此PPI也有自己的寄存器定義,如下圖所示,其主要用來配置PPI通道等。簡言之,PPI就是一個數字邏輯系統,它可以通過某一個PPI通道把外設1的event跟外設2的task相連,這樣一旦外設1的event置起(相關寄存器為1),將會自動觸發外設2的task(將相關寄存器自動置1)。

 

 

下面以一個實際例子來加深大家對PPI的理解,假設我們要實現如下功能:啟動timer,定時10ms,10ms到后啟動ADC模塊。這里我們以兩種方式來實現該例子要求:傳統方式和PPI方式,大家仔細比較兩者的區別,以理解PPI的積極作用。

傳統方式

傳統方式大致需要如下步驟:

  1. 初始化Timer模塊和ADC模塊——(CPU工作)
  2. 啟動Timer——(CPU工作)
  3. 等待Timer中斷——(CPU不工作)
  4. 10ms到,進入Timer timeout handler,啟動ADC——(CPU工作)

PPI方式

PPI方式大致需要如下步驟:

  1. 初始化Timer模塊,ADC模塊,以及PPI模塊,將Timer模塊的timeout event和ADC模塊的start task相連——(CPU工作)
  2. 啟動Timer——(CPU工作)
  3. 等待Timer timeout,10ms到,PPI將自動啟動ADC——(CPU不工作)

通過比較傳統方式和PPI方式,PPI方式可以少進入一次timeout handler,從而減少CPU工作時間,降低系統功耗。由於不需要進入timeout handler,因此啟動ADC的操作就不存在被其他高優先級中斷打斷的可能,這是PPI帶來的第二個好處:系統實時性更好。

除了一個event對應一個task,PPI通過fork機制可以讓一個event同時啟動2個task,即把一個event同時和一個task以及task對應的fork相連,以實現一個event到來,2個task同時啟動的目的。

大家可以參考SDK自帶例子(Keil5工程):

SDK安裝目錄\examples\peripheral\ppi\pca10040\blank\arm5_no_packs

或者

SDK安裝目錄\examples\peripheral\gpiote\pca10040\blank\arm5_no_packs

來進一步理解PPI的工作原理和編程注意事項。

下面為一段PPI使用代碼示例,它實現的功能是:當定時時間到,通過PPI自動去操作IO口

err_code = nrf_drv_ppi_init();

APP_ERROR_CHECK(err_code);

err_code = nrf_drv_ppi_channel_alloc(&ppi_channel);

APP_ERROR_CHECK(err_code);


compare_evt_addr = nrf_drv_timer_event_address_get(&timer, NRF_TIMER_EVENT_COMPARE0);

gpiote_task_addr = nrf_drv_gpiote_out_task_addr_get(GPIO_OUTPUT_PIN_NUMBER);

 
err_code = nrf_drv_ppi_channel_assign(ppi_channel, compare_evt_addr, gpiote_task_addr);

APP_ERROR_CHECK(err_code);

err_code = nrf_drv_ppi_channel_enable(ppi_channel);

APP_ERROR_CHECK(err_code);

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM