精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

keil5+STM32F103C8T6設計的智能插座+人體感應燈 支持跨平臺編譯運行

1126626497 ? 來源:DS小龍哥-嵌入式技術 ? 作者:DS小龍哥-嵌入式技 ? 2022-02-28 13:19 ? 次閱讀

?

一、環境介紹

MCU: STM32F103C8T6

程序開發IDE: keil5

STM32程序風格: 采用寄存器方式開發,注釋齊全,執行效率高,方便移植

手機APP: 采用QT設計,程序支持跨平臺編譯運行(AndroidIOS、Windows、Linux都可以編譯運行,對應平臺上QT的環境搭建,之前博客已經發了文章講解)

硬件包含:SRM32F103C8T6最小系統板、紅外熱釋電人體感應模塊、DHT11溫濕度傳感器、0.96寸單色OLED顯示屏、ESP8266、繼電器、RGB大功率白燈.

?

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

?

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

?

?

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

二、功能介紹

這是基于STM32設計的智能插座+人體感應燈。

硬件包含:

1. SRM32F103C8T6最小系統板: 基礎的系統板,引出了所有IO口

2. 紅外熱釋電人體感應模塊: 用來檢測人體

3. DHT11溫濕度傳感器: 檢測環境的溫度、濕度

4. 0.96寸單色OLED顯示屏 : 顯示狀態信息。比如: WIFI狀態、RTC時鐘、插座狀態、溫濕度值

5. ESP8266: 用來與手機APP之間通信

6. 繼電器: 模擬插座開關

7. RGB大功率白燈: 模擬正常的燈泡

支持的功能如下:

1. 使用熱釋電人體感應模塊檢測人體,檢測到人體自動開燈,30秒(時間可以根據要求調整)沒有檢測到人體就自動關燈。

2. 檢測環境溫濕度,使用OLED顯示屏在界面上實時顯示出來。 如果環境溫度高于閥值,強制關閉插座、如果濕度高于閥值,也會強制關閉插座;防止火災隱患。 溫度最高閥值設置為: 30°,濕度閥值為80%, 這些都可以根據設計要求調整。

并且RGB燈也會根據不同的溫度閥值亮不同顏色的燈。 比如: 溫度高于30°亮紅色、溫度20°黃色 、溫度10°青色

3. 設置ESP8266WIFI模塊為AP模式(路由器模式),手機或者電腦可以連接到ESP8266.搭建局域網。

4. 設計手機APP和電腦客戶端軟件,可以實時顯示收到的溫濕度數據(3秒上傳一次).可以顯示歷史. 點擊手機APP上的按鈕,可以用來控制插座開關。

5. OLED一共有4個頁面。 RTC實時時鐘顯示頁面、溫濕度顯示頁面、智能插座開關狀態頁面、WIFI熱點信息頁面

6. OLED顯示屏的第一頁是實時時鐘頁面,時間可以通過手機APP來校準。 在手機APP上有一個RTC校準按鈕,點擊一下就可以校準設備上的時間。

三、使用的相關硬件介紹

3.1DTH11 溫濕度傳感器

?

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

DHT11數字溫濕度傳感器是一款含有已校準數字信號輸出的溫濕度復合傳感器,它應用專用的數字模塊采集技術和溫濕度傳感技術,確保產品具有極高的可靠性和卓越的長期穩定性。傳感器包括一個電阻式感濕元件和一個NTC測溫元件,并與一個高性能8位單片機相連接。因此該產品具有品質卓越、超快響應、抗干擾能力強、性價比極高等優點。每個DHT11傳感器都在極為精確的濕度校驗室中進行校準。校準系數以程序的形式存在OTP內存中,傳感器內部在檢測信號的處理過程中要調用這些校準系數。單線制串行接口,使系統集成變得簡易快捷。超小的體積、極低的功耗,使其成為該類應用中,在苛刻應用場合的最佳選擇。產品為4針單排引腳封裝,連接方便。

3.2熱釋電傳感器

?

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

?

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

熱釋電紅外傳感器在結構上引入場效應管,其目的在于完成阻抗變換。由于熱釋電元輸出的是電荷信號,并不能直接使用,因而需要用電阻將其轉換為電壓形式。故引入的N溝道結型場效應管應接成共漏形式來完成阻抗變換。熱釋電紅外傳感器由傳感探測元、干涉濾光片和場效應管匹配器三部分組成。設計時應將高熱電材料制成一定厚度的薄片,并在它的兩面鍍上金屬電極,然后加電對其進行極化,這樣便制成了熱釋電探測元。
熱釋電紅外傳感器的外形如上圖所示。其可以檢測人體發出的紅外線信號,并將其轉換成電信號輸出。傳感器頂部的長方形窗口加有濾光片,可以使人體發出的9~10μm波長的紅外線通過,而其它波長的紅外線被濾除,這樣便提高了抗干擾能。熱釋電紅外傳感器由濾光片、熱釋電探測元和前置放大器組成,補償型熱釋電傳感器還帶有溫度補償元件,圖所示為熱釋電傳感器的內部結構。為防止外部環境對傳感器輸出信號的干擾,上述元件被真空封裝在一個金屬營內。熱釋電傳感器的濾光片為帶通濾光片,它封裝在傳感器殼體的頂端,使特定波長的紅外輻射選擇性地通過,到達熱釋電探測元+在其截止范圍外的紅外輻射則不能通過。
熱釋電探測元是熱釋電傳感器的核心元件,它是在熱釋電晶體的兩面鍍上金屬電極后,加電極化制成,相當于一個以熱釋電晶體為電介質的平板電容器。當它受到非恒定強度的紅外光照射時,產生的溫度變化導致其表面電極的電荷密度發生改變,從而產生熱釋電電流。
前置放大器由一個高內阻的場效應管源極跟隨器構成,通過阻抗變換,將熱釋電探測元微弱的電流信號轉換為有用的電壓信號輸出。

3.3ESP8266串口WIFI模塊

?

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

ESP8266系列無線模塊是高性價比WIFI SOC模組,該系列模塊支持標準的IEEE802.11b/g/n協議,內置完整的TCP/IP協議棧。用戶可以使用該系列模塊為現有的設備添加聯網功能,也可以構建獨立的網絡控制器

