Funpack3-5 BeagleBone® Black 点灯!使用PRU控制一个LED实现呼吸灯
该项目使用了BeagleBone® Black,实现了使用PRU控制一个LED实现呼吸灯的设计,它的主要功能为:采集一个红外传感器的引脚判断是否存在障碍物,并以此信号作为开关信号,来调整呼吸灯的频率。。
标签
点灯
FunPack3-5
BeagleBone® Black
Dearjim
更新2025-01-14
15

我将为大家详细阐述我在 BBB 学习过程中的经历与感悟,以及在相关任务开发工作方面的具体情况介绍。

下面分成几个部分:

一、任务介绍和完成代码


二、学习笔记

  • 访问BeagleBone
  • 让BBB能否上网(以后可能会用到)
  • DEMO代码学习


任务介绍

image.png

在BBB的PRU中进行编程,通过采集红外距离传感器的信号引脚,来调整LED的呼吸PWM调节速度。

当没有障碍物时,传感器的信号引脚处于拉高状态,PRU中调节LED的PWM速度设置为低速,呼吸频率为慢速。

当有障碍物时,传感器的信号引脚处于拉低状态,PRU中的调节LED的PWM速度设置为高速。呼吸频率为快速。

image.png

PWM 基本概念


PWM 即脉冲宽度调制(Pulse Width Modulation),是一种对模拟信号电平进行数字编码的方法。简单来说,就是通过控制一系列脉冲的宽度(占空比)来等效地获得所需要的模拟信号输出。


在 PWM 信号中,有两个重要的参数:


  • 周期(T):是指 PWM 信号完成一个完整的循环所需要的时间。例如,如果一个 PWM 信号的周期是 10ms,那么这个信号每 10ms 就会重复一次它的变化模式。
  • 占空比(D):是指在一个周期内,脉冲信号处于高电平的时间(t)与周期(T)的比值,即 D = t/T。占空比通常用百分比来表示。例如,占空比为 50% 意味着脉冲信号在一个周期内有一半的时间是高电平,另一半时间是低电平。


PWM 工作原理类比


可以把 PWM 想象成一个水龙头控制水流的过程。周期就像是你打开水龙头和关闭水龙头这个动作循环一次所用的时间。而占空比就像是在这个循环时间里,水龙头打开的时间占总时间的比例。如果占空比为 100%,那就相当于水龙头一直开着;如果占空比为 0%,水龙头就一直关着;如果占空比是 50%,则水龙头有一半的时间在出水,另一半时间关闭,平均下来的水流大小就介于一直开着和一直关闭之间。


在电子电路中,PWM 信号通常用于控制功率器件(如功率晶体管)的导通和关断。例如,当用 PWM 信号控制一个直流电机的转速时,较高的占空比会使电机两端的平均电压升高,从而使电机转速加快;相反,较低的占空比会使电机两端的平均电压降低,电机转速减慢。

image.png

完成代码

////////////////////////////////////////
// blinkInternalLED.pru0.c
// Blinks two of the bulit in USR LEDs using the PRU
// Wiring: None
// Setup: Turn off the USR LEDs triggers
// See: prugpio.h to see to which ports the USR LEDs are attached
// PRU: Any
////////////////////////////////////////
#include <stdint.h>
#include <pru_cfg.h>
#include "resource_table_empty.h"
#include "prugpio.h"
#include <stdint.h>
#include <pru_cfg.h>
#include <pru_ctrl.h>
#include <stddef.h>
#include <rsc_types.h>
#include "resource_table_empty.h"
#include "prugpio.h"
volatile register unsigned int __R30;
volatile register unsigned int __R31;
#include <stdio.h>
#include <stdlib.h>

#define DATAIN 0x138/4

