一、前言
煤氣泄漏是一個(gè)嚴(yán)重的安全隱患,可能導(dǎo)致火災(zāi)、爆炸以及對(duì)人體健康的威脅。為了提高家庭和工業(yè)環(huán)境中煤氣泄漏的檢測和預(yù)防能力,設(shè)計(jì)了一種基于單片機(jī)的防煤氣泄漏裝置。
單片機(jī)選擇STC89C52作為主控芯片。為了檢測煤氣泄漏,采用了MQ4傳感器,能夠快速、準(zhǔn)確地檢測煤氣濃度。通過采集MQ4傳感器的模擬信號(hào),使用PCF8591模數(shù)轉(zhuǎn)換芯片將模擬信號(hào)轉(zhuǎn)換為數(shù)字信號(hào)。采用IIC接口的OLED顯示屏,將采集到的數(shù)據(jù)顯示出來,方便用戶獲取檢測結(jié)果。用戶可以通過兩個(gè)獨(dú)立按鍵設(shè)置煤氣泄漏的報(bào)警閾值,以適應(yīng)不同環(huán)境的需求。
當(dāng)檢測到煤氣泄漏超過設(shè)定的閾值時(shí),裝置會(huì)觸發(fā)蜂鳴器進(jìn)行報(bào)警,并同時(shí)打開換氣扇進(jìn)行通風(fēng)換氣,以迅速排除煤氣并降低安全風(fēng)險(xiǎn)。
這種基于單片機(jī)設(shè)計(jì)的防煤氣泄漏裝置具有以下優(yōu)點(diǎn):高效可靠的煤氣泄漏檢測能力、靈活的報(bào)警閾值設(shè)置、直觀清晰的數(shù)據(jù)顯示以及及時(shí)的安全響應(yīng)措施??梢詮V泛應(yīng)用于家庭、工業(yè)和商業(yè)場所,提供有效的煤氣泄漏監(jiān)測和安全保護(hù)。
二、硬件選型
在設(shè)計(jì)基于單片機(jī)的防煤氣泄漏裝置時(shí),硬件選型是非常關(guān)鍵的。以下是詳細(xì)介紹硬件選型的相關(guān)內(nèi)容:
【1】主控芯片選擇:STC89C52 STC89C52是一款8051架構(gòu)的單片機(jī),具有豐富的接口資源、較高的性能和穩(wěn)定可靠的工作特性,廣泛應(yīng)用于各種嵌入式系統(tǒng)中。具有8位數(shù)據(jù)總線、16位地址總線和4KB的內(nèi)部存儲(chǔ)器。STC89C52具備多個(gè)通用I/O口、定時(shí)器/計(jì)數(shù)器、串口等功能,非常適合本項(xiàng)目需求。
【2】煤氣傳感器選擇:MQ4 MQ4傳感器是一種能夠檢測多種可燃?xì)怏w,如天然氣、甲烷等的傳感器。具有高靈敏度和快速響應(yīng)的特點(diǎn),能夠準(zhǔn)確地檢測煤氣泄漏情況。MQ4傳感器的輸出為模擬信號(hào),需要通過模數(shù)轉(zhuǎn)換器將其轉(zhuǎn)換為數(shù)字信號(hào)供主控芯片處理。
【3】模數(shù)轉(zhuǎn)換器選擇:PCF8591 PCF8591是一款集成了8位模數(shù)/數(shù)模轉(zhuǎn)換和4個(gè)模擬輸入通道的模數(shù)轉(zhuǎn)換器。采用IIC總線通訊接口,能夠?qū)⒛M信號(hào)轉(zhuǎn)換為數(shù)字信號(hào),并通過IIC協(xié)議發(fā)送給主控芯片。本項(xiàng)目中,PCF8591用于采集MQ4傳感器輸出的模擬信號(hào),并將其轉(zhuǎn)換為數(shù)字信號(hào)供STC89C52處理。
【4】顯示屏選擇:0.96寸OLED顯示屏(IIC接口) 本設(shè)計(jì)采用基于IIC接口的OLED顯示屏,具有高亮度、對(duì)比度和快速響應(yīng)的特點(diǎn)。通過簡單的通訊方式,可以將煤氣濃度信息實(shí)時(shí)顯示在屏幕上。OLED顯示屏使用面積小、功耗低,在嵌入式系統(tǒng)中應(yīng)用廣泛。
【5】按鍵選擇:獨(dú)立按鍵 本設(shè)計(jì)采用兩個(gè)獨(dú)立按鍵來設(shè)置報(bào)警的閥值。一個(gè)按鍵用于遞增閥值,另一個(gè)按鍵用于遞減閥值。獨(dú)立按鍵具有簡單可靠、使用方便等特點(diǎn),適合本項(xiàng)目需求。
【6】報(bào)警裝置選擇:蜂鳴器和換氣扇 當(dāng)檢測到煤氣泄漏超過設(shè)定的報(bào)警閥值時(shí),蜂鳴器將發(fā)出警報(bào),用于提醒周圍人員。同時(shí),為了降低煤氣濃度,需要啟動(dòng)換氣扇進(jìn)行通風(fēng)換氣。具體的報(bào)警和換氣扇電路可以根據(jù)實(shí)際需求設(shè)計(jì)。
三、設(shè)計(jì)思路
軟件設(shè)計(jì)思路如下:
【1】初始化:在程序開始時(shí),進(jìn)行主控芯片STC89C52的初始化設(shè)置,包括引腳配置、定時(shí)器設(shè)置等。同時(shí),初始化PCF8591和OLED顯示屏,確保它們可以正常工作。
【2】傳感器檢測:通過MQ4傳感器檢測煤氣是否泄漏。將MQ4傳感器與STC89C52的模擬輸入引腳連接,通過讀取該引腳的模擬電壓值,獲取煤氣濃度數(shù)據(jù)。
【3】數(shù)據(jù)采集與處理:使用PCF8591模數(shù)轉(zhuǎn)換芯片,將MQ4傳感器的模擬輸出信號(hào)轉(zhuǎn)換為數(shù)字信號(hào),并通過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è)置:通過兩個(gè)獨(dú)立按鍵實(shí)現(xiàn)報(bào)警閾值的設(shè)置。將按鍵與STC89C52的GPIO引腳連接,通過讀取按鍵狀態(tài)來判斷用戶是否進(jìn)行閾值設(shè)置操作。當(dāng)按鍵按下時(shí),進(jìn)入設(shè)置模式,用戶可以通過按鍵的不同組合來調(diào)整報(bào)警閾值,并將設(shè)置的值保存在相應(yīng)的變量中。
【6】報(bào)警與通風(fēng)控制:根據(jù)當(dāng)前采集到的煤氣濃度數(shù)據(jù)和用戶設(shè)置的報(bào)警閾值進(jìn)行比較。如果煤氣濃度超過設(shè)定的閾值,觸發(fā)蜂鳴器進(jìn)行報(bào)警,并控制換氣扇打開進(jìn)行通風(fēng)換氣。反之,當(dāng)煤氣濃度低于或等于設(shè)定的閾值時(shí),停止報(bào)警并關(guān)閉換氣扇。
【7】循環(huán)監(jiān)測:使用主循環(huán)結(jié)構(gòu),不斷進(jìn)行煤氣泄漏檢測、數(shù)據(jù)采集、數(shù)據(jù)顯示和閾值比較等操作,以實(shí)現(xiàn)持續(xù)的監(jiān)測和反饋。
四、項(xiàng)目模塊代碼
4.1 PCF8591采集代碼
下面是使用STC89C52單片機(jī)通過PCF8591讀取MQ4傳感器的ADC數(shù)據(jù)的代碼。使用IIC總線進(jìn)行PCF8591之間的通信,使用了自定義的IIC總線函數(shù)。通過readADC()
函數(shù)實(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總線時(shí)鐘線
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í)一段時(shí)間
}
}
// 延時(shí)函數(shù)
void delay(uint ms) {
uint i, j;
for(i = ms; i > 0; i--) {
for(j = 110; j > 0; j--);
}
}
// IIC總線起始信號(hào)
void startIIC() {
SDA = 1;
_nop_();
SCL = 1;
_nop_();
SDA = 0;
_nop_();
SCL = 0;
_nop_();
}
// IIC總線停止信號(hào)
void stopIIC() {
SDA = 0;
_nop_();
SCL = 1;
_nop_();
SDA = 1;
_nop_();
}
// 發(fā)送一個(gè)字節(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_();
}
// 接收一個(gè)字節(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ù)實(shí)現(xiàn)代碼。
#include <reg51.h>
#define SCL P1_0 // IIC時(shí)鐘線
#define SDA P1_1 // IIC數(shù)據(jù)線
#define OLED_ADDR 0x78 // OLED顯示屏的IIC地址
// OLED顯示屏初始化函數(shù)
void OLED_Init() {
// 初始化OLED顯示屏
// ...
// 發(fā)送初始化命令到OLED顯示屏
// ...
}
// IIC總線開始信號(hào)
void IIC_Start() {
SDA = 1;
SCL = 1;
SDA = 0;
SCL = 0;
}
// IIC總線停止信號(hào)
void IIC_Stop() {
SDA = 0;
SCL = 1;
SDA = 1;
}
// IIC總線發(fā)送一個(gè)字節(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顯示屏上顯示一個(gè)字符
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時(shí)鐘線
#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; // 報(bào)警閾值,默認(rèn)為100
unsigned int adcValue = 0; // 保存ADC采集到的數(shù)值
// IIC總線開始信號(hào)
void IIC_Start() {
SDA = 1;
SCL = 1;
SDA = 0;
SCL = 0;
}
// IIC總線停止信號(hào)
void IIC_Stop() {
SDA = 0;
SCL = 1;
SDA = 1;
}
// IIC總線發(fā)送一個(gè)字節(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顯示屏上顯示一個(gè)字符
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è)置初始報(bào)警閾值
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ā)報(bào)警
if (adcValue > alarmThreshold) {
// 觸發(fā)報(bào)警
ActivateAlarm();
} else {
// 取消報(bào)警
DeactivateAlarm();
}
// 檢測是否按下設(shè)置閾值的按鍵
if (IsThresholdButtonPressed()) {
// 設(shè)置報(bào)警閾值
SetAlarmThreshold();
}
// 檢測是否按下通風(fēng)換氣的按鍵
if (IsVentilationButtonPressed()) {
// 控制通風(fēng)換氣
ControlVentilation();
}
}
}