Funpack3-5 BBB板 网页点灯
该项目使用了BBB, c++,实现了网页点灯的设计,它的主要功能为:点灯,子线程呼吸灯。
标签
嵌入式系统
Funpack活动
BBB
姜小豆
更新2025-01-13
32

1. 项目描述

项目介绍:

在此项目中,我使用了 BeagleBone Black (BBB) 进行了一系列试验,包括 GPIO 控制、页面 IDE 体验和基于 Web 界面的点灯(开关和呼吸灯)功能实现。

BeagleBone Black 提供了完善的开发环境,可以通过 USB 模拟网卡和SSL连接并访问。页面IDE功能强大,我现在版本是Cloud9,如果刷了最近固件就是 Vscode 环境,更友好了。


设计思路:

首先,来点亮板上的LED,再通过控制外部GPIO 点呼吸灯,最后写网页控制点灯。

软件端,后台:使用 httplib 开启http服务,提供4个接口(点亮、熄灭、呼吸灯开启、呼吸灯关闭);前台:使用layui框架,可以增加一些好看的UI。


硬件连接和软件架构:

image.png

通过IO口P9.12(对应GPIO_60)来控制LED。


2. 主要功能展示

2.1. 点亮板载led

#include <stdio.h>
#include <unistd.h>

int main() {
FILE * trigger = fopen("/sys/class/leds/beaglebone:green:usr3/trigger", "w");
FILE * brightness = fopen("/sys/class/leds/beaglebone:green:usr3/brightness", "w");
int on = 0;

fprintf(trigger, "none\n");

while(1) {
fprintf(brightness, "%d\n", on);
fflush(brightness);
if(!on)
on = 1;
else
on = 0;
usleep(100000);
}
}


通过系统内的LED驱动来控制板载LED,使其闪烁。


2.2. gpio 介绍

使用GPIO的时候首先需要来看一下引脚

image (1).png


BBB 板的每一侧都有两个 23 引脚列,总共有 92 个引脚可供用户使用。右侧标头指定为 P8,左侧标头指定为 P9。


GPIO 编号方案:bbb 的 GPIO 引脚分为 3 组,每组 32 个: GPIO0 、 GPIO1和GPIO2。

可以使用约定GPIOX_Y来引用单个引脚,其中X是其 GPIO 寄存器, Y是其在该寄存器中的编号。然而,在软件中对特定引脚的所有引用都使用其绝对引脚号!一个GPIO的 绝对引脚编号按以下方式计算: Z = 32*X + Y ,其中X又是 GPIO 寄存器, Y是该寄存器内的位置。


所以一个IO口有三种编号:PX_Y, GPIOX_Y, GPIOX。

以我用的 P9_12 为例:

P9_12 内部是 GPIO1_28, 也是 GPIO60 (1*32+28) , 60 就是绝对引脚号。


2.3. 控制外部GPIO

#include <iostream>
#include <fstream>
#include <unistd.h>


#define GPIO_PIN "60" // p9-12


// 导出 GPIO 引脚
void exportGPIO(const std::string &pin) {
// 检测目录是否存在,存在就跳过,否则会报错并取消导入
std::ifstream f("/sys/class/gpio/gpio" + pin);
if(f.good()){
std::cout<<"目录已存在,跳过导入";
return;
}

std::cout<<"/sys/class/gpio/export"<<pin<<std::endl;
std::ofstream exportFile("/sys/class/gpio/export");
exportFile << pin;
exportFile.close();
}

// 设置 GPIO 引脚方向
void setGPIODirection(const std::string &pin, const std::string &direction) {
std::ofstream directionFile("/sys/class/gpio/gpio" + pin + "/direction");
directionFile << direction;
directionFile.close();
}

// 设置 GPIO 引脚值
void setGPIOValue(const std::string &pin, int value) {
std::cout<<pin<<" "<<value<<std::endl;
std::ofstream valueFile("/sys/class/gpio/gpio" + pin + "/value");
valueFile << value;
valueFile.close();
}


int main()
{
int a;
// 初始化 GPIO:导出并设置输出模式
exportGPIO(GPIO_PIN);
usleep(100);
setGPIODirection(GPIO_PIN, "out");

usleep(100);
while(1){
setGPIOValue(GPIO_PIN, 0);
usleep(1000000);
setGPIOValue(GPIO_PIN, 1);
usleep(1000000);
}
return 0;
}


控制外部gpio,主要是这三个步骤:

  • 导出gpio引脚:将引脚从内核空间导出到用户空间
  • 设置方向:配置为out为输出模式
  • 设置引脚值:设置为高低电平

也可以在系统内直接使用,以GPIO60为例

echo 60 > /sys/class/gpio/export  				# 导出
echo out > /sys/class/gpio/gpio60/direction # 设置方向
echo 1 > /sys/class/gpio/gpio49/value # 设置值


2.4. 软件 PWM 实现 呼吸灯

#include <iostream>
#include <fstream>
#include <cmath>
#include <unistd.h>


#define GPIO_PIN "60" // p9-12


