项目介绍
这次项目基于 BeagleBone Black ,通过其 PRU 控制 LED 的闪烁,同时通过 Web 服务提供一个网页给用户控制 LED 闪烁的开关。
BeagleBone Black 是一个低成本、高扩展性、社区支持的开发平台,支持运行 Linux 系统,它配备了最少的外围设备,让用户可以体验处理器的强大功能,还提供了多种外设接口,可以方便扩展和开发。
PRU(Programmable Real-Time Units)是独立在 ARM 核心之后的 32位微处理器,可以单周期地访问内存和外设,可以用来实现复杂的实时控制功能。
环境搭建
这次开发使用了 ArchLinux,没有官方的支持,需要正确配置 PRU 编译器和 ARM 交叉编译器。
Ti 提供了官方的 PRU 编译器,AUR 上有现成的打包脚本,使用下面命令安装:
paru -S ti-pru-cgt
同时需要 Ti 提供的 pru-software-support-package,可以在 Ti 官网下载。
Web 服务使用 Rust 开发,需要用一个 ARM 的交叉编译器进行链接,这里使用了 musl.cc 提供的工具链:
wget https://musl.cc/armv7l-linux-musleabihf-cross.tgz
tar xf armv7l-linux-musleabihf-cross.tgz
fish_add_path ~/armv7l-linux-musleabihf-cross/bin
配置 Cargo 使用上面的工具链进行静态链接,在 cargo 配置文件添加:
[target.armv7-unknown-linux-musleabihf]
linker = "armv7l-linux-musleabihf-gcc"
rustflags = ["-Ctarget-feature=+crt-static"]
通过 rustup 添加 armv7-unknown-linux-musleabihf target:
rustup target add armv7-unknown-linux-musleabihf
程序实现
设计框图
实现分为两个部分:
- PRU 程序:用来实现 LED 闪烁
- Web 服务:提供网页和接口给用户控制 PRU 程序,运行在 Linux 系统上面,使用 Rust 开发
PRU 控制 LED 闪烁
PRU 控制参考了 PRU Cookbook 提供例子。首先需要配置编译器和软件支持包的路径。可以在 Makefile 里配置:
PRU_CGT:=/opt/ti-pru-cgt
PRU_SUPPORT:=/home/momo/beagle/pru-software-support-package
因为我是在笔记本上交叉编译,所有 PRU Cookbook 里的检测型号的功能没有用,可以直接在 Makefile 里写上配置:
CHIP=am335x
CHIP_REV=am335x
PRU_DIR=/sys/class/remoteproc/remoteproc1
PRUN=0
PROC=pru
配置完成后,可以直接进行编译:
make TARGET=blink.pru0
源代码基于 hello.pru0.c 稍作修改,改成无限循环和加快闪烁频率:
#include <stdint.h>
#include <pru_cfg.h>
#include "resource_table_empty.h"
#include "prugpio.h"
volatile register unsigned int __R30;
volatile register unsigned int __R31;
void main(void) {
int i;
uint32_t *gpio1 = (uint32_t *)GPIO1;
/* Clear SYSCFG[STANDBY_INIT] to enable OCP master port */
CT_CFG.SYSCFG_bit.STANDBY_INIT = 0;
for(;;) { // 改成无限循环
gpio1[GPIO_SETDATAOUT] = USR3; // The the USR3 LED on
__delay_cycles(500000000/10); // Wait 1/4 second
gpio1[GPIO_CLEARDATAOUT] = USR3;
__delay_cycles(500000000/10);
}
__halt();
}
PRU Cookbook 还提供了一个 write_init_pins.sh 脚本,可以读取 init_pins 段然后配置引脚。我们直接在 BBB 上执行命令就行,不需要用 write_init_pins.sh
把编译得到的 blink.pru0.out :
scp blink.pru0.out debian@192.168.7.2:/lib/firmware/am335x-pru0-fw
然后在 BBB 上执行下面的命令开启 PRU:
# 先关掉全部的 LED 触发,避免其他模块影响 LED效果
echo none > /sys/class/leds/beaglebone\:green\:usr0/trigger
echo none > /sys/class/leds/beaglebone\:green\:usr1/trigger
echo none > /sys/class/leds/beaglebone\:green\:usr2/trigger
echo none > /sys/class/leds/beaglebone\:green\:usr3/trigger
# 开启 PRU
echo start > /sys/class/remoteproc/remoteproc1/state
这样就可以看到有一个 LED 在闪了。
关闭 PRU 也是写文件:
echo stop > /sys/class/remoteproc/remoteproc1/state
Web 服务控制 PRU
上面已经实现了 LED 的闪烁和开关,再通过一个 Web 服务提供了一个简单的页面给用户,不需要 ssh 执行命令,更加易用。
Web 服务使用 Rust 开发,提供了 3 个路由:
- /blink-on 打开 LED, 开始闪烁
- /blink-off 关闭 LED
- / 首页,返回一个 HTML 页面
use astra::{Body, Request, Response, ResponseBuilder, Server};
fn main() {
Server::bind("0.0.0.0:3000")
.max_workers(2)
.serve(|req: Request, _info| -> Response {
let path = req.uri().path();
match path {
"/blink-on" => blink_on(),
"/blink-off" => blink_off(),
_ => index(),
}
})
.expect("serve failed");
}
这里使用 astra 框架,主要是简单,编译快,也不需要异步。
下面是三个路由的实现:
// 开始 LED 闪烁
fn blink_on() -> Response {
let mut file = File::options()
.append(true)
.open("/sys/class/remoteproc/remoteproc1/state")
.unwrap();
file.write(b"start").unwrap();
Response::new(Body::new("blink on"))
}
// 关闭 LED
fn blink_off() -> Response {
let mut file = File::options()
.write(true)
.open("/sys/class/remoteproc/remoteproc1/state")
.unwrap();
file.write(b"stop").unwrap();
Response::new(Body::new("blink off"))
}
// 首页
fn index() -> Response {
let body = std::fs::read_to_string("index.html").unwrap();
ResponseBuilder::new()
.header("Content-Type", "text/html")
.body(Body::new(body))
.unwrap()
}
可以看到,开关 LED 其实是上一节一样,都是把 start/stop 写到 /sys/class/remoteproc/remoteproc1/state 文件。首页返回的 HTML 有两个按钮,点击的时候调用对应的接口。
document.getElementById("blinkon").addEventListener('click', () => {
fetch("/blink-on")
})
document.getElementById("blinkoff").addEventListener('click', () => {
fetch("/blink-off")
}
使用 cargo 编译,然后把文件复制到板上:
cargo build --target armv7-unknown-linux-musleabihf --release
scp ./target/armv7-unknown-linux-musleabihf/release/funpack3-5 debian@192.168.7.2:/home/debian
scp index.html debian@192.168.7.2:/home/debian
最后在板上执行 funpack3-5 即可。
总结
BeagleBone Black 已经发布十多年了,依旧是一个非常有趣的平台。在这次项目里接触到了 ARM Linux 的开发(主要是交叉编译 Rust 程序)。PRU 使用还不够深入,可以再看看 PRU Cookbook,以后做些更有趣的项目。