Funpack3-5-BeagleBoneBlack-基于PRU的按键控制LED呼吸灯
该项目使用了BeagleBone Black,实现了按键控制LED呼吸灯的设计,它的主要功能为:使用PRU控制一个LED实现呼吸灯,并且使用一个按键按下后切换闪烁的速度。
标签
嵌入式系统
Funpack活动
C语言
Beaglebone
tricker
更新2025-01-15
天津大学
6

1 项目需求

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


2 完成的功能及达到的性能

2.1LED可以实现呼吸灯的效果

7569995c46e13ea31a912a9f771dc49.jpg

LED灯的io接口为P9_31,按键的io接口为P9_25。使用了面包板和杜邦线来进行实验,原理图见下。

Schematic_New-Project_2024-12-30.png

实际测量中,发现LED的电阻不能太大,1K电阻下LED的亮度非常小,几乎看不出来呼吸灯的变化,所以使用到了330Ω的电阻。另外,VCC和GND由开发板上的P9_3和P9_2提供,VCC为3.3v。

有很多例程点亮LED灯是使用的IO口来驱动,个人不推荐这样做。虽然BBB板子IO口驱动的能力比一些开发板如51单片机而言强,但是难免会损伤IO口。个人习惯使用灌电流的方式,所以点灯的方式会与例程不同,在代码节会详解。


2.2呼吸灯可以设置不同的速度

我使用的是模拟pwm输出,PWM的周期为1ms。呼吸灯的最大时间和按键改变的步进精度都可以在程序中设置,之后将在代码节详解。视频中演示的是最大时间5s,步进精度1s的呼吸灯。

为了方便观察,当呼吸灯时间为0时,LED的状态是关闭。连续摁下按键超过最大值后,便会返回关闭状态。

5d476f9964ae2ae36409a0b1dbce7c9.jpg


2.3整体功能总结

当给PRU烧录程序时,LED处于关闭状态。摁下摁键后,LED灯开始闪烁,此时的闪烁速度最快。再摁下摁键后,如果不松开摁键,LED灯会保持关闭状态。松开摁键后,之前的LED呼吸灯会停止,执行下一个呼吸灯,此时的呼吸灯速度会变慢。继续摁下摁键会重复上述操作,直到到达呼吸灯的最慢速度。在此之后,再次摁下按键会返回一开始关闭的状态。

1735557195159.png


3 软件程序

软件程序分成.sh文件和.c文件,.sh文件用来配置IO口为输入输出,.c文件用来编写整体流程的代码。

3.1验证代码

官方的例程中有可以验证IO口输入输出正确的代码,在pru-cookbook-code/05blocks/input.pru0.c下,运行.sh文件

cd pru-cookbook-code/05blocks
source input_setup.sh​

此时会将P9_25设置为输入,P9_31设置为输出。

再运行.c文件

make​

将其分别连接到按键和LED上,便可以通过按键控制LED灯的亮灭。如果能够实现效果则证明IO口的输入输出没有问题。

3.2sh文件代码

可以直接使用上述例程中的.sh文件

#!/bin/bash
#
export TARGET=my_file.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_31 pruout
    config-pin -q P9_31
    config-pin P9_25 pruin
    config-pin -q P9_25
elif [ $machine = "Blue" ]; then
    echo " Found"
    pins=""
elif [ $machine = "PocketBeagle" ]; then
    echo " Found"
    config-pin P1_36 pruout
    config-pin -q P1_36
    config-pin P1_29 pruin
    config-pin -q P1_29
else
    echo " Not Found"
    pins=""
fi

如果需要自己设置IO口,则可以将P9_31和P9_25进行替换即可。

3.3c文件代码

主要是由几个函数组成

A.延迟函数

官方的库中只有延迟一个时钟周期的代码,我们可以自己写一个延迟1us的函数。PRU的频率是200MHZ,因此,1us即是200个时钟周期。

void delay_us(uint32_t us)
{
    uint32_t i;
    for(i = 0; i<us;i++)
    __delay_cycles(200);
}

