• 【转】 使用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 ");    
    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 - prussdrvincludepruss_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 这两句以及大循环的减一和判断结束指令也占用了时间。
  • 相关阅读:
    201706120024 编译原理第一次作业
    P2014 选课 题解(树形DP)
    基础算法·二分答案
    P4285 [SHOI2008]汉诺塔 题解 (乱搞)
    2018.12-2019.1 TO-DO LIST
    记录一枚蒟蒻的成长(持续更新)
    P3796 【模板】AC自动机(加强版) 题解(Aho-Corasick Automation)
    BuaacodingT651 我知道你不知道圣诞节做什么 题解(逻辑)
    P2766 最长不下降子序列问题 题解(网络流)
    P2516 [HAOI2010]最长公共子序列 题解(LCS)
  • 原文地址:https://www.cnblogs.com/dolphi/p/3663017.html
Copyright © 2020-2023  润新知