能卓越
ESP8266EX 芯片內置超低功耗 Tensilica L106 32 位 RISC 處理器CPU 時鐘速度最?可達 160 MHz,?持實時操作系統 (RTOS) 和 Wi-Fi 協議棧,可將?達 80% 的處理能?應用于編程和開發。
高度集成
ESP8266 芯片高度集成天線開關、射頻巴倫、功率放大器、低噪聲接收放大器、濾波器等射頻模塊。模組尺寸小巧,尤其適用于空間受限的產品設計。

認證齊全
RF 認證:SRRC、FCC、CE-RED、KCC、TELEC/MIC、IC 和 NCC 認證;
環保認證:RoHS、REACH;
可靠性認證:HTOL、HTSL、μHAST、TCT、ESD

豐富的產品應用
ESP8266 模組既可以通過 ESP-AT 指令固件,為外部主機 MCU 提供 Wi-Fi 連接功能;也可以作為獨立 Wi-Fi MCU 運行,用戶通過基于 RTOS 的 SDK 開發帶 Wi-Fi 連接功能的產品。用戶可以輕松實現開箱即用的云連接、低功耗運行模式,以及包括 WPA3 在內的 Wi-Fi 安全支持等功能。

3.4OLED顯示屏

?

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

OLED顯示屏是利用有機電自發光二極管制成的顯示屏。由于同時具備自發光有機電激發光二極管,不需背光源、對比度高、厚度薄、視角廣、反應速度快、可用于撓曲性面板、使用溫度范圍廣、構造及制程較簡單等優異之特性,被認為是下一代的平面顯示器新興應用技術。
有機發光二極管 (OLED)顯示器越來越普遍,在手機、媒體播放器及小型入門級電視等產品中最為顯著。不同于標準的液晶顯示器,OLED 像素是由電流源所驅動。若要了解 OLED 電源供應如何及為何會影響顯示器畫質,必須先了解 OLED 顯示器技術及電源供應需求。本文將說明最新的 OLED 顯示器技術,并探討主要的電源供應需求及解決方案,另外也介紹專為 OLED 電源供應需求而提出的創新性電源供應架構。
背板技術造就軟性顯示器 高分辨率彩色主動式矩陣有機發光二極管 (AMOLED) 顯示器需要采用主動式矩陣背板,此背板使用主動式開關進行各像素的開關。液晶 (LC) 顯示器非晶硅制程已臻成熟,可供應低成本的主動式矩陣背板,并且可用于 OLED。許多公司正針對軟性顯示器開發有機薄膜晶體管 (OTFT) 背板制程,此一制程也可用于 OLED 顯示器,以實現全彩軟性顯示器的推出。不論是標準或軟性 OLED,都需要運用相同的電源供應及驅動技術。若要了解 OLED 技術、功能及其與電源供應之間的互動,必須深入剖析這項技術本身。OLED 顯示器是一種自體發光顯示器技術,完全不需要任何背光。OLED 采用的材質屬于化學結構適用的有機材質。 OLED 技術需要電流控制驅動方法 OLED 具有與標準發光二極管 (LED) 相當類似的電氣特性,亮度均取決于 LED 電流。若要開啟和關閉 OLED 并控制 OLED 電流,需要使用薄膜晶體管 (TFT)的控制電路

OLED為自發光材料,不需用到背光板,同時視角廣、畫質均勻、反應速度快、較易彩色化、用簡單驅動電路即可達到發光、制程簡單、可制作成撓曲式面板,符合輕薄短小的原則,應用范圍屬于中小尺寸面板。
顯示方面:主動發光、視角范圍大;響應速度快,圖像穩定;亮度高、色彩豐富、分辨率高。
工作條件:驅動電壓低、能耗低,可與太陽能電池、集成電路等相匹配。
適應性廣:采用玻璃襯底可實現大面積平板顯示;如用柔性材料做襯底,能制成可折疊的顯示器。由于OLED是全固態、非真空器件,具有抗震蕩、耐低溫(-40℃)等特性,在軍事方面也有十分重要的應用,如用作坦克、飛機等現代化武器的顯示終端。

3.5LED大功率燈模塊

?

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

LED燈是一塊電致發光的半導體材料芯片,用銀膠或白膠固化到支架上,然后用銀線或金線連接芯片和電路板,四周用環氧樹脂密封,起到保護內部芯線的作用,最后安裝外殼,所以 LED 燈的抗震性能好。

LED(Light Emitting Diode),發光二極管,是一種能夠將電能轉化為可見光的固態的半導體器件,它可以直接把電轉化為光。LED的心臟是一個半導體的晶片,晶片的一端附在一個支架上,一端是負極,另一端連接電源的正極,使整個晶片被環氧樹脂封裝起來。
半導體晶片由兩部分組成,一部分是P型半導體,在它里面空穴占主導地位,另一端是N型半導體,在這邊主要是電子。但這兩種半導體連接起來的時候,它們之間就形成一個P-N結。當電流通過導線作用于這個晶片的時候,電子就會被推向P區,在P區里電子跟空穴復合,然后就會以光子的形式發出能量,這就是LED燈發光的原理。而光的波長也就是光的顏色,是由形成P-N結的材料決定的。

LED可以直接發出紅、黃、藍、綠、青、橙、紫、白色的光。

3.6STM32F103C8T6最小系統板

?

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

