/**--------------File Info--------------------------------------------------------------
** Descriptions: 读MQ-9数据并显示
** Created by: Li_moji
** Created date: 2021/11/7
** Version: 1.0
**---------------------------------------------------------------------------------------**/
#include<reg52.h>
#include<intrins.h>
#define uchar unsigned char
#define uint unsigned int
#define PCF8591ADDR 0x90 //PCF8591地址字,末位0读1写
#define I2cRead 1 //I2C读写方向
#define I2cWrite 0 //I2C读写方向
#define at24c02addr 0xA0 //器件地址,由接线决定
sbit beep = P2^3;//蜂鸣器
sbit Dout = P2^0;//SDA数模转换数字输出
sbit Din = P1^0;//传感器数字输出,超过设定值为0
sbit inter = P3^3;//外部中断1触发引脚
sbit SDA = P2^0;//I2C数据线
sbit SCL = P2^1;//I2C时钟线
bit AckFlag;//应答标志
sbit LCDEN = P3^4;
sbit LCDRS = P3^5;//寄存器选择,0为指令寄存器,1为数据寄存器
sbit LCDRW = P3^6;//读写信号线,0为写,1为读
uchar title[] = {"CO-potency"};//要显示的固定内容(第一行)
uchar table[] = {" ppm"};//要显示的数据,空出来,到时候填充
uint datAddr;
uchar dat;
/**
Descriptions:定时器中断0初始化函数
Parameter:
Return:
**/
void timer0Init()
{
EA = 1;//打开总中断
ET0 = 1;//打开定时器中断0
TR0 = 1;//启动定时器0
TMOD |= 0x01;//定时器工作模式1,16位定时模式
TH0 = 0xED;
TL0 = 0xFF;//定时5ms
}
/**
Descriptions: 外部中断1初始化函数
Parameter:
Return:
**/
void int1Init()
{
EA = 1;//打开总中断
EX1 = 1;//打开外部中断1
IT1 = 1;//下降沿触发
}
/**
Descriptions:延时n毫秒
Parameter:int n
Return:
**/
void delay(uint n)
{
uint i=0,j=0;
for(i=0;i<n;i++)
for(j=0;j<123;j++);
}
/**
Descriptions: 延时5us
Parameter:
Return:
**/
void delay5us()
{
_nop_();
}
/**
Descriptions:I2C通信起始函数 ,SCL置高,SDA下降沿即为起始,但两线每种电平都要保持5us以上才有效
Parameter:
Return:
**/
void I2cStart()
{
SCL = 1;
SDA = 1;
delay5us();
SDA = 0;
delay5us();
}
/**
Descriptions: I2C停止函数
Parameter:
Return:
**/
void I2cStop()
{
SDA = 0;
SCL = 1;
delay5us();
SDA = 1;
delay5us();
}
/**
Descriptions:读SDA的从机应答状态
Parameter:
Return:bit 0应答,1不应答
**/
bit readACK()
{
SCL = 1;
delay5us();
if(SDA){
SCL = 0;//拉低
return 1;
}else{
SCL = 0;
return 0;
}
}
/**
Descriptions:主机发送应答
Parameter:bit 1非应答 0应答
Return:
**/
void sendACK(bit answerCode)
{
if(answerCode){
SDA = 1;
}else{
SDA = 0;
}
SCL = 1;
delay5us();
SCL = 0;//用完拉低时钟
SDA= 1;//释放数据总线
}
/**
Descriptions:总线发送一个字节到从站
Parameter:uchar dat一个字的数据
Return:
**/
void I2cSendByte(uchar dat)
{
uchar i;
for(i = 0;i < 8;i ++){
SCL = 0;//时钟为低才允许数据变化
if(dat & 0x80)//判断最高位为1?
{
SDA = 1;//发1
}else
{
SDA = 0;//发0
}
SCL = 1;//1位1位的发,所以一开一关
dat <<= 1;//dat左移一位,次高位变为最高位,下次循环继续判断最高位
}
SCL = 0;
SDA = 1;//释放数据总线
}
/**
Descriptions:主站从从站读一个字节
Parameter:addr从机地址
Return:
**/
uchar I2cReadByte(){
uchar count,dat;
for(count = 0; count < 8; count ++){
dat <<= 1;
//上次dat得到的一位数据左移一位,为下一位读取准备,放在后面第八位读进来时会顶掉最开始读进来一位
SCL = 0;//允许数据变化,从机操作SDA
SCL = 1;//置高才将移出的一位放在SDA上
if(SDA)
dat |= 0x01;//1|=1=1;dat得到一位数据
}
return dat;
}
/**
Descriptions:发送数据到从站
Parameter:uchar addr写入的首地址
Return:
**/
void at24c02write(uchar addr,dat)
{
I2cStart();//起始
I2cSendByte(at24c02addr + I2cWrite);//发送 从机地址 + 读写方向
if(readACK()){//读应答函数:1为不应答,0为应答
AckFlag = 1;
}else{
AckFlag = 0;
}
I2cSendByte(addr);//从机的首地址
if(readACK())
AckFlag = 1;
else
AckFlag = 0;
I2cSendByte(dat);//发送一个字节
if(readACK())
AckFlag = 1;
else
AckFlag = 0;
}
/**
Descriptions:主站从从站读数据 ,中途由写改变为读的方式(只读的方式与此不同):
Parameter:addr从机数据的首地址
Return:dat返回的一个字节
**/
void at24c02read_2(uchar addr){
uchar dat;
I2cStart();
I2cSendByte(at24c02addr + I2cWrite);//发送从机地址和读写方向
if(readACK())
AckFlag = 1;
else
AckFlag = 0;
I2cSendByte(addr);//发送从机数据的首地址
readACK();//不管是不是应答了
I2cStart();//重新开始
dat = I2cReadByte();//读一字节
sendACK(1);//非应答
I2cStop();
}
/**
Descriptions:主站从从站读数据
Parameter:addr从机数据的首地址
Return:dat返回的一个字节
**/
unsigned char at24c02read_1(uchar addr){
uchar dat;
I2cStart(); //开始
I2cSendByte(at24c02addr + I2cWrite);//发送从站地址和写方向
if(readACK()) //读从站应答
AckFlag = 1;
else
AckFlag = 0; //应答
I2cSendByte(addr); //发送读的首地址
if(readACK())
AckFlag = 1;
else
AckFlag = 0; //读从站应答
I2cSendByte(at24c02addr + I2cRead);//发送从站地址和读方向
if(readACK()) //读从站应答
AckFlag = 1;
else
AckFlag = 0;
dat = I2cReadByte(); //读从站一个字节数据
sendACK(1); //给从站回复非应答信号
I2cStop(); //停止
return dat; //把读到的数据返回出来
}
/**
Descriptions:判断LCD是否忙
Parameter:
Return:
**/
void read_lcd_busy(){
uchar busy;
P0 = 0xff; //1111 1111,本开发板P0为数据口
LCDRS = 0; //
LCDRW = 1; //读写1为读
do{
LCDEN = 1;//使能开
busy = P0;//P0口状态忙不忙存出来
LCDEN = 0;//关使能
}while(0x80 & busy);//1000 0000 判断忙不忙
}
/**
Descriptions:LCD写控制指令
Parameter:指令码
Return:
**/
void write_cmd(uchar cmd){
read_lcd_busy();//先读忙不忙
LCDRS = 0;//命令寄存器
LCDRW = 0;//写命令
P0 = cmd;
delay(5);
LCDEN = 1;
LCDEN = 0;
}
/**
Descriptions:LCD写数据
Parameter:数据
Return:
**/
void write_dat(uchar dat){
read_busy();
LCDRS = 1;//数据寄存器
LCDRW = 0;//写
P0 = dat;
delay(5);
LCDEN = 1;
LCDEN = 0;
}
/**
Descriptions:LCD初始化