Funpack3-5 基于BeagleBone Black PRU的呼吸灯控制设计
该项目使用了BeagleBone® Black的可编程实时单元(PRU),实现了呼吸灯的设计,它的主要功能为:通过按键调节呼吸灯闪烁的速度。
标签
Funpack活动
呼吸灯
FunPack3-5
PRU
BeagleBone Black
TetraPak
更新2025-01-13
10

项目背景

很高兴参加funpack第三季第五期的活动,本次活动的开发板是BeagleBone® Black

功能介绍

任务描述

  • 使用PRU控制一个LED实现呼吸灯
  • 使用一个按键按下后切换呼吸灯闪烁的速度

硬件介绍

BeagleBone® Black是一种微型计算机,非常适合学习和制作电子原型,板卡处理器选用的是 TI 公司 AM3358 芯片, 处理器集成了高达 1GHz 的 ARM Cortex™ A8 内核,板载512MB DDR3L内存,4GB eMMC闪存,TPS65217C电源管理,支持JTAG调试,miniUSB和直流电源供电。它配备了丰富的外设,使用户能够体验处理器的强大功能,还提供了许多接口,用于开发拓展的电路板,例如,提供了USB 2.0主/客户端,10/100M以太网,UART串行端口,HDMI、LCD接口,46针扩展接口,配备4个用户LED。

设计思路

主要困难

从任务描述中可以看出,实现该任务有两个核心功能:

  1. 构建基础呼吸灯模块
  2. 外部按键触发,调整呼吸灯频率

解决思路

BeagleBone® Black既有运行 Linux 的 ARM 处理器,又有可编程实时单元(PRU)。PRU 具有 32 位内核,独立于 ARM 处理器运行,因此可以对其进行编程,使其对输入做出快速响应,并产生非常精确的定时输出。一般情况下,完成呼吸灯的操作是在Linux系统上编程的,但是为了熟悉PRU的操作,本次呼吸灯代码将利用PRU完成编程。

可编程实时单元(Programmable Real-time Unit SubSystem,PRUSS),是Cotex A8内核中的一个子系统,它可运行在1/2CPU时钟频率下,具有本地的指令和数据RAM,并可寻址访问整个片上系统资源。PRU模块接口是由两个内部寄存器R30和R31构成。R31为PRU的通用输入端(GPI)中断控制器(INTC)提供接口。通过读取R31的值便可获得GPI引脚以及INTC的状态信息。写R31则可为PRU系统产生事件信息。R30为PRU模块的通用输出端(GPO)的状态接口。

对于当前BeagleBone® Black系统版本,可能对某些IO口没有很好的支持,查看配置端口映射向量表的头文件,选择端口 P9_27 作为PRU控制LED的输出端口,选择端口 P9_30 作为按键的输入端口


核心功能一

呼吸灯的原理很简单,一般来说,呼吸灯是采用 PWM (脉冲宽度调制)的方式,在固定的频率下,通过调整占空比来控制 LED 灯亮度的变化,即在同样小时间段内,小灯亮的时间依次增加到最大后再依次减小,从而实现渐亮到渐灭的“呼吸”效果。

但是由于PRU没有PWM的控制功能,使用IO口模拟PWM,控制IO口输出电平翻转的时长即可。具体来说,选择固定的频率,调节占空比,在同一频率下占空比越高,led越亮,反之led越暗。实现方法是使用一个占空比变量,逐步增加或减少这个变量的值。LED灯的亮度由占空比控制,当占空比达到最大值时,亮度开始逐渐减小;当占空比降到最小值时,亮度又逐渐增加。

核心功能二

呼吸灯控制部分主要用于检测按钮的按下事件,并通过改变过改变亮度变化的延迟时间来调节呼吸灯的速度。如果检测到按钮被按下,延长灯亮和灯灭之间的时间,从而使呼吸灯的频率变慢。当延迟时间达到一定上限后会被重置为初始值。

软件流程图

功能展示

核心配置及代码片段

根据官方文档配置好系统后,下载PRU示例代码,本项目功能将在示例代码的基础上实现。

cd /opt/source
git clone https://git.beagleboard.org/beagleboard/pru-cookbook-code
cd pru-cookbook-code
sudo ./install.sh

查看BeagleBone的引脚复用系统

