AM335X的汇编语言与c语言,AM335x的PRUSSv2简介与使用

PRUSSv2= Programmable Real-time Unit Sub-System = PRU-ICSS = PRU Industrial Communication Sub-System = 上一代PRUSS的v2进化版

这是一个AM335x等芯片上自带的,独立于ARM CPU运行的子系统。其时钟频率为200MHz,可以直接控制特定的IO口,可以达到非常高的实时性要求。一般两种情况需要用到它:一是linux系统的实时性不满足要求的时候;二是芯片的功能模块不够用的时候(比如你想要10个UART,但芯片上只有6个,那你可以用它再创造4个)。

几个可能的应用场合:

高速ADC

摄像机接口

显示屏接口

音频处理

电机反馈控制

从某种意义上说,有了它,我们就有了一个片上CPLD或FPGA,只不过PRU编程不是用VDHL语言,而是用专门的汇编语言。它的汇编语句都是在一个时钟周期内执行完的,没有流水线之类可能扰乱程序时序的因素,这使得程序的可预测性大大提高。

AM335x芯片上带有两个PRU。(不知道能不能同时工作?)

配置BBB,安装PRU assembler和示例程序

root@beaglebone:~/tmp# git clone git://github.com/beagleboard/am335x_pru_package.git

Cloning into 'am335x_pru_package'...

...

root@beaglebone:~/tmp# cd am335x_pru_package/pru_sw/app_loader/interface

root@beaglebone:~/tmp/am335x_pru_package/pru_sw/app_loader/interface# make CROSS_COMPILE="" #无需交叉编译

root@beaglebone:~/tmp/am335x_pru_package/pru_sw/app_loader/interface# cd ../../utils/pasm_source

root@beaglebone:~/tmp/am335x_pru_package/pru_sw/utils/pasm_source# ./linuxbuild

root@beaglebone:~/tmp/am335x_pru_package/pru_sw/utils/pasm_source# cd ../../example_apps

root@beaglebone:~/tmp# git clone git://github.com/beagleboard/am335x_pru_package.git

Cloning into 'am335x_pru_package'...

...

root@beaglebone:~/tmp# cd am335x_pru_package/pru_sw/app_loader/interface

root@beaglebone:~/tmp/am335x_pru_package/pru_sw/app_loader/interface# make CROSS_COMPILE="" #无需交叉编译

root@beaglebone:~/tmp/am335x_pru_package/pru_sw/app_loader/interface# cd ../../utils/pasm_source

root@beaglebone:~/tmp/am335x_pru_package/pru_sw/utils/pasm_source# ./linuxbuild

root@beaglebone:~/tmp/am335x_pru_package/pru_sw/utils/pasm_source# cd ../../example_apps

编辑一下example_apps目录中的Makefile,把PASM?=../utils/pasm_2改成PASM?=../utils/pasm_2.arm,然后继续

root@beaglebone:~/tmp/am335x_pru_package/pru_sw/example_apps# make CROSS_COMPILE=""

root@beaglebone:~/tmp/am335x_pru_package/pru_sw/example_apps# make CROSS_COMPILE=""

编译完成以后PRU assembler和示例程序就安装好了。但BBB默认是没有使能pruss的(BB white好像不用进行下面这一步),所以还需要修改一下系统dtb文件

cd /boot

cp am335x-boneblack.dtb am335x-boneblack.dtb_orig

dtc -I dtb -O dts am335x-boneblack.dtb > am335x-boneblack.dts

cd /boot

cp am335x-boneblack.dtb am335x-boneblack.dtb_orig

dtc -I dtb -O dts am335x-boneblack.dtb > am335x-boneblack.dts

(插一句,我之前没有仔细看过dtc命令,原来还可以反编译dtb文件啊。。。)

找到

pruss@4a30000 {

...

status = "disabled";

...

};

pruss@4a30000 {

...

status = "disabled";

...

};

把"disabled"改成"okay",保存。

