使用Beaglebone Black的PRU(三)——实现高达100MHz的GPIO输出
2014-03-11 18:23
525 查看
友情提示:请先按照本系列(一)(二)的说明安装PRU工具并跑通hello world再继续按本文操作。
PRU操作GPIO有很多种方式,本系列之(二)中的是一种,但最快速的方式是通过直接“写”r30和“读”r31这两个寄存器的相应位来操作对应的IO口:比如将r30的第14位置1就会把P8.12这个引脚置成高电平,很简单吧?要注意PRU中的r30寄存器对应的管脚只能输出,r31寄存器对应的管脚只能输入。
还以P8.12这个引脚为例,查表得它的偏移地址是0x30。它的第6个功能pr1_pru0_pru_r30_14是我们想要的,所以我们就需要把引脚功能配置成0x06。另外BBB默认是禁用PRU的,所以还需要在dts中开启PRU。
对应的dts文件如下:
写完以后用命令
写完之后编译一下,按照本系列前文的方法:
写好后编译一下:
代码中 SET r30.t14 和 CLR r30.t14 这两句分别将P8.12管脚置成高电平和低电平。在它们后面各放了一段循环延时的程序。因为PRU的主频是200MHz,每条指令执行时间是固定的1/200000000秒。因此通过恰当地设置延时循环的次数,可以精确控制高低电平的时间。比如在本代码中高低电平各自持续了250*2/200M秒(乘2是因为每次循环都有“减一”和“判断结束”两个指令),即产生了周期为200KHz的方波。经过示波器验证十分精确……
……好吧,其实这个例子显然并不会刚好输出200KHz的方波。是因为 SET r30.t14 和 CLR r30.t14 这两句以及大循环的减一和判断结束指令也占用了时间。
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
代码中 SET r30.t14 和 CLR r30.t14 这两句分别将P8.12管脚置成高电平和低电平。在它们后面各放了一段循环延时的程序。因为PRU的主频是200MHz,每条指令执行时间是固定的1/200000000秒。因此通过恰当地设置延时循环的次数,可以精确控制高低电平的时间。比如在本代码中高低电平各自持续了250*2/200M秒(乘2是因为每次循环都有“减一”和“判断结束”两个指令),即产生了周期为200KHz的方波。经过示波器验证十分精确……
第四步:执行程序
首先记得加载dtbo文件,配置引脚功能:echo BB-BONE-PRU > $SLOTS然后就可以运行程序了:
./mytest
……好吧,其实这个例子显然并不会刚好输出200KHz的方波。是因为 SET r30.t14 和 CLR r30.t14 这两句以及大循环的减一和判断结束指令也占用了时间。
相关文章推荐
- 【转】 使用Beaglebone Black的PRU(三)——实现高达100MHz的GPIO输出
- 在Beaglebone Black上使用C++实现can通讯
- Beaglebone Black——使用和配置Qt creator的Kit,实现瞬间跨平台
- 使用Beaglebone Black的PRU(一)
- 使用Beaglebone Black的PRU(二)——Hello World!
- 使用高德地图仿最新版微信发送位置实现,相似度高达99.99%!!!
- STM32学习笔记——使用函数库编程控制GPIO口输出
- APP开发实战148-使用AOP技术输出Log的具体实现
- 饼干是这样压缩的――PHP使用zlib扩展实现页面GZIP压缩输出
- ffmpeg使用tee实现单次编码多路输出
- Beaglebone Black教程使用SSH通过USB和因特网连接Beaglebone Black
- 使用Filter实现网站文本数据压缩后再输出
- 从键盘输入某个十进制小数或整数,转换成对应的二进制小数并输出。 (查询十进制小数转换成二进制小数的算法,使用循环来实现。 最多保留小数位后7位数字即可)算法
- 完成一个学生管理程序,使用学号作为键添加5个学生对象,并可以将全部信息保存在文件中,可以实现对学生信息的学号查找,输出全部学生信息的功能。
- 使用递归实现全排列输出
- Java小案例——使用双重for循环实现杨辉三角的输出
- PHP使用zlib扩展实现页面GZIP压缩输出
- PHP使用range协议实现输出文件断点续传代码实例
- 4. 使用循环输出:100,95,90,85,。。。,5。使用编程实现。