文章目录
- 一、作品背景
- 二、功能要求
- 三、实现基础功能
- (一)、首先是要选材
- (二)、原理图设计
- (二)、第一版本PCB设计
- (三)、焊接PCB板
- (四)编写单片机程序
- (五)下载程序验证
- 四、外壳设计
- (一)CAD图纸设计
- (二)磨砂亚克力板
- 五、重新设计PCB
- 六、QT安卓APP设计
- (一)界面设计
- (二)QT程序设计
- (三)APP功能设计
作品哔哩哔哩视频:【待上映】
资料链接:【待更新】
一、作品背景
在智能的2021年代,年轻的小伙伴都患上了懒惰的症状,我也一样。
有一个难以入眠的夜晚,我打开了王者荣耀,我习惯了玩手机都要开着灯打,这样可以减少对眼睛的损伤,终于,赢了好几把,时间已经到了凌晨2点半,我也开始有了睡意,当我放下手机,准备闭眼入睡时,发现灯光格外耀眼,心烦意乱,实在不想按下那下床走好几步才能触碰到的开关,但是房间的设计就是这样,无法改变。无奈的我还是挣扎地下床按下了开关,这才安心入睡。
作为学电子专业的我并不妥协,我一定要设计一个不下床就可以关掉的灯。
二、功能要求
有了想法,就要大胆给自己一个功能要求,为了不下床关灯,冲!!!
蓝牙彩灯的功能初步打算:
1、可以用手机控制灯的亮灭
2、可以用手机控制灯的亮度
3、可以用手机控制灯的任意颜色
三、实现基础功能
有了功能要求,那能不能实现就要靠自己的技术了。
(一)、首先是要选材
1、LED选择:WS2812
既然是想做任意颜色的灯,那么毫无疑问选择最普遍的WS2812,24位全彩RGB彩灯,可以发出2^24=16777215种颜色。
2、单片机选择:STM32G0
在当今MCU那么稀贵的情况下,当然是要为自己的腰包考虑,WS2812的驱动时钟大概需要800KHZ,速度要求很高,首先选择stm32,看了一下价格,选择了和蔼可亲的stm32g030c8t6,6元还包邮
3、通信模块选择:蓝牙模块JDY-31
要手机控制灯,首先想到用蓝牙模块,价格考虑,选择全网最便宜的蓝牙模块JDY-31,比起HC-05,它更加小巧,就是连接速度不是很快
有了这3个主要材料,我们就可以开始设计一下原理图
(二)、原理图设计
1、单片机需要3.3V供电,首先设计一个电源部分,先用usb进行供电5V给ws2812,再用降压芯片降压到3.3V供给MCU,
2、设计一个单片机最小系统,以及预留一个下载接口,方便使用ST-LINK进行下载程序
3、然后要连接一个蓝牙和一个按键作为控制,再预留一个LED作为指示灯
4、RGB灯的电路设计,这里两组LED,用两个IO口控制,防止LED过多导致信号失真
这样一张原理图就设计好啦
(二)、第一版本PCB设计
1、根据原理图给定相应的封装导入PCB,再进行布局与布线,设计好一块给淘宝客服能够打印出来的PCB图纸
2D:
3D:
然后交给淘宝,这里推荐嘉立创,便宜,质量也高。
这是打印出来的第一板PCB:
(三)、焊接PCB板
1、打印出PCB之后,当然是要把元器件焊接到PCB板上,第一块板焊接的时候先不用一次性全部焊接上去,先焊接电源部分,看看电源芯片是否能够正常工作,比如我画的这块板子USB母座封装与原理图不对应,导致正负极直接反向,就很容易导致元器件损坏,检测完电压正常之后,再焊接其他元器件
(四)编写单片机程序
单片机程序包含了很多知识
1、轻量级多任务系统
2、蓝牙数据自定义控制协议、蓝牙无线升级单片机
3、多种控制方式按键+蓝牙
4、多功能按键,单击、双击、长按
5、ws2812串联控制
6、呼吸灯算法
7、颜色渐变算法
…
/****************************************
* 函数名称: DIS_TASK()
* 输入参数: 无
* 输出参数: 无
* 功 能: 显示任务
*
*****************************************/
void DIS_TASK(void)
{
static u8 r=0,g=0,b=0,a=0,dir=0;
static int i,cnt=0;
static int color_rgb;
SCHTaskBegin(); //开始固定格式一定要的
while (1)
{
if(SysState.Dis_flag == 1)//可以更新显示
{
/***********************静态*******************************/
if(SysState.Dismode == DisMode_Static)//静态
{
SysState.Dis_flag = 0;
RGB_Refresh(SysState.StaticRgb,LED_NUM);//显示
RGB2_Refresh(SysState.StaticRgb,LED_NUM);//显示
}
/***********************呼吸*******************************/
else if(SysState.Dismode == DisMode_Breathe)//呼吸
{
SysState.Dedlay_Time=20;
if(dir==0)
{
a += (1+a*10/0xff);
if(a > 0xf0)dir = 1;
}else if(dir)
{
a -= (1+a*10/0xff);
if(a <= 4)dir = 0;
}
r = ((SysState.StaticRgb>>16)%0x100)*a/0xff;
g = ((SysState.StaticRgb>>8)%0x100)*a/0xff;
b = ((SysState.StaticRgb>>0)%0x100)*a/0xff;
color_rgb = (r<<16) + (g<<8) + b;
printf("%d %d %d %d\r\n",r,g,b,a);
RGB_Refresh(color_rgb,LED_NUM);//显示
RGB2_Refresh(color_rgb,LED_NUM);//显示
SCHCurTaskDly(SysState.Dedlay_Time);
}
/***********************闪烁*******************************/
else if(SysState.Dismode ==DisMode_Twinkle)//闪烁
{
SysState.Dedlay_Time=200;//*SysState.Dedlay_Ratio/0x0f;;
RGB_Refresh(SysState.StaticRgb,LED_NUM);
RGB2_Refresh(SysState.StaticRgb,LED_NUM);
SCHCurTaskDly(SysState.Dedlay_Time);
RGB_Refresh(0,LED_NUM);
RGB2_Refresh(0,LED_NUM);
SCHCurTaskDly(SysState.Dedlay_Time);
}
/***********************渐变*******************************/
else if(SysState.Dismode ==DisMode_GraChange)//渐变
{
extern u8 GraChange_flag;
SysState.Dedlay_Time=100;//*SysState.Dedlay_Ratio/0x0f;
RgbAlg(&SysState.StaticRgb,&GraChange_flag);//渐变算法
RGB_Refresh(SysState.StaticRgb,LED_NUM);//显示
RGB2_Refresh(SysState.StaticRgb,LED_NUM);//显示
SCHCurTaskDly(SysState.Dedlay_Time);
}
/***********************蹦迪*******************************/
else if(SysState.Dismode == DisMode_DiscoDance)//蹦迪
{
SysState.Dedlay_Time=20;//*SysState.Dedlay_Ratio/0x0f;
RGB_Refresh(Static_DisColor[cnt],LED_NUM);
RGB2_Refresh(Static_DisColor[cnt],LED_NUM);
SCHCurTaskDly(SysState.Dedlay_Time);
RGB_Refresh(0,LED_NUM);
RGB2_Refresh(0,LED_NUM);
SCHCurTaskDly(SysState.Dedlay_Time*50);
}
/***********************流水*******************************/
else if(SysState.Dismode == DisMode_RunWater)//流水
{
static int i=0,flag=0;
SysState.Dedlay_Time=100;
i++;
if(i == LED_NUM)
{
i=0;flag=!flag;
}
if(flag){ //设置颜色
RGB_Refresh(SysState.StaticRgb,i+1);
RGB2_Refresh(SysState.StaticRgb,i+1);
SysState.Dedlay_Time=50;//*SysState.Dedlay_Ratio/0x0f;
SCHCurTaskDly(SysState.Dedlay_Time);
}
else{ //灭
RGB_Refresh(0,i+1);
RGB2_Refresh(0,i+1);
SysState.Dedlay_Time=50;//*SysState.Dedlay_Ratio/0x0f;
SCHCurTaskDly(SysState.Dedlay_Time);
}
}
/***********************用户*******************************/
else if(SysState.Dismode ==DisMode_User1) //用户
{
SysState.Dedlay_Time=1000;
RGB_Refresh(SysState.StaticRgb,1);
RGB2_Refresh(0,1);
SCHCurTaskDly(SysState.Dedlay_Time);
RGB2_Refresh(SysState.StaticRgb,1);
RGB_Refresh(0,1);
SCHCurTaskDly(SysState.Dedlay_Time);
}
}
SCHCurTaskDly(10);
}
SCHTaskEnd(); //结束固定格式一定要的
}
(五)下载程序验证
下载程序后测试ws2812是否正常工作
四、外壳设计
(一)CAD图纸设计
感觉没有一个外壳会很难看,添加一个外壳,让世界变得美丽
(二)磨砂亚克力板
淘宝搜磨砂亚克力板定制,发送CAD图纸给师傅,就可以给你做了
这是做好的亚克力板,是按照PCB板尺寸量身定做的
五、重新设计PCB
重新布局设计出来第3版本成品板:PCBV1.3
这是打样后焊接好的样子:
六、QT安卓APP设计
(一)界面设计
(二)QT程序设计
展示部分代码:
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
this->Start_Init();
this->File_Init();
this->BuleTooth_Init();
this->Label_Init();
this->PushButton_Init();
this->ColorSlider_Init();
this->setFocus();
}
MainWindow::~MainWindow()
{
bin_save(FileInfo);
delete ui;
}
//起始代码
void MainWindow::Start_Init()
{
//设置背景图片
this->setStyleSheet("QMainWindow{border-image: url(:/pic/btmenuv2.jpg);}");
//获取屏幕大小
QScreen *screen = QApplication::screens().at(0);
src_w = screen->size().width();
src_h = screen->size().height();
if(src_w <= 0 || src_h <= 0)
{
src_h = 2267;src_w = 1080;
this->setGeometry(0,0,src_w,src_h);//1080 2267
qDebug() << "src get err ======== "<< src_w <<src_h << endl;
}
else
{
this->setGeometry(0,0,src_w,src_h);//1080 2267
qDebug() << "src get ok ======== " << src_w <<src_h << endl;
}
}
//蓝牙初始化
void MainWindow::BuleTooth_Init(void)
{
//蓝牙连接初始化代码
timer_conflag = new QTimer;
ptimer = new QTimer;
//QBluetoothDeviceDiscoveryAgent 这个是指扫描周围蓝牙设备!
discoveryAgent = new QBluetoothDeviceDiscoveryAgent();
//QBluetoothLocalDevice 是指配置获取设备的蓝牙状态信息等!
localDevice = new QBluetoothLocalDevice();
//QBluetoothSocket指进行链接蓝牙设备,读写信息!
socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol);
//多窗口初始化
btcwindow = new BTMainWindow(this);
btcwindow->hide();
aboutwindow = new AboutMainWindow(this);
aboutwindow->hide();
connect(socket,
SIGNAL(readyRead()),
this,
SLOT(readBluetoothDataEvent())
);
connect(socket,
SIGNAL(connected()),
this,
SLOT(bluetoothConnectedEvent())
);
connect(socket,
SIGNAL(disconnected()),
this,
SLOT(bluetoothDisconnectedEvent())
);
localDevice->powerOn();//打开蓝牙
discoveryAgent->start();//开始扫描
}
//颜色条初始化
void MainWindow::ColorSlider_Init(void)
{
QColor color;
color.setRgb(0x00,0x00,0x00);
colorslider_R = new ColorSlider(this);
colorslider_G = new ColorSlider(this);
colorslider_B = new ColorSlider(this);
colorslider_A = new ColorSlider(this);
colorslider_R->init(ColorSlider::RGB,ColorSlider::RED,color,0x00,0xFF);qDebug() << color << endl;
colorslider_G->init(ColorSlider::RGB,ColorSlider::GREEN,color,0x00,0xff);qDebug() << color << endl;
colorslider_B->init(ColorSlider::RGB,ColorSlider::BLUE,color,0x00,0xff);qDebug() << color << endl;
colorslider_A->init(ColorSlider::RGB,ColorSlider::ALPHA,color,0x00,0xff);qDebug() << color << endl;
colorslider_R->setGeometry(100,200,880,60);
colorslider_G->setGeometry(100,400,880,60);
colorslider_B->setGeometry(100,600,880,60);
colorslider_A->setGeometry(100,800,880,60);
}
//按钮初始化
void MainWindow::PushButton_Init(void)
{
//刷新定时器
static QColor last_Color;
time1= new QTimer(this);
time1->start(1000);
connect(time1,&QTimer::timeout,[=](){
time1->start(100);
if(Connect_Flag == 1)//连接指示
{
Connect_Flag = 0;
btcwindow->hide();
this->show();
QMessageBox::information(this,tr("提示"),tr("蓝牙连接成功!"));
QByteArray arrayData; //发送空指令
QString s = QString("NONE\r\n");
qDebug() << s << endl;
arrayData = s.toUtf8();
socket->write(arrayData);
s.clear();
arrayData.clear();
}
if(last_Color != Color_sum)//发送指令
{
update();//更新
unsigned int color_d = ((Color_sum.alpha()/16)<<24)+(Color_sum.red()<<16) + (Color_sum.green()<<8)
+ (Color_sum.blue()<<0) ;
QByteArray arrayData;
QString s = QString("COLOR:%1\r\n").arg(color_d);
qDebug() << s << endl;
arrayData = s.toUtf8();
socket->write(arrayData);
s.clear();
arrayData.clear();
}
last_Color = Color_sum;
});
//色块按钮
// QPushButton *phbutton[10];
for(int i=0; i<10; i++)
{
int r,g,b;
r = FileInfo->color_tab[i]>>16;
g = (FileInfo->color_tab[i]>>8)%256;
b = FileInfo->color_tab[i]%256;
phbutton[i] = new QPushButton(this);
if(i<5)phbutton[i]->setGeometry(90+i*200*src_w/1080,1600*src_h/2267,100*src_w/1080,100*src_h/2267);
else if(i>=5)phbutton[i]->setGeometry(90+(i-5)*200*src_w/1080,1800*src_h/2267,100*src_w/1080,100*src_h/2267);
QString s = QString("background-color: rgb(%1, %2, %3);").arg(r).arg(g).arg(b);
phbutton[i]->setStyleSheet(s);
phbutton[i]->setWindowFlags(Qt::WindowStaysOnTopHint);
connect( phbutton[i],&myPushButton::clicked,[=](){
int r,g,b;
if(SaveColorFlag != 0)
{
r = Color_sum.red();
g = Color_sum.green();
b = Color_sum.blue();
SaveColorFlag = 0;
update();
FileInfo->color_tab[i] = (r<<16)+(g<<8)+b;
QString s = QString("background-color: rgb(%1, %2, %3);").arg(r).arg(g).arg(b);
phbutton[i]->setStyleSheet(s);
}
else
{
r = FileInfo->color_tab[i]>>16;
g = (FileInfo->color_tab[i]>>8)%256;
b = FileInfo->color_tab[i]%256;
Color_sum.setRgb(r,g,b,Color_sum.alpha());
ColorSlider_paint_Flag = 2;
}
});
}
(三)APP功能设计
1、主界面功能
按钮 | 功能说明 |
连接按钮 | 进入蓝牙连接界面/双击连接设备 |
关于按钮 | 进入软件介绍/升级MCU界面 |
红色滑动条 | 调节红色色彩 |
绿色滑动条 | 调节绿色色彩 |
蓝色滑动条 | 调节蓝色色彩 |
灰白滑动条 | 调节总体亮度 |
颜色预览球 | 预览当前显示颜色 |
颜色快捷显示 | 单击可以显示按钮颜色 |
显示模式 | 显示模式切换 |
底部信息显示 | 显示接收到的信息/欢迎信息 |
连接信息显示 | 显示当前连接状态 |
先点颜色预览球,会出现保存选择框,
再点击颜色选择按钮,即可保存当前调节好的颜色
2、蓝牙连接界面
按钮 | 功能说明 |
设备连接选择 | 双击连接设备 |
返回按钮 | 返回主界面 |
1、连接成功自动返回主界面
2、自动连接功能:打开APP无需动作,自动找寻蓝牙彩灯设备自动连接,无需手动连接
3、关于界面
按钮 | 功能说明 |
作者信息显示 | 只读 |
返回按钮 | 返回主界面 |
显示版本 | 蓝牙彩灯应答信号 例如1.2版本闪红灯1次,绿灯两次 |
升级MCU | 连接好蓝牙,点击升级,可以使单片机软件升到当前版本,软件版本一样切勿反复升级,可能升级失败 |
升级进度条 | 显示升级进度 单片机升级失败后需要进入升级模式重新升级,否则将无法正常使用。 |
4、升级代码提示
显示类型 | 显示内容 |
升级失败 | 蓝牙彩灯显示红色 |
升级成功 | 蓝牙彩灯显示绿色 |
升级过程 | 蓝牙彩灯显示淡蓝色进度 |