数字电压表要求:在AT89C52系统中采用PCF8591芯片,测量0-5V范围内的直流电压,并在2位数码管上显示电压值。
问题咨询请联系-》群名:IT项目交流群 群号:245022761
PROTEUS仿真电路:
在KEIL中编写的源程序:
#include<reg52.h> #include <intrins.h> #define AddWr 0x90 //PCF8591 地址 sbit
scl=P2^0; //I2C 时钟 sbit sda=P2^1; //I2C 数据 bit ack; //应答标志位 unsigned char date;
sbit C1=P2^6;//数码管1 sbit C2=P2^7;//数码管2 sbit Dp=P2^5;//小数点 table[10]=
{0x3f,0x06,0x5B,0x4F,0x66, 0x6D,0x7D,0x07,0x7F,0x6F};//0~9 unsigned int data
dis[3]={0x00,0x00,0x00}; unsigned int getData;
/******************************************************************* 起动总线函数
函数原型: void Start_I2c(); 功能: 启动I2C总线,即发送I2C起始条件.
********************************************************************/ void
Start_I2c() { sda=1; /*发送起始条件的数据信号*/ _nop_(); scl=1; _nop_();
/*起始条件建立时间大于4.7us,延时*/ _nop_(); _nop_(); _nop_(); _nop_(); sda=0; /*发送起始信号*/
_nop_(); /* 起始条件锁定时间大于4μs*/ _nop_(); _nop_(); _nop_(); _nop_(); scl=0;
/*钳住I2C总线,准备发送或接收数据 */ _nop_(); _nop_(); }
/******************************************************************* 结束总线函数
函数原型: void Stop_I2c(); 功能: 结束I2C总线,即发送I2C结束条件.
********************************************************************/ void
Stop_I2c() { sda=0; /*发送结束条件的数据信号*/ _nop_(); /*发送结束条件的时钟信号*/ scl=1;
/*结束条件建立时间大于4μs*/ _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); sda=1;
/*发送I2C总线结束信号*/ _nop_(); _nop_(); _nop_(); _nop_(); }
/******************************************************************* 字节数据发送函数
函数原型: void I2C_SendByte(UCHAR c); 功能: 将数据c发送出去,可以是地址,也可以是数据,发完后等待应答,并对
此状态位进行操作.(不应答或非应答都使ack=0) 发送数据正常,ack=1; ack=0表示被控器无应答或损坏。
********************************************************************/ void
I2C_SendByte(unsigned char c) { unsigned char i; for(i=0;i<8;i++)
/*要传送的数据长度为8位*/ { if((c<<i)&0x80)sda=1; /*判断发送位*/ else sda=0; _nop_(); scl=1;
/*置时钟线为高,通知被控器开始接收数据位*/ _nop_(); _nop_(); /*保证时钟高电平周期大于4μs*/ _nop_(); _nop_();
_nop_(); scl=0; } _nop_(); _nop_(); sda=1; /*8位发送完后释放数据线,准备接收应答位*/ _nop_();
_nop_(); scl=1; _nop_(); _nop_(); _nop_(); if(sda==1)ack=0; else ack=1;
/*判断是否接收到应答信号*/ scl=0; _nop_(); _nop_(); }
/******************************************************************* 字节数据接收函数
函数原型: UCHAR I2C_RcvByte(); 功能: 用来接收从器件传来的数据,并判断总线错误(不发应答信号), 发完后请用应答函数应答从机。
********************************************************************/ unsigned
char I2C_RcvByte() { unsigned char retc=0,i; sda=1; /*置数据线为输入方式*/
for(i=0;i<8;i++) { _nop_(); scl=0; /*置时钟线为低,准备接收数据位*/ _nop_(); _nop_();
/*时钟低电平周期大于4.7μs*/ _nop_(); _nop_(); _nop_(); scl=1; /*置时钟线为高使数据线上数据有效*/
_nop_(); _nop_(); retc=retc<<1; if(sda==1)retc=retc+1; /*读数据位,接收的数据位放入retc中 */
_nop_(); _nop_(); } scl=0; _nop_(); _nop_(); return(retc); }
/******************************************************************** 应答子函数
函数原型: void Ack_I2c(bit a); 功能: 主控器进行应答信号(可以是应答或非应答信号,由位参数a决定)
********************************************************************/ void
Ack_I2c(bit a) { if(a==0)sda=0; /*在此发出应答或非应答信号 */ else sda=1; /*0为发出应答,1为非应答信号
*/ _nop_(); _nop_(); _nop_(); scl=1; _nop_(); _nop_(); /*时钟低电平周期大于4μs*/
_nop_(); _nop_(); _nop_(); scl=0; /*清时钟线,住I2C总线以便继续接收*/ _nop_(); _nop_(); }
/************************************************************ * 函数名 :
Pcf8591_DaConversion * 函数功能 : PCF8591的输出端输出模拟量 * 输入 :
addr(器件地址),channel(转换通道),value(转换的数值) * 输出 : 无 *******************
*****************************************/ bit Pcf8591_DaConversion(unsigned
char addr,unsigned char channel, unsigned char Val) { Start_I2c(); //启动总线
I2C_SendByte(addr); //发送器件地址 if(ack==0)return(0); I2C_SendByte(0x40|channel);
//发送控制字节 if(ack==0)return(0); I2C_SendByte(Val); //发送DAC的数值
if(ack==0)return(0); Stop_I2c(); //结束总线 return(1); }
/************************************************************ * 函数名 :
Pcf8591_SendByte * 函数功能 : 写入一个控制命令 * 输入 : addr(器件地址),channel(转换通道) * 输出 : 无
************************************************************/ bit
PCF8591_SendByte(unsigned char addr,unsigned char channel) { Start_I2c();
//启动总线 I2C_SendByte(addr); //发送器件地址 if(ack==0)return(0);
I2C_SendByte(0x40|channel); //发送控制字节 if(ack==0)return(0); Stop_I2c(); //结束总线
return(1); } /************************************************************ *
函数名 : PCF8591_RcvByte * 函数功能 : 读取一个转换值 * 输入 : * 输出 : dat
************************************************************/ unsigned char
PCF8591_RcvByte(unsigned char addr) { unsigned char dat; Start_I2c(); //启动总线
I2C_SendByte(addr+1); //发送器件地址 if(ack==0)return(0); dat=I2C_RcvByte(); //读取数据0
Ack_I2c(1); //发送非应答信号 Stop_I2c(); //结束总线 return(dat); }
/*------------------------------------------------ 串口初始化函数
------------------------------------------------*/ void init_com(void) { EA=1;
//开总中断 ES=1; //允许串口中断 ET1=1; TMOD=0x22; //定时器T1,在方式2中断产生波特率 PCON=0x00; //SMOD=0
SCON=0x50; // 方式1 由定时器控制 TH1=0xfd; //波特率设置为9600 TL1=0xfd; TR1=1; //开定时器T1运行控制位
} /*------------------------------------------------ 延时函数
------------------------------------------------*/ void delay(unsigned char i)
{ unsigned char j,k; for(j=i;j>0;j--) for(k=125;k>0;k--); }
/*------------------------------------------------ 把读取值转换成一个一个的字符,给串口显示
------------------------------------------------*/ void To_ascii(unsigned char
num) { SBUF=num/100+'0'; delay(200); SBUF=num/10%10+'0'; delay(200);
SBUF=num%10+'0'; delay(200); }
/*------------------------------------------------ 主函数
------------------------------------------------*/ int main() { while(1) {
PCF8591_SendByte(AddWr,0); //启动转换 getData=PCF8591_RcvByte(AddWr); //读转换完的数字信号
dis[1]=getData/51; //整数位 dis[2]=getData%51; //dis[2]位中间暂存数据位 dis[2]=dis[2]*10;
dis[0]=dis[2]/51; //计算输出电压的小数值 C1=1; Dp=0; //打开小数点 P0=table[dis[0]];
//显示整数部分及小数点 C1=0; delay(10); C2=1; //打开第二位数码管 Dp=1; //关闭小数点 P0=table[dis[1]];
//显示小数部分 C2=0; } }
仿真效果: