一、前言
煤氣泄漏是一個嚴(yán)重的安全隱患,可能導(dǎo)致火災(zāi)、爆炸以及對人體健康的威脅。為了提高家庭和工業(yè)環(huán)境中煤氣泄漏的檢測和預(yù)防能力,設(shè)計了一種基于單片機的防煤氣泄漏裝置。
單片機選擇STC89C52作為主控芯片。為了檢測煤氣泄漏,采用了MQ4傳感器,能夠快速、準(zhǔn)確地檢測煤氣濃度。通過采集MQ4傳感器的模擬信號,使用PCF8591模數(shù)轉(zhuǎn)換芯片將模擬信號轉(zhuǎn)換為數(shù)字信號。采用IIC接口的OLED顯示屏,將采集到的數(shù)據(jù)顯示出來,方便用戶獲取檢測結(jié)果。用戶可以通過兩個獨立按鍵設(shè)置煤氣泄漏的報警閾值,以適應(yīng)不同環(huán)境的需求。
當(dāng)檢測到煤氣泄漏超過設(shè)定的閾值時,裝置會觸發(fā)蜂鳴器進(jìn)行報警,并同時打開換氣扇進(jìn)行通風(fēng)換氣,以迅速排除煤氣并降低安全風(fēng)險。
這種基于單片機設(shè)計的防煤氣泄漏裝置具有以下優(yōu)點:高效可靠的煤氣泄漏檢測能力、靈活的報警閾值設(shè)置、直觀清晰的數(shù)據(jù)顯示以及及時的安全響應(yīng)措施。可以廣泛應(yīng)用于家庭、工業(yè)和商業(yè)場所,提供有效的煤氣泄漏監(jiān)測和安全保護(hù)。
二、硬件選型
在設(shè)計基于單片機的防煤氣泄漏裝置時,硬件選型是非常關(guān)鍵的。以下是詳細(xì)介紹硬件選型的相關(guān)內(nèi)容:
【1】主控芯片選擇:STC89C52 STC89C52是一款8051架構(gòu)的單片機,具有豐富的接口資源、較高的性能和穩(wěn)定可靠的工作特性,廣泛應(yīng)用于各種嵌入式系統(tǒng)中。具有8位數(shù)據(jù)總線、16位地址總線和4KB的內(nèi)部存儲器。STC89C52具備多個通用I/O口、定時器/計數(shù)器、串口等功能,非常適合本項目需求。
【2】煤氣傳感器選擇:MQ4 MQ4傳感器是一種能夠檢測多種可燃?xì)怏w,如天然氣、甲烷等的傳感器。具有高靈敏度和快速響應(yīng)的特點,能夠準(zhǔn)確地檢測煤氣泄漏情況。MQ4傳感器的輸出為模擬信號,需要通過模數(shù)轉(zhuǎn)換器將其轉(zhuǎn)換為數(shù)字信號供主控芯片處理。
【3】模數(shù)轉(zhuǎn)換器選擇:PCF8591 PCF8591是一款集成了8位模數(shù)/數(shù)模轉(zhuǎn)換和4個模擬輸入通道的模數(shù)轉(zhuǎn)換器。采用IIC總線通訊接口,能夠?qū)⒛M信號轉(zhuǎn)換為數(shù)字信號,并通過IIC協(xié)議發(fā)送給主控芯片。本項目中,PCF8591用于采集MQ4傳感器輸出的模擬信號,并將其轉(zhuǎn)換為數(shù)字信號供STC89C52處理。
【4】顯示屏選擇:0.96寸OLED顯示屏(IIC接口) 本設(shè)計采用基于IIC接口的OLED顯示屏,具有高亮度、對比度和快速響應(yīng)的特點。通過簡單的通訊方式,可以將煤氣濃度信息實時顯示在屏幕上。OLED顯示屏使用面積小、功耗低,在嵌入式系統(tǒng)中應(yīng)用廣泛。
【5】按鍵選擇:獨立按鍵 本設(shè)計采用兩個獨立按鍵來設(shè)置報警的閥值。一個按鍵用于遞增閥值,另一個按鍵用于遞減閥值。獨立按鍵具有簡單可靠、使用方便等特點,適合本項目需求。
【6】報警裝置選擇:蜂鳴器和換氣扇 當(dāng)檢測到煤氣泄漏超過設(shè)定的報警閥值時,蜂鳴器將發(fā)出警報,用于提醒周圍人員。同時,為了降低煤氣濃度,需要啟動換氣扇進(jìn)行通風(fēng)換氣。具體的報警和換氣扇電路可以根據(jù)實際需求設(shè)計。
三、設(shè)計思路
軟件設(shè)計思路如下:
【1】初始化:在程序開始時,進(jìn)行主控芯片STC89C52的初始化設(shè)置,包括引腳配置、定時器設(shè)置等。同時,初始化PCF8591和OLED顯示屏,確保它們可以正常工作。
【2】傳感器檢測:通過MQ4傳感器檢測煤氣是否泄漏。將MQ4傳感器與STC89C52的模擬輸入引腳連接,通過讀取該引腳的模擬電壓值,獲取煤氣濃度數(shù)據(jù)。
【3】數(shù)據(jù)采集與處理:使用PCF8591模數(shù)轉(zhuǎn)換芯片,將MQ4傳感器的模擬輸出信號轉(zhuǎn)換為數(shù)字信號,并通過STC89C52的IIC接口與PCF8591進(jìn)行通信,獲取轉(zhuǎn)換后的數(shù)字?jǐn)?shù)據(jù)。
【4】數(shù)據(jù)顯示:將采集到的煤氣濃度數(shù)據(jù)通過IIC接口的OLED顯示屏進(jìn)行顯示。使用STC89C52的IIC通信功能,將數(shù)據(jù)發(fā)送給OLED顯示屏,通過顯示屏將數(shù)據(jù)以可讀的方式展示給用戶。
【5】閾值設(shè)置:通過兩個獨立按鍵實現(xiàn)報警閾值的設(shè)置。將按鍵與STC89C52的GPIO引腳連接,通過讀取按鍵狀態(tài)來判斷用戶是否進(jìn)行閾值設(shè)置操作。當(dāng)按鍵按下時,進(jìn)入設(shè)置模式,用戶可以通過按鍵的不同組合來調(diào)整報警閾值,并將設(shè)置的值保存在相應(yīng)的變量中。
【6】報警與通風(fēng)控制:根據(jù)當(dāng)前采集到的煤氣濃度數(shù)據(jù)和用戶設(shè)置的報警閾值進(jìn)行比較。如果煤氣濃度超過設(shè)定的閾值,觸發(fā)蜂鳴器進(jìn)行報警,并控制換氣扇打開進(jìn)行通風(fēng)換氣。反之,當(dāng)煤氣濃度低于或等于設(shè)定的閾值時,停止報警并關(guān)閉換氣扇。
【7】循環(huán)監(jiān)測:使用主循環(huán)結(jié)構(gòu),不斷進(jìn)行煤氣泄漏檢測、數(shù)據(jù)采集、數(shù)據(jù)顯示和閾值比較等操作,以實現(xiàn)持續(xù)的監(jiān)測和反饋。
四、項目模塊代碼
4.1 PCF8591采集代碼
下面是使用STC89C52單片機通過PCF8591讀取MQ4傳感器的ADC數(shù)據(jù)的代碼。使用IIC總線進(jìn)行PCF8591之間的通信,使用了自定義的IIC總線函數(shù)。通過readADC()
函數(shù)實現(xiàn)了讀取MQ4傳感器模擬量的ADC轉(zhuǎn)換結(jié)果。
#include <reg52.h>
#define uchar unsigned char
#define uint unsigned int
sbit SDA = P2^0; // IIC總線數(shù)據(jù)線
sbit SCL = P2^1; // IIC總線時鐘線
sbit MQ4_DOUT = P3^0; // MQ4傳感器數(shù)字輸出引腳
sbit MQ4_AIN = P3^1; // MQ4傳感器模擬輸入引腳
sfr IAP_DATA = 0xe2; // 定義IAP_DATA寄存器
sfr IAP_ADDRH = 0xe3; // 定義IAP_ADDRH寄存器
sfr IAP_ADDRL = 0xe4; // 定義IAP_ADDRL寄存器
sfr IAP_CMD = 0xe5; // 定義IAP_CMD寄存器
sfr IAP_TRIG = 0xe6; // 定義IAP_TRIG寄存器
sfr IAP_CONTR = 0xe7; // 定義IAP_CONTR寄存器
// 函數(shù)聲明
void delay(uint ms);
void startIIC();
void stopIIC();
void sendByte(uchar dat);
uchar receiveByte();
void writeDAC(uchar dat);
uchar readADC();
void main() {
uchar mq4Value;
while (1) {
mq4Value = readADC(); // 讀取ADC轉(zhuǎn)換結(jié)果
// 處理mq4Value值,進(jìn)行相應(yīng)操作
delay(100); // 延時一段時間
}
}
// 延時函數(shù)
void delay(uint ms) {
uint i, j;
for(i = ms; i > 0; i--) {
for(j = 110; j > 0; j--);
}
}
// IIC總線起始信號
void startIIC() {
SDA = 1;
_nop_();
SCL = 1;
_nop_();
SDA = 0;
_nop_();
SCL = 0;
_nop_();
}
// IIC總線停止信號
void stopIIC() {
SDA = 0;
_nop_();
SCL = 1;
_nop_();
SDA = 1;
_nop_();
}
// 發(fā)送一個字節(jié)的數(shù)據(jù)
void sendByte(uchar dat) {
uchar i;
for (i = 0; i < 8; i++) {
SDA = dat >> 7;
_nop_();
SCL = 1;
_nop_();
SCL = 0;
_nop_();
dat <<= 1;
}
SDA = 1;
_nop_();
SCL = 1;
_nop_();
SCL = 0;
_nop_();
}
// 接收一個字節(jié)的數(shù)據(jù)
uchar receiveByte() {
uchar i, dat = 0;
SDA = 1;
for (i = 0; i < 8; i++) {
dat <<= 1;
SCL = 1;
_nop_();
dat |= SDA;
SCL = 0;
_nop_();
}
return dat;
}
// 寫入DAC數(shù)值
void writeDAC(uchar dat) {
startIIC();
sendByte(0x90); // 地址和寫命令
receiveByte(); // 接收應(yīng)答
sendByte(0x40); // DAC通道A,并寫入數(shù)據(jù)
receiveByte(); // 接收應(yīng)答
sendByte(dat); // DAC數(shù)據(jù)
receiveByte(); // 接收應(yīng)答
stopIIC();
}
// 讀取ADC轉(zhuǎn)換結(jié)果
uchar readADC() {
uchar adcValue;
startIIC();
sendByte(0x91); // 地址和讀命令
receiveByte(); // 接收應(yīng)答
adcValue = receiveByte(); // 讀取ADC數(shù)據(jù)
stopIIC();
return adcValue;
}
4.2 OLED顯示屏代碼
以下是通過STC89C52控制IIC接口的OLED顯示屏顯示ADC數(shù)據(jù)實現(xiàn)代碼。
#include <reg51.h>
#define SCL P1_0 // IIC時鐘線
#define SDA P1_1 // IIC數(shù)據(jù)線
#define OLED_ADDR 0x78 // OLED顯示屏的IIC地址
// OLED顯示屏初始化函數(shù)
void OLED_Init() {
// 初始化OLED顯示屏
// ...
// 發(fā)送初始化命令到OLED顯示屏
// ...
}
// IIC總線開始信號
void IIC_Start() {
SDA = 1;
SCL = 1;
SDA = 0;
SCL = 0;
}
// IIC總線停止信號
void IIC_Stop() {
SDA = 0;
SCL = 1;
SDA = 1;
}
// IIC總線發(fā)送一個字節(jié)的數(shù)據(jù)
void IIC_WriteByte(unsigned char dat) {
unsigned char i;
for (i = 0; i < 8; i++) {
SCL = 0;
if (dat & 0x80)
SDA = 1;
else
SDA = 0;
SCL = 1;
dat <<= 1;
}
SCL = 0;
SDA = 1;
SCL = 1;
}
// 設(shè)置OLED顯示屏光標(biāo)位置
void OLED_SetPos(unsigned char x, unsigned char y) {
IIC_Start();
IIC_WriteByte(OLED_ADDR);
IIC_WriteByte(0xb0 + y);
IIC_WriteByte(((x & 0xf0) >> 4) | 0x10);
IIC_WriteByte((x & 0x0f) | 0x01);
IIC_Stop();
}
// 在OLED顯示屏上顯示一個字符
void OLED_ShowChar(unsigned char x, unsigned char y, unsigned char ch) {
unsigned char c = 0, i = 0;
c = ch - ' '; // 獲取字符在字庫中的偏移量
if (x > 128 - 8 || y > 64 - 16)
return; // 超出屏幕范圍,退出函數(shù)
OLED_SetPos(x, y);
for (i = 0; i < 8; i++) {
IIC_Start();
IIC_WriteByte(OLED_ADDR);
IIC_WriteByte(0x40);
IIC_WriteByte(*(OLED_CharSet + c * 16 + i));
IIC_Stop();
x++;
}
}
// 在OLED顯示屏上顯示字符串
void OLED_ShowString(unsigned char x, unsigned char y, unsigned char *str) {
while (*str) {
OLED_ShowChar(x, y, *str);
x += 8;
str++;
}
}
// ADC模擬數(shù)值轉(zhuǎn)換為字符串
void ADC_ToString(unsigned int adcValue, unsigned char *str) {
unsigned char i, j;
unsigned int temp;
temp = adcValue;
for (i = 0; i < 4; i++) {
str[i] = temp % 10 + '0';
temp /= 10;
}
// 反轉(zhuǎn)字符串
i = 0;
j = 3;
while (i < j) {
temp = str[i];
str[i] = str[j];
str[j] = temp;
i++;
j--;
}
str[4] = '?'; // 字符串結(jié)束符
}
// 主函數(shù)
void main() {
unsigned int adcValue = 0;
unsigned char str[5];
// 初始化OLED顯示屏
OLED_Init();
while (1) {
// 讀取ADC數(shù)據(jù)
adcValue = ADC_Read(); // 假設(shè)使用的函數(shù)為ADC_Read(),用于讀取ADC數(shù)據(jù)
// 將ADC數(shù)據(jù)轉(zhuǎn)換為字符串
ADC_ToString(adcValue, str);
// 在OLED顯示屏上顯示ADC數(shù)據(jù)
OLED_ShowString(0, 0, "ADC Value:");
OLED_ShowString(0, 2, str);
}
}
4.3 主代碼邏輯
#include <reg51.h>
#define SCL P1_0 // IIC時鐘線
#define SDA P1_1 // IIC數(shù)據(jù)線
#define OLED_ADDR 0x78 // OLED顯示屏的IIC地址
#define MQ4_PIN P2 // MQ4傳感器連接的引腳
sbit Buzzer = P3^0; // 蜂鳴器控制引腳
sbit VentilationFan = P3^1; // 換氣扇控制引腳
// 全局變量
unsigned int alarmThreshold = 100; // 報警閾值,默認(rèn)為100
unsigned int adcValue = 0; // 保存ADC采集到的數(shù)值
// IIC總線開始信號
void IIC_Start() {
SDA = 1;
SCL = 1;
SDA = 0;
SCL = 0;
}
// IIC總線停止信號
void IIC_Stop() {
SDA = 0;
SCL = 1;
SDA = 1;
}
// IIC總線發(fā)送一個字節(jié)的數(shù)據(jù)
void IIC_WriteByte(unsigned char dat) {
unsigned char i;
for (i = 0; i < 8; i++) {
SCL = 0;
if (dat & 0x80)
SDA = 1;
else
SDA = 0;
SCL = 1;
dat <<= 1;
}
SCL = 0;
SDA = 1;
SCL = 1;
}
// 設(shè)置OLED顯示屏光標(biāo)位置
void OLED_SetPos(unsigned char x, unsigned char y) {
IIC_Start();
IIC_WriteByte(OLED_ADDR);
IIC_WriteByte(0xb0 + y);
IIC_WriteByte(((x & 0xf0) >> 4) | 0x10);
IIC_WriteByte((x & 0x0f) | 0x01);
IIC_Stop();
}
// 在OLED顯示屏上顯示一個字符
void OLED_ShowChar(unsigned char x, unsigned char y, unsigned char ch) {
unsigned char c = 0, i = 0;
c = ch - ' '; // 獲取字符在字庫中的偏移量
if (x > 128 - 8 || y > 64 - 16)
return; // 超出屏幕范圍,退出函數(shù)
OLED_SetPos(x, y);
for (i = 0; i < 8; i++) {
IIC_Start();
IIC_WriteByte(OLED_ADDR);
IIC_WriteByte(0x40);
IIC_WriteByte(*(OLED_CharSet + c * 16 + i));
IIC_Stop();
x++;
}
}
// 在OLED顯示屏上顯示字符串
void OLED_ShowString(unsigned char x, unsigned char y, unsigned char *str) {
while (*str) {
OLED_ShowChar(x, y, *str);
x += 8;
str++;
}
}
// ADC轉(zhuǎn)換函數(shù)
unsigned int ADC_Convert(unsigned char channel) {
ADC_CONTR = ADC_POWER | ADC_START | channel | ADC_SPEED;
_nop_();
_nop_();
_nop_();
_nop_();
while (!(ADC_CONTR & ADC_FLAG))
;
ADC_CONTR &= ~ADC_FLAG;
return (ADC_RES * 256 + ADC_RESL);
}
// ADC模擬數(shù)值轉(zhuǎn)換為字符串
void ADC_ToString(unsigned int adcValue, unsigned char *str) {
unsigned char i, j;
unsigned int temp;
temp = adcValue;
for (i = 0; i < 4; i++) {
str[i] = temp % 10 + '0';
temp /= 10;
}
// 反轉(zhuǎn)字符串
i = 0;
j = 3;
while (i < j) {
temp = str[i];
str[i] = str[j];
str[j] = temp;
i++;
j--;
}
str[4] = '?'; // 字符串結(jié)束符
}
主函數(shù)
void main() {
unsigned char str[5];
// 初始化OLED顯示屏
OLED_Init();
// 設(shè)置初始報警閾值
SetAlarmThreshold();
while (1) {
// 讀取MQ4傳感器的ADC數(shù)值
adcValue = ADC_Convert(0); // 假設(shè)MQ4傳感器連接到ADC的通道0
// 將ADC數(shù)值轉(zhuǎn)換為字符串
ADC_ToString(adcValue, str);
// 在OLED顯示屏上顯示ADC數(shù)值
OLED_ShowString(0, 0, "Gas Level:");
OLED_ShowString(0, 2, str);
// 判斷是否觸發(fā)報警
if (adcValue > alarmThreshold) {
// 觸發(fā)報警
ActivateAlarm();
} else {
// 取消報警
DeactivateAlarm();
}
// 檢測是否按下設(shè)置閾值的按鍵
if (IsThresholdButtonPressed()) {
// 設(shè)置報警閾值
SetAlarmThreshold();
}
// 檢測是否按下通風(fēng)換氣的按鍵
if (IsVentilationButtonPressed()) {
// 控制通風(fēng)換氣
ControlVentilation();
}
}
}