基于ATMEGA16单片机,MQ-3酒精传感器,LCD1602液晶显示的酒精浓度检测阈值报警仪
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
本文章为个人单片机课程大作业,代码都是自己写的,只是记录自己的学习过程,不做任何商用。
目录
单片机大作业:
一、使用仪器、材料
二、步骤
1.接线
2.写代码
main.c:
ADC.h:
key.h:
1602.H:
3.烧录
三、实验结果
四、总结
单片机大作业:
基于ATMEGA16单片机,MQ-3酒精传感器,LCD1602液晶显示的酒精浓度检测阈值报警仪
功能:
1.将MQ-3传感器模块靠近酒精气体,LCD1602显示屏上显示酒精数值Alcohol mg/L;
2.按键s1设置阈值Set加一,按键s2调节阈值Set减一,按键s3调节阈值Set加十,按键s4调节阈值Set减十;
3.当MQ-3测得酒精浓度数值大于等于阈值(Set)时,蜂鸣器持续响;当MQ-3测得酒精浓度数值小于阈值(Set)时,蜂鸣器停止响;
4.按键每按一下,阈值(Set)改变的同时蜂鸣器响一声。
一、使用仪器、材料
硬件:ATMega16单片机,LCD1602液晶显示,MQ-3酒精传感器,蜂鸣器,杜邦线若干
软件:ICCV7 for AVR(用于编译),Visual Studio Code(用于写代码),progisp.exe (用于烧录)
二、步骤
1.接线
将ATMega16单片机,MQ-3传感器,lcd1602,蜂鸣器用杜邦线连接好;
接线方式:
ATMEGA16 PA0 ---- MQ-3 A0
ATMEGA16 VCC ---- MQ-3 VCC
ATMEGA16 GND ---- MQ-3 GND
备注:MQ-3 D0 未接线(可以不接)
ATMEGA16 ---- LCD1602
ATMEGA16 PD4 ---- 蜂鸣器 I/O
ATMEGA16 VCC ---- 蜂鸣器 VCC
ATMEGA16 GND ---- 蜂鸣器 GND
2.写代码
代码包含.c文件main.c,头文件ADC.h,key.h, 1602.H,把他们放到一个project里面,选择单片机为ATMega16,编译,生成hex文件。
main.c:
#include <iom16v.h>
#include <macros.h>
#include "ADC.h"
#include "math.h"
#include "key.h"
void io_init(void)//IO口初始化
{DDRC = 0xFF; //LCD端口输出PORTC = 0xff;DDRD = 0xf0; //输出(1111 0000),PD0,PD1,PD2三位控制LCD,PD4控制蜂鸣器PORTD = 0xFF; //输出高电平DDRB = 0x00; //按钮输入口(1110 0000)PORTB = 0xFF;PORTA = 0XFF; //设置AD采集端口ADC为输入,本项目用PA0作为ADC输入DDRA = 0X00;
}void DisplayAlcohol(uchar Line)//显示MQ3酒精浓度
{float Vrl=0;Vrl=(adc_data*5.0)/1024.0;//将PA0模拟输出转换为电压值adc_data=pow((11.5428*35.904*Vrl)/(25.5-5.1*Vrl),0.6549);//ppm即计算后的adc_dataten_4 = (adc_data) / 1000;ten_3 = (adc_data) / 100%10;ten_2 = (adc_data) / 10%10;ten_1 = adc_data%10;ten_1 += 0x30;//转换成这个数字对应的ASCII码,作用跟 ten_1 += '0' 相同ten_2 += 0x30;ten_3 += 0x30;ten_4 += 0x30;WriteNum(Line, 8, (ten_4));//在第Line行,第八列lcd开始显示adc_data的千位数WriteNum(Line, 9, (ten_3));WriteNum(Line, 10, (ten_2));WriteNum(Line, 11, (ten_1));
}void DisplaySetAlcohol(uchar Line)//显示设定酒精(阈值)浓度
{ten_4 = (SetAlcohol) / 1000;ten_3 = (SetAlcohol) / 100%10;ten_2 = (SetAlcohol) / 10%10;ten_1 = SetAlcohol%10;ten_1 += 0x30;ten_2 += 0x30;ten_3 += 0x30;ten_4 += 0x30;WriteNum(Line, 8, (ten_4));WriteNum(Line, 9, (ten_3));WriteNum(Line, 10, (ten_2));WriteNum(Line, 11, (ten_1));
}void main(void)//主函数
{uint ad1,ad2;io_init();//io初始化LcdInit();//lcd初始化WriteChar(1, 0, 16, Alcohol);//显示数组ALcohol,即显示lcd第一行的 Alcohol: mg/LWriteChar(2, 0, 16, Set);while (1){key_scan();//按键扫描函数get_AD();//ADC初始化函数adc_data=ad_cat();DisplayAlcohol(1);//将测得的酒精浓度数值显示在lcd第一行delay(100);DisplaySetAlcohol(2);//将酒精浓度阈值数值显示在lcd第二行delay(100);if(adc_data>=SetAlcohol)//测得酒精浓度大于阈值{//delay(100);PORTD &= ~(1 << PD4); //(1110 1111),低电平蜂鸣器响}if(adc_data<SetAlcohol){PORTD |= BIT(4); //(1111 1111),高电平蜂鸣器关}}
}
ADC.h:
#include "1602.H"
#define uchar unsigned char
#define uint unsigned int
uchar Alcohol[] = {"Alcohol: mg/L"};//lcd第一行要显示的数组
uchar Set[] = {"Set : mg/L"};//lcd第二行要显示的数组
uchar ten_1, ten_2, ten_3,ten_4;//个十百千位要显示的数字
uint adc_data, adc_l, adc_h;
void get_AD(void)//ADC初始化
{ADMUX = 0x40;//ADC0,ADCSRA = 0x00;//关掉ADCADCSRA = (1 << ADEN) | (1 << ADSC) | (1 << ADFR) | 0x07;//128分频,连续转换s_ms(500);//延时adc_l = ADCL;adc_h = ADCH;adc_data = adc_h << 8 | adc_l;adc_data = adc_data >> 1;adc_data -= 35;
}unsigned int ad_cat(void)//电压采集函数
{unsigned int t1,t2;ADCSRA = 0X00;//disable ADCADMUX=0x00;//ref 左对齐 ADC0ACSR = 0x80;//使能ADC可用,不用修改ADCSRA|=BIT(ADEN); //ADC使能 ADCSRA|=BIT(ADSC); //开始转换 while(!(ADCSRA&(BIT(ADIF))));//ADIF置一,adc转换结束时,跳出循环ADCSRA&=~BIT(ADIF);//清零t1 = (unsigned int)ADCL;t2 = (unsigned int)ADCH;t2 = (t2<<8)+t1;//高位左移8位加上低位return t2;
}
key.h:
unsigned int i=0,SetAlcohol=400;
void delay(uint time)//延时函数
{uint i,j;for(i=0;i<254;i++){for(j=0;j<time;j++){;}}
}void key_scan(void)//按键函数,实现按键每按一下阈值变化的同时蜂鸣器响一声
{if((PINB&(1<<PB0))==0) //按键PB0,s1{if(SetAlcohol<1666){SetAlcohol++;} delay(500);PORTD &= ~(1 << PD4);}if((PINB&(1<<PB1))==0) //按键PB1,s2{if(SetAlcohol>0){SetAlcohol--;}delay(500);PORTD&=~(1<<PD4);}if((PINB&(1<<PB2))==0) //按键PB2,s3{if(SetAlcohol<1666){SetAlcohol+=10;} delay(500);PORTD&=~(1<<PD4);} if((PINB&(1<<PB3))==0) //按键PB3,s4{if(SetAlcohol>=10){SetAlcohol-=10;}delay(500);PORTD&=~(1<<PD4);}
}
1602.H:
#define uchar unsigned char
#define uint unsigned int #define RS 7
#define RW 6
#define EN 5void s_ms(uint ms)
{for(;ms>1;ms--);
} //查忙
void busy(void)
{uchar temp;s_ms(500);PORTD&=~(1<<RS); //RS=7s_ms(500);PORTD|=(1<<RW); //RW=6s_ms(500);while(temp){PORTD|=(1<<EN); //EN=5s_ms(500);DDRC=0x00; //A口变输入PORTC=0xff; //上拉使能s_ms(500);temp = PINC&0x80; //读取C口s_ms(500); DDRC=0xff; PORTC=0xff; //C口变输出s_ms(500);PORTD&=~(1<<EN); //EN=0s_ms(500);}
}//写指令
void writecom(uchar com)
{busy();s_ms(500);PORTD&=~(1<<RS); //RS=7s_ms(500);PORTD&=~(1<<RW); //RW=6s_ms(500);PORTD|=(1<<EN); //EN=5s_ms(500);PORTC = com; //输出指令s_ms(500);PORTD&=~(1<<EN); //EN=0s_ms(500);
}//1602初始化
void LcdInit(void)
{writecom(0x38);s_ms(1000);writecom(0x01);s_ms(10000);s_ms(1000);s_ms(1000);s_ms(1000);s_ms(1000);s_ms(1000);s_ms(1000);writecom(0x02);s_ms(1000);writecom(0x06);s_ms(1000);writecom(0x0c);s_ms(1000);writecom(0x38); s_ms(1000);
} //写数据
void writedata(uchar data)
{busy();s_ms(500);PORTD|=(1<<RS); //RS=7s_ms(500);PORTD&=~(1<<RW); //RW=6s_ms(500);PORTD|=(1<<EN); //EN=5s_ms(500);PORTC = data; //输出数据s_ms(500);PORTD&=~(1<<EN); //EN=0s_ms(500);
}//读数据
uchar readdata(void)
{uchar temp;busy();s_ms(500);PORTD|=(1<<RS); //RS=7s_ms(500);PORTD|=(1<<RW); //RW=6s_ms(500);PORTD|=(1<<EN); //EN=5s_ms(500);DDRC=0x00; //A端口变输入s_ms(500);temp = PINC; //读A端口s_ms(500);DDRC=0xff; //A端口变输出s_ms(500);PORTD&=~(1<<EN); //EN=0s_ms(500);return temp;
}//=================================================
// 描述: 写LCD内部CGRAM函数
// 入口: ‘num’要写的数据个数
// ‘pbuffer’要写的数据的首地址
// 出口: 无
//================================================
void WriteCGRAM(uint num, const uint *pBuffer)
{uint i,t;writecom(0x40);PORTD|=(1<<RS);PORTD&=~(1<<RW);for(i=num;i!=0;i--){t = *pBuffer;PORTD|=(1<<EN);PORTC = t;PORTD&=~(1<<EN); pBuffer++;}}//=================================================
//描述:写菜单函数,本程序使用的LCD规格为 16 * 2
//入口:菜单数组首地址
//出口:无
//=================================================
void WriteMenu(const uchar *pBuffer)
{uchar i,t;writecom(0x80); //数据地址PORTD|=(1<<RS);PORTD&=~(1<<RW);s_ms(50);for(i=0;i<16;i++){t = *pBuffer;PORTC = t;PORTD|=(1<<EN);s_ms(50);PORTD&=~(1<<EN); pBuffer++;}writecom(0xC0);PORTD|=(1<<RS);PORTD&=~(1<<RW);s_ms(50); for(i=0;i<16;i++){t = *pBuffer;PORTC = t;PORTD|=(1<<EN);s_ms(50);PORTD&=~(1<<EN); pBuffer++;}
}
//====================================================
// 描述:在任意位置写数字函数
// 入口:’row‘表示要写数字所在的行地址,只能为1或2
// ’col‘表示要写数字所在的列地址,只能为0--15
// ‘num’表示要写的数字,只能为0--9
// 出口:无
//===================================================
void WriteNum(uchar row,uchar col,uchar num)
{if (row == 1) row = 0x80 + col;else row = 0xC0 + col;writecom(row);PORTD|=(1<<RS);s_ms(500);PORTD&=~(1<<RW);s_ms(500);PORTC = num;s_ms(500);PORTD|=(1<<EN);s_ms(500);PORTD&=~(1<<EN); s_ms(500);
}
//================================================================
// 描述:在任意位置写任意多个字符
// 入口:’row‘要写的字符所在的行,只能为1或2;
// ‘col’要写的字符所在的列,只能为0---15
// ‘num’要写字符的个数
// ‘pbuffer’要写字符的首地址
//==================================================================
void WriteChar(uchar row,uchar col,uint num,uchar *pBuffer)
{uchar i,t;if (row == 1) row = 0x80 + col;else row = 0xC0 + col;writecom(row);PORTD|=(1<<RS);s_ms(500);PORTD&=~(1<<RW);s_ms(500);for(i=num;i!=0;i--){t = *pBuffer;s_ms(500);PORTC = t;s_ms(500);PORTD|=(1<<EN);s_ms(500);PORTD&=~(1<<EN); s_ms(500); pBuffer++;}}
3.烧录
找到生成的hex文件,将单片机连接上USBISP线后,通过烧录软件烧录到接好线的单片机中,观察实验结果。
三、实验结果
当MQ-3传感器原理酒精时,也就是只暴露在正常的空气中时,LCD1602显示 Alcohol:13mg/L
当MQ-3传感器原理酒精时,也就是只暴露在正常的空气中时,LCD1602显示 Alcohol:1666mg/L
备注:ATMega的ADC模块最高只有十位,此时输入的电压值有限,因此此时显示的有最大值,至于为什么是1666,我也没花时间去弄明白。有兴趣的小伙伴可以自己慢慢钻研哈。其实将传感器慢慢离酒精远一点显示屏上面的数值会变化,这里不方便放图片了。
下面那一行Set的数值(也就是自己设置的酒精浓度阈值)可以通过按按键改变:
四、总结
没啥总结的,就是有几点需要注意:
线路一定不要接错;
MQ-3模块一开始工作的时候可以预热一会,这样测出来更精准(这个我也是看到别人说的);
MQ-3模块工作的时候会发热,但是温度如果烫手了的话应该就是出问题了。
最后,如果是要自己学习单片机的话,建议不要用这个板子,真不好用。
祝各位不负青春!
基于ATMEGA16单片机,MQ-3酒精传感器,LCD1602液晶显示的酒精浓度检测阈值报警仪相关推荐
- 模块称重51单片机电子秤称重压力检测阈值报警仪系统 源码+原理图仿
HX711模块称重51单片机电子秤称重压力检测阈值报警仪系统 源码+原理图仿真(文件,文件,文件) 本系统由STC89C52单片机.LCD1602液晶显示.HX711称重传感器. 蜂鸣器报警.按键及电 ...
- 基于ATmega16单片机和GPS的多用途定位仪设计
目录 摘要 I Abstract II 第1章绪论 1 1.1 课题研究的背景及意义 1 1.2国内外研究现状 1 1.2.1 外国研究发展现状 1 1.2.2国内研究发展现状 2 1.3课题研究技术 ...
- 基于ATMEGA16单片机的空调控制器
点击链接获取Keil源码与Project Backups仿真图: https://download.csdn.net/download/qq_64505944/87853101 源码获取 主要内容: ...
- 基于51单片机和红外漫反射传感器的久坐提醒神器(番茄钟)的设计
一.背景介绍 我们工作久了,久坐导致的毛病就显现出来了,腰酸背痛颈椎疼,最近看到利用番茄钟工作法挺好,工作25分钟,休息5分钟,既能调整工作节奏,避免精力过分消耗,也能避免久坐导致的身体问题. 我刚开 ...
- 基于51单片机的时钟闹钟温度计LCD1602显示proteus仿真原理图PCB
功能介绍: 0.本系统采用STC89C52作为单片机 1.LCD1602液晶实时显示时间/当前温度 2.按键可进行闹钟设置和温度上下限设置 3.到达闹钟时间或温度超过阈值范围,蜂鸣器报警 4.到达闹钟 ...
- 34、基于51单片机锂电池电压电流容量检测仪表LCD液晶显示 原理图PCB程序设计
方案选择 单片机的选择 方案一:AT89C52是美国ATMEL公司生产的低电压,高性能CMOS型8位单片机,器件采用ATMEL公司的高密度.非易失性存储技术生产,兼容标准MCS-51指令系统,片内置通 ...
- 98、基于51单片机锂电池电压电流容量检测仪表LCD液晶显示
毕设帮助.开题指导.技术解答(有偿)见文末. 目录 摘要 一.硬件方案 二.设计功能 三.实物图 四.原理图 五.PCB图 六.部分程序源码 七.资料包括 摘要 锂离子电池是 1990年后逐渐发展起来 ...
- 70、基于STM32单片机的蓝牙智能热水器控制系统设计温度温控水温水位检测
毕设帮助.开题指导.技术解答(有偿)见文末. 目录 摘要 一.硬件方案 二.设计功能 三.实物图 四.原理图 五.PCB图 六.硬件框图 七.程序源码 八.资料包括 摘要 随着社会的发展,人类科技的进 ...
- 基于C51单片机的万年历设计(LCD1602显示)
C51单片机万年历设计 注:该程序基于普中科技C51 V2.2开发板设计,库函数和硬件资料均来自普中科技,侵删.main.c大部分为原创,如有雷同,纯属巧合. 名称:C51万年历. 硬件:以C51芯片 ...
最新文章
- bar图设置距离 python_python画图设置坐标轴的位置及角度及设置colorbar
- window-运行perl脚本(搭建health-check环境)
- 【LeetCode】剑指 Offer 18. 删除链表的节点
- JAVA核心技术_【读】Java核心技术卷1
- 百度起诉今日头条;腾讯云超 8000 名员工获 iPhone 11 Pro 奖励;PHP 7.4.1 发布 | 极客头条...
- 【Linux】肝!Shell 脚本编程最佳实践
- 软件质量管理体系 type:pdf_普宁iso14001环境管理体系AAA信用等级认证
- java中文繁体简体转换
- 万字 常用设计软件大盘点|如何选择自己最适合的软件【建议收藏】
- 从 flask 服务端代码自动生成客户端代码 -- flask-native-stubs 库介绍
- 学籍管理html,河南学籍管理系统
- 彻底解决unable to find valid certification path to requested target
- 腾讯云永久修改主机名
- 【论文翻译 IJCAI-20】Heterogeneous Network Representation Learning 异构网络表示学习
- 双堆1.数据流的中位数
- Windows系统win10系统流程图软件推荐
- 大数据导论答案_2020高校邦《数据科学与大数据技术导论》课后作业答案
- php多线程原子操作,C语言线程互斥和原子操作
- flink的java api_Flink 流处理API之二
- 万历皇帝的金丝蟠龙翼善冠