STM32F系列屬于中低端的32位ARM微控制器,該系列芯片是意法半導體(ST)公司出品,其內核是Cortex-M3。
該系列芯片按片內Flash的大小可分為三大類:小容量(16K和32K)、中容量(64K和128K)、大容量(256K、384K和512K)。
芯片集成定時器Timer,CANADC,SPI,I2CUSBUART等多種外設功能。
內核
--ARM 32位的Cortex-M3
--最高72MHz工作頻率,在存儲器的0等待周期訪問時可達1.25DMips/MHZ(DhrystONe2.1)
--單周期乘法和硬件除法
存儲器
--從16K到512K字節的閃存程序存儲器(STM32F103XXXX中的第二個X表示FLASH容量,其中:“4”=16K,“6”=32K,“8”=64K,B=128K,C=256K,D=384K,E=512K)
--最大64K字節的SRAM
電源管理
--2.0-3.6V供電和I/O引腳
--上電/斷電復位(POR/PDR)、可編程電壓監測器(PVD)
--4-16MHZ晶振
--內嵌經出廠調校的8MHz的RC振蕩器
--內嵌帶校準的40KHz的RC振蕩器
--產生CPU時鐘的PLL
--帶校準的32KHz的RC振蕩器
低功耗
--睡眠、停機和待機模式
--Vbat為RTC和后備寄存器供電
模數轉換器
--2個12位模數轉換器,1us轉換時間(多達16個輸入通道)
--轉換范圍:0至3.6V
--雙采樣和保持功能
--溫度傳感器
DMA
--2個DMA控制器,共12個DMA通道:DMA1有7個通道,DMA2有5個通道
--支持的外設:定時器、ADC、SPI、USB、IIC和UART
--多達112個快速I/O端口(僅Z系列有超過100個引腳)
--26/37/51/80/112個I/O口,所有I/O口一塊映像到16個外部中斷;幾乎所有的端口均可容忍5V信號
調試模式
--串行單線調試(SWD)和JTAG接口
--多達8個定時器
--3個16位定時器,每個定時器有多達4個用于輸入捕獲/輸出比較/PWM或脈沖計數的通道和增量編碼器輸入
--1個16位帶死區控制和緊急剎車,用于電機控制的PWM高級控制定時器
--2個看門狗定時器(獨立的和窗口型的)
--系統時間定時器:24位自減型計數器
--多達9個通信接口
2個I2C接口(支持SMBus/PMBus)
3個USART接口(支持ISO7816接口,LIN,IrDA接口和調制解調控制)
2個SPI接口(18M位/秒)
CAN接口(2.0B主動)
USB 2.0全速接口
計算單元
CRC計算單元,96位的新批唯一代碼
封裝
ECOPACK封裝

3.7杜邦線

?

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

3.8 繼電器

?

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

?

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

四、STM32核心代碼

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

?

4.1 STM32: main.c

#include "stm32f10x.h"
#include "beep.h"
#include "delay.h"
#include "led.h"
#include "key.h"
#include "sys.h"
#include "usart.h"
#include 
#include 
#include "exti.h"
#include "timer.h"
#include "rtc.h"
#include "wdg.h"
#include "oled.h"
#include "fontdata.h"
#include "adc.h"
#include "FunctionConfig.h"
#include "dht11.h"
#include "HumanDetection.h"
#include "esp8266.h"

/*
函數功能: 繪制時鐘表盤框架
*/
void DrawTimeFrame(void)
{
	u8 i;
	OLED_Circle(32,32,31);//畫外圓
	OLED_Circle(32,32,1); //畫中心圓
	//畫刻度
	for(i=0;i<60;i++)
	{
		if(i%5==0)OLED_DrawAngleLine(32,32,6*i,31,3,1);
	}
	OLED_RefreshGRAM();  //刷新數據到OLED屏幕
}

/*
函數功能: 更新時間框架顯示,在RTC中斷里調用
*/
char TimeBuff[20];
void Update_FrameShow(void)
{
	/*1. 繪制秒針、分針、時針*/
	OLED_DrawAngleLine2(32,32,rtc_clock.sec*6-6-90,27,0);//清除之前的秒針
	OLED_DrawAngleLine2(32,32,rtc_clock.sec*6-90,27,1); //畫秒針
	
	OLED_DrawAngleLine2(32,32,rtc_clock.min*6-6-90,24,0);
	OLED_DrawAngleLine2(32,32,rtc_clock.min*6-90,24,1);
	
	OLED_DrawAngleLine2(32,32,rtc_clock.hour*30-6-90,21,0);
	OLED_DrawAngleLine2(32,32,rtc_clock.hour*30-90,21,1);
	
	//繪制電子鐘時間
	sprintf(TimeBuff,"%d",rtc_clock.year);
	OLED_ShowString(65,16*0,16,TimeBuff);  //年份字符串
	OLED_ShowChineseFont(66+32,16*0,16,4); //顯示年
	
	sprintf(TimeBuff,"%d/%d",rtc_clock.mon,rtc_clock.day);
	OLED_ShowString(75,16*1,16,TimeBuff); //月
	
	if(rtc_clock.sec==0)OLED_ShowString(65,16*2,16,"        ");	//清除多余的數據
	sprintf(TimeBuff,"%d:%d:%d",rtc_clock.hour,rtc_clock.min,rtc_clock.sec);
	OLED_ShowString(65,16*2,16,TimeBuff); //秒
	
	//顯示星期
	OLED_ShowChineseFont(70,16*3,16,5); //星
	OLED_ShowChineseFont(70+16,16*3,16,6); //期
	OLED_ShowChineseFont(70+32,16*3,16,rtc_clock.week+7); //具體的值
}


/*
函數功能: 溫濕度顯示
*/
void ShowTemperatureAndHumidity(u8 temp,u8 humi)
{
    sprintf(TimeBuff,"T: %d",temp);
	OLED_ShowString(40,16*1,16,TimeBuff); 
    sprintf(TimeBuff,"H: %d%%",humi);
	OLED_ShowString(40,16*2,16,TimeBuff); 
}

/*
函數功能: OLED所有顯示頁面信息初始化
*/
u8 ESP8266_Stat=0;   			//存放ESP8266狀態  1 OK ,0 error
u8 ESP8266_WIFI_AP_SSID[10];  //存放WIFI的名稱	
#define STM32_96BIT_UID (0x1FFFF7E8) //STM32內部96位唯一芯片標識符寄存器地址

/*
函數功能: ESP8266 WIFI 顯示頁面
*/
char ESP8266_PwdShow[20];
char ESP8266_IP_PortAddr[30];  //存放ESP8266 IP地址與端口號地址
u8 Save_ESP8266_SendCmd[30];  //保存WIFI發送的命令
u8 Save_ESP8266_SendData[50];  //保存WIFI發送的數據