debian@BeagleBone:/opt/source/pru-cookbook-code/02start$ sudo ls /sys/devices/platform/ocp/
40300000.sram 4a000000.interconnect ocp:P8_09_pinmux ocp:P8_19_pinmux ocp:P9_19_pinmux ocp:P9_42_pinmux
44c00000.interconnect 4b144400.interconnect ocp:P8_10_pinmux ocp:P8_26_pinmux ocp:P9_20_pinmux ocp:P9_91_pinmux
47400000.target-module 4c000000.emif ocp:P8_11_pinmux ocp:P9_11_pinmux ocp:P9_21_pinmux ocp:P9_92_pinmux
478102fc.target-module 53100100.target-module ocp:P8_12_pinmux ocp:P9_12_pinmux ocp:P9_22_pinmux ocp:cape-universal
47c00000.interconnect 53500080.target-module ocp:P8_13_pinmux ocp:P9_13_pinmux ocp:P9_23_pinmux of_node
48000000.interconnect 5600fe00.target-module ocp:P8_14_pinmux ocp:P9_14_pinmux ocp:P9_24_pinmux power
49000000.target-module driver_override ocp:P8_15_pinmux ocp:P9_15_pinmux ocp:P9_26_pinmux subsystem
49800000.target-module modalias ocp:P8_16_pinmux ocp:P9_16_pinmux ocp:P9_27_pinmux supplier:platform:fixedregulator0
49900000.target-module ocp:P8_07_pinmux ocp:P8_17_pinmux ocp:P9_17_pinmux ocp:P9_30_pinmux uevent
49a00000.target-module ocp:P8_08_pinmux ocp:P8_18_pinmux ocp:P9_18_pinmux ocp:P9_41_pinmux

查看pru-cookbook-code/common/prugpio.h中的配置

// R30 output bits on pru0
#define P9_31   (1<<0)
#define P9_29   (1<<1)
#define P9_30   (1<<2)
#define P9_28   (1<<3)
#define P9_92   (1<<4)
#define P9_27   (1<<5)
#define P9_91   (1<<6)
#define P9_25   (1<<7)

综上,查看引脚复用端口和PRU端口重合部分,选择端口 P9_27 作为PRU控制LED的输出端口,连接LED灯,选择端口 P9_30 作为按键的输入端口,连接按键。


配置引脚

config-pin P9_27 pruout
config-pin P9_30 pruin

呼吸灯控制代码实现breath_led.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;

#define BREATH_GPIO_B P9_27
#define BUTTON_GPIO P9_30

#define PWM_MAX 500  // PWM 的最大周期
#define STEP 2        // 亮度增加/减少的步长

// 初始的 DELAY_MULTIPLIER 值
unsigned int DELAY_MULTIPLIER = 20;
// 按钮状态
int last_button_state = 0;  // 默认为低电平

// 自定义延时函数
void custom_delay(unsigned int cycles) {
    volatile unsigned int i;
    for (i = 0; i < cycles; i++) {
        __delay_cycles(1);  // PRU 自带的短延时
    }
}

// 软件 PWM 呼吸灯函数
void software_pwm_breathing(int duty_cycle) {
    __R30 |= BREATH_GPIO_B;  // 输出高电平
    // __R30 &= ~BREATH_GPIO_R; // 输出低电平
    custom_delay(duty_cycle * DELAY_MULTIPLIER);  // 按占空比延时
    // __R30 |= BREATH_GPIO_R;  // 输出高电平
    __R30 &= ~BREATH_GPIO_B; // 输出低电平
    custom_delay((PWM_MAX - duty_cycle) * DELAY_MULTIPLIER); // 剩余时间延时
}


// 检查按钮按下和释放状态
void check_button() {
    int button_state = __R31 & BUTTON_GPIO;  // 读取按钮状态
   
    // 检查按键是否从释放变为按下 (上升沿检测)
    if ((__R31 & BUTTON_GPIO) && !last_button_state) {
        // 每次按下按钮后,增加 DELAY_MULTIPLIER 的值
        DELAY_MULTIPLIER += 30;
        if (DELAY_MULTIPLIER > 140) {
            DELAY_MULTIPLIER = 20;  // 超过 140 后重置为 20
        }
    }
    // 记录当前按钮状态,便于下次检测
    last_button_state = button_state;
}

void main(void) {
    int duty_cycle = 0;
    int increasing = 1;  // 亮度增加标志

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

    while (1) {
        // 调整占空比,模拟呼吸效果
        software_pwm_breathing(duty_cycle);
        // 检查按钮状态并更新 DELAY_MULTIPLIER
        check_button();
        // 根据当前状态增加或减少占空比
        if (increasing) {
            duty_cycle += STEP;
            if (duty_cycle >= PWM_MAX) {
                increasing = 0;  // 到达最大亮度,开始变暗
            }
        } else {
            duty_cycle -= STEP;
            if (duty_cycle <= 0) {
                increasing = 1;  // 到达最暗,开始变亮
            }
        }
    }
}


运行程序

make TARGET=breath_led.pru0

实现效果

具体演示效果请观看演示视频

总结

本项目依托BeagleBone® Black平台,完成呼吸灯频率调节任务。在此过程中阅读BeagleBone® Black使用手册数据手册和PRU驱动代码,分别实现呼吸灯闪烁和外部按键控制呼吸灯功能,最终顺利完成任务。




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