【轉】 使用Beaglebone Black的PRU(三)——實現高達100MHz的GPIO輸出


友情提示:請先按照本系列(一)(二)的說明安裝PRU工具並跑通hello world再繼續按本文操作。

PRU操作GPIO有很多種方式,本系列之(二)中的是一種,但最快速的方式是通過直接“寫”r30和“讀”r31這兩個寄存器的相應位來操作對應的IO口:比如將r30的第14位置1就會把P8.12這個引腳置成高電平,很簡單吧?要注意PRU中的r30寄存器對應的管腳只能輸出,r31寄存器對應的管腳只能輸入。

第一步:寫dts文件配置引腳功能

我們知道BBB每個引腳都有很多個功能(PINMUX)。要想用上述方式操作某個引腳,必須首先配置該引腳為相應的功能。引腳的功能需要查閱自帶手冊的“Expansion Header P8/9 Pinout”這兩個圖表。配置引腳功能目前沒有別的辦法,只能通過編寫device tree文件來實現。

還以P8.12這個引腳為例,查表得它的偏移地址是0x30。它的第6個功能pr1_pru0_pru_r30_14是我們想要的,所以我們就需要把引腳功能配置成0x06。另外BBB默認是禁用PRU的,所以還需要在dts中開啟PRU。

對應的dts文件如下:

 

  1. /dts-v1/;  
  2. /plugin/;  
  3.   
  4. / {  
  5. compatible = "ti,beaglebone""ti,beaglebone-black";  
  6.   
  7. /* identification */  
  8. part-number = "BB-BONE-PRU";  
  9. version = "00A0";  
  10.   
  11. exclusive-use =  
  12. "P8.12";  
  13.   
  14. fragment@0 {  
  15.     target = <&am33xx_pinmux>;  
  16.     __overlay__ {  
  17.         mygpio: pinmux_mygpio{  
  18.         pinctrl-single,pins = <  
  19.             0x30 0x06  
  20.             >;  
  21.     };  
  22.     };  
  23. };  
  24.   
  25. fragment@1 {  
  26.     target = <&ocp>;  
  27.     __overlay__ {  
  28.         test_helper: helper {  
  29.         compatible = "bone-pinmux-helper";  
  30.         pinctrl-names = "default";  
  31.         pinctrl-0 = <&mygpio>;  
  32.         status = "okay";  
  33.     };  
  34.     };  
  35. };  
  36.   
  37. fragment@2{  
  38.     target = <&pruss>;  
  39.     __overlay__ {  
  40.         status = "okay";  
  41.     };  
  42.     };  
  43. };  

寫完以后用命令

 

  1. dtc -@ -O dtb -o BB-BONE-PRU-00A0.dtbo BB-BONE-PRU-00A0.dts  

生成dtbo文件,然后拷貝到 /lib/firmare目錄中。

 

 

第二步:編寫linux中運行的C程序

 
跟本系列之(二)一樣,就不贅述了。代碼如下:
  1. #include <stdio.h>    
  2. #include <prussdrv.h>    
  3. #include <pruss_intc_mapping.h>    
  4.     
  5. #define PRU_NUM 0    
  6.     
  7. int main (void)    
  8. {    
  9.     unsigned int ret;    
  10.     tpruss_intc_initdata pruss_intc_initdata = PRUSS_INTC_INITDATA;    
  11.         
  12.     prussdrv_init ();//Initialize the PRU    
  13.     if (prussdrv_open(PRU_EVTOUT_0))//Open PRU Interrupt    
  14.     {   
  15.         printf("prussdrv_open open failed\n");    
  16.         return (-1);    
  17.     }    
  18.     prussdrv_pruintc_init(&pruss_intc_initdata);    
  19.     prussdrv_exec_program (PRU_NUM, "./prucode.bin");//Execute example on PRU    
  20.     prussdrv_pru_wait_event (PRU_EVTOUT_0);//Waiting for this instruction: MOV r31.b0, PRU0_ARM_INTERRUPT+16    
  21.     prussdrv_pru_clear_event (PRU_EVTOUT_0, PRU0_ARM_INTERRUPT);    
  22.     prussdrv_pru_disable (PRU_NUM);//Disable PRU and close memory mapping    
  23.     prussdrv_exit ();    
  24.     
  25.     return(0);    
  26. }    
寫完之后編譯一下,按照本系列前文的方法:
  1. gcc mytest.c -lpthread -lprussdrv -o mytest   

第三步:編寫在PRU中運行的匯編程序

 
這個例子實際上比點亮BBB上的led還好理解:
  1. .origin 0    
  2. .entrypoint START    
  3.     
  4. //Refer to this mapping in the file - \prussdrv\include\pruss_intc_mapping.h    
  5. #define PRU0_ARM_INTERRUPT      19    
  6. #define CONST_PRUCFG            C4   
  7.     
  8. START:    
  9.     // Enable OCP master port    
  10.     LBCO      r0, CONST_PRUCFG, 4, 4    
  11.     CLR       r0, r0, 4         // Clear SYSCFG[STANDBY_INIT] to enable OCP master port    
  12.     SBCO      r0, CONST_PRUCFG, 4, 4    
  13.   
  14.     MOV r1, 10000000  
  15. LOOP1:  
  16.   
  17.     SET r30.t14 //set P8.12  
  18.         
  19.     MOV r0, 250   
  20. DELAY1:    
  21.     SUB r0, r0, 1    
  22.     QBNE DELAY1, r0, 0   
  23.       
  24.     CLR r30.t14 //clear P8.12  
  25.       
  26.     MOV r0, 250    
  27. DELAY2:    
  28.     SUB r0, r0, 1    
  29.     QBNE DELAY2, r0, 0    
  30.       
  31.     SUB r1, r1, 1  
  32.     QBNE LOOP1, r1, 0  
  33.        
  34.     
  35.     // Send notification to Host for program completion    
  36.     MOV       r31.b0, PRU0_ARM_INTERRUPT+16    
  37.     
  38.     // Halt the processor    
  39.     HALT    
寫好后編譯一下:
  1. pasm -b prucode.p  
 
代碼中 SET r30.t14 和 CLR r30.t14 這兩句分別將P8.12管腳置成高電平和低電平。在它們后面各放了一段循環延時的程序。因為PRU的主頻是200MHz,每條指令執行時間是固定的1/200000000秒。因此通過恰當地設置延時循環的次數,可以精確控制高低電平的時間。比如在本代碼中高低電平各自持續了250*2/200M秒(乘2是因為每次循環都有“減一”和“判斷結束”兩個指令),即產生了周期為200KHz的方波。經過示波器驗證十分精確……
 
 

第四步:執行程序

 
首先記得加載dtbo文件,配置引腳功能:
  1. echo BB-BONE-PRU > $SLOTS  
然后就可以運行程序了:
  1. ./mytest  


 
……好吧,其實這個例子顯然並不會剛好輸出200KHz的方波。是因為 SET r30.t14 和 CLR r30.t14 這兩句以及大循環的減一和判斷結束指令也占用了時間。


免責聲明!

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



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