void main(void) {
// Points to the two GPIO ports that are used
uint32_t *gpio1 = (uint32_t *)GPIO1;

//uint32_t *gpio3 = (uint32_t *)GPIO3;
int PWM=0;
int i;
int step=0;
int T =20; //Breath speed
int Breath =1;
/* Clear SYSCFG[STANDBY_INIT] to enable OCP master port */
CT_CFG.SYSCFG_bit.STANDBY_INIT = 0; while(1) {







if( gpio1[DATAIN]&P9_14) //如果P19_14高电位

{



T=50;//每隔50*0.1=5ms 调整一次PWM

}

else

{

T=10;//每隔10*0.1=1ms 调整一次PWM

}

if(step % T==0) //呼吸的周期为 200* T =1秒 或 200毫秒

{

PWM=PWM+Breath;



if(PWM==100)

{

Breath=-1;

}

if(PWM==0)

{

Breath =1;

}

}





//gpio1[GPIO_SETDATAOUT] = USR2; // Turn USR2 LED off

gpio1[GPIO_SETDATAOUT] = 0x00E00000; // Turn the USR1 LED on

for(i=0 ;i<PWM;i++)

{



__delay_cycles(1000); //500000000 means 2.5s ,this is 0.0001s 0.1ms

}

gpio1[GPIO_CLEARDATAOUT] = 0x00E00000;

//gpio1[GPIO_CLEARDATAOUT] = USR1; // Off

//gpio1[GPIO_SETDATAOUT] = USR2; // On

for(i=0 ;i<100-PWM;i++)

{



__delay_cycles(1000); //500000000 means 2.5s,this is 0.0001s

}



step++;

}

__halt();

}

//



// Turns off triggers

#pragma DATA_SECTION(init_pins, ".init_pins")

#pragma RETAIN(init_pins)

const char init_pins[] =

"/sys/class/leds/beaglebone:green:usr1/trigger\0none\0" \

"/sys/class/leds/beaglebone:green:usr2/trigger\0none\0" \

"/sys/class/gpio/gpio50/direction\0in\0" \

"/sys/devices/platform/ocp/ocp:P9_14_pinmux/state\0gpio\0" \



"\0\0";




访问BeagleBone

1.使用USB连接 BeagleBone
2.电脑识别出U盘,打开U盘后找到START.htm
查看STEP2:Enable a network connection
IP Address Connection Type Operating System(s) Status
192.168.7.2 USB Windows
192.168.6.2 USB Mac OS X, Linux
192.168.8.1 WiFi all
beaglebone.local all mDNS enabled
beaglebone-2.local all mDNS enabled
3. 系统会识别出2个网络,我这里使用的是linux电脑,访问beaglebone.local,需要LinuxFoundation以太网连接,并获取到一个192.168.6.1的IP地址。

4.打开浏览器输入192.168.6.2 进入cloud9的IDE后 点击左上角BeagleBone-examples,可以查看到demos

BBB 通过USB共享网络

BBB通过USB和LINUX电脑连接后,首先需要在电脑端设置网络共享 ,这里电脑识别出BBB 的网络一共有两个enx182c65030efa 和enx182c65030efc ,分别对应BBB中的USB0 和USB1

# 启用 IP 转发
sudo sysctl -w net.ipv4.ip_forward=1

# 启用 NAT 转发
sudo iptables -t nat -A POSTROUTING -o wlp0s20f3 -j MASQUERADE

# 允许相关和已建立的连接
sudo iptables -A FORWARD -i wlp0s20f3 -o enx182c65030efa -m state --state RELATED,ESTABLISHED -j ACCEPT

# 允许从 enx182c65030efa 到 wlp0s20f3 的流量
sudo iptables -A FORWARD -i enx182c65030efa -o wlp0s20f3 -j ACCEPT

# 允许相关和已建立的连接
sudo iptables -A FORWARD -i wlp0s20f3 -o enx182c65030efc -m state --state RELATED,ESTABLISHED -j ACCEPT

# 允许从 enx182c65030efc 到 wlp0s20f3 的流量
sudo iptables -A FORWARD -i enx182c65030efc -o wlp0s20f3 -j ACCEPT

然后在 route -n 发现BBB中没有设置默认路由网卡

debian@beaglebone:/etc$ route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
192.168.6.0 0.0.0.0 255.255.255.0 U 0 0 0 usb1
192.168.7.0 0.0.0.0 255.255.255.0 U 0 0 0 usb0
sudo route add default gw 192.168.7.1 dev usb0
cat /etc/resolv.conf 如果文件没有就touch /etc/resolv.conf

nameserver 8.8.8.8

sudo route add default gw 192.168.7.1 dev usb0 #编辑 /etc/dhcpcd.conf 文件

添加静态网关配置: 在文件末尾添加以下内容:

interface usb0
static routers=192.168.7.1

demo代码学习

autorun

Place files in here that you want to run whenever the Beagle starts.
自动启动的文件放在这里

BeagleBone

Here you’ll find demos for all of the BeagleBones. The PocketBeagle demos are in another folder.
当前文件夹包含了所有 BeagleBone 的演示示例,而 PocketBeagle 的演示示例则存放在另一个文件夹中。

BeagleBone/AI

