1 簡(jiǎn)介
Hi,大家好,學(xué)長(zhǎng)今天向大家介紹一個(gè) 單片機(jī)項(xiàng)目
基于STM32的空氣質(zhì)量檢測(cè)儀
大家可用于 課程設(shè)計(jì) 或 畢業(yè)設(shè)計(jì)
2 系統(tǒng)設(shè)計(jì)概述
如今人們大約 80%的時(shí)間是在室內(nèi)度過(guò)的, 室內(nèi)空氣質(zhì)量與我們每個(gè)人的工作和生活都息息相關(guān), 因此對(duì)生活環(huán)境的空氣質(zhì)量提出了更高的要求。 針對(duì)霧霾、 室內(nèi)裝修等污染問(wèn)題, 人們還沒(méi)有有效的辦法控制空氣中的有害物質(zhì), 這一問(wèn)題也成為人們的健康隱患, 因此對(duì)室內(nèi)的空氣質(zhì)量進(jìn)行檢測(cè)至關(guān)重要。
當(dāng)室內(nèi)有害氣體的濃度超標(biāo)時(shí), 會(huì)對(duì)人們的身心健康帶來(lái)不可忽視的影響。 為了讓人們及時(shí)準(zhǔn)確地獲得室內(nèi)有害氣體甲醛、 PM2.5 的濃度, 采取有效措施改善生活環(huán)境的空氣質(zhì)量, 提高工作效率。 根據(jù)我國(guó)室內(nèi)空氣監(jiān)管標(biāo)準(zhǔn) GB50325-2010, 學(xué)長(zhǎng)設(shè)計(jì)了一款基于STM32F103C8T6 單片機(jī)的便攜式、 低功耗室內(nèi)空氣質(zhì)量檢測(cè)儀。 該檢測(cè)儀可以實(shí)時(shí)測(cè)量室內(nèi)的溫濕度、 甲醛濃度、 PM2.5 濃度, 并在液晶顯示屏 LCD12864 上實(shí)時(shí)顯示出來(lái), 當(dāng)甲醛濃度或 PM2.5 濃度超過(guò)報(bào)警值時(shí), 測(cè)試儀通過(guò)亮紅燈和蜂鳴器鳴叫的方式提醒用戶室內(nèi)有害氣體超標(biāo)。
用戶可以根據(jù)需要通過(guò) GSM 短信方式將室內(nèi)空氣質(zhì)量情況發(fā)送到移動(dòng)終端, 最后系統(tǒng)在 Visual Studio 2012 平臺(tái)運(yùn)用 C#語(yǔ)言開發(fā)上位機(jī), 可以實(shí)時(shí)顯示空氣質(zhì)量指標(biāo)。
3 系統(tǒng)總體方案
學(xué)長(zhǎng)設(shè)計(jì)為了能夠?qū)崟r(shí)檢測(cè)室內(nèi)的空氣質(zhì)量情況, 提高系統(tǒng)的穩(wěn)定性和自動(dòng)化程度, 將系統(tǒng)分為硬件設(shè)計(jì)和軟件設(shè)計(jì)。 本文研制的室內(nèi) 空氣質(zhì) 量檢測(cè)儀是基于 32 位單片機(jī)STM32C8T6 進(jìn)行設(shè)計(jì)的, 將整個(gè)系統(tǒng)模塊化, 主要包括 STM32 最小系統(tǒng)、 溫濕度檢測(cè)模塊、 甲醛氣體檢測(cè)模塊、 PM2.5 檢測(cè)模塊、 按鍵、 液晶顯示器、 蜂鳴器、 GSM 模塊、藍(lán)牙模塊、 移動(dòng)終端和上位機(jī)以及電源管理電路。 其系統(tǒng)結(jié)構(gòu)框圖如圖所示。
在整個(gè)室內(nèi)空氣質(zhì)量檢測(cè)系統(tǒng)中, STM32 芯片是整個(gè)系統(tǒng)的主控制器, 整個(gè)檢測(cè)系統(tǒng)都是在它的控制下完成的, 并且 STM32 為測(cè)試儀提供外部接口, 如液晶顯示屏、
按鍵、 數(shù)據(jù)采集電路。 首先通過(guò)溫濕度、 甲醛濃度以及 PM2.5 等傳感器進(jìn)行溫濕度、 甲醛氣體及 PM2.5 濃度信號(hào)采集, 再將采集到的信號(hào)送至中央處理單元 STM32 單片機(jī)進(jìn)行信號(hào)轉(zhuǎn)換、 放大和處理。 通過(guò)庫(kù)函數(shù)編程控制檢測(cè)系統(tǒng)的工作流程, 可以通過(guò)按鍵輸入控制輸出顯示不同的測(cè)量數(shù)據(jù)以及設(shè)置甲醛和 PM2.5 濃度報(bào)警值, 當(dāng)氣體濃度值超過(guò)報(bào)警值時(shí), 通過(guò)蜂鳴器鳴叫的方式提醒用戶室內(nèi)有害氣體超標(biāo), 并在液晶屏上實(shí)時(shí)顯示。利用 GSM 通信模塊可以通過(guò)短信的形式將用戶室內(nèi)的空氣質(zhì)量情況發(fā)到移動(dòng)終端, 實(shí)現(xiàn)了儀器的智能化。 STM32 最小系統(tǒng)通過(guò)藍(lán)牙模塊與上位機(jī)通信, 上位機(jī)可以實(shí)時(shí)顯示測(cè)量數(shù)據(jù)。
4 硬件設(shè)計(jì)方案
學(xué)長(zhǎng)設(shè)計(jì)的系統(tǒng)所研究的室內(nèi)空氣質(zhì)量檢測(cè)儀, 系統(tǒng)硬件由 STM32 單片機(jī)、 電源、 溫濕度傳感器、 甲醛檢測(cè)模塊、 PM2. 5 檢測(cè)模塊、 按鍵輸入模塊、 液晶顯示模塊、 報(bào)警電路、 GSM短信接收模塊和藍(lán)牙模塊等部分組成。 該系統(tǒng)采用 USB 供電, 通過(guò) USB 數(shù)據(jù)線與電腦USB 接口相連, 輸出+5V 的直流電壓, 輸出電壓在 500mA 左右, 在目前普遍采用 USB接口的情況, 這樣也提高了電源的來(lái)源以及可靠性, 用戶也可以通過(guò)手機(jī)充電器、 充電寶對(duì)測(cè)試儀供電, 給用戶使用該測(cè)試儀帶來(lái)了很大的便利 模塊化的設(shè)計(jì)可以使系統(tǒng)設(shè)計(jì)更加簡(jiǎn)單, 系統(tǒng)功耗降低, 操作變得簡(jiǎn)單, 可以進(jìn)行現(xiàn)場(chǎng)檢測(cè), 通過(guò)液晶顯示屏用戶直觀地讀出測(cè)量數(shù)據(jù)。 空氣質(zhì)量檢測(cè)儀檢的整體硬件設(shè)計(jì)結(jié)構(gòu)如下圖所示。
4.1 stm32 主控
STM32C8T6(最小核心板),當(dāng)然,用其他型號(hào)的32,如STM32ZET6也是可以的。
4.2 溫度采集模塊
系統(tǒng)采用的傳感器為含有已校準(zhǔn)數(shù)字信號(hào)輸出的溫濕度復(fù)合傳感器 DHT11。該傳感器是一款已經(jīng)得到業(yè)界廣泛認(rèn)可的安全性高、 穩(wěn)定性較強(qiáng)、 性價(jià)比高的集中數(shù)字化傳感器的溫濕度。
傳感器可以傳輸信號(hào)高達(dá) 20 米以上, 可以滿足大部分場(chǎng)合的測(cè)量要求;具有四針的單排引腳封裝系統(tǒng), 這樣外部連接就變得簡(jiǎn)單; 同時(shí)因其超小的體積和極低的功耗在暖通空調(diào)、 汽車、 氣象站、 除濕器等各行業(yè)廣泛應(yīng)用。
4.3 甲醛濃度檢測(cè)模塊
綜合考慮甲醛檢測(cè)精度和測(cè)量成本后, 系統(tǒng)選用 ZE08-CH 2 O 電化學(xué)甲醛傳感器檢測(cè)室內(nèi)空氣中的甲醛濃度含量, 因該模塊精度高、 功耗低,體積小的特點(diǎn), 成為了制作空氣質(zhì)量檢測(cè)儀、 空氣凈化器的優(yōu)良器件。 相比其他甲醛
傳感器主要有以下特點(diǎn) :
- (1) 分辨率和靈敏度高;
- (2) 功耗低、 使用壽命長(zhǎng);
- (2) UART、 模擬電壓信號(hào)、 PWM 等多方式輸出;
- (3) 具有優(yōu)秀的抗干擾性、 穩(wěn)定性高;
- (4) 內(nèi)置溫度補(bǔ)償單元, 線性輸出。
4.4 PM2. 5 濃度檢測(cè)模塊
系統(tǒng)選用夏普公司授權(quán)弘成基科技有限公司生產(chǎn)的型號(hào)為 YW-51GJ 的 PM2.5 氣敏粉塵傳感器, 該產(chǎn)品主要針對(duì)香煙顆粒、 細(xì)微顆粒等特別細(xì)微的顆粒等檢測(cè)對(duì)象, 通過(guò)脈沖高度來(lái)判斷細(xì)顆粒物濃度, 常用于空氣凈化器、 車載空氣凈化系統(tǒng)以及民用空氣質(zhì)量檢測(cè)系統(tǒng)中, 符合本系統(tǒng)的設(shè)計(jì)要求。
YW-51GJ PM2.5 粉塵傳感器實(shí)物圖如圖所示。
可以看到該 PM2.5 傳感器中間有一個(gè)圓孔, 其內(nèi)部安裝紅外線發(fā)光二極管和光電晶體管, 當(dāng)微小顆粒物進(jìn)入傳感器的圓孔內(nèi)時(shí), 根據(jù)微小顆粒物對(duì)光的散射原理, 當(dāng)細(xì)顆粒物(PM2.5) 經(jīng)過(guò)傳感器內(nèi)部的檢測(cè)孔時(shí)會(huì)對(duì)光線形成散射作用。 部分光線通過(guò)光軸,經(jīng)過(guò)透鏡后聚焦到感光元件上, 光信號(hào)經(jīng)過(guò)感光元件轉(zhuǎn)換為電信號(hào)輸出。
4.5 液晶顯示模塊設(shè)計(jì)
4.6 GSM 模塊
經(jīng)綜合考慮學(xué)長(zhǎng)選用了 SIM800C GSM 無(wú)線通信模塊, 這是 SIMCOM 公司生產(chǎn)的基于 MT6261 芯片平臺(tái)的新一代 GSM 模塊, 性價(jià)比高, 支持藍(lán)牙功能, 支持 51、MSP430、STM32 等主流單片機(jī)開發(fā) 。
在該模塊中, 通信接口為 TTL 電平串口, 引出四根排針與控制器通信。 在 IPX 天線接口連接通信天線, Micro-SIM 卡卡座可以插入移動(dòng)、 聯(lián)通和物聯(lián)網(wǎng)卡, 插入 SIM 卡即可發(fā)信息給用戶移動(dòng)終端, SIM 卡插進(jìn)去聽(tīng)到“咔噠” 響聲表明插卡正確, SIM800C GSM 模塊實(shí)物圖如圖所示。
4.7 藍(lán)牙模塊
為了能夠?qū)崿F(xiàn)測(cè)試儀與上位機(jī)的通信, 本文采用的是 HC-05 嵌入式藍(lán)牙串口通信模塊[65] , 分為兩個(gè)小模塊 a、 b, 如下圖所示依次左右排列, 模塊 a 與通過(guò) USB 接口與電腦相連, 模塊 b 通過(guò)四根排母線與測(cè)試儀對(duì)應(yīng)的排針相連接。
為了實(shí)現(xiàn)主控制器 STM32 與藍(lán)牙模塊的連接, 通過(guò)導(dǎo)線連接對(duì)應(yīng)接口, 藍(lán)牙接口電路原理圖如圖所示。
5 軟件部分設(shè)計(jì)
系統(tǒng)主要實(shí)現(xiàn)溫濕度、 甲醛濃度以及 PM2.5 濃度的檢測(cè)與顯示功能, 通過(guò)傳感器不斷采集數(shù)據(jù), 同時(shí)對(duì)這些數(shù)據(jù)進(jìn)行處理、 修正和補(bǔ)償, 將數(shù)據(jù)轉(zhuǎn)換為溫濕度以及氣體濃度進(jìn)行顯示, 讓用戶可以知道室內(nèi)的空氣質(zhì)量。 與硬件系統(tǒng)一樣分模塊設(shè)計(jì), 軟件設(shè)計(jì)分成人機(jī)交互模塊和數(shù)據(jù)處理模塊兩部分
5.1 初始化
系統(tǒng)上電后, 先對(duì)系統(tǒng)的各模塊傳感器、 12864LCD 顯示屏、 按鍵等外設(shè)進(jìn)行初始化, 設(shè)置標(biāo)志位和定時(shí)器。 之后讀取溫濕度、 甲醛濃度、 PM2.5 濃度對(duì)應(yīng)的模擬電流電壓等電信號(hào)參數(shù)值, 將數(shù)據(jù)進(jìn)行 AD 轉(zhuǎn)換, 對(duì)數(shù)字量處理后轉(zhuǎn)換為對(duì)應(yīng)的溫濕度、 甲醛濃度、 PM2.5 濃度顯示出來(lái)。 軟件設(shè)計(jì)的主程序流程圖下圖所示。
5.2 溫濕度檢測(cè)程序設(shè)計(jì)
DHT11 溫濕度傳感器硬件設(shè)計(jì)可以知道溫濕度檢測(cè)電路連接十分簡(jiǎn)單,DHT11 通過(guò)引腳 2(DATA) 給 STM32 發(fā)送單總線數(shù)字信號(hào)。 在電路開始工作的時(shí)候,為了跳過(guò)不穩(wěn)定狀態(tài), DHT11 需要用一秒的時(shí)間不發(fā)任何指令。
5.3 甲醛濃度檢測(cè)程序設(shè)計(jì)
由甲醛硬件模塊設(shè)計(jì)可以知道本系統(tǒng)采用 ZE08-CH2O 電化學(xué)甲醛傳感器采集甲醛濃度信號(hào), 利用系統(tǒng) STM32 單片機(jī)自帶的 2 個(gè) AD 轉(zhuǎn)換器對(duì)甲醛濃度進(jìn)行采集, 這些AD 轉(zhuǎn)換器具有 10 個(gè)通道, 可以單獨(dú)使用, 也可以實(shí)現(xiàn)復(fù)用。
5.4 PM2. 5 濃度檢測(cè)程序設(shè)計(jì)
章 PM2.5 硬件設(shè)計(jì)可以知道系統(tǒng)選用了 YM-51 PM2.5 傳感器, 通過(guò)該模塊可以對(duì)室內(nèi) PM2.5 濃度測(cè)量, 傳感器通過(guò) RXD 向 STM32 發(fā)送數(shù)據(jù), 接線圖如下所示
PM2.5 濃度檢測(cè)軟件程序流程與甲醛濃度檢測(cè)流程類似, 首先關(guān)閉中斷, 再對(duì)與PM2.5 檢測(cè)模塊相關(guān)的時(shí)鐘、 AD 轉(zhuǎn)換器進(jìn)行初始化設(shè)置, 確定 AD 轉(zhuǎn)換器的工作模式、觸發(fā)方式等。 之后打開中斷在中斷服務(wù)程序中實(shí)現(xiàn)數(shù)據(jù)采集, 1 秒內(nèi)采集 10 次, 對(duì)這些數(shù)據(jù)采用限幅平均濾波法進(jìn)行軟件濾波, 去除因電路干擾產(chǎn)生干擾有效數(shù)據(jù)的毛刺。
5.5 短信發(fā)送程序設(shè)計(jì)
章 GSM 模塊硬件設(shè)計(jì)可知本文采用 SIM800C GSM 模塊實(shí)現(xiàn)短信收發(fā)的功能。 手機(jī)短消息主要采用 PDU(協(xié)議數(shù)據(jù)單元) 模式和 TEXT 模式, PDU 模式同時(shí)支持中文和英文短信, 而 TEXT 模式只支持英文短信
效果展示
設(shè)計(jì)好各個(gè)模塊原理圖, 然后制作出 PCB 板, 通過(guò)導(dǎo)線將各個(gè)模塊與系統(tǒng)主板連接, 室內(nèi)空氣質(zhì)量檢測(cè)檢測(cè)儀實(shí)物圖如下所示。
室內(nèi)空氣質(zhì)量實(shí)時(shí)顯示文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-497588.html
短信收發(fā)測(cè)試文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-497588.html
6 項(xiàng)目源碼
6.1 ADC部分
#include "adc.h"
#include "delay.h"
//初始化ADC
//這里我們僅以規(guī)則通道為例
//我們默認(rèn)將開啟通道0~3
void Adc_Init(void)
{
ADC_InitTypeDef ADC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1 , ENABLE ); //使能ADC1通道時(shí)鐘
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //設(shè)置ADC分頻因子6 72M/6=12,ADC最大時(shí)間不能超過(guò)14M
//PA1 作為模擬通道輸入引腳
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模擬輸入引腳
GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC_DeInit(ADC1); //復(fù)位ADC1,將外設(shè) ADC1 的全部寄存器重設(shè)為缺省值
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:ADC1和ADC2工作在獨(dú)立模式
ADC_InitStructure.ADC_ScanConvMode = DISABLE; //模數(shù)轉(zhuǎn)換工作在單通道模式
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //模數(shù)轉(zhuǎn)換工作在單次轉(zhuǎn)換模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //轉(zhuǎn)換由軟件而不是外部觸發(fā)啟動(dòng)
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC數(shù)據(jù)右對(duì)齊
ADC_InitStructure.ADC_NbrOfChannel = 1; //順序進(jìn)行規(guī)則轉(zhuǎn)換的ADC通道的數(shù)目
ADC_Init(ADC1, &ADC_InitStructure); //根據(jù)ADC_InitStruct中指定的參數(shù)初始化外設(shè)ADCx的寄存器
ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1
ADC_ResetCalibration(ADC1); //使能復(fù)位校準(zhǔn)
while(ADC_GetResetCalibrationStatus(ADC1)); //等待復(fù)位校準(zhǔn)結(jié)束
ADC_StartCalibration(ADC1); //開啟AD校準(zhǔn)
while(ADC_GetCalibrationStatus(ADC1)); //等待校準(zhǔn)結(jié)束
// ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的軟件轉(zhuǎn)換啟動(dòng)功能
}
//獲得ADC值
//ch:通道值 0~3
u16 Get_Adc(u8 ch)
{
//設(shè)置指定ADC的規(guī)則組通道,一個(gè)序列,采樣時(shí)間
ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 ); //ADC1,ADC通道,采樣時(shí)間為239.5周期
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的軟件轉(zhuǎn)換啟動(dòng)功能
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待轉(zhuǎn)換結(jié)束
return ADC_GetConversionValue(ADC1); //返回最近一次ADC1規(guī)則組的轉(zhuǎn)換結(jié)果
}
u16 Get_Adc_Average(u8 ch,u8 times)
{
u32 temp_val=0;
u8 t;
for(t=0;t<times;t++)
{
temp_val+=Get_Adc(ch);
delay_ms(5);
}
return temp_val/times;
}
6.2 DS18B20
#include "ds18b20.h"
#include "delay.h"
//復(fù)位DS18B20
void DS18B20_Rst(void)
{
DS18B20_IO_OUT(); //SET PA0 OUTPUT
DS18B20_DQ_OUT=0; //拉低DQ
delay_us(750); //拉低750us
DS18B20_DQ_OUT=1; //DQ=1
delay_us(15); //15US
}
//等待DS18B20的回應(yīng)
//返回1:未檢測(cè)到DS18B20的存在
//返回0:存在
u8 DS18B20_Check(void)
{
u8 retry=0;
DS18B20_IO_IN();//SET PA0 INPUT
while (DS18B20_DQ_IN&&retry<200)
{
retry++;
delay_us(1);
};
if(retry>=200)return 1;
else retry=0;
while (!DS18B20_DQ_IN&&retry<240)
{
retry++;
delay_us(1);
};
if(retry>=240)return 1;
return 0;
}
//從DS18B20讀取一個(gè)位
//返回值:1/0
u8 DS18B20_Read_Bit(void) // read one bit
{
u8 data;
DS18B20_IO_OUT();//SET PA0 OUTPUT
DS18B20_DQ_OUT=0;
delay_us(2);
DS18B20_DQ_OUT=1;
DS18B20_IO_IN();//SET PA0 INPUT
delay_us(12);
if(DS18B20_DQ_IN)data=1;
else data=0;
delay_us(50);
return data;
}
//從DS18B20讀取一個(gè)字節(jié)
//返回值:讀到的數(shù)據(jù)
u8 DS18B20_Read_Byte(void) // read one byte
{
u8 i,j,dat;
dat=0;
for (i=1;i<=8;i++)
{
j=DS18B20_Read_Bit();
dat=(j<<7)|(dat>>1);
}
return dat;
}
//寫一個(gè)字節(jié)到DS18B20
//dat:要寫入的字節(jié)
void DS18B20_Write_Byte(u8 dat)
{
u8 j;
u8 testb;
DS18B20_IO_OUT();//SET PA0 OUTPUT;
for (j=1;j<=8;j++)
{
testb=dat&0x01;
dat=dat>>1;
if (testb)
{
DS18B20_DQ_OUT=0;// Write 1
delay_us(2);
DS18B20_DQ_OUT=1;
delay_us(60);
}
else
{
DS18B20_DQ_OUT=0;// Write 0
delay_us(60);
DS18B20_DQ_OUT=1;
delay_us(2);
}
}
}
//開始溫度轉(zhuǎn)換
void DS18B20_Start(void)// ds1820 start convert
{
DS18B20_Rst();
DS18B20_Check();
DS18B20_Write_Byte(0xcc);// skip rom
DS18B20_Write_Byte(0x44);// convert
}
//初始化DS18B20的IO口 DQ 同時(shí)檢測(cè)DS的存在
//返回1:不存在
//返回0:存在
u8 DS18B20_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能PORTA口時(shí)鐘
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //PORTA0 推挽輸出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_SetBits(GPIOA,GPIO_Pin_0); //輸出1
DS18B20_Rst();
return DS18B20_Check();
}
//從ds18b20得到溫度值
//精度:0.1C
//返回值:溫度值 (-550~1250)
short DS18B20_Get_Temp(void)
{
u8 temp;
u8 TL,TH;
short tem;
DS18B20_Start (); // ds1820 start convert
DS18B20_Rst();
DS18B20_Check();
DS18B20_Write_Byte(0xcc);// skip rom
DS18B20_Write_Byte(0xbe);// convert
TL=DS18B20_Read_Byte(); // LSB
TH=DS18B20_Read_Byte(); // MSB
if(TH>7)
{
TH=~TH;
TL=~TL;
temp=0;//溫度為負(fù)
}else temp=1;//溫度為正
tem=TH; //獲得高八位
tem<<=8;
tem+=TL;//獲得底八位
tem=(float)tem*0.625;//轉(zhuǎn)換
if(temp)return tem; //返回溫度值
else return -tem;
}
6.3 RTC部分
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "rtc.h"
//Mini STM32開發(fā)板
//RTC實(shí)時(shí)時(shí)鐘 驅(qū)動(dòng)代碼
//正點(diǎn)原子@ALIENTEK
//2010/6/6
_calendar_obj calendar;//時(shí)鐘結(jié)構(gòu)體
static void RTC_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn; //RTC全局中斷
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占優(yōu)先級(jí)1位,從優(yōu)先級(jí)3位
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //先占優(yōu)先級(jí)0位,從優(yōu)先級(jí)4位
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能該通道中斷
NVIC_Init(&NVIC_InitStructure); //根據(jù)NVIC_InitStruct中指定的參數(shù)初始化外設(shè)NVIC寄存器
}
//實(shí)時(shí)時(shí)鐘配置
//初始化RTC時(shí)鐘,同時(shí)檢測(cè)時(shí)鐘是否工作正常
//BKP->DR1用于保存是否第一次配置的設(shè)置
//返回0:正常
//其他:錯(cuò)誤代碼
u8 RTC_Init(void)
{
//檢查是不是第一次配置時(shí)鐘
u8 temp=0;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); //使能PWR和BKP外設(shè)時(shí)鐘
PWR_BackupAccessCmd(ENABLE); //使能后備寄存器訪問(wèn)
if (BKP_ReadBackupRegister(BKP_DR1) != 0x5050) //從指定的后備寄存器中讀出數(shù)據(jù):讀出了與寫入的指定數(shù)據(jù)不相乎
{
BKP_DeInit(); //復(fù)位備份區(qū)域
RCC_LSEConfig(RCC_LSE_ON); //設(shè)置外部低速晶振(LSE),使用外設(shè)低速晶振
while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET) //檢查指定的RCC標(biāo)志位設(shè)置與否,等待低速晶振就緒
{
temp++;
delay_ms(10);
}
if(temp>=250)return 1;//初始化時(shí)鐘失敗,晶振有問(wèn)題
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //設(shè)置RTC時(shí)鐘(RTCCLK),選擇LSE作為RTC時(shí)鐘
RCC_RTCCLKCmd(ENABLE); //使能RTC時(shí)鐘
RTC_WaitForLastTask(); //等待最近一次對(duì)RTC寄存器的寫操作完成
RTC_WaitForSynchro(); //等待RTC寄存器同步
RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能RTC秒中斷
RTC_WaitForLastTask(); //等待最近一次對(duì)RTC寄存器的寫操作完成
RTC_EnterConfigMode();/// 允許配置
RTC_SetPrescaler(32767); //設(shè)置RTC預(yù)分頻的值
RTC_WaitForLastTask(); //等待最近一次對(duì)RTC寄存器的寫操作完成
RTC_Set(2020,5,5,12,59,00); //設(shè)置時(shí)間
RTC_ExitConfigMode(); //退出配置模式
BKP_WriteBackupRegister(BKP_DR1, 0X5050); //向指定的后備寄存器中寫入用戶程序數(shù)據(jù)
}
else//系統(tǒng)繼續(xù)計(jì)時(shí)
{
RTC_WaitForSynchro(); //等待最近一次對(duì)RTC寄存器的寫操作完成
RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能RTC秒中斷
RTC_WaitForLastTask(); //等待最近一次對(duì)RTC寄存器的寫操作完成
}
RTC_NVIC_Config();//RCT中斷分組設(shè)置
RTC_Get();//更新時(shí)間
return 0; //ok
}
//RTC時(shí)鐘中斷
//每秒觸發(fā)一次
//extern u16 tcnt;
void RTC_IRQHandler(void)
{
if (RTC_GetITStatus(RTC_IT_SEC) != RESET)//秒鐘中斷
{
RTC_Get();//更新時(shí)間
}
if(RTC_GetITStatus(RTC_IT_ALR)!= RESET)//鬧鐘中斷
{
RTC_ClearITPendingBit(RTC_IT_ALR); //清鬧鐘中斷
}
RTC_ClearITPendingBit(RTC_IT_SEC|RTC_IT_OW); //清鬧鐘中斷
RTC_WaitForLastTask();
}
//判斷是否是閏年函數(shù)
//月份 1 2 3 4 5 6 7 8 9 10 11 12
//閏年 31 29 31 30 31 30 31 31 30 31 30 31
//非閏年 31 28 31 30 31 30 31 31 30 31 30 31
//輸入:年份
//輸出:該年份是不是閏年.1,是.0,不是
u8 Is_Leap_Year(u16 year)
{
if(year%4==0) //必須能被4整除
{
if(year%100==0)
{
if(year%400==0)return 1;//如果以00結(jié)尾,還要能被400整除
else return 0;
}else return 1;
}else return 0;
}
//設(shè)置時(shí)鐘
//把輸入的時(shí)鐘轉(zhuǎn)換為秒鐘
//以1970年1月1日為基準(zhǔn)
//1970~2099年為合法年份
//返回值:0,成功;其他:錯(cuò)誤代碼.
//月份數(shù)據(jù)表
u8 const table_week[12]={0,3,3,6,1,4,6,2,5,0,3,5}; //月修正數(shù)據(jù)表
//平年的月份日期表
const u8 mon_table[12]={31,28,31,30,31,30,31,31,30,31,30,31};
u8 RTC_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec)
{
u16 t;
u32 seccount=0;
if(syear<1970||syear>2099)return 1;
for(t=1970;t<syear;t++) //把所有年份的秒鐘相加
{
if(Is_Leap_Year(t))seccount+=31622400;//閏年的秒鐘數(shù)
else seccount+=31536000; //平年的秒鐘數(shù)
}
smon-=1;
for(t=0;t<smon;t++) //把前面月份的秒鐘數(shù)相加
{
seccount+=(u32)mon_table[t]*86400;//月份秒鐘數(shù)相加
if(Is_Leap_Year(syear)&&t==1)seccount+=86400;//閏年2月份增加一天的秒鐘數(shù)
}
seccount+=(u32)(sday-1)*86400;//把前面日期的秒鐘數(shù)相加
seccount+=(u32)hour*3600;//小時(shí)秒鐘數(shù)
seccount+=(u32)min*60; //分鐘秒鐘數(shù)
seccount+=sec;//最后的秒鐘加上去
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); //使能PWR和BKP外設(shè)時(shí)鐘
PWR_BackupAccessCmd(ENABLE); //使能RTC和后備寄存器訪問(wèn)
RTC_SetCounter(seccount); //設(shè)置RTC計(jì)數(shù)器的值
RTC_WaitForLastTask(); //等待最近一次對(duì)RTC寄存器的寫操作完成
RTC_Get();
return 0;
}
//得到當(dāng)前的時(shí)間
//返回值:0,成功;其他:錯(cuò)誤代碼.
u8 RTC_Get(void)
{
static u16 daycnt=0;
u32 timecount=0;
u32 temp=0;
u16 temp1=0;
timecount=RTC_GetCounter();
temp=timecount/86400; //得到天數(shù)(秒鐘數(shù)對(duì)應(yīng)的)
if(daycnt!=temp)//超過(guò)一天了
{
daycnt=temp;
temp1=1970; //從1970年開始
while(temp>=365)
{
if(Is_Leap_Year(temp1))//是閏年
{
if(temp>=366)temp-=366;//閏年的秒鐘數(shù)
else {temp1++;break;}
}
else temp-=365; //平年
temp1++;
}
calendar.w_year=temp1;//得到年份
temp1=0;
while(temp>=28)//超過(guò)了一個(gè)月
{
if(Is_Leap_Year(calendar.w_year)&&temp1==1)//當(dāng)年是不是閏年/2月份
{
if(temp>=29)temp-=29;//閏年的秒鐘數(shù)
else break;
}
else
{
if(temp>=mon_table[temp1])temp-=mon_table[temp1];//平年
else break;
}
temp1++;
}
calendar.w_month=temp1+1; //得到月份
calendar.w_date=temp+1; //得到日期
}
temp=timecount%86400; //得到秒鐘數(shù)
calendar.hour=temp/3600; //小時(shí)
calendar.min=(temp%3600)/60; //分鐘
calendar.sec=(temp%3600)%60; //秒鐘
calendar.week=RTC_Get_Week(calendar.w_year,calendar.w_month,calendar.w_date);//獲取星期
return 0;
}
//獲得現(xiàn)在是星期幾
//功能描述:輸入公歷日期得到星期(只允許1901-2099年)
//輸入?yún)?shù):公歷年月日
//返回值:星期號(hào)
u8 RTC_Get_Week(u16 year,u8 month,u8 day)
{
u16 temp2;
u8 yearH,yearL;
yearH=year/100; yearL=year%100;
// 如果為21世紀(jì),年份數(shù)加100
if (yearH>19)yearL+=100;
// 所過(guò)閏年數(shù)只算1900年之后的
temp2=yearL+yearL/4;
temp2=temp2%7;
temp2=temp2+day+table_week[month-1];
if (yearL%4==0&&month<3)temp2--;
return(temp2%7);
}
6.4 main部分
#include "led.h"
#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "lcd.h"
#include "adc.h"
#include "math.h"
#include "ds18b20.h"
#include "rtc.h"
int main(void)
{
u16 adcx;
float temp,quality;
short temperature;
u8 t;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 設(shè)置中斷優(yōu)先級(jí)分組2
delay_init(); //延時(shí)函數(shù)初始化
uart_init(9600); //串口初始化為9600
LED_Init(); //初始化與LED連接的硬件接口
LCD_Init();
Adc_Init(); //ADC初始化
POINT_COLOR=BLACK;
LCD_DrawLine(0,80,240,80);
LCD_DrawLine(120,80,120,240);
LCD_DrawLine(0,240,240,240);
LCD_DrawLine(0,280,240,280);
LCD_DrawLine(120,120,120,240);
LCD_DrawLine(120,160,240,160);
LCD_ShowString(40,20,230,16,16,"AIR MONITIRING SYSTEM");
LCD_ShowString(76,40,230,16,16,"BASED ON RTOS");
LCD_ShowString(10,100,230,16,16,"AIR QUALITY:");
LCD_ShowString(130,90,230,16,16,"HARMFUL GAS");
LCD_ShowString(125,110,230,16,16,"CONCENTRATION:");
LCD_ShowString(150,130,230,16,16," PPM");
LCD_ShowString(130,170,230,16,16,"TEMPERATURE:");
LCD_ShowString(150,190,230,16,16," . C");
//LCD_ShowString(10,250,200,16,16,"2020-4-30 16:00");
LCD_ShowString(10,290,200,16,16,"STM32F103,MQ135,DS18B20");
while(DS18B20_Init()) //DS18B20初始化
{
LCD_ShowString(150,210,200,16,16,"DS18B20 Error");
delay_ms(200);
LCD_Fill(60,130,239,130+16,WHITE);
delay_ms(200);
}
while(RTC_Init()) //RTC初始化 ,一定要初始化成功
{
LCD_ShowString(10,250,200,16,16,"RTC ERROR! ");
delay_ms(800);
LCD_ShowString(10,250,200,16,16,"RTC Trying...");
}
//顯示時(shí)間
LCD_ShowString(10,250,200,16,16," - - ");
LCD_ShowString(100,250,200,16,16," : : ");
//顯示提示信息
//LCD_ShowString(60,130,200,16,16,"ADC_CH1_VAL:");
//LCD_ShowString(60,150,200,16,16,"ADC_CH1_VOL:0.000V");
//LCD_ShowString(60,170,200,16,16,"KQ_ZHILIANG: ppm");
while(1)
{
adcx=Get_Adc_Average(ADC_Channel_1,10);
//LCD_ShowxNum(156,130,adcx,4,16,0);//顯示ADC的值
temp=(float)adcx*(3.3/4096);
quality=pow((11.5428 * 35.904 * temp )/(25.5 - 5.1 * temp),1.0/0.6549);
adcx=temp;
//LCD_ShowxNum(156,150,adcx,1,16,0);//顯示電壓值
temp-=adcx;
//temp*=1000;
//LCD_ShowxNum(172,150,temp,3,16,0X80);
LCD_ShowxNum(150,130,quality,4,16,0);//顯示轉(zhuǎn)化后的PPM值
//根據(jù)有害氣體濃度判斷空氣質(zhì)量
if(0<= quality && quality <=75)
LCD_ShowString(20,150,200,16,24,"NORMAL");
else if(quality<=150)
LCD_ShowString(20,150,200,16,24,"MILD ");
else if(quality<=500)
LCD_ShowString(20,150,200,16,24,"MIDDLE");
else
LCD_ShowString(20,150,200,16,24,"SEVERE");
temperature=DS18B20_Get_Temp();
if(temperature<0)
{
LCD_ShowChar(60+40,150,'-',16,0); //顯示負(fù)號(hào)
temperature=-temperature; //轉(zhuǎn)為正數(shù)
}else LCD_ShowChar(60+40,150,' ',16,0); //去掉負(fù)號(hào)
LCD_ShowNum(150+8,190,temperature/10,2,16); //顯示正數(shù)部分
LCD_ShowNum(150+32,190,temperature%10,1,16); //顯示小數(shù)部分
//LCD_ShowString(170,190,290,16,16,"26");//顯示溫度
if(quality>=150) //當(dāng)?shù)竭_(dá)中度污染時(shí)紅燈閃爍報(bào)警
{
LED1=1;
LED0=!LED0;
POINT_COLOR=RED;//設(shè)置字體為紅色
}
else
{
LED0=1;
LED1=!LED1; //正常時(shí)綠燈閃爍
POINT_COLOR=BLUE;//設(shè)置字體為藍(lán)色
}
//顯示時(shí)間
if(t!=calendar.sec)
{
POINT_COLOR=BLACK;//設(shè)置字體為黑色
t=calendar.sec;
LCD_ShowNum(10,250,calendar.w_year,4,16);
LCD_ShowNum(50,250,calendar.w_month,2,16);
LCD_ShowNum(74,250,calendar.w_date,2,16);
LCD_ShowNum(100,250,calendar.hour,2,16);
LCD_ShowNum(124,250,calendar.min,2,16);
LCD_ShowNum(148,250,calendar.sec,2,16);
switch(calendar.week)
{
case 0:
LCD_ShowString(170,250,200,16,16,"Sunday");
break;
case 1:
LCD_ShowString(170,250,200,16,16,"Monday");
break;
case 2:
LCD_ShowString(170,250,200,16,16,"Tuesday");
break;
case 3:
LCD_ShowString(170,250,200,16,16,"Wednesday");
break;
case 4:
LCD_ShowString(170,250,200,16,16,"Thursday");
break;
case 5:
LCD_ShowString(170,250,200,16,16,"Friday");
break;
case 6:
LCD_ShowString(60,148,200,16,16,"Saturday ");
break;
}
}
delay_ms(200);
}
}
7 最后
到了這里,關(guān)于單片機(jī)畢業(yè)設(shè)計(jì) STM32智能空氣質(zhì)量檢測(cè)儀 - 環(huán)境檢測(cè)盒子 嵌入式 物聯(lián)網(wǎng)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!