void ESP8266_ShowPageTable(void)
{
    if(ESP8266_Stat)OLED_ShowString(0,16*0,16,"WIFI STAT:ERROR");
    else OLED_ShowString(0,16*0,16,"WIFI STAT:OK");

    //顯示字符串
    //OLED_ShowString(0,2,(u8*)"                "); //清除一行的顯示
    memset((u8*)ESP8266_PwdShow,0,20); //清空內存
    sprintf((char*)ESP8266_PwdShow,"WIFI:%s",ESP8266_WIFI_AP_SSID);
    ESP8266_PwdShow[15]='\0';
    OLED_ShowString(0,16*1,16,ESP8266_PwdShow);	
    memset((u8*)ESP8266_PwdShow,0,20); //清空內存

    sprintf((char*)ESP8266_PwdShow,"PWD:%s",wifiap_password);
    OLED_ShowString(0,16*2,16,ESP8266_PwdShow);
    OLED_ShowString(0,16*3,16,"192.168.4.1:8089");	    
}


void OledShowPageTableInit(void)
{
    u8 data[100],i;
    u32 stm32_uid=0;
    u8 *uid;

/*1. ESP8266 WIFI相關信息初始化*/

    OLED_ShowString(0,2,16,"             "); //清空一行的顯示
    OLED_ShowString(0,2,16,"WifiInit...");  	//顯示頁面提示信息

    /*1.1 設置WIFI AP模式 */
    if(ESP8266_SendCmd("AT+CWMODE=2\r\n","OK",50))
    {
            ESP8266_Stat=1;  //OK
    }
    else
    {
            ESP8266_Stat=0;  //ERROR
    }
    
    /*1.2 重啟模塊 */
    ESP8266_SendCmd("AT+RST\r\n","OK",20);
    
    /*1.3 延時3S等待重啟成功*/
    DelayMs(1000);            
    DelayMs(1000);
    DelayMs(1000);
    
    //得到WIFI的名稱
    uid=(u8*)STM32_96BIT_UID; //轉為指針為1個字節
    for(i=0;i<96;i++)
    {	
        stm32_uid+=*uid++; //計算校驗和,得到WIFI數字編號
    }
    printf("stm32_uid=%d\r\n",stm32_uid);
    sprintf((char*)data,"%d",stm32_uid);
    strcpy((char*)ESP8266_WIFI_AP_SSID,"wbyq_");
    strcat((char*)ESP8266_WIFI_AP_SSID,(char*)data);
    printf("請用設備連接WIFI熱點:%s,%s,%s\r\n",(u8*)ESP8266_WIFI_AP_SSID,(u8*)wifiap_encryption,(u8*)wifiap_password);
    
    /*1.4 配置模塊AP模式無線參數*/
    memset(data,0,100); //清空數組
    sprintf((char*)data,"AT+CWSAP=\"%s\",\"%s\",1,4\r\n",ESP8266_WIFI_AP_SSID,wifiap_password); 
    ESP8266_SendCmd(data,"OK",1000);

    /*1.5  設置多連接模式:0單連接,1多連接(服務器模式必須開啟)*/
    ESP8266_SendCmd("AT+CIPMUX=1\r\n","OK",20); 
    
    /*1.6 開啟Server模式(0,關閉;1,打開),端口號為portnum */
    memset(data,0,100); //清空數組
    sprintf((char*)data,"AT+CIPSERVER=1,%s\r\n",(u8*)portnum);
    ESP8266_SendCmd(data,"OK",50);
    
    /*1.7 獲取當前模塊的IP*/
    ESP8266_GetWanip((u8*)ESP8266_IP_PortAddr);
    strcat(ESP8266_IP_PortAddr,":");
    strcat(ESP8266_IP_PortAddr,portnum);
    printf("IP地址:%s\r\n",ESP8266_IP_PortAddr);
    
    OLED_ShowString(0,2,16,"              "); //清空一行的顯示
    OLED_ShowString(0,2,16,"WifiInitOk");  	//顯示頁面提示信息
}


/*
函數功能: 顯示開關狀態
*/
void Show_Switch(int state)
{
    //清屏
    OLED_Clear(0);
    
    if(state)
    {
        sprintf(TimeBuff,"Socket:ON");
        OLED_ShowString(20,16*2,16,TimeBuff); 
    }   
    else
    {
        sprintf(TimeBuff,"Socket:OFF");
        OLED_ShowString(20,16*2,16,TimeBuff); 
    }
}

//int main1(void)
//{
//    HumanDetection_Init(); //熱釋電模塊初始化
//    UsartInit(USART1,72,115200);//串口1的初始化
//    LED_Init();  //初始化LED
//    while(1)
//    {
//    //熱釋電狀態  為真表示有人
//        if(HumanState)
//        {
//            LED1=0; //開燈
//        }
//        else  //為假
//        {
//            LED1=1; //關燈
//        }
//        printf("%d\n",HumanState);
//    }
//}