Demos for the BeagleBone AI.

Directory

Learn how to use the ...

tidl

built-in embedded machine learning accelerators.

pru

on-board ultra-low latency PRU microcontrollers.

c66

on-board digital signal processors.

/BeagleBone/AI/pru/bitflip.arm.c

这段代码展示了如何使用共享内存来实现 ARM 和 PRU 之间的通信。
共享内存是一种进程间通信(IPC)机制,它允许多个进程共享同一块内存区域。在这种情况下,ARM 和 PRU 通过共享内存进行数据交换。

  • 共享内存映射
    ARM 通过 mmap 系统调用将一块物理内存映射到自己的虚拟地址空间。
    PRU 也可以访问这块物理内存,因为 PRU 也有直接访问物理内存的能力。
  • 内存访问
    ARM 和 PRU 可以分别读写这块共享内存区域,从而实现数据的交换。
shared_dataram = mmap(NULL,
16 + PRUSS_SHARED_RAM_OFFSET, // 抓取 16 字节的共享内存,必须分配偏移量为 0
PROT_READ | PROT_WRITE,
MAP_SHARED,
mem_dev,
0);
  • 使用 mmap 将共享内存区域映射到 ARM 的虚拟地址空间。
  • PRUSS_SHARED_RAM_OFFSET 定义了共享内存的偏移量,通常为 0x10000。
shared_dataram += (PRUSS_SHARED_RAM_OFFSET/4);
printf("shared_dataram = %p\n", shared_dataram);
j = *shared_dataram;
printf("Read 0x%08x\n", j);
for (i = 0; i < 10; i++) {
printf("Writing 0x%08x\n", i);
*shared_dataram = i;
usleep(1);
j = *shared_dataram;
printf("Read 0x%08x (%s, Mask=0x%08x)\n", j, j == i ? "not flipped" : "flipped!", j ^ i);
}
  • 循环写入共享内存中的值,并立即读回,验证是否被修改。

/BeagleBone/AI/pru/bitflip.pru1_1.c

代码展示了如何在 PRU(Programmable Real-time Unit)上使用共享内存与 ARM 进行通信。以下是代码的详细解释和分析:

#define SHARED_RAM_ADDRESS 0x10000
unsigned int volatile __far * const SHARED_RAM = (unsigned int *) (SHARED_RAM_ADDRESS);
  • SHARED_RAM_ADDRESS 定义了共享内存的地址。
  • SHARED_RAM 是一个指向共享内存的指针,volatile 保证每次读写都是从实际内存中读取或写入。
void main(void) {
unsigned int value = 0;

/* Set the SHARED_RAM value to 0 */
*SHARED_RAM = 0;

while(1) {
/* Look for the ARM to modify the SHARED_RAM value */
if(value != *SHARED_RAM) {
/* Flip every other bit and write the value back */
value = *SHARED_RAM;
value ^= 0xAAAAAAAA;
*SHARED_RAM = value;
}
}
}
  • 初始化 value 为 0。
  • 将共享内存的值设置为 0。
  • 进入无限循环:
    • 检查共享内存的值是否发生变化。
    • 如果发生变化,则翻转每两个位,并将新值写回共享内存。
**总结**
共享内存是一种进程间通信(IPC)机制,它允许多个进程共享同一块内存区域。
PRU中使用 volatile 声明指针变量,可以保证每次读写都是从实际内存中读取或写入。
volatile 关键字可以保证对变量的读写都是直接从内存中读取或写入,而不会使用寄存器中的缓存值。
ARM中使用 mmap 函数将共享内存映射到自己的虚拟地址空间,并使用指针访问共享内存中的值。

/BeagleBone/AI/pru/blinkExternalLED.pru1_1.c

寄存器定义:
定义了两个寄存器变量 __R30 和 __R31,但它们在代码中未被使用。
主函数实现:
初始化 GPIO6 寄存器地址,用于控制 P9_25 引脚。
清除 SYSCFG[STANDBY_INIT] 位以启用 OCP 主端口。
在无限循环中,通过设置和清除 GPIO 输出值来控制 LED 闪烁,并使用 __delay_cycles 函数实现延时。
初始化引脚方向:
使用 init_pins 数组通过系统文件接口将 P9_25 设置为输出模式。