dtc -I dts -O dtb am335x-boneblack.dts > am335x-boneblack.dtb_pru

cp am335x-boneblack.dtb_pru am335x-boneblack.dtb

dtc -I dts -O dtb am335x-boneblack.dts > am335x-boneblack.dtb_pru

cp am335x-boneblack.dtb_pru am335x-boneblack.dtb

然后重启。至此,BBB也已经配置好了。

重启后我们用自带的example_apps测试一下

root@beaglebone:~#cd tmp/am335x_pru_package/pru_sw/example_apps/bin

root@beaglebone:~/tmp/am335x_pru_package/pru_sw/example_apps/bin# modprobe uio_pruss #每次重启都要运行这句话

root@beaglebone:~/tmp/am335x_pru_package/pru_sw/example_apps/bin# ./PRU_memAccessPRUDataRam

INFO: Starting PRU_memAccessPRUDataRam example.

AM33XX

INFO: Initializing example.

INFO: Executing example.

File ./PRU_memAccessPRUDataRam.bin open passed

INFO: Waiting for HALT command.

INFO: PRU completed transfer.

INFO: Example executed succesfully.

root@beaglebone:~#cd tmp/am335x_pru_package/pru_sw/example_apps/bin

root@beaglebone:~/tmp/am335x_pru_package/pru_sw/example_apps/bin# modprobe uio_pruss #每次重启都要运行这句话

root@beaglebone:~/tmp/am335x_pru_package/pru_sw/example_apps/bin# ./PRU_memAccessPRUDataRam

INFO: Starting PRU_memAccessPRUDataRam example.

AM33XX

INFO: Initializing example.

INFO: Executing example.

File ./PRU_memAccessPRUDataRam.bin open passed

INFO: Waiting for HALT command.

INFO: PRU completed transfer.

INFO: Example executed succesfully.

测试成功。

本文最开始的git文件建议在电脑上也clone一份,里面有几个关于PRU的pdf文档可能需要经常查看。

现在,已经安装好了am335x_pru_package,本文将用它来编写、编译一个最简单的PRU程序——闪烁BBB上的led灯。

大体来说每个PRU程序都包括两部分:

在ARM核的Linux系统中运行的C语言程序

在PRU中运行的汇编程序(编译成.bin文件)

其中c语言程序有两个作用:将汇编程序传到PRU中;与PRU程序互相传递数据和互动。

汇编程序源文件包括.p和.hp两种后缀的文件,最后编译成.bin文件被传到PRU执行。.hp文件类似于c语言的.h头文件,可以在.p文件中被include,它不是必须的,本例为了简单起见也不用.hp文件。

OK,我们开始做吧!

Step by step步骤

首先新建两个文件,内容分别如下:

//mytest.c

#include

#include

#include

#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 (PRU0_ARM_INTERRUPT);

prussdrv_pru_disable (PRU_NUM);//Disable PRU and close memory mapping

prussdrv_exit ();

return(0);

}

//mytest.c

#include

#include

#include

#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 (PRU0_ARM_INTERRUPT);

prussdrv_pru_disable (PRU_NUM);//Disable PRU and close memory mapping

prussdrv_exit ();

return(0);

}

//prucode.p

.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

//Refer to AM335X Technical Reference Manual & BBB SRM

#define GPIO1 0x4804c000

#define GPIO_CLEARDATAOUT 0x190

#define GPIO_SETDATAOUT 0x194

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, 3 // loop 3 times

LOOP0:

MOV r2, 1<<22

MOV r3, GPIO1 | GPIO_SETDATAOUT

SBBO r2, r3, 0, 4

MOV r0, 100000000

DEL1:

SUB r0, r0, 1

QBNE DEL1, r0, 0

MOV r2, 1<<22

MOV r3, GPIO1 | GPIO_CLEARDATAOUT

SBBO r2, r3, 0, 4

MOV r0, 100000000

DEL2:

SUB r0, r0, 1

QBNE DEL2, r0, 0