int main(void)
{
    u8 rlen;
    char *p;
    u8 temp;  //溫度
    u8 humi;  //濕度
    u8 stat;
    u8 key_val;
	u32 TimeCnt=0;
    u32 wifi_TimeCnt=0;
    u16 temp_data; //溫度數據
    u8 page_cnt=0; //顯示的頁面
     char *time;
    u8 socket_state=0;
    
    UsartInit(USART1,72,115200);//串口1的初始化
    
	BEEP_Init(); //初始化蜂鳴器
	LED_Init();  //初始化LED
	KEY_Init();  //按鍵初始化
    
     printf("正在初始化OLED...\r\n");  
	OLED_Init(0xc8,0xa1); //OLED顯示屏初始化--正常顯示
    
    //OLED_Init(0xc0,0xa0); //OLED顯示屏初始化--翻轉顯示

	OLED_Clear(0x00); 						//清屏	
    
   
    UsartInit(USART3,36,115200); //WIFI的波特率為115200
    Timer2Init(72,10000); 		   //10ms中斷一次,輔助串口3接收數據--WIFI數據
	
    
    printf("正在初始化ESP8266..\r\n");  
    //ESP8266初始化
    OledShowPageTableInit();
     //清屏
    OLED_Clear(0);
      
    printf("正在初始化RTC...\r\n");         
    RTC_Init(); //RTC初始化
 
    DrawTimeFrame();  						//畫時鐘框架
	
    USART3_RX_STA=0; //清空串口的接收標志位
    USART3_RX_CNT=0; //清空計數器
        
     printf("初始化DHT11...\r\n");   
    DHT11_Init(); //初始化DHT11
     printf("熱釋電模塊初始化...\r\n");   
    HumanDetection_Init(); //熱釋電模塊初始化
    
    //OLED_Clear(0);
    
    printf("開始進入while(1)\r\n");    
    while(1)
	{	
        key_val=KEY_GetValue();
        if(key_val)
        {
            page_cnt++;
            printf("page_cnt:%d\r\n",page_cnt);
            
             //清屏
             OLED_Clear(0);
            
             //時鐘頁面
            if(page_cnt==0)
            {
                DrawTimeFrame();  						//畫時鐘框架
                RTC->CRH|=1<<0; //開啟秒中斷
            }
            //溫濕度頁面
            else if(page_cnt==1)
            {
                RTC->CRH&=~(1<<0); //關閉秒中斷
                //溫濕度
                ShowTemperatureAndHumidity(temp,humi);
            }
            //ESP8266顯示
            else if(page_cnt==2)
            {
                ESP8266_ShowPageTable();
            }
            else if(page_cnt==3)
            {
                 Show_Switch(socket_state);
            }
            else
            {
                DrawTimeFrame();  						//畫時鐘框架
                RTC->CRH|=1<<0; //開啟秒中斷
                page_cnt=0;
            }    
        }
        
        //時間記錄
        DelayMs(10);
        TimeCnt++;
        wifi_TimeCnt++;
        if(TimeCnt>=100) //1000毫秒一次
        {
             TimeCnt=0;
             
             //讀取溫濕度數據
             DHT11_Read_Data(&temp,&humi);
            
            //濕度大于90就關閉開關
            if(humi>90)
            {
                socket_state=0;
                if(page_cnt==3)
                {
                     Show_Switch(socket_state);
                }
            }
            
            //溫濕度頁面
            if(page_cnt==1)
            {
                //溫濕度
                ShowTemperatureAndHumidity(temp,humi);
            }
	    }
        
        if(wifi_TimeCnt>=300) //3000毫秒一次
        {
            wifi_TimeCnt=0;
            //溫濕度1秒上傳一次
            sprintf((char*)Save_ESP8266_SendData,"#%d,%d",temp,humi);  //拼接數據
            sprintf((char*)Save_ESP8266_SendCmd,"AT+CIPSEND=0,%d\r\n",strlen((char*)Save_ESP8266_SendData));
            ESP8266_SendCmd(Save_ESP8266_SendCmd,(u8*)"OK",200); //WIFI設置發送數據長度
            ESP8266_SendData(Save_ESP8266_SendData,(u8*)"OK",100); //WIFI發送數據
        }
        
        //熱釋電狀態  為真表示有人
        if(HumanState)
        {
            LED1=0; //開燈
        }
        else  //為假
        {
            LED1=1; //關燈
        }
        
        /*輪詢掃描數據*/
        if(USART3_RX_STA)		  //WIFI 接收到一次數據了
        { 
            rlen=USART3_RX_CNT;	//得到本次接收到的數據長度
            USART3_RX_BUF[rlen]='\0';		  //添加結束符 
        //	printf("接收的數據: %s\r\n",USART3_RX_BUF);	//發送到串口   

            {
                /*判斷是否收到客戶端發來的數據  */
                p=strstr((char*)USART3_RX_BUF,"+IPD");
                if(p!=NULL) //正常數據格式: +IPD,0,7:LED1_ON    +IPD,0表示第0個客戶端   7:LED1_ON表示數據長度與數據
                {
                        /*解析上位機發來的數據*/
                        p=strstr((char*)USART3_RX_BUF,":");
                        if(p!=NULL)
                        {
                                p+=1; //向后偏移1個字節
                                if(*p=='*')  //設置RTC時間
                                {
                                        p+=1; //向后偏移,指向正確的時間
                                        time=p;
                                        rtc_clock.year=(time[0]-48)*1000+(time[1]-48)*100+(time[2]-48)*10+(time[3]-48)*1;
                                        rtc_clock.mon=(time[4]-48)*10+(time[5]-48)*1;
                                        rtc_clock.day=(time[6]-48)*10+(time[7]-48)*1;
                                        rtc_clock.hour=(time[8]-48)*10+(time[9]-48)*1;
                                        rtc_clock.min=(time[10]-48)*10+(time[11]-48)*1;
                                        rtc_clock.sec=(time[12]-48)*10+(time[13]-48)*1;
                                        RTC_SetTime(rtc_clock.year,rtc_clock.mon,rtc_clock.day,rtc_clock.hour,rtc_clock.min,rtc_clock.sec);

                                        if(page_cnt==0)
                                        {
                                            OLED_Clear(0); //OLED清屏
                                            DrawTimeFrame();//畫時鐘框架   
                                        }                                    
                                }
                                else if(strcmp(p,"LED1_ON")==0)
                                {
                                    socket_state=1;
                                }
                                else if(strcmp(p,"LED1_OFF")==0)
                                {
                                    socket_state=0;
                                }
                                
                                if(page_cnt==3)
                                {
                                     Show_Switch(socket_state);
                                }
                        }
                }
          }
            USART3_RX_STA=0;
            USART3_RX_CNT=0;
        }
    }        
}

4.2 STM32: rtc.c

#include "rtc.h"

//定義RTC標準結構體
struct RTC_CLOCK rtc_clock; 

