使用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文件如下:

/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 这两句以及大循环的减一和判断结束指令也占用了时间。


  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值