概述
A/D转换
选用芯片:AT89S52
函数功能:如下代码
/*******************************************************************
此函数功能为:PCF8591芯片的使用,A/D转换,四路通道。通道一为可调电阻,
通道二为热敏电阻,通道三为光敏电阻,通道四接地。分别转换出它们的对应
电压,显示在液晶LCD1602上。
作者:Crazy Wind
日期:2020.11.2
version:1.0.0
********************************************************************/
#include"reg52.h"
#include"intrins.h"
#define uchar unsigned char
#define uint unsigned int
sbit SCL=P3^4; //模拟时钟口
sbit SDA=P3^5; //模拟数据口
bit askflag;
#define disdata P0 //显示数据码输出口
sbit LCD_RS=P2^0; //寄存器选择位RS为P2.0
sbit LCD_RW=P2^1; //读写选择位RW为P2.1
sbit LCD_E=P2^2; //使能信号位E为P2.2
uint data dis[4]={0x00,0x00,0x00,0x00}; //定义3个显示数据单元和1个数据存储单元
uchar code dis4[] = {"1- . V 2- . V"};
uchar code dis5[] = {"3- . V 4- . V"};
#define delayNOP(); {_nop_();_nop_();_nop_();_nop_();_nop_();}; //延时5个机器周期
bit bdata SystemError; //从机错误标志位
/*********************** PCF8591专用变量定义 ***********************/
#define PCF8591_WRITE 0x90
#define PCF8591_READ 0x91
#define NUM 4 //接收和发送缓存区的深度
uchar idata receivebuf[NUM]; //数据接收缓冲区
/*****************************************************
函数功能:ms延时
******************************************************/
void delayms(uint ms) //毫秒延时
{
uint i,j;
for(i=0;i<ms;i++)
{
for(j=115;j>0;j--);
}
}
/*****************************************************
函数功能:判断液晶模块的忙碌状态
返回值:busy。busy=1,忙碌;busy=0,不忙
******************************************************/
bit BusyTest(void)
{
bit busy;
LCD_RS=0; //RS为低电平,RW为高电平时,可以读状态
LCD_RW=1;
LCD_E=1; //E=1,才允许读写
_nop_(); //空操作四个机器周期,给硬件反应时间
_nop_();
_nop_();
_nop_();
busy=(bit)(P0&0x80); //将忙碌标志电平赋给busy(通过按位“与”运算将最高位赋给busy)
LCD_E=0;
return busy; //将忙碌标志电平赋给busy
}
/*****************************************************
函数功能:将模式设置指令或显示地址写入液晶模块
入口参数:z
******************************************************/
void Write_zl(uchar z)
{
while(BusyTest()==1); //如果忙就等待
LCD_RS=0; //RS和RW同时为低电平时,可以写入指令
LCD_RW=0;
LCD_E=0; //使能信号E置低电平(写指令前先赋低电平0)
_nop_();
_nop_(); //空操作两个机器周期,给硬件反应时间
P0=z; //将数据送入P0口(数据总线line),即写入指令或地址
_nop_();
_nop_();
_nop_();
_nop_(); //空操作四个机器周期,给硬件反应时间
LCD_E=1; //使能信号E上升沿(0到1)时读取信息
_nop_();
_nop_();
_nop_();
_nop_(); //空操作四个机器周期,给硬件反应时间
LCD_E=0; //使能信号E下降沿(1到0)时执行指令
}
/*****************************************************
函数功能:指定字符显示的实际地址
入口参数:x
******************************************************/
void WriteAddress(uchar x)
{
Write_zl(x|0x80); //显示位置的确定方法规定为"地址码x+80H"
}
/*****************************************************
函数功能:将数据(字符的标准ASCII码)写入液晶模块
入口参数:y(为字符常量)
******************************************************/
void WriteData(uchar y)
{
while(BusyTest()==1); //如果忙就等待
LCD_RS=1; //RS为高电平,RW为低电平时,可以写入数据
LCD_RW=0;
LCD_E=0; //使能信号E置低电平(写数据前先赋低电平0)
_nop_();
_nop_(); //空操作两个机器周期,给硬件反应时间
P0=y; //将数据送入P0口(数据总线line),即将数据写入液晶模块
_nop_();
_nop_();
_nop_();
_nop_(); //空操作四个机器周期,给硬件反应时间
LCD_E=1; //使能信号E上升沿(0到1)时读取信息
_nop_();
_nop_();
_nop_();
_nop_(); //空操作四个机器周期,给硬件反应时间
LCD_E=0; //使能信号E下降沿(1到0)时执行指令
}
/*****************************************************
函数功能:对LCD的显示模式进行初始化设置
******************************************************/
void LcdInit(void)
{
delayms(15); //延时15ms,首次写指令时应给LCD一段较长的反应时间
Write_zl(0x38); //显示模式设置:16×2显示,5×7点阵,8位数据接口
delayms(5); //延时5ms
Write_zl(0x38);
delayms(5);
Write_zl(0x38); //3次写 设置模式
delayms(5);
Write_zl(0x0c); //显示模式设置:显示开,无光标,光标不闪烁
delayms(5);
Write_zl(0x06); //显示模式设置:光标右移,字符不移
delayms(5);
Write_zl(0x01); //清屏幕指令,将以前的显示内容清除
delayms(5);
}
/*****************************************************
函数功能:将采集到的数据进行16进制转ASCLL码
******************************************************/
void Transform(uchar dat)
{
dis[2]=dat/51; //AD值转换为3位BCD码(256/5),最大为5.00V。
dis[2]=dis[2]+0x30; //转换为ACSII码
dis[3]=dat%51; //余数暂存
dis[3]=dis[3]*10; //计算小数第一位
dis[1]=dis[3]/51;
dis[1]=dis[1]+0x30; //转换为ACSII码
dis[3]=dis[3]%51;
dis[3]=dis[3]*10; //计算小数第二位
dis[0]=dis[3]/51; //
dis[0]=dis[0]+0x30; //转换为ACSII码
}
/*****************************************************
函数功能:启动I2C总线
******************************************************/
void iic_start(void)
{
SDA=1;
SCL=1; //时钟保持高,数据线从高到低一次跳变,I2C通信开始
delayNOP(); // 延时5us
SDA=0;
delayNOP();
SCL=0;
}
/*****************************************************
函数功能:停止I2C总线数据传送
******************************************************/
void iic_stop(void)
{
SDA=0; //时钟保持高,数据线从低到高一次跳变,I2C通信停止
SCL=1;
delayNOP();
SDA=1;
delayNOP();
SCL=0;
}
/*****************************************************
函数功能:初始化I2C总线
******************************************************/
void iicInit(void)
{
SCL=0;
iic_stop();
}
/*****************************************************
函数功能:从机发送应答位
******************************************************/
void slave_ACK(void)
{
SDA=0;
SCL=1;
delayNOP();
SCL=0;
}
/*****************************************************
函数功能:从机发送非应答位,迫使数据传输过程结束
******************************************************/
void slave_NOACK(void)
{
SDA=1;
SCL=1;
delayNOP();
SDA=0;
SCL=0;
}
/*****************************************************
函数功能:主机应答位检查,迫使数据传输过程结束
******************************************************/
void check_ACK(void)
{
SDA=1; //将p1.1设置成输入,必须先向端口写1
SCL=1;
askflag=0;
delayNOP();
if(SDA==1) //若SDA=1表明非应答,置位非应答标志askflag
askflag=1;
SCL=0;
}
/*****************************************************
函数功能:发送一个字节
入口参数:ch
******************************************************/
void IICSendByte(uchar ch)
{
unsigned char idata n=8; //向SDA上发送一位数据字节,共八位
while(n--)
{
if((ch&0x80)==0x80) //若要发送的数据最高位为1则发送位1
{
SDA=1; //传送位1
SCL=1;
delayNOP();
// SDA=0;
SCL=0;
}
else
{
SDA=0; //否则传送位0
SCL=1;
delayNOP();
SCL=0;
}
ch=ch<<1; //数据左移一位
}
}
/*****************************************************
函数功能:接收一个字节
******************************************************/
uchar IICreceiveByte(void)
{
uchar idata n=8; //从SDA线上读取一位数据字节,共八位
uchar tdata=0;
while(n--)
{
SDA=1;
SCL=1;
tdata=tdata<<1; //左移一位
if(SDA==1)
tdata=tdata|0x01; //若接收到的位为1,则数据的最后一位置1(按位”或“)
else
tdata=tdata&0xfe; //否则数据的最后一位置0(按位“与”)
SCL=0;
}
return(tdata);
}
/*****************************************************
函数功能:发送n位数据子程序
入口参数:slave_add从机地址,n要发送的数据个数
******************************************************/
void DAC_PCF8591(uchar controlbyte,uchar w_data)
{
iic_start(); //启动I2C
delayNOP();
IICSendByte(PCF8591_WRITE); // 发送地址位
check_ACK();
if(askflag==1)
{
SystemError=1;
return; //若非应答,置错误标志位
}
IICSendByte(controlbyte&0x77); //Control byte
check_ACK(); //检查应答位
if(askflag==1)
{
SystemError=1;
return; // 若非应答,置错误标志位
}
IICSendByte(w_data); //data byte
check_ACK(); // 检查应答位
if(askflag==1)
{
SystemError=1;
return; // 若非应答表明器件错误或已坏,置错误标志位SystemError
}
iic_stop(); // 全部发完则停止
delayNOP();
delayNOP();
delayNOP();
delayNOP();
}
/*****************************************************
函数功能:连续读入4路通道的A/D转换结果到receivebuf
入口参数:controlbyte控制字
******************************************************/
void ADC_PCF8591(uchar controlbyte)
{
uchar idata receive_da,i=0;
iic_start();
IICSendByte(PCF8591_WRITE); //控制字
check_ACK();
if(askflag==1)
{
SystemError=1;
return;
}
IICSendByte(controlbyte); //控制字
check_ACK();
if(askflag==1)
{
SystemError=1;
return;
}
iic_start(); //重新发送开始命令
IICSendByte(PCF8591_READ); //控制字
check_ACK();
if(askflag==1)
{
SystemError=1;
return;
}
IICreceiveByte(); //空读一次,调整读顺序
slave_ACK(); //收到一个字节后发送一个应答位
while(i<4)
{
receive_da=IICreceiveByte();
receivebuf[i++]=receive_da;
slave_ACK(); //收到一个字节后发送一个应答位
}
slave_NOACK(); //收到最后一个字节后发送一个非应答位
iic_stop();
}
/*****************************************************
函数功能:主程序
******************************************************/
main()
{
uchar i,l;
delayms(10);
LcdInit(); //初始化LCD
WriteAddress(0); //设置显示位置为第一行的第1个字符
i=0;
while(dis4[i]!='