标题:
单片机AD转换 PCF8591数显电压表代码Proteus仿真原理图
[打印本页]
作者:
小张小张1
时间:
2022-11-13 11:10
标题:
单片机AD转换 PCF8591数显电压表代码Proteus仿真原理图
以单片机为核心器件,组成一个简单数字电压表。
(1)采用1路模拟量输入,能够测量0-10V(20v、30v、40v)之间的直流电压值。
(2)电压显示可采用LCD1602显示,能够显示两位小数。
简易数字电压表的制作,主要涉及数据(电压)测量、A/D转换及控制显示方面的知识。
(1)A/D转换采用PCF8591实现。
(2)电压显示采用LCD1602。
(3)单片机选用AT89C5.
仿真原理图如下(proteus仿真工程文件可到本帖附件中下载)
仿真图.png
(51.03 KB, 下载次数: 35)
下载附件
2022-11-13 11:45 上传
单片机源程序如下:
#include <reg52.h>
#include <intrins.h> //包含函数_nop_()定义的头文件
#define u8 unsigned char
#define u16 unsigned int
#define AddWr 0x90 //PCF8591写地址
#define AddRd 0x91 //PCF8591读地址
#define w_com P0
sbit RS=P2^2;
sbit RW=P2^3;
sbit E=P2^4;
sbit Sda=P1^2; //定义IIC总线引脚
sbit Scl=P1^1;
u8 datetab[10]; //存入电压值
u8 ADtemp; //通道转换后存入的数值
float V_1602;
u16 num=0;
unsigned long num_pow(u8 m,u8 n) //指数运算
{
unsigned long result=1;
while(n--) result*=m;
return result;
}
void float2str(u8 str[],double floa,u8 len1,u8 len2) //浮点型转字符型数组
{
u8 i=0,j=0;
u16 zhengshu=floa;
u16 xiaoshu=(floa-zhengshu)*num_pow(10,len2);
while(len1--)
{
str[len1]=zhengshu%10+0x30;
zhengshu=zhengshu /10;
i++;
}
str[i]='.';
while(len2--)
{
str[i+len2+1]=xiaoshu%10+0x30;
xiaoshu=xiaoshu/10;
j++;
}
str[i+j+1]='\0';
}
/*------------------------------------------------
启动IIC总线
------------------------------------------------*/
void Start(void)
{
Sda=1;
_nop_();
Scl=1;
_nop_();
Sda=0; //sda负跳变
_nop_();
Scl=0;
}
/*------------------------------------------------
停止IIC总线
------------------------------------------------*/
void Stop(void)
{
Sda=0;
_nop_();
Scl=1;
_nop_();
Sda=1; //sda正跳变
_nop_();
Scl=0;
}
/*------------------------------------------------
IIC总线应答
------------------------------------------------*/
void Ack(void)
{
Sda=0; //scl高电平期间sda输出低电平
_nop_();
Scl=1;
_nop_();
Scl=0;
_nop_();
}
/*------------------------------------------------
IIC总线非应答
------------------------------------------------*/
void NoAck(void)
{
Sda=1; //scl高电平期间sda输出高电平
_nop_();
Scl=1;
_nop_();
Scl=0;
_nop_();
}
/*------------------------------------------------
向PCF8591发送1字节数据(/地址/控制命令等)
------------------------------------------------*/
void Send(u8 Data)
{
u8 BitCounter=8; //定义发送位数为8
do
{
Scl=0;
_nop_();
if((Data&0x80)==0x80) //逐位判别1/0后送至SDA
Sda=1; //scl低电平期间数据送至sda线
else
Sda=0;
Scl=1; //scl变高输出数据(写入pcf8591)
Data=Data<<1;
BitCounter--;
}while(BitCounter);
Scl=0;
}
/*------------------------------------------------
从PCF8591读入1字节(例如A/D转换结果)
------------------------------------------------*/
u8 Read(void)
{
u8 temp=0;
u8 BitCounter=8;
Sda=1; //拉高数据线
do
{
temp=temp<<1; //逐位移入temp
Scl=0;
_nop_();
Scl=1; //scl变高采样sda线
_nop_();
if(Sda) //判别SDA线上为1/0
temp=temp|0x01;
else
temp=temp&0xfe;
BitCounter--;
}while(BitCounter);
return(temp); //返回值为读入的字节
}
void DACconversion(u8 sla,u8 c, u8 Val) //8591输出一个数据
{
Start();//启动总线
Send(sla);//发送器件地址
Ack();
Send(c);//发送控制字节
Ack();
Send(Val);//发送DAC的数值
Ack();
Stop();//结束总线
}
/*----------------------------------------------------------
启动PCF8591 Chl 通道A/D转换并读取转换结果
--------------------------------------------------------------*/
u8 ReadADC(u8 Chl)
{
u8 Data;
Start(); //开始信号
Send(AddWr); //发写地址(0x90)
Ack(); //等待 PCF8591应答
Send(0x40|Chl); //写入控制字并启动 Chl通道A/D转换
Ack(); //等待 PCF8591应答
Start(); //重发开始信号
Send(AddRd); //写入读地址
Ack();
Data=Read(); //读取A/D转换结果(注意是上次的)
NoAck(); //发非应答信号
Stop(); //停止总线操作
return Data; //返回A/D转换结果
}
void delay(u16 i)
{
while(i--);
}
void LCD_busy(void)
{
u8 temp;
do{
temp =0xff;
E = 0;
RS= 0;
RW = 1;
E =1;
temp = w_com;
}while(temp&0x80); // 如果 BF =1 则持续检查
E = 0;
}
void write_command(u8 com)
{
LCD_busy();
E= 0;
RS =0;
RW =0;
w_com = com;
E =1;
delay(100);
E = 0;
}
void write_data(u8 com)
{
LCD_busy();
E=0;
RS= 1;
RW =0;
w_com = com;
E=1;
delay(100);
E =0;
}
void lcd_initial(void)
{
write_command(0x38); //8位数据接口,双行显示
delay(100);
write_command(0x0c); //开显示屏
delay(100);
write_command(0x01); //清屏
delay(100);
write_command(0x06); //光标移动
delay(100);
}
void string(u8 y,u8 x, u8 *s) //x表示行,y表示列
{
if (x==1)
{
write_command(0xc0|y); //因为DDRAM寻址指令是 0x80开头。
}
else
write_command(0x80|y);
while(*s != '\0')
{
write_data(*s++);
}
}
void string_word(u8 y,u8 x,u8 num)
{
if (x==1)
{
write_command(0xc0|y); //因为DDRAM寻址指令是 0x80开头。
}
else
write_command(0x80|y);
write_data(num);
}
void main()
{
lcd_initial();
while(1)
{
ADtemp=ReadADC(1); //读取通道1 A/D转换结果
V_1602=ADtemp*(10.0/256); //计算电压值 10.0 20.0 30.0 40.0
float2str(datetab,V_1602,2,2); //显示整数位与小数位的个数
string(5,0,datetab); //显示的位置和内容
string_word(10,0,'V'); //显示单位
delay(10000);
}
}
复制代码
Keil代码与Proteus8.13版本的仿真下载(注意不能兼容其他版本 只能用8.13打开):
程序+Proteus8.13仿真图.7z
(74.12 KB, 下载次数: 77)
2022-11-15 18:10 上传
点击文件名下载附件
下载积分: 黑币 -5
欢迎光临 (http://www.51hei.com/bbs/)
Powered by Discuz! X3.1