void main(void) {
// Points to the GPIO port that is used
uint32_t *gpio6 = (uint32_t *)GPIO6;

/* Clear SYSCFG[STANDBY_INIT] to enable OCP master port */
CT_CFG.SYSCFG_bit.STANDBY_INIT = 0;

while(1) {
gpio6[GPIO_SETDATAOUT] = P9_25; // Turn the USR1 LED on
__delay_cycles(500000000/5); // Wait 1/2 second
gpio6[GPIO_CLEARDATAOUT] = P9_25; // Off
__delay_cycles(500000000/5);
}
__halt();
}

初始化 GPIO6 端口。
清除 SYSCFG[STANDBY_INIT] 以启用 OCP 主端口。
进入无限循环:
设置 GPIO6 的输出以打开 LED。
延迟 1/2 秒。
清除 GPIO6 的输出以关闭 LED。
再次延迟 1/2 秒。

#pragma DATA_SECTION(init_pins, ".init_pins")
#pragma RETAIN(init_pins)
const char init_pins[] =
"/sys/class/gpio/export\0 177\0" \
"/sys/class/gpio/gpio177/direction\0out\0" \
"\0\0";

使用 #pragma 指令将初始化字符串放置在 .init_pins 段中。
初始化字符串用于设置 GPIO 引脚的方向为输出。

uint32_t *gpio6 = (uint32_t *)GPIO6;
  • GPIO6 是一个指向 GPIO6 端口的指针。
  • gpio6 指向 GPIO6 端口的基地址。
CT_CFG.SYSCFG_bit.STANDBY_INIT = 0;
  • 清除 SYSCFG[STANDBY_INIT] 位,使能 OCP 主端口。

/BeagleBone/AI/pru/blinkInternalLED.pru1_1.c

while(1) {
gpio5[GPIO_SETDATAOUT] = USR1; // Turn the USR1 LED on
gpio3[GPIO_CLEARDATAOUT] = USR2; // Turn USR2 LED off

__delay_cycles(500000000/5); // Wait 1/2 second

gpio5[GPIO_CLEARDATAOUT] = USR1; // Off
gpio3[GPIO_SETDATAOUT] = USR2; // On

__delay_cycles(500000000/5);
}
  • 在无限循环中,设置 GPIO5 和 GPIO3 的输出值以控制 LED 闪烁。
  • 每次循环,设置 GPIO5 的输出值以打开 USR1 LED,清除 GPIO3 的输出值以关闭 USR2 LED。

/BeagleBone/AI/pru/blinkR30.pru1_1.c

////////////////////////////////////////
// blinkR30.c
// Blinks LEDs wired to P9_25 (and others) by writing register R30 on the PRU
// Wiring: P9_16 connects to the plus lead of an LED. The negative lead of the
// LED goes to a 220 Ohm resistor. The other lead of the resistor goes
// to ground.
// Setup: None
// See: prugpio.h to see which pins attach to R30
// PRU: pru1_1
////////////////////////////////////////
#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) {
// Select which pins to toggle. These are all on pru1_1
uint32_t gpio = P9_16 | P8_15 | P8_16 | P8_26;

while(1) {
__R30 |= gpio; // Set the GPIO pin to 1
__delay_cycles(500000000/5); // Wait 1/2 second
__R30 &= ~gpio; // Clear the GPIO pin
__delay_cycles(500000000/5);
}
__halt();
}

// No need to turn off triggers or set pin direction

/BeagleBone/AI/pru/inputR31.pru1_1.c

////////////////////////////////////////
// inputR31.c
// Reads input in P8_19 via the R31 register and blinks the USR3 LED
// Wiring: Wire a switch between P8_19 and 3.3V (P9_3 or P9_4)
// Setup: None
// See: prugpio.h to see which pins attach to R31
// PRU: pru1_1
////////////////////////////////////////
#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) {
uint32_t *gpio3 = (uint32_t *)GPIO3;

/* Clear SYSCFG[STANDBY_INIT] to enable OCP master port */
CT_CFG.SYSCFG_bit.STANDBY_INIT = 0;

while(1) {
if(__R31 & P8_19) {
gpio3[GPIO_SETDATAOUT] = USR3; // Turn off LED
} else
gpio3[GPIO_CLEARDATAOUT] = USR3; // Turn on LED
}
__halt();
}

// Turns off triggers
#pragma DATA_SECTION(init_pins, ".init_pins")
#pragma RETAIN(init_pins)
const char init_pins[] =
"/sys/class/leds/beaglebone:green:usr3/trigger\0none\0" \
"\0\0";

BeagleBone/Black

Demos for the Black and Green families of Bones. This includes the wirelss versions too.
BeagleBone/Blue Demos that run on the EduMIP balancing robot.