B.按键检测代码

可以仿照例程中的按键进行编写,需要注意的是,按键检测需要在LED呼吸灯运行中进行扫描,如果在呼吸灯程序外边进行检测,那么只有当一个呼吸灯完成的时候才可以进行按键检测,这样检测时间会非常短,容易检测不到按键。但是在呼吸灯程序中进行按键检测就又会产生一个问题,就是检测太快容易检测到多个按键摁下。这种情况下我进行了下面描述程序效果:等待摁键被松开,然后再延迟10ms。这样就可以避免重复检测。

另外当按键被检测到后,时间寄存器precision_now会增加,到达最大值就会返回0。这也就是上面一节描述的效果。最大精度可以自己设置,我设置的5。

#define key_precision 5
void key_scan(void)
{
    if((__R31&sw) == sw)
    {
        while((__R31&sw) == sw)__R30 |= led;;
        delay_us(10000);
        precision_now ++;
        if(precision_now > key_precision)
            precision_now = 0;
        stop_flag = 1;
    }
}


C.PWM程序

PWM程序非常简单,就是在一个周期内,前面的时间让灯开,后面的时间让灯灭。我为了方便理解,使用到了百分比来进行控制。程序中的cycle_time 就是我设置的时间,其单位为us。

#define cycle_time 1000
void led_pwm(float percent)
{
    uint32_t time_on,time_off;
    time_on = cycle_time *percent;
    time_off = cycle_time - time_on;
    __R30 |= led;
    delay_us(time_on);
    __R30 &= ~led;
    delay_us(time_off);
}

D.呼吸灯程序

呼吸灯程序中,依然使用百分比来作为输入。由于呼吸灯有两段,分别是从亮到暗,从暗到亮,所以程序中的for循环有两段。另外,程序中最需要注意的是stop_flag这个标志位。这个标志位用来结束当前的呼吸灯程序。当发现呼吸灯的速度改变的时候,这个标志位就会被置位,这样呼吸灯程序就会退出。为了防止其影响下一个呼吸灯程序,就需要在最后将其归零。最后是按键扫描程序,他不仅仅需要在呼吸灯的运行中进行扫描,也需要在两个for循环前面进行扫描,这是因为,当速度为0,即不执行呼吸灯程序时,for循环就不会执行,所以需要在外面调用一次来防止这种情况。

void led_flash(float speed)
{
    uint32_t speed_time = (speed *max_speed_time)/2;
    float percent_now;
    uint32_t index = 0;
    key_scan();
    for(index = 0;index<speed_time;index ++)
    {
        percent_now = (float)index/speed_time;
        led_pwm(percent_now);
        key_scan();
        if(stop_flag == 1)break;
    }
    for(;index!=0;index--)
    {
        percent_now = (float)index/speed_time;
        led_pwm(percent_now);
        key_scan();
        if(stop_flag == 1)break;
    }
    if(stop_flag == 1)
    {
        stop_flag =0;
        __R30 |= led;
    }
}

E.主函数

主函数没有多少内容,就是开始的时候将位进行初始化,然后执行呼吸灯程序即可。

void main(void)
{
    /* Clear SYSCFG[STANDBY_INIT] to enable OCP master port */
    CT_CFG.SYSCFG_bit.STANDBY_INIT = 0;
    __R30 |= led;
    while(1)
    {
        led_flash(precision_now/key_precision);
    }
}


4 实物展示

3e9f6a5e3b89e0d63f792d99ab15b90.jpg

1ddc45112d51f3d094e8eb81affece3.jpg

8a1af4a146484370fd70ef939462c34.jpg

315a684b875466b37184db1ca52639a.jpg


5 未来的计划建议

PRU只是BBB板子的一小部分内容,其LIUNX系统的强大功能尚没有学习,包括联网等都需要学习。另外,LED呼吸灯是非常简单的程序,其并没有体现PRU中的实时性的优点。未来有机会的继续学习。


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