友情提示:請先按照本系列(一)(二)的說明安裝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文件如下:
- /dts-v1/;
- /plugin/;
- / {
- compatible = "ti,beaglebone", "ti,beaglebone-black";
- /* identification */
- part-number = "BB-BONE-PRU";
- version = "00A0";
- exclusive-use =
- "P8.12";
- fragment@0 {
- target = <&am33xx_pinmux>;
- __overlay__ {
- mygpio: pinmux_mygpio{
- pinctrl-single,pins = <
- 0x30 0x06
- >;
- };
- };
- };
- fragment@1 {
- target = <&ocp>;
- __overlay__ {
- test_helper: helper {
- compatible = "bone-pinmux-helper";
- pinctrl-names = "default";
- pinctrl-0 = <&mygpio>;
- status = "okay";
- };
- };
- };
- fragment@2{
- target = <&pruss>;
- __overlay__ {
- status = "okay";
- };
- };
- };
寫完以后用命令
- dtc -@ -O dtb -o BB-BONE-PRU-00A0.dtbo BB-BONE-PRU-00A0.dts
生成dtbo文件,然后拷貝到 /lib/firmare目錄中。
第二步:編寫linux中運行的C程序
跟本系列之(二)一樣,就不贅述了。代碼如下:
- #include <stdio.h>
- #include <prussdrv.h>
- #include <pruss_intc_mapping.h>
- #define PRU_NUM 0
- int main (void)
- {
- unsigned int ret;
- tpruss_intc_initdata pruss_intc_initdata = PRUSS_INTC_INITDATA;
- prussdrv_init ();//Initialize the PRU
- if (prussdrv_open(PRU_EVTOUT_0))//Open PRU Interrupt
- {
- printf("prussdrv_open open failed\n");
- return (-1);
- }
- prussdrv_pruintc_init(&pruss_intc_initdata);
- prussdrv_exec_program (PRU_NUM, "./prucode.bin");//Execute example on PRU
- prussdrv_pru_wait_event (PRU_EVTOUT_0);//Waiting for this instruction: MOV r31.b0, PRU0_ARM_INTERRUPT+16
- prussdrv_pru_clear_event (PRU_EVTOUT_0, PRU0_ARM_INTERRUPT);
- prussdrv_pru_disable (PRU_NUM);//Disable PRU and close memory mapping
- prussdrv_exit ();
- return(0);
- }
寫完之后編譯一下,按照本系列前文的方法:
- gcc mytest.c -lpthread -lprussdrv -o mytest
第三步:編寫在PRU中運行的匯編程序
這個例子實際上比點亮BBB上的led還好理解:
- .origin 0
- .entrypoint START
- //Refer to this mapping in the file - \prussdrv\include\pruss_intc_mapping.h
- #define PRU0_ARM_INTERRUPT 19
- #define CONST_PRUCFG C4
- START:
- // Enable OCP master port
- LBCO r0, CONST_PRUCFG, 4, 4
- CLR r0, r0, 4 // Clear SYSCFG[STANDBY_INIT] to enable OCP master port
- SBCO r0, CONST_PRUCFG, 4, 4
- MOV r1, 10000000
- LOOP1:
- SET r30.t14 //set P8.12
- MOV r0, 250
- DELAY1:
- SUB r0, r0, 1
- QBNE DELAY1, r0, 0
- CLR r30.t14 //clear P8.12
- MOV r0, 250
- DELAY2:
- SUB r0, r0, 1
- QBNE DELAY2, r0, 0
- SUB r1, r1, 1
- QBNE LOOP1, r1, 0
- // Send notification to Host for program completion
- MOV r31.b0, PRU0_ARM_INTERRUPT+16
- // Halt the processor
- HALT
寫好后編譯一下:
- pasm -b prucode.p
第四步:執行程序
首先記得加載dtbo文件,配置引腳功能:
- echo BB-BONE-PRU > $SLOTS
- ./mytest
……好吧,其實這個例子顯然並不會剛好輸出200KHz的方波。是因為 SET r30.t14 和 CLR r30.t14 這兩句以及大循環的減一和判斷結束指令也占用了時間。