/*
函數功能: RTC初始化函數
*/
void RTC_Init(void)
{
        //檢查是不是第一次配置時鐘
	u8 temp=0;
	if(BKP->DR1!=0X5051)//之前使用的不是LSE
	{	 
	  	RCC->APB1ENR|=1<<28;     //使能電源時鐘	    
		RCC->APB1ENR|=1<<27;     //使能備份時鐘	    
		PWR->CR|=1<<8;           //取消備份區寫保護
		RCC->BDCR|=1<<16;        //備份區域軟復位	   
		RCC->BDCR&=~(1<<16);     //備份區域軟復位結束	  	 
	    RCC->BDCR|=1<<0;         //開啟外部低速振蕩器 
	    while((!(RCC->BDCR&0X02))&&temp<250)//等待外部時鐘就緒	 
		{
			temp++;
			DelayMs(10);
		};
		
		if(temp>=250)  //return 1;//初始化時鐘失敗,晶振有問題	   
		{
			
			RCC->CSR|=1<<0; //開啟外部低速振蕩器 
			while(!(RCC->CSR&(1<<1)));//外部低速振蕩器 
			RCC->BDCR|=2<<8; //LSI作為RTC時鐘 
			BKP->DR1=0X5050; //標記使用LSI作為RTC時鐘
		}
		else 
        {
            RCC->BDCR|=1<<8; //LSE作為RTC時鐘 
            BKP->DR1=0X5051; //標記使用LSE作為RTC時鐘
        }
		RCC->BDCR|=1<<15;//RTC時鐘使能	
		
	  	while(!(RTC->CRL&(1<<5)));//等待RTC寄存器操作完成	 
    	while(!(RTC->CRL&(1<<3)));//等待RTC寄存器同步  
    	RTC->CRH|=0X01;  		  //允許秒中斷
    	while(!(RTC->CRL&(1<<5)));//等待RTC寄存器操作完成	 
		RTC->CRL|=1<<4;           //允許配置	 
		RTC->PRLH=0X0000;
		RTC->PRLL=32767;          //時鐘周期設置(有待觀察,看是否跑慢了?)理論值:32767	
		RTC_SetTime(2021,4,25,20,36,20);  //設置時間	  
		RTC->CRL&=~(1<<4);           //配置更新
		while(!(RTC->CRL&(1<<5)));   //等待RTC寄存器操作完成		 									  
	 	printf("FIRST TIME\n");
	}else//系統繼續計時
	{
    	while(!(RTC->CRL&(1<<3)));//等待RTC寄存器同步  
    	RTC->CRH|=0X01;  		  //允許秒中斷
    	while(!(RTC->CRL&(1<<5)));//等待RTC寄存器操作完成
		printf("OK\n");
	}		    				  
	STM32_NVIC_SetPriority(RTC_IRQn,2,2); //優先級   
    
}

extern void Update_FrameShow(void);
/*
函數功能: RTC鬧鐘中斷服務函數
*/
void RTC_IRQHandler(void)
{
	  u32 SecCnt;
		if(RTC->CRL&1<<0)
		{
				SecCnt=RTC->CNTH<<16;//獲取高位
				SecCnt|=RTC->CNTL;   //獲取低位
				RTC_GetTime(SecCnt); //轉換標準時間
				RTC_GetWeek(SecCnt);
			 // printf("%d-%d-%d %d:%d:%d week:%d\n",rtc_clock.year,rtc_clock.mon,rtc_clock.day,rtc_clock.hour,rtc_clock.min,rtc_clock.sec,rtc_clock.week);
				Update_FrameShow(); //更新顯示
				RTC->CRL&=~(1<<0); //清除秒中斷標志位
		}
		
		if(RTC->CRL&1<<1)
		{
//				printf("鬧鐘時間到達!....\n");
//			  BEEP=1;
//			  DelayMs(500);
//				BEEP=0;
				RTC->CRL&=~(1<<1); //清除鬧鐘中斷標志位
		}
}



//閏年的月份
static int mon_r[12]={31,29,31,30,31,30,31,31,30,31,30,31};
//平年的月份
static int mon_p[12]={31,28,31,30,31,30,31,31,30,31,30,31};


/*
函數功能: 設置RTC時間
函數形參:
    u32 year;   2018
	  u32 mon;     8
	  u32 day;
		u32 hour;
		u32 min;
	  u32 sec;
*/
void RTC_SetTime(u32 year,u32 mon,u32 day,u32 hour,u32 min,u32 sec)
{
		u32 i;
	  u32 SecCnt=0; //總秒數
		/*1. 累加已經過去的年份*/
		for(i=2017;iAPB1ENR|=1<<28;//使能電源時鐘
    RCC->APB1ENR|=1<<27;//使能備份時鐘
	PWR->CR|=1<<8;    //取消備份區寫保護
	//上面三步是必須的!
	RTC->CRL|=1<<4;   //允許配置 
	RTC->CNTL=SecCnt&0xffff;
	RTC->CNTH=SecCnt>>16;
	RTC->CRL&=~(1<<4);//配置更新
	while(!(RTC->CRL&(1<<5)));//等待RTC寄存器操作完成 
}


/*
函數功能: 獲取RTC時間
函數參數: u32 sec 秒單位時間
*/
void RTC_GetTime(u32 sec)
{
		u32 i;
		rtc_clock.year=2017; //基準年份
		
	  /*1. 計算當前的年份*/
	  while(1)
		{
			  if(RTC_GetYearState(rtc_clock.year))
				{
						if(sec>=366*24*60*60) //夠一年
						{
								sec-=366*24*60*60;
								rtc_clock.year++;
						}
						else break;
				}
				else
				{
						if(sec>=365*24*60*60) //夠一年
						{
								sec-=365*24*60*60;
								rtc_clock.year++;
						}
						else break;
				}
		}
		
		/*2. 計算當前的月份*/
		rtc_clock.mon=1;
		for(i=0;i<12;i++)
		{
				if(RTC_GetYearState(rtc_clock.year))
				{
					 	if(sec>=mon_r[i]*24*60*60)
						{
							 sec-=mon_r[i]*24*60*60;
							 rtc_clock.mon++;
						}
						else break;		
				}
				else
				{
						if(sec>=mon_p[i]*24*60*60)
						{
							 sec-=mon_p[i]*24*60*60;
							 rtc_clock.mon++;
						}
						else break;	
				}
		}
		
		/*3. 計算當前的天數*/
		rtc_clock.day=1;
		while(1)
		{
				if(sec>=24*60*60)
				{
						sec-=24*60*60;
						rtc_clock.day++;
				}
				else break;
		}
		
		/*4. 計算當前的小時*/
		rtc_clock.hour=0;
		while(1)
		{
				if(sec>=60*60)
				{
						sec-=60*60;
						rtc_clock.hour++;
				}
				else break;
		}
		
		/*5. 計算當前的分鐘*/
		rtc_clock.min=0;
		while(1)
		{
				if(sec>=60)
				{
						sec-=60;
						rtc_clock.min++;
				}
				else break;
		}
		
		/*6. 計算當前的秒*/
		rtc_clock.sec=sec;
}


/*
函數功能: 判斷年份是否是平年、閏年
返回值  : 0表示平年 1表示閏年
*/
u8 RTC_GetYearState(u32 year)
{
	 if((year%4==0&&year%100!=0)||year%400==0)
	 {
		 return 1;
	 }
	 return 0;
}


/*
函數功能: 獲取星期
*/
void RTC_GetWeek(u32 sec)
{
	u32 day1=sec/(60*60*24); //將秒單位時間轉為天數
	switch(day1%7)
	{
		case 0:
			rtc_clock.week=0;
			break;
	 	case 1:
			rtc_clock.week=1;
			break;
		case 2:
			rtc_clock.week=2;
			break;
		case 3:
			rtc_clock.week=3;
			break;
		case 4:
			rtc_clock.week=4;
			break;
		case 5:
			rtc_clock.week=5;
			break;
		case 6:
			rtc_clock.week=6;
			break;
	}
}

;i++)>
poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