This folder contains demos of the BeagleBone's PRU (Programable Real-time Unit)
The PRUs have 32-bit cores which run independently of the ARM processor,
therefore they can be programmed to respond quickly to inputs and produce
very precisely timed outputs.

There are many projects that use the PRU
(http://processors.wiki.ti.com/index.php/PRU_Projects)
to do things that can’t be done with just a SBC or just a microcontroller.

Here we demonstrate:

blinkinternalledc //闪烁一些内置USR led
blinkexternalledc //连接外部LED并闪烁
blinkR31.c //使用更快(50MHz)的方法与外部LED闪烁
neopixelStatic.c //连接NeoPixel (WS2812) led并将其设置为颜色
neopixelDynamic.c //相同的led,但移动显示
neopixelRpmsg.c //使用rpmsg控制来自ARM的NeoPixels进行消息传递
neopixelRainbow.py //在ARM上运行,并将彩虹图案写入PRU
inputR31.c //通过快速R31寄存器读取输入引脚
bitflip.c //显示如何在ARM和PRU之间共享内存
Ring .c //执行“Ring测试”以查看I/O切换的速度

The Black has two PRUs, pru0 and pru1.

The filename tells which PRU to run on. For example blinkInternalLED.pru0.c will
run on pru0. These demos must all run on pru0, except blinkInternalLED, which
can run on ether of the PRUs.

/BeagleBone/Black/pru/blinkInternalLED.pru0.c

////////////////////////////////////////
// blinkInternalLED.pru0.c
// Blinks two of the bulit in USR LEDs using the PRU
// Wiring: None
// Setup: Turn off the USR LEDs triggers
// See: prugpio.h to see to which ports the USR LEDs are attached
// PRU: Any
////////////////////////////////////////
#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) {
// Points to the two GPIO ports that are used
uint32_t *gpio1 = (uint32_t *)GPIO1;

/* Clear SYSCFG[STANDBY_INIT] to enable OCP master port */
CT_CFG.SYSCFG_bit.STANDBY_INIT = 0;

while(1) {
gpio1[GPIO_SETDATAOUT] = USR1; // Turn the USR1 LED on
gpio1[GPIO_CLEARDATAOUT] = USR2; // Turn USR2 LED off

__delay_cycles(500000000/20); // Wait 1/2 second

gpio1[GPIO_CLEARDATAOUT] = USR1; // Off
gpio1[GPIO_SETDATAOUT] = USR2; // On

__delay_cycles(500000000/20);
}
__halt();
}

// Turns off triggers
#pragma DATA_SECTION(init_pins, ".init_pins")
#pragma RETAIN(init_pins)
const char init_pins[] =
"/sys/class/leds/beaglebone:green:usr1/trigger\0none\0" \
"/sys/class/leds/beaglebone:green:usr2/trigger\0none\0" \
"\0\0";

总结

  • PRU 中设置GPIO 输入输出
#pragma DATA_SECTION(init_pins, ".init_pins")
#pragma RETAIN(init_pins)
const char init_pins[] =
"/sys/class/leds/beaglebone:green:usr1/trigger\0none\0" \
"/sys/class/leds/beaglebone:green:usr2/trigger\0none\0" \
"/sys/class/gpio/gpio50/direction\0in\0" \
"/sys/devices/platform/ocp/ocp:P9_14_pinmux/state\0gpio\0" \
"\0\0";
  • 寄存器访问direction设置
    uint32_t *gpio1 = (uint32_t *)GPIO1;
    uint32_t *gpio2 = (uint32_t *)GPIO2;
    uint32_t *gpio3 = (uint32_t *)GPIO3;
    对应的gpio计算方法,gpio2_10,就是2×32+10 = gpio74
    echo in > /sys/class/gpio/gpio50/direction
  • 寄存器访问设置
    gpio1[GPIO_SETDATAOUT] gpio1对应32个IO口,指定的bit输出高电压的配置
    gpio1[GPIO_DATAIN] gpio1对应32个IO口,IO状态读取
  • #define GPIO_DATAIN 0x138/4
    这里0x138在芯片手册中可以查询到, /4的原因是定义了uint32 *gpio1
  • 对应引脚地址
    P9_14,在BBB手册中可以查到引脚对应的gpio然后根据公式据算出对应的bit位
    对应的gpio计算方法,gpio2_10,就是2×32+10 = gpio74


附件下载
blinkInternalLED_pwm.pru0.c
团队介绍
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号