1 簡介
Hi,大家好,學長今天向大家介紹一個 單片機項目
基于STM32的空氣質(zhì)量檢測儀
大家可用于 課程設(shè)計 或 畢業(yè)設(shè)計
2 系統(tǒng)設(shè)計概述
如今人們大約 80%的時間是在室內(nèi)度過的, 室內(nèi)空氣質(zhì)量與我們每個人的工作和生活都息息相關(guān), 因此對生活環(huán)境的空氣質(zhì)量提出了更高的要求。 針對霧霾、 室內(nèi)裝修等污染問題, 人們還沒有有效的辦法控制空氣中的有害物質(zhì), 這一問題也成為人們的健康隱患, 因此對室內(nèi)的空氣質(zhì)量進行檢測至關(guān)重要。
當室內(nèi)有害氣體的濃度超標時, 會對人們的身心健康帶來不可忽視的影響。 為了讓人們及時準確地獲得室內(nèi)有害氣體甲醛、 PM2.5 的濃度, 采取有效措施改善生活環(huán)境的空氣質(zhì)量, 提高工作效率。 根據(jù)我國室內(nèi)空氣監(jiān)管標準 GB50325-2010, 學長設(shè)計了一款基于STM32F103C8T6 單片機的便攜式、 低功耗室內(nèi)空氣質(zhì)量檢測儀。 該檢測儀可以實時測量室內(nèi)的溫濕度、 甲醛濃度、 PM2.5 濃度, 并在液晶顯示屏 LCD12864 上實時顯示出來, 當甲醛濃度或 PM2.5 濃度超過報警值時, 測試儀通過亮紅燈和蜂鳴器鳴叫的方式提醒用戶室內(nèi)有害氣體超標。
用戶可以根據(jù)需要通過 GSM 短信方式將室內(nèi)空氣質(zhì)量情況發(fā)送到移動終端, 最后系統(tǒng)在 Visual Studio 2012 平臺運用 C#語言開發(fā)上位機, 可以實時顯示空氣質(zhì)量指標。
3 系統(tǒng)總體方案
學長設(shè)計為了能夠?qū)崟r檢測室內(nèi)的空氣質(zhì)量情況, 提高系統(tǒng)的穩(wěn)定性和自動化程度, 將系統(tǒng)分為硬件設(shè)計和軟件設(shè)計。 本文研制的室內(nèi) 空氣質(zhì) 量檢測儀是基于 32 位單片機STM32C8T6 進行設(shè)計的, 將整個系統(tǒng)模塊化, 主要包括 STM32 最小系統(tǒng)、 溫濕度檢測模塊、 甲醛氣體檢測模塊、 PM2.5 檢測模塊、 按鍵、 液晶顯示器、 蜂鳴器、 GSM 模塊、藍牙模塊、 移動終端和上位機以及電源管理電路。 其系統(tǒng)結(jié)構(gòu)框圖如圖所示。
在整個室內(nèi)空氣質(zhì)量檢測系統(tǒng)中, STM32 芯片是整個系統(tǒng)的主控制器, 整個檢測系統(tǒng)都是在它的控制下完成的, 并且 STM32 為測試儀提供外部接口, 如液晶顯示屏、
按鍵、 數(shù)據(jù)采集電路。 首先通過溫濕度、 甲醛濃度以及 PM2.5 等傳感器進行溫濕度、 甲醛氣體及 PM2.5 濃度信號采集, 再將采集到的信號送至中央處理單元 STM32 單片機進行信號轉(zhuǎn)換、 放大和處理。 通過庫函數(shù)編程控制檢測系統(tǒng)的工作流程, 可以通過按鍵輸入控制輸出顯示不同的測量數(shù)據(jù)以及設(shè)置甲醛和 PM2.5 濃度報警值, 當氣體濃度值超過報警值時, 通過蜂鳴器鳴叫的方式提醒用戶室內(nèi)有害氣體超標, 并在液晶屏上實時顯示。利用 GSM 通信模塊可以通過短信的形式將用戶室內(nèi)的空氣質(zhì)量情況發(fā)到移動終端, 實現(xiàn)了儀器的智能化。 STM32 最小系統(tǒng)通過藍牙模塊與上位機通信, 上位機可以實時顯示測量數(shù)據(jù)。
4 硬件設(shè)計方案
學長設(shè)計的系統(tǒng)所研究的室內(nèi)空氣質(zhì)量檢測儀, 系統(tǒng)硬件由 STM32 單片機、 電源、 溫濕度傳感器、 甲醛檢測模塊、 PM2. 5 檢測模塊、 按鍵輸入模塊、 液晶顯示模塊、 報警電路、 GSM短信接收模塊和藍牙模塊等部分組成。 該系統(tǒng)采用 USB 供電, 通過 USB 數(shù)據(jù)線與電腦USB 接口相連, 輸出+5V 的直流電壓, 輸出電壓在 500mA 左右, 在目前普遍采用 USB接口的情況, 這樣也提高了電源的來源以及可靠性, 用戶也可以通過手機充電器、 充電寶對測試儀供電, 給用戶使用該測試儀帶來了很大的便利 模塊化的設(shè)計可以使系統(tǒng)設(shè)計更加簡單, 系統(tǒng)功耗降低, 操作變得簡單, 可以進行現(xiàn)場檢測, 通過液晶顯示屏用戶直觀地讀出測量數(shù)據(jù)。 空氣質(zhì)量檢測儀檢的整體硬件設(shè)計結(jié)構(gòu)如下圖所示。
4.1 stm32 主控
STM32C8T6(最小核心板),當然,用其他型號的32,如STM32ZET6也是可以的。
4.2 溫度采集模塊
系統(tǒng)采用的傳感器為含有已校準數(shù)字信號輸出的溫濕度復(fù)合傳感器 DHT11。該傳感器是一款已經(jīng)得到業(yè)界廣泛認可的安全性高、 穩(wěn)定性較強、 性價比高的集中數(shù)字化傳感器的溫濕度。
傳感器可以傳輸信號高達 20 米以上, 可以滿足大部分場合的測量要求;具有四針的單排引腳封裝系統(tǒng), 這樣外部連接就變得簡單; 同時因其超小的體積和極低的功耗在暖通空調(diào)、 汽車、 氣象站、 除濕器等各行業(yè)廣泛應(yīng)用。
4.3 甲醛濃度檢測模塊
綜合考慮甲醛檢測精度和測量成本后, 系統(tǒng)選用 ZE08-CH 2 O 電化學甲醛傳感器檢測室內(nèi)空氣中的甲醛濃度含量, 因該模塊精度高、 功耗低,體積小的特點, 成為了制作空氣質(zhì)量檢測儀、 空氣凈化器的優(yōu)良器件。 相比其他甲醛
傳感器主要有以下特點 :
- (1) 分辨率和靈敏度高;
- (2) 功耗低、 使用壽命長;
- (2) UART、 模擬電壓信號、 PWM 等多方式輸出;
- (3) 具有優(yōu)秀的抗干擾性、 穩(wěn)定性高;
- (4) 內(nèi)置溫度補償單元, 線性輸出。
4.4 PM2. 5 濃度檢測模塊
系統(tǒng)選用夏普公司授權(quán)弘成基科技有限公司生產(chǎn)的型號為 YW-51GJ 的 PM2.5 氣敏粉塵傳感器, 該產(chǎn)品主要針對香煙顆粒、 細微顆粒等特別細微的顆粒等檢測對象, 通過脈沖高度來判斷細顆粒物濃度, 常用于空氣凈化器、 車載空氣凈化系統(tǒng)以及民用空氣質(zhì)量檢測系統(tǒng)中, 符合本系統(tǒng)的設(shè)計要求。
YW-51GJ PM2.5 粉塵傳感器實物圖如圖所示。
可以看到該 PM2.5 傳感器中間有一個圓孔, 其內(nèi)部安裝紅外線發(fā)光二極管和光電晶體管, 當微小顆粒物進入傳感器的圓孔內(nèi)時, 根據(jù)微小顆粒物對光的散射原理, 當細顆粒物(PM2.5) 經(jīng)過傳感器內(nèi)部的檢測孔時會對光線形成散射作用。 部分光線通過光軸,經(jīng)過透鏡后聚焦到感光元件上, 光信號經(jīng)過感光元件轉(zhuǎn)換為電信號輸出。
4.5 液晶顯示模塊設(shè)計
4.6 GSM 模塊
經(jīng)綜合考慮學長選用了 SIM800C GSM 無線通信模塊, 這是 SIMCOM 公司生產(chǎn)的基于 MT6261 芯片平臺的新一代 GSM 模塊, 性價比高, 支持藍牙功能, 支持 51、MSP430、STM32 等主流單片機開發(fā) 。
在該模塊中, 通信接口為 TTL 電平串口, 引出四根排針與控制器通信。 在 IPX 天線接口連接通信天線, Micro-SIM 卡卡座可以插入移動、 聯(lián)通和物聯(lián)網(wǎng)卡, 插入 SIM 卡即可發(fā)信息給用戶移動終端, SIM 卡插進去聽到“咔噠” 響聲表明插卡正確, SIM800C GSM 模塊實物圖如圖所示。
4.7 藍牙模塊
為了能夠?qū)崿F(xiàn)測試儀與上位機的通信, 本文采用的是 HC-05 嵌入式藍牙串口通信模塊[65] , 分為兩個小模塊 a、 b, 如下圖所示依次左右排列, 模塊 a 與通過 USB 接口與電腦相連, 模塊 b 通過四根排母線與測試儀對應(yīng)的排針相連接。
為了實現(xiàn)主控制器 STM32 與藍牙模塊的連接, 通過導線連接對應(yīng)接口, 藍牙接口電路原理圖如圖所示。
5 軟件部分設(shè)計
系統(tǒng)主要實現(xiàn)溫濕度、 甲醛濃度以及 PM2.5 濃度的檢測與顯示功能, 通過傳感器不斷采集數(shù)據(jù), 同時對這些數(shù)據(jù)進行處理、 修正和補償, 將數(shù)據(jù)轉(zhuǎn)換為溫濕度以及氣體濃度進行顯示, 讓用戶可以知道室內(nèi)的空氣質(zhì)量。 與硬件系統(tǒng)一樣分模塊設(shè)計, 軟件設(shè)計分成人機交互模塊和數(shù)據(jù)處理模塊兩部分
5.1 初始化
系統(tǒng)上電后, 先對系統(tǒng)的各模塊傳感器、 12864LCD 顯示屏、 按鍵等外設(shè)進行初始化, 設(shè)置標志位和定時器。 之后讀取溫濕度、 甲醛濃度、 PM2.5 濃度對應(yīng)的模擬電流電壓等電信號參數(shù)值, 將數(shù)據(jù)進行 AD 轉(zhuǎn)換, 對數(shù)字量處理后轉(zhuǎn)換為對應(yīng)的溫濕度、 甲醛濃度、 PM2.5 濃度顯示出來。 軟件設(shè)計的主程序流程圖下圖所示。
5.2 溫濕度檢測程序設(shè)計
DHT11 溫濕度傳感器硬件設(shè)計可以知道溫濕度檢測電路連接十分簡單,DHT11 通過引腳 2(DATA) 給 STM32 發(fā)送單總線數(shù)字信號。 在電路開始工作的時候,為了跳過不穩(wěn)定狀態(tài), DHT11 需要用一秒的時間不發(fā)任何指令。
5.3 甲醛濃度檢測程序設(shè)計
由甲醛硬件模塊設(shè)計可以知道本系統(tǒng)采用 ZE08-CH2O 電化學甲醛傳感器采集甲醛濃度信號, 利用系統(tǒng) STM32 單片機自帶的 2 個 AD 轉(zhuǎn)換器對甲醛濃度進行采集, 這些AD 轉(zhuǎn)換器具有 10 個通道, 可以單獨使用, 也可以實現(xiàn)復(fù)用。
5.4 PM2. 5 濃度檢測程序設(shè)計
章 PM2.5 硬件設(shè)計可以知道系統(tǒng)選用了 YM-51 PM2.5 傳感器, 通過該模塊可以對室內(nèi) PM2.5 濃度測量, 傳感器通過 RXD 向 STM32 發(fā)送數(shù)據(jù), 接線圖如下所示
PM2.5 濃度檢測軟件程序流程與甲醛濃度檢測流程類似, 首先關(guān)閉中斷, 再對與PM2.5 檢測模塊相關(guān)的時鐘、 AD 轉(zhuǎn)換器進行初始化設(shè)置, 確定 AD 轉(zhuǎn)換器的工作模式、觸發(fā)方式等。 之后打開中斷在中斷服務(wù)程序中實現(xiàn)數(shù)據(jù)采集, 1 秒內(nèi)采集 10 次, 對這些數(shù)據(jù)采用限幅平均濾波法進行軟件濾波, 去除因電路干擾產(chǎn)生干擾有效數(shù)據(jù)的毛刺。
5.5 短信發(fā)送程序設(shè)計
章 GSM 模塊硬件設(shè)計可知本文采用 SIM800C GSM 模塊實現(xiàn)短信收發(fā)的功能。 手機短消息主要采用 PDU(協(xié)議數(shù)據(jù)單元) 模式和 TEXT 模式, PDU 模式同時支持中文和英文短信, 而 TEXT 模式只支持英文短信
效果展示
設(shè)計好各個模塊原理圖, 然后制作出 PCB 板, 通過導線將各個模塊與系統(tǒng)主板連接, 室內(nèi)空氣質(zhì)量檢測檢測儀實物圖如下所示。
室內(nèi)空氣質(zhì)量實時顯示文章來源:http://www.zghlxwxcb.cn/news/detail-798123.html
短信收發(fā)測試文章來源地址http://www.zghlxwxcb.cn/news/detail-798123.html
6 項目源碼
6.1 ADC部分
#include "adc.h"
#include "delay.h"
//初始化ADC
//這里我們僅以規(guī)則通道為例
//我們默認將開啟通道0~3
void Adc_Init(void)
{
ADC_InitTypeDef ADC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1 , ENABLE ); //使能ADC1通道時鐘
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //設(shè)置ADC分頻因子6 72M/6=12,ADC最大時間不能超過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工作在獨立模式
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ā)啟動
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC數(shù)據(jù)右對齊
ADC_InitStructure.ADC_NbrOfChannel = 1; //順序進行規(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ù)位校準
while(ADC_GetResetCalibrationStatus(ADC1)); //等待復(fù)位校準結(jié)束
ADC_StartCalibration(ADC1); //開啟AD校準
while(ADC_GetCalibrationStatus(ADC1)); //等待校準結(jié)束
// ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的軟件轉(zhuǎn)換啟動功能
}
//獲得ADC值
//ch:通道值 0~3
u16 Get_Adc(u8 ch)
{
//設(shè)置指定ADC的規(guī)則組通道,一個序列,采樣時間
ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 ); //ADC1,ADC通道,采樣時間為239.5周期
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的軟件轉(zhuǎn)換啟動功能
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:未檢測到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讀取一個位
//返回值: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讀取一個字節(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;
}
//寫一個字節(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 同時檢測DS的存在
//返回1:不存在
//返回0:存在
u8 DS18B20_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能PORTA口時鐘
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;//溫度為負
}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實時時鐘 驅(qū)動代碼
//正點原子@ALIENTEK
//2010/6/6
_calendar_obj calendar;//時鐘結(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)先級1位,從優(yōu)先級3位
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //先占優(yōu)先級0位,從優(yōu)先級4位
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能該通道中斷
NVIC_Init(&NVIC_InitStructure); //根據(jù)NVIC_InitStruct中指定的參數(shù)初始化外設(shè)NVIC寄存器
}
//實時時鐘配置
//初始化RTC時鐘,同時檢測時鐘是否工作正常
//BKP->DR1用于保存是否第一次配置的設(shè)置
//返回0:正常
//其他:錯誤代碼
u8 RTC_Init(void)
{
//檢查是不是第一次配置時鐘
u8 temp=0;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); //使能PWR和BKP外設(shè)時鐘
PWR_BackupAccessCmd(ENABLE); //使能后備寄存器訪問
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標志位設(shè)置與否,等待低速晶振就緒
{
temp++;
delay_ms(10);
}
if(temp>=250)return 1;//初始化時鐘失敗,晶振有問題
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //設(shè)置RTC時鐘(RTCCLK),選擇LSE作為RTC時鐘
RCC_RTCCLKCmd(ENABLE); //使能RTC時鐘
RTC_WaitForLastTask(); //等待最近一次對RTC寄存器的寫操作完成
RTC_WaitForSynchro(); //等待RTC寄存器同步
RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能RTC秒中斷
RTC_WaitForLastTask(); //等待最近一次對RTC寄存器的寫操作完成
RTC_EnterConfigMode();/// 允許配置
RTC_SetPrescaler(32767); //設(shè)置RTC預(yù)分頻的值
RTC_WaitForLastTask(); //等待最近一次對RTC寄存器的寫操作完成
RTC_Set(2020,5,5,12,59,00); //設(shè)置時間
RTC_ExitConfigMode(); //退出配置模式
BKP_WriteBackupRegister(BKP_DR1, 0X5050); //向指定的后備寄存器中寫入用戶程序數(shù)據(jù)
}
else//系統(tǒng)繼續(xù)計時
{
RTC_WaitForSynchro(); //等待最近一次對RTC寄存器的寫操作完成
RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能RTC秒中斷
RTC_WaitForLastTask(); //等待最近一次對RTC寄存器的寫操作完成
}
RTC_NVIC_Config();//RCT中斷分組設(shè)置
RTC_Get();//更新時間
return 0; //ok
}
//RTC時鐘中斷
//每秒觸發(fā)一次
//extern u16 tcnt;
void RTC_IRQHandler(void)
{
if (RTC_GetITStatus(RTC_IT_SEC) != RESET)//秒鐘中斷
{
RTC_Get();//更新時間
}
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è)置時鐘
//把輸入的時鐘轉(zhuǎn)換為秒鐘
//以1970年1月1日為基準
//1970~2099年為合法年份
//返回值:0,成功;其他:錯誤代碼.
//月份數(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ù)
seccount+=(u32)min*60; //分鐘秒鐘數(shù)
seccount+=sec;//最后的秒鐘加上去
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); //使能PWR和BKP外設(shè)時鐘
PWR_BackupAccessCmd(ENABLE); //使能RTC和后備寄存器訪問
RTC_SetCounter(seccount); //設(shè)置RTC計數(shù)器的值
RTC_WaitForLastTask(); //等待最近一次對RTC寄存器的寫操作完成
RTC_Get();
return 0;
}
//得到當前的時間
//返回值:0,成功;其他:錯誤代碼.
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ù)對應(yīng)的)
if(daycnt!=temp)//超過一天了
{
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)//超過了一個月
{
if(Is_Leap_Year(calendar.w_year)&&temp1==1)//當年是不是閏年/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; //小時
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ù):公歷年月日
//返回值:星期號
u8 RTC_Get_Week(u16 year,u8 month,u8 day)
{
u16 temp2;
u8 yearH,yearL;
yearH=year/100; yearL=year%100;
// 如果為21世紀,年份數(shù)加100
if (yearH>19)yearL+=100;
// 所過閏年數(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)先級分組2
delay_init(); //延時函數(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...");
}
//顯示時間
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); //顯示負號
temperature=-temperature; //轉(zhuǎn)為正數(shù)
}else LCD_ShowChar(60+40,150,' ',16,0); //去掉負號
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) //當?shù)竭_中度污染時紅燈閃爍報警
{
LED1=1;
LED0=!LED0;
POINT_COLOR=RED;//設(shè)置字體為紅色
}
else
{
LED0=1;
LED1=!LED1; //正常時綠燈閃爍
POINT_COLOR=BLUE;//設(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)于畢業(yè)設(shè)計 STM32空氣質(zhì)量檢測儀 - 單片機 嵌入式的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!