项目介绍
朋友突然丢了一个链接过来,发现搞的活动中有一个叫PRU编程的东东,之前玩TI的芯片从来没玩过这个,因此就直接下单参加这个活动了。
下单后,直接在网上查阅PRU相关的资料,发现网上共享出来的资料写的都比较简单,直接汇编代码编程,而汇编这种东西,对芯片平台指令集熟悉程度要求特别高,其实并不适合刚接触PRU的小白使用。因此在查阅了资料后,考虑使用arm核写主要业务逻辑,pru实现固定频率的亮灭(占空比可调)的思路实现。
而在后面群友的沟通中,有群友截了张图,直接用C编写PRU代码,因此便再一次搜索相关资料,终于在狗板官方资料中找到了PRU的参考实现例程。虽然没板卡验证,但是基本上可以确认,纯PRU编程难度已经降低了。完全可以按照裸机C编程的方法实现PRU的呼吸灯效果。
硬件设计思路
由于狗板没有用户操作的按键,因此考虑飞线跳接一个按键出来,这里选用了RT1021_MB板卡的旋钮按键实现(有现成的就不用再去找面包板甚至烙铁去折腾了,杜邦线搞定)。
而查看PRU例程,发现狗板上的四颗LED灯都可以通过PRU直接控制,因此之前考虑的灯也飞到RT1021_MB上的思路可以使用狗板的LED灯替代(纯粹的因为想少接几根线,连接不稳定的问题少一些)。
因此最终的硬件接口为:
BeagleBone Black | RT1021_MB | Function |
P9_1 | J1_1 | GND |
P9_3 | J1_3 | 3_3V |
P9_27 | J1_15 | Key(3.3V When not pressed) |
USR0 LED | Key press state | |
UDR1 LED | Breath LED | |
USR2 LED | Breath LED | |
USR3 LED | Breath LED |
软件设计思路
按照功能需求,软件需要实现这么个功能,按键按一下,呼吸灯呼吸速度变化一次。因此考虑按键释放通知上层变更呼吸灯速率。具体设计如下:
至于流程图,按照上面的思路,几乎可以不用画了,就如下框图:
具体实现
具体是现代码见:PROJECT_LOCATION\docs\09_led_pwm\led_pwm.pru0.c
按键功能
int isKeyPressed(void) {
unsigned int sw = 0x1 << 5; // P9_27
unsigned int keyState = 0;
unsigned int *gpio1 = (unsigned int *)GPIO1;
static int flag = 0;
if(!(__R31 & sw)) {
flag = 1;
gpio1[GPIO_SETDATAOUT] |= USR0;
} else if(flag) {
keyState = 1;
flag = 0;
} else {
gpio1[GPIO_CLEARDATAOUT] |= USR0;
}
return keyState;
}
上层业务逻辑
#define BREATH_FAST 20
#define BREATH_SLOW 60
void main(void) {
unsigned int keyState = 0;
unsigned int speed = BREATH_FAST;
unsigned int *gpio1 = (unsigned int *)GPIO1;
/* Clear SYSCFG[STANDBY_INIT] to enable OCP master port */
CT_CFG.SYSCFG_bit.STANDBY_INIT = 0;
while (1) {
if(isKeyPressed()) {
keyState = !keyState;
}
breath(speed);
if(keyState) {
speed = BREATH_SLOW;
} else {
speed = BREATH_FAST;
}
}
}
呼吸灯执行模块
void delay(int time) {
while(time--)
__delay_cycles(1000);
}
void setBrightness(int brightness) {
unsigned int *gpio1 = (unsigned int *)GPIO1;
gpio1[GPIO_SETDATAOUT] |= USR1;
gpio1[GPIO_SETDATAOUT] |= USR2;
gpio1[GPIO_SETDATAOUT] |= USR3;
delay(brightness);
gpio1[GPIO_CLEARDATAOUT] |= USR1;
gpio1[GPIO_CLEARDATAOUT] |= USR2;
gpio1[GPIO_CLEARDATAOUT] |= USR3;
delay(100 - brightness);
}
void breath(int speed) {
static int dutyCount = 0;
static int currentLevel = 0;
static int dir = 0;
int needChange = 0;
dutyCount++;
if(dutyCount >= speed) {
needChange = 1;
dutyCount = 0;
}
if(needChange) {
if(dir) {
currentLevel--;
if(currentLevel == 0) {
dir = 0;
}
} else {
currentLevel++;
if(currentLevel == 100) {
dir = 1;
}
}
}
setBrightness(currentLevel);
return;
}
辅助实现
由于实现是直接基于例程实现的,因此需要按照例程架构实现两个文件,一个是setup.sh,可以理解为PRU对应的外围硬件配置。另一个是Makefile,可以认为是编译并运行PRU程序的执行配置。
setup.sh
#!/bin/bash
#
export TARGET=led_pwm.pru0
echo TARGET=$TARGET
# Configure the PRU pins based on which Beagle is running
machine=$(awk '{print $NF}' /proc/device-tree/model)
echo -n $machine
if [ $machine = "Black" ]; then
echo " Found"
config-pin P9_27 pruin
config-pin -q P9_27
fi
Makefile
include /var/lib/cloud9/common/Makefile
效果验证
在工程目录中输入以下命令并执行
source setup.sh
make
总结
经过此次活动,我大致掌握了如何快速入门PRU的方法,后续项目中若遇到有使用PRU的需求,可以快速上手并给出产出。另外,感觉狗板的设计思路感觉和arduino有些相像,偏向于将某些对于门外汉而言比较复杂的内容简单化,以达到加快项目落地的效果。