SUB r1, r1, 1

QBNE LOOP0, r1, 0

// Send notification to Host for program completion

MOV r31.b0, PRU0_ARM_INTERRUPT+16

// Halt the processor

HALT

//prucode.p

.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

//Refer to AM335X Technical Reference Manual & BBB SRM

#define GPIO1 0x4804c000

#define GPIO_CLEARDATAOUT 0x190

#define GPIO_SETDATAOUT 0x194

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, 3 // loop 3 times

LOOP0:

MOV r2, 1<<22

MOV r3, GPIO1 | GPIO_SETDATAOUT

SBBO r2, r3, 0, 4

MOV r0, 100000000

DEL1:

SUB r0, r0, 1

QBNE DEL1, r0, 0

MOV r2, 1<<22

MOV r3, GPIO1 | GPIO_CLEARDATAOUT

SBBO r2, r3, 0, 4

MOV r0, 100000000

DEL2:

SUB r0, r0, 1

QBNE DEL2, r0, 0

SUB r1, r1, 1

QBNE LOOP0, r1, 0

// Send notification to Host for program completion

MOV       r31.b0, PRU0_ARM_INTERRUPT+16

// Halt the processor

HALT

然后编译它们。编译之前先从am335x_pru_package中复制一些必要文件到标准目录中,省得我们添加引用目录。

把 pru_sw/app_loader/lib目录中的libprussdrv.a拷贝到/usr/lib中;

把 pru_sw/app_loader/include目录中的两个文件都拷贝到/usr/include中;

把 pru_sw/utils目录中的pasm拷贝到/usr/bin中。

下面开始编译,执行下面两条命令。

gcc mytest.c -lpthread -lprussdrv -o mytest

pasm -b prucode.p

gcc mytest.c -lpthread -lprussdrv -o mytest

pasm -b prucode.p

(其中 -lpthread -lprussdrv参数是指要使用libpthread.so和libprussdrv.a这两个库文件。)

编译完以后就生成了要在linux中运行的mytest程序和要传到PRU中的prucode.bin文件。

注意,在运行程序前,还需要做一件事。用lsmod命令查看一下uio_pruss模块有没有加载。如果没有的话,手动加载一下。这个应该加载一次就可以了。

modprobe uio_pruss

modprobe uio_pruss

一切就绪!下面输入 ./mytest来执行一下程序吧!

顺利的话你会看到第二个led灯亮灭了三次,每次亮和灭的时间是1秒钟。

几点分析

在执行过程中,终端处于忙碌状态,如果在这期间你按下Crtl+C终止了程序,会发现led还在按照原来的规律继续闪烁,可见灯的亮灭,或者说PRU的程序运行并不受linux程序的影响。

PRU简介中提到AM33xx中PRUSSv2的主频是200MHz,每条汇编指令执行时间是一个时钟周期。通过这个程序也可以得到验证:

MOV r0, 100000000

:

SUB r0, r0, 1

QBNE DEL1, r0, 0

MOV r0, 100000000

DEL1:

SUB r0, r0, 1

QBNE DEL1, r0, 0

在这个循环等待的部分,不断对寄存器r0执行自减1和判断是否为零这两条指令,执行了100M次,所以延时为1秒。

汇编程序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

// 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

它把SYSCFG寄存器的STANDBY_INIT位置零,否则的话将无法访问任何外部memory,包括ARM的内存和IO口等。所以记得在每个程序一开始都加上这三条指令。

file:///C:/Users/ADMINI~1/AppData/Local/Temp/ksohtml/wps_clip_image-11722.png

Second choices

最后,如果你不想用官方工具,这里有second choice:http://www.element14.com/community/community/knode/single-board_computers/next-gen_beaglebone/blog/2013/06/16/bbb--getting-ace-working

如果你想用java脚本与PRU里的程序互动,可以参考这里:https://npmjs.org/package/pru

上面这两个东西我都没仔细看过,仅列出来供参考。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值