4.3 ESP8266.c

#include "esp8266.h"

/*
函數功能:向ESP82668266發送命令
函數參數:
				cmd:發送的命令字符串
				ack:期待的應答結果,如果為空,則表示不需要等待應答
				waittime:等待時間(單位:10ms)
返 回 值:
				 0,發送成功(得到了期待的應答結果)
         1,發送失敗
*/
u8 ESP8266_SendCmd(u8 *cmd,u8 *ack,u16 waittime)
{
	u8 res=0; 
	USART3_RX_STA=0;
	USART3_RX_CNT=0;
	UsartStringSend(USART3,cmd);//發送命令
	if(ack&&waittime)		//需要等待應答
	{
		while(--waittime)	//等待倒計時
		{
			DelayMs(10);
			if(USART3_RX_STA)//接收到期待的應答結果
			{
				if(ESP8266_CheckCmd(ack))
				{
					res=0;
					//printf("cmd->ack:%s,%s\r\n",cmd,(u8*)ack);
					break;//得到有效數據 
				}
				USART3_RX_STA=0;
				USART3_RX_CNT=0;
			} 
		}
		if(waittime==0)res=1; 
	}
	return res;
}


/*
函數功能:ESP8266發送命令后,檢測接收到的應答
函數參數:str:期待的應答結果
返 回 值:0,沒有得到期待的應答結果
				 其他,期待應答結果的位置(str的位置)
*/
u8* ESP8266_CheckCmd(u8 *str)
{
	char *strx=0;
	if(USART3_RX_STA)  //接收到一次數據了
	{ 
		USART3_RX_BUF[USART3_RX_CNT]=0;//添加結束符
		strx=strstr((const char*)USART3_RX_BUF,(const char*)str); //查找是否應答成功
		//printf("RX=%s",USART3_RX_BUF);
	}
	return (u8*)strx;
}

/*
函數功能:向ESP8266發送指定數據
函數參數:
				data:發送的數據(不需要添加回車)
				ack:期待的應答結果,如果為空,則表示不需要等待應答
				waittime:等待時間(單位:10ms)
返 回 值:0,發送成功(得到了期待的應答結果)luojian
*/
u8 ESP8266_SendData(u8 *data,u8 *ack,u16 waittime)
{
	u8 res=0; 
	USART3_RX_STA=0;
	UsartStringSend(USART3,data);//發送數據
	if(ack&&waittime)		//需要等待應答
	{
		while(--waittime)	//等待倒計時
		{
			DelayMs(10);
			if(USART3_RX_STA)//接收到期待的應答結果
			{
				if(ESP8266_CheckCmd(ack))break;//得到有效數據 
				USART3_RX_STA=0;
				USART3_RX_CNT=0;
			} 
		}
		if(waittime==0)res=1; 
	}
	return res;
}
	
	

/*
函數功能:ESP8266退出透傳模式
返 回 值:0,退出成功;
         1,退出失敗
*/
u8 ESP8266_QuitTrans(void)
{
	while((USART3->SR&0X40)==0);	//等待發送空
	USART3->DR='+';      
	DelayMs(15);					//大于串口組幀時間(10ms)
	while((USART3->SR&0X40)==0);	//等待發送空
	USART3->DR='+';      
	DelayMs(15);					//大于串口組幀時間(10ms)
	while((USART3->SR&0X40)==0);	//等待發送空
	USART3->DR='+';      
	DelayMs(500);					//等待500ms
	return ESP8266_SendCmd("AT\r\n","OK",20);//退出透傳判斷.
}

/*
函數功能:獲取ESP8266模塊的連接狀態
返 回 值:0,未連接;1,連接成功.
*/
u8 ESP8266_ConstaCheck(void)
{
	u8 *p;
	u8 res;
	if(ESP8266_QuitTrans())return 0;			  //退出透傳 
	ESP8266_SendCmd("AT+CIPSTATUS\r\n",":",50);	//發送AT+CIPSTATUS指令,查詢連接狀態
	p=ESP8266_CheckCmd("+CIPSTATUS\r\n:"); 
	res=*p;																	//得到連接狀態	
	return res;
}

/*
函數功能:獲取ip地址
函數參數:ipbuf:ip地址輸出緩存區
*/
void ESP8266_GetWanip(u8* ipbuf)
{
	  u8 *p,*p1;
		if(ESP8266_SendCmd("AT+CIFSR\r\n","OK",50))//獲取WAN IP地址失敗
		{
			ipbuf[0]=0;
			return;
		}		
		p=ESP8266_CheckCmd(""");
		p1=(u8*)strstr((const char*)(p+1),""");
		*p1=0;
		sprintf((char*)ipbuf,"%s",p+1);	
}

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

五、QT設計的上位機代碼: Android手機APP+Windows系統上位機

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

