以单片机为核心器件,组成一个简单数字电压表。
(1)采用1路模拟量输入,能够测量0-10V(20v、30v、40v)之间的直流电压值。
(2)电压显示可采用LCD1602显示,能够显示两位小数。
简易数字电压表的制作,主要涉及数据(电压)测量、A/D转换及控制显示方面的知识。
(1)A/D转换采用PCF8591实现。
(2)电压显示采用LCD1602。
(3)单片机选用AT89C5.
仿真原理图如下(proteus仿真工程文件可到本帖附件中下载)
单片机源程序如下:
- #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)
|