8.3 多路数字电压表的设计
数字电压表是电子测量中经常用到的电子器件,传统的指针式电压表功能单一、精度低、不能满足数字时代的要求。而采用单片机的数字电压表精度高、抗干扰能力强、可扩展性强、使用方便,在日常生活中广泛应用。
8.3.1 多路数字电压表的功能要求
多路数字电压表的功能要求如下: (1) 输入电压为8路。
(2) 电压值的范畴为0~5V。
(3) 测量的最小分辨率为0.019V,测量误差为0.02V。。
(4) 能通过显示器显示通道和通道电压,有效位数为小数点后两位
8.3.2 多路数字电压表的总体设计
多路数字电压表的总体结构如图8.9所示,处理过程如下:先用A/D转换器对各路电压值进行采样,得到相应的数字量,再按数字量与模拟量成正比关系运算得到对应的模拟电压值,然后把模拟值通过显示器显示出来,另外可以通过按键选择通道。
51单片机 ADC0808 复位电路 按键 LCD 时钟电路
图8.9 多路数字电压表的总体结构图
根据系统的功能要求,控制系统采用AT89C52单片机,A/D转换器采用ADC0808(0809)。ADC0808(0809)是8位的A/D转换器。当输入电压为5.00V时,输出的数据值为255(0FFH),因此最大分辨率为0.0196V(5/255)。ADC0808(0809)具有8路模拟量输入端口,通过3位地址输入端能从8路中选择一路进行转换。如每隔一段时间依次轮流改变3位地址输入端的地址,就能依次对8路输入电压进行测量。显示器采用LCD显示器,显示效果好。按键可只设定一个,用于选择显示的当前通道。
8.3.3 多路数字电压表硬件电路
多路数字电压表具体硬件电路如图8.10所示。
RESPACK-8RP1LM016LLCD11VSSVDDVEEC11nFRSRWEP174P165P156CRYSTAL23456789P00P01P02P03P04P05P06P07123X1C21nFU119XTAL1P0.0/AD0P0.1/AD1P0.2/AD2P0.3/AD3P0.4/AD4P0.5/AD5P0.6/AD6P0.7/AD7P2.0/A8P2.1/A9P2.2/A10P2.3/A11P2.4/A12P2.5/A13P2.6/A14P2.7/A15P3.0/RXDP3.1/TXDP3.2/INT0P3.3/INT1P3.4/T0P3.5/T1P3.6/WRP3.7/RD39383736353433322122232425262728P00P01P02P03P04P05P06P07P20P21P22P23P24P25P26P2718XTAL2C391nFRSTU29OEVREF(-)VREF(+)ALEADD CADD BADD AIN7IN6IN5IN4IN3IN2IN1IN0161222ST23ADDC24ADDB25ADDA54321282726R4200293031PSENALEEA1714158181920217610OUT8OUT7OUT6OUT5OUT4OUT3OUT2OUT1EOCSTARTCLOCKADC0808P007P018P029P0310P0411P0512P0613P0714D0D1D2D3D4D5D6D7RV156%1kR110kP15P16P1712345678P1.0/T2P1.1/T2EXP1.2P1.3P1.4P1.5P1.6P1.7AT89C5210ADDA11ADDB12ADDC13141516ST1750%1kRV2 图8.10 多路数字电压表的电路原理图
其中,ADC0808(0809) 的数据线D0~D7与AT89C52的P2口相连,地址输入端
ADDA、ADDB、ADDC与AT89C52的P3口的低3位P3.0、P3.1、P3.2相连,地址锁存控制端ALE和启动信号START连接在一起与P3.7相连,数据输出允许控制端OE与P3.6相连,转换结束信号EOC与P1.3相连。ADC0809的时钟信号输入端CLOCK与P1.4相连,而P1.4由定时/计数器0控制,每10s取反一次,则CLOCK的时钟周期为20s,频率为50KHz,满足ADC0808(0809)的时钟要求。参考电压VREF+接+5V电源,参考电压VREF-接地,则当输入电压为5.00V时,输出的数据值为255(0FFH),当输入电压为0V时,输出的数据值为0(00H),最大分辨率为0.0196V(5/255)。
显示器LCD1602的数据线与89C 52的P0口相连,RS与P1.7相连,R/W与P1.6相连,E端与P1.5相连。按键只设定了一个K1,与AT89C52的P1.0,用于进行通道选择,当按下一次,通道加1,显示下一个通道。
8.3.4 多路数字电压表软件程序
多路数字电压表系统软件程序由主程序、A/D转换子程序和显示驱动程序组成,这里只介绍主程序、A/D转换子程序。
1.主程序
主程序流程如图8.11所示。首先是对定时计数器和LCD初始化,在LCD上显示提示信息,然后进入循环,在循环中依次为:调用A/D转换子程序对8个通道转换一次,
判通道键是否按下,按下则当前通道地址加1,当前通道值转换成电压值,显示当前通道。
2.A/D转换子程序
A/D转换子程序用于对ADC0808的8路输入模拟电压进行一次A/D转换,并将转换的数值存入8个相应的存储单元中,流程图如图8.12所示。A/D转换子程序每隔一定时间调用一次,即隔一段时间对输入电压采样一次。
开始 定时/计数器初始化 LCD初始化 开始 LCD显示提示信息 启动一次转换 调用A/D转换子程序 A/D转换结束EOC=1? 否 判通道键是否按是 下,按下加1 取数据(OE=1) 当前通道数字量0808通道地址加1 转换成电压 是 地址数小于8 显示当前通道值 否
返回 图8.11 主程序流程 图8.12 A/D转换子程序流程
3.汇编语言源程序清单
;30H~37H存放转换的数字量
;38H~3BH分别放电压当前通道电压的个位、小数点后1位、小数点后2位 ;3CH单片为通道计数器 RS BIT P1.7 ;定义LCD1602端口线 RW BIT P1.6 E BIT P1.5 ST BIT P3.7 ;定义0808控制线 OE BIT P3.6 EOC BIT P1.3 CLK BIT P1.4 KEY1 BIT P1.0 ;通道选择按键 ORG 0000H LJMP MAIN ORG 000BH CPL CLK ; 定时/计数器0中断,产生转换时钟 RETI
ORG 50H
3
;主程序
MAIN: MOV SP,#50H MOV 39H,#'.' MOV TMOD,#02H MOV TH0,#246 MOV TL0,#246 SETB ET0 SETB EA SETB TR0 LCALL DL10MS ACALL INIT MOV A,#81H ACALL WC51R MOV A,#'A' ACALL WC51DDR MOV A,#'D' ACALL WC51DDR MOV A,#'D' ACALL WC51DDR MOV A,#'R' ACALL WC51DDR MOV A,#':' ACALL WC51DDR MOV A,#0C0H ACALL WC51R MOV A,#'V' ACALL WC51DDR MOV A,#'A' ACALL WC51DDR MOV A,#'L' ACALL WC51DDR MOV A,#'U' ACALL WC51DDR MOV A,#'E' ACALL WC51DDR MOV A,#':' ACALL WC51DDR LOOP: LCALL TEST JB KEY1,NEXT WAIT2: JNB KEY1,WAIT2 INC 3CH MOV A,3CH CJNE A,#08,NEXT MOV 3CH,#00
NEXT: MOV A,#30H ADD A,3CH MOV R0,A MOV A,@R0 MOV B,#51 DIV AB ADD A,#30H ;写入显示缓冲区起始地址为第1行第1列 ;第1行第2列显示字母'H' ;第1行第3列显示字母\"O\" ;第1行第4列显示字母'W' ;第1行第5列显示字母'U'
;第2行第6列显示字母'!'
;写入显示缓冲区起始地址为第2行第5列 ;第2行第5列显示字母'A' ;第2行第6列显示字母'R' ;第2行第7列显示字母'E' ;第2行第8列显示字母' ' ;第2行第9列显示字母'Y'
;第2行第10列显示字母'O'
;调用ADC0808转换程序8个通道转换一次
;有键按下,当前通道地址加1
;取出当前通道值,转换成电压值所对应的字符
MOV 38H,A MOV A,B CLR F0 SUBB A,#1AH MOV F0,C MOV A,#10 MUL AB MOV B,#51 DIV AB JB F0,LOOP2 ADD A,#5
LOOP2: ADD A,#30H MOV 3AH,A MOV A,B CLR F0 SUBB A,#1AH MOV F0,C MOV A,#10 MUL AB MOV B,#51 DIV AB JB F0,LOOP3 ADD A,#5
LOOP3: ADD A,#30H MOV 3BH,A MOV A,#88H ;写入显示缓冲区起始地址为第1行第9列 ACALL WC51R MOV A,3CH ADD A,#30H ;第1行第9列显示通道号 ACALL WC51DDR MOV A,#0C8H ;写入显示缓冲区起始地址为第2行第9列 ACALL WC51R MOV A,38H ;第2行第9列显示整数部分 ACALL WC51DDR MOV A,39H ;第2行第10列显示小数点 ACALL WC51DDR MOV A,3AH ;第2行第11列显示小数点后1位 ACALL WC51DDR MOV A,3BH ;第2行第12列显示小数点后2位 ACALL WC51DDR AJMP LOOP ;初始化子程序
INIT: MOV A,#00000001H ;清屏 ACALL WC51R MOV A,#00111000B ;使用8位数据,显示两行,使用5×7的字型 LCALL WC51R MOV A,#00001100B ;显示器开,光标关,字符不闪烁 LCALL WC51R MOV A,#00000110B ;字符不动,光标自动右移一格 LCALL WC51R RET 5
;检查忙子程序
F_BUSY:PUSH ACC ;保护现场 MOV P0,#0FFH CLR RS SETB RW WAIT: CLR E SETB E JB P0.7,WAIT ;忙,等待 POP ACC ;不忙,恢复现场 RET ;写入命令子程序
WC51R: ACALL F_BUSY CLR E CLR RS CLR RW SETB E MOV P0,ACC CLR E RET ;写入数据子程序
WC51DDR:ACALL F_BUSY CLR E SETB RS CLR RW SETB E MOV P0,ACC CLR E RET
;***************************************************** ;A/D转换子程序,8个通道转换一次转换结果依次存入30H~37H
;***************************************************** TEST: MOV R0,#30H MOV R2,#00H TESTART:MOV P2,#0FFH MOV A,R2 MOV P3,A CLR ST NOP NOP SETB ST NOP NOP CLR ST NOP NOP
WAIT1: JNB EOC,WAIT1 MOVD: SETB OE NOP NOP MOV A,P2 MOV @R0,A
CLR OE NOP NOP INC R0 INC R2 CJNE R2,#8,TESTART RET
;************************************* ;延时子程序
;*************************************
DL10MS: MOV R6,#0D0H ;延时10MS子程序 DL1: MOV R7,#10H DL2: DJNZ R7,DL2 DJNZ R6,DL1 RET END
4.C语言源程序清单
#include #include #define uchar unsigned char #define uint unsigned int sbit RS=P1^7; //定义LCD1602端口线 sbit RW=P1^6; sbit EN=P1^5; sbit ST=P3^7; //定义0808控制线 sbit OE=P3^6; sbit EOC=P1^3; sbit CLK=P1^4; sbit key1=P1^0; //通道选择按键 uchar data chnumber; //存放当前通道号 uchar disbuffer[4]={0,'.',0,0}; //定义显示缓冲区 uchar data ad_data[8]={0,0,0,0,0,0,0,0}; //0808的8个通道转换数据缓冲区uint temp; //检查忙函数 void fbusy() { P0 = 0xff; RS = 0; RW = 1; EN = 1; EN = 0; while((P0 & 0x80)) { EN = 0; EN = 1; } } //写命令函数 7 void wc51r(uchar j) { fbusy(); EN = 0; RS = 0; RW = 0; EN = 1; P0 = j; EN = 0; } //写数据函数 void wc51ddr(uchar j) { fbusy(); //读状态; EN = 0; RS = 1; RW = 0; EN = 1; P0 = j; EN = 0; } void init() { wc51r(0x01); //清屏 wc51r(0x38); //使用8位数据,显示两行,使用5*7的字型 wc51r(0x0c); //显示器开,光标开,字符不闪烁 wc51r(0x06); //字符不动,光标自动右移一格 } /********0808转换子函数********/ test() { uchar m; for (m=0;m<8;m++) { P3=m; //送通道地址 ST=0;_nop_();_nop_();ST=1;_nop_();_nop_();ST=0;//锁存通道地址启动转换 _nop_();_nop_();_nop_();_nop_(); while (EOC==0); //等待转换结束 OE=1;ad_data[m]=P2;OE=0; //读取当前通道转换数据 } } //************延时函数************ void delay(uint i) //延时函数 {uint y,j; for (j=0;jfor (y=0;y<0xff;y++){;}} } //定时器/计数器T0产生0808的时钟 void T0X(void)interrupt 1 using 0 { CLK=~CLK; } void main(void) { uchar i; SP=0X50;TMOD=0x02;TH0=246;TL0=246; ET0=1;EA=1;TR0=1; delay(10); init(); wc51r(0x81); //写入显示缓冲区起始地址为第1行第1列 wc51ddr('A'); //第1行第1列显示字母A wc51ddr('D'); //第1行第2列显示字母D wc51ddr('D'); //第1行第3列显示字母 D wc51ddr('R'); //第1行第4列显示字母 R wc51ddr(':'); //第1行第4列显示字母 : wc51r(0xC0); //写入显示缓冲区起始地址为第2行第1列 wc51ddr('V'); //第2行第1列显示字母V wc51ddr('A'); //第2行第2列显示字母A wc51ddr('L'); //第2行第3列显示字母 L wc51ddr('U'); //第2行第4列显示字母 U wc51ddr('E'); //第2行第5列显示字母 E wc51ddr(':'); //第2行第6列显示字母 : while(1) { test(); //调用ADC0808转换程序8个通道转换一次 if (key1==0) {while(key1==0); chnumber++;if (chnumber==8)chnumber=0;} //有键按下,当前通道地址加1 temp=ad_data[chnumber]; //取出当前通道值,转换成电压值所对应的字符 temp=(temp*100)/51; disbuffer[0]=temp/100+0x30; temp=temp%100; disbuffer[2]=temp/10+0x30; disbuffer[3]=temp%10+0x30; wc51r(0x88); wc51ddr(chnumber+0x30); wc51r(0xc8); //显示当前通道 for (i=0;i<4;i++) wc51ddr(disbuffer[i]); } } 9 因篇幅问题不能全部显示,请点此查看更多更全内容