?

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • Linux
    +關注

    關注

    87

    文章

    11230

    瀏覽量

    208934
  • STM32
    +關注

    關注

    2266

    文章

    10873

    瀏覽量

    354843
  • 智能家居
    +關注

    關注

    1926

    文章

    9516

    瀏覽量

    184331
  • 人體感應
    +關注

    關注

    3

    文章

    64

    瀏覽量

    16964
  • 智能插座
    +關注

    關注

    5

    文章

    194

    瀏覽量

    27698
收藏 人收藏

    評論

    相關推薦

    STM32F103×8/STM32F103×B MCU手冊

    1. Q: STM32F103C8T6如何正確配置時鐘系統?A: STM32F103C8T6的時鐘系統配置通常涉及HSE(高速外部時鐘)、HSI(高速內部時鐘)、PLL(相位鎖定環)等。配置時,首先
    發表于 11-18 15:14 ?0次下載

    19.3-星曈科技 openmv Hopenmv發送五個uchar Openmv+STM32F103C8T6視覺巡線小車 STM32循跡小車系列教程

    19.3-【星曈科技】openmv Hopenmv發送五個uchar Openmv+STM32F103C8T6視覺巡線小車 STM32循跡小車系列教程 使用OpenMV循跡 openMV尋跡與小車控制
    的頭像 發表于 08-20 11:32 ?812次閱讀
    19.3-星曈科技 openmv Hopenmv發送五個uchar Openmv+<b class='flag-5'>STM32F103C8T6</b>視覺巡線小車 <b class='flag-5'>STM32</b>循跡小車系列教程

    19.4-STM32接收數據-狀態顯示在屏幕 openMV尋跡與小車控制 Openmv+STM32F103C8T6視覺巡線小車

    19.4-STM32接收數據-狀態顯示在屏幕 openMV尋跡與小車控制 Openmv+STM32F103C8T6視覺巡線小車
    的頭像 發表于 08-20 11:12 ?780次閱讀
    19.4-<b class='flag-5'>STM32</b>接收數據-狀態顯示在屏幕 openMV尋跡與小車控制 Openmv+<b class='flag-5'>STM32F103C8T6</b>視覺巡線小車

    第七章-V1.5 STM32超聲波測距STM32F103C8t6超聲波避障小車 超聲波避障模塊 STM32超聲波測距

    5STM32超聲波測距STM32F103C8t6超聲波避障小車
    的頭像 發表于 08-15 16:25 ?601次閱讀
    第七章-V1.5 <b class='flag-5'>STM32</b>超聲波測距<b class='flag-5'>STM32F103C8t6</b>超聲波避障小車 超聲波避障模塊 <b class='flag-5'>STM32</b>超聲波測距

    怎么做才能在5.40版本的keil正常給F103c8t6下載程序?

    有一片F103c8t6,使用PDA下載器在5.14keil可以正常下載程序,但是在5.40版本的keil卻顯示\"Error: Not a genuine ST Device! Abort connection.\" 請問我該怎
    發表于 07-24 06:58

    STMC32F103C8T6核心板原理圖

    STM32F103C8T6原理圖
    發表于 07-15 11:47 ?3次下載

    CW2015_STM32F1XX_C8T6資料包分享

    CW2015_STM32F1XX_C8T6
    發表于 06-07 12:46 ?8次下載

    STM32為什么首選F103C8T6

    STM32F103C8T6是STMicroelectronics(ST)推出的一款基于ARMCortex-M3內核的32位微控制器單片機(MCU)。它具有一系列優勢和廣泛的應用場景。這里就來介紹一下
    的頭像 發表于 05-18 08:04 ?4859次閱讀
    學<b class='flag-5'>STM32</b>為什么首選<b class='flag-5'>F103C8T6</b>?

    STM32F030K6T6STM32F103C8T6的晶振換成25M的可以嗎?

    大神們,如果 把STM32F030K6T6STM32F103C8T6 的晶振換成25M的,可以嗎?
    發表于 04-26 06:12

    從程序STM32L151C8T6移植到STM32F103C8T6需要注意些什么?

    請問從程序STM32L151C8T6移植到STM32F103C8T6需要注意些什么?
    發表于 04-15 06:06

    用ST-LINK燒錄STM32F103C8T6程序,燒錄程序就會出現無法連接的情況是為什么?

    請教一個古老的問題.我用ST-LINK燒錄一個STM32F103C8T6的程序,并設置了燒錄完成自動運行,程序開始跑了。然后在中途過程中,如果我再燒錄程序就會出現無法連接,必須手動按下復位鍵(不松
    發表于 04-07 07:44

    用USB轉TTL線怎么把程序燒寫到stm32f103C8T6

    ,我們需要準備以下材料: 1. 一臺計算機(Windows操作系統) 2. STM32F103C8T6開發板 3. USB轉TTL線(也稱為串口線) 4. 充電線(用于給開發板供電) 5. ST-LINK
    的頭像 發表于 01-31 15:46 ?7072次閱讀

    stm32f103c8t6讀內部溫度

    STM32F103C8T6是一款32位的Cortex-M3內核微控制器,它擁有豐富的外設資源和強大的計算能力,可以廣泛應用于各種嵌入式系統中。其中一個重要的功能是能夠使用內部傳感器來讀取芯片的溫度
    的頭像 發表于 01-08 13:48 ?3526次閱讀

    stm32f103c8t6怎么連接噪聲傳感器

    (ADC引腳)。 在STM32F103C8T6上配置ADC功能。首先,你需要確定使用哪個ADC通道連接噪聲傳感器。然后,使用STM32的開發工具,如STCubeMX或者Keil等,配置GPIO引腳
    的頭像 發表于 12-21 16:10 ?1398次閱讀

    在GD32F103移植STM32F103代碼

    使用相同FLASH和管腳數量相同的芯片,例如GDF103C8T6移植STM32F103C8T6程序。雖然兩個款芯片的寄存器地址以及架構基本相同。但是需要注意的是GD32F10x主頻是108兆,但是
    的頭像 發表于 12-15 13:37 ?1603次閱讀
    在GD32<b class='flag-5'>F103</b>移植<b class='flag-5'>STM32F103</b>代碼