?
一、環境介紹
程序開發IDE: keil5
STM32程序風格: 采用寄存器方式開發,注釋齊全,執行效率高,方便移植
手機APP: 采用QT設計,程序支持跨平臺編譯運行(Android、IOS、Windows、Linux都可以編譯運行,對應平臺上QT的環境搭建,之前博客已經發了文章講解)
硬件包含:SRM32F103C8T6最小系統板、紅外熱釋電人體感應模塊、DHT11溫濕度傳感器、0.96寸單色OLED顯示屏、ESP8266、繼電器、RGB大功率白燈.
?
?
?
?
二、功能介紹
這是基于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 溫濕度傳感器
?
DHT11數字溫濕度傳感器是一款含有已校準數字信號輸出的溫濕度復合傳感器,它應用專用的數字模塊采集技術和溫濕度傳感技術,確保產品具有極高的可靠性和卓越的長期穩定性。傳感器包括一個電阻式感濕元件和一個NTC測溫元件,并與一個高性能8位單片機相連接。因此該產品具有品質卓越、超快響應、抗干擾能力強、性價比極高等優點。每個DHT11傳感器都在極為精確的濕度校驗室中進行校準。校準系數以程序的形式存在OTP內存中,傳感器內部在檢測信號的處理過程中要調用這些校準系數。單線制串行接口,使系統集成變得簡易快捷。超小的體積、極低的功耗,使其成為該類應用中,在苛刻應用場合的最佳選擇。產品為4針單排引腳封裝,連接方便。
3.2熱釋電傳感器
?
?
熱釋電紅外傳感器在結構上引入場效應管,其目的在于完成阻抗變換。由于熱釋電元輸出的是電荷信號,并不能直接使用,因而需要用電阻將其轉換為電壓形式。故引入的N溝道結型場效應管應接成共漏形式來完成阻抗變換。熱釋電紅外傳感器由傳感探測元、干涉濾光片和場效應管匹配器三部分組成。設計時應將高熱電材料制成一定厚度的薄片,并在它的兩面鍍上金屬電極,然后加電對其進行極化,這樣便制成了熱釋電探測元。
熱釋電紅外傳感器的外形如上圖所示。其可以檢測人體發出的紅外線信號,并將其轉換成電信號輸出。傳感器頂部的長方形窗口加有濾光片,可以使人體發出的9~10μm波長的紅外線通過,而其它波長的紅外線被濾除,這樣便提高了抗干擾能。熱釋電紅外傳感器由濾光片、熱釋電探測元和前置放大器組成,補償型熱釋電傳感器還帶有溫度補償元件,圖所示為熱釋電傳感器的內部結構。為防止外部環境對傳感器輸出信號的干擾,上述元件被真空封裝在一個金屬營內。熱釋電傳感器的濾光片為帶通濾光片,它封裝在傳感器殼體的頂端,使特定波長的紅外輻射選擇性地通過,到達熱釋電探測元+在其截止范圍外的紅外輻射則不能通過。
熱釋電探測元是熱釋電傳感器的核心元件,它是在熱釋電晶體的兩面鍍上金屬電極后,加電極化制成,相當于一個以熱釋電晶體為電介質的平板電容器。當它受到非恒定強度的紅外光照射時,產生的溫度變化導致其表面電極的電荷密度發生改變,從而產生熱釋電電流。
前置放大器由一個高內阻的場效應管源極跟隨器構成,通過阻抗變換,將熱釋電探測元微弱的電流信號轉換為有用的電壓信號輸出。
3.3ESP8266串口WIFI模塊
?
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顯示屏
?
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大功率燈模塊
?
LED燈是一塊電致發光的半導體材料芯片,用銀膠或白膠固化到支架上,然后用銀線或金線連接芯片和電路板,四周用環氧樹脂密封,起到保護內部芯線的作用,最后安裝外殼,所以 LED 燈的抗震性能好。
LED(Light Emitting Diode),發光二極管,是一種能夠將電能轉化為可見光的固態的半導體器件,它可以直接把電轉化為光。LED的心臟是一個半導體的晶片,晶片的一端附在一個支架上,一端是負極,另一端連接電源的正極,使整個晶片被環氧樹脂封裝起來。
半導體晶片由兩部分組成,一部分是P型半導體,在它里面空穴占主導地位,另一端是N型半導體,在這邊主要是電子。但這兩種半導體連接起來的時候,它們之間就形成一個P-N結。當電流通過導線作用于這個晶片的時候,電子就會被推向P區,在P區里電子跟空穴復合,然后就會以光子的形式發出能量,這就是LED燈發光的原理。而光的波長也就是光的顏色,是由形成P-N結的材料決定的。
LED可以直接發出紅、黃、藍、綠、青、橙、紫、白色的光。
3.6STM32F103C8T6最小系統板
?
STM32F系列屬于中低端的32位ARM微控制器,該系列芯片是意法半導體(ST)公司出品,其內核是Cortex-M3。
該系列芯片按片內Flash的大小可分為三大類:小容量(16K和32K)、中容量(64K和128K)、大容量(256K、384K和512K)。
芯片集成定時器Timer,CAN,ADC,SPI,I2C,USB,UART等多種外設功能。
內核
--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杜邦線
?
3.8 繼電器
?
?
四、STM32核心代碼
?
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++)>
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);
}
五、QT設計的上位機代碼: Android手機APP+Windows系統上位機
?
-
Linux
+關注
關注
87文章
11230瀏覽量
208934 -
STM32
+關注
關注
2266文章
10873瀏覽量
354843 -
智能家居
+關注
關注
1926文章
9516瀏覽量
184331 -
人體感應
+關注
關注
3文章
64瀏覽量
16964 -
智能插座
+關注
關注
5文章
194瀏覽量
27698
發布評論請先 登錄
相關推薦
評論