// 导出 GPIO 引脚
void exportGPIO(const std::string &pin) {
// 检测目录是否存在,存在就跳过,否则会报错并取消导入
std::ifstream f("/sys/class/gpio/gpio" + pin);
if(f.good()){
std::cout<<"目录已存在,跳过导入";
return;
}

std::cout<<"/sys/class/gpio/export"<<pin<<std::endl;
std::ofstream exportFile("/sys/class/gpio/export");
exportFile << pin;
exportFile.close();
}

// 设置 GPIO 引脚方向
void setGPIODirection(const std::string &pin, const std::string &direction) {
std::ofstream directionFile("/sys/class/gpio/gpio" + pin + "/direction");
directionFile << direction;
directionFile.close();
}

// 设置 GPIO 引脚值
void setGPIOValue(const std::string &pin, int value) {
std::ofstream valueFile("/sys/class/gpio/gpio" + pin + "/value");
valueFile << value;
valueFile.close();
}

// 模拟 PWM 信号输出函数
void softwarePWM(const std::string &pin, int dutyCycle, int period_us) {
int highTime = period_us * dutyCycle / 100; // 高电平持续时间
int lowTime = period_us - highTime; // 低电平持续时间

// 设置高电平
setGPIOValue(pin, 1);
usleep(highTime);

// 设置低电平
setGPIOValue(pin, 0);
usleep(lowTime);
}

int main()
{
// 呼吸灯周期 2秒 (steps*period_us/1e6)
const int steps = 100; // 每个周期的步数
const int period_us = 20000; // 模拟 PWM 的周期,单位为微秒

// 初始化 GPIO:导出并设置输出模式
exportGPIO(GPIO_PIN);
usleep(100);
setGPIODirection(GPIO_PIN, "out");
usleep(100);

while (true) {
// 渐亮
for (int i = 0; i <= steps; ++i) {
double brightness = (1 - cos(2 * M_PI * i / steps)) / 2 * 100; // 占空比百分比
softwarePWM(GPIO_PIN, static_cast<int>(brightness), period_us);

}
// 渐暗
for (int i = steps; i >= 0; --i) {
double brightness = (1 - cos(2 * M_PI * i / steps)) / 2 * 100; // 占空比百分比
softwarePWM(GPIO_PIN, static_cast<int>(brightness), period_us);
}
}

return 0;
}


代码注释写的很清楚,就不赘述了。


2.5. 网页端控制

用httplib 开启http 服务,并开四个接口

    httplib::Server svr;

svr.Get("/api/ledOn", [&](const httplib::Request& req, httplib::Response& res) {
fprintf(brightness, "%d\n", 1);
fflush(brightness);
});

svr.Get("/api/ledOff", [&](const httplib::Request& req, httplib::Response& res) {
fprintf(brightness, "%d\n", 0);
fflush(brightness);
});

svr.Get("/api/breathOn", [&](const httplib::Request& req, httplib::Response& res) {
startBreath();
});
svr.Get("/api/breathOff", [&](const httplib::Request& req, httplib::Response& res) {
stopBreath();
});

svr.listen("192.168.7.2", 1234);

led的开关(ledOn, ledOff)直接控制就行,呼吸灯需要开启线程执行

std::atomic<bool> breathRunningFlag(false);
std::thread taskThread;

// 任务
void taskBreath(){
Util::BBB_Breath b;
b.init();
while(breathRunningFlag){
b.once();
}
}
// 启动任务
void startBreath(){
if(breathRunningFlag)
return;
breathRunningFlag = true;
taskThread = std::thread(taskBreath);
}
void stopBreath(){
if(!breathRunningFlag)
return;
breathRunningFlag = false;
if(taskThread.joinable()){
taskThread.join();
}
}


BBB_Breath 类,init初始化,once用来闪一周期

void Util::BBB_Breath::init(){
// 初始化 GPIO:导出并设置输出模式
exportGPIO(this->GPIO_PIN);
usleep(100);
setGPIODirection(this->GPIO_PIN, "out");
usleep(100);
}

void Util::BBB_Breath::once(){
// 渐亮
for (int i = 0; i <= this->STEPS; ++i) {
double brightness = (1 - cos(2 * M_PI * i / this->STEPS)) / 2 * 100; // 占空比百分比
softwarePWM(this->GPIO_PIN, static_cast<int>(brightness), this->PERIOD_US);

}
// 渐暗
for (int i = this->STEPS; i >= 0; --i) {
double brightness = (1 - cos(2 * M_PI * i / this->STEPS)) / 2 * 100; // 占空比百分比
softwarePWM(this->GPIO_PIN, static_cast<int>(brightness), this->PERIOD_US);
}
}


完整代码会放到附件中。

编译:

g++ main.cpp util.cpp lib/json/cJSON.c -lpthread -std=c++11


展示

前端页面


image.png

呼吸靓照

image (2).png


3. 心得体会

BBB板还是挺好玩,后续把BBB固件升了,再玩一玩。硬禾的活动也参加过不少了,每个活动都挺好,感谢硬禾,感谢得捷,感谢BeagleBone基金会。

要说这个活动的缺点嘛,还是有的,这个活动时间太长了,毕竟dealine才是第一生产力。嘿嘿嘿


4. 参考:

Setting Up the BeagleBone Black's GPIO Pins



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