基於stm32作品設計:懶人藍芽彩燈、手機APP無線控制ws2812,MCU無線升級程式

2021-09-27 10:01:30

作品嗶哩嗶哩視訊:【待上映】
資料連結:【待更新】
在這裡插入圖片描述

一、作品背景

在智慧的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、升級程式碼提示

顯示型別顯示內容
升級失敗藍芽彩燈顯示紅色
升級成功藍芽彩燈顯示綠色
升級過程藍芽彩燈顯示淡藍色進度

在這裡插入圖片描述