电路图如下:
1、简单描述ADC0809工作过程
ADC0809工作过程:
(1)把通道地址送到ADDA~ADDC上,选择一个模拟输入端。
(2)在通道地址信号有效期间,ALE上的上升沿把该地址锁存到内部地址锁存器。
(3)START引脚上的下降沿启动A/D转换。
(4)变换开始后EOC引脚呈现低电平,EOC重新变成高电平时表示转换结束。
(5)OE信号打开输出锁存器的三态门并送出转换结果。
2、在LCD1602上实现采样电压值的显示,输出参考下图(可自定义显示格式,但必须显示实际电压,精确到0.01V),修改程序。
编程思路:
1、首先程序开头:头文件申明,宏定义,端口定义,函数申明,变量定义
2、然后是主程序,在main()函数里面,首先对LCD1602显示屏和定时器0进行初始化,然后在while循环里面 输入转换通道数,开始转换,显示转换结果。
3、子函数的定义
3.1 输入转换通道号的函数adc_channel(bit ac,bit ab,bit aa)——分别对ADDC~ADDA赋值来选择一个模拟输入端。
3.2 ADC转换函数adc_change(void)——START下降沿启动转换,查询EOC为1时,因为ADC0809的输出数据连到P0口,
所以OE信号置1后,开始读P0 口,将读的数据赋给adc_data,最后OE置0。
3.3 定时器0控制ADC的CLK信号——定时器0的初始化 time_init():采用方式1,周期为200,启动定时器0,打开中断分开关和中断总开关;定时器0中断函数——因为采用的是方式1,所以要手动赋初值,然后clk信号取反,输出方波。
3.4 LCD显示信息函数display_data(uchar input)——显示器第一行利用dis_char函数分别显示转换数据的百、十、个位。第二行显示输入电压值,利用公式Vin=D500/256 将转换数据换成电压值,**这时一定要注意将Voltage的数据类型定义为long型 ,因为int型在计算D500可能会超过65536而溢出!!!**最后也利用dis_char函数分别显示电压的整数位和小数点后两位。
3.5 后面的延时函数delay(uint j),查忙函数check_busy(void),写控制指令函数 write_command(uchar com),写数据指令函数write_data(uchar dat),液晶屏初始化函数LCD_initial(void),输出字符串函数string(uchar ad,uchar *s),输出字符函数dis_char(uchar ad, uchar input) 因为是LCD1602显示器的一系列函数,所以就不说啦!!
代码如下:
/** 功能描述: 程序运行后,实现ADC一个通道的数据采样,并将采样结果送LCD显示 **/
#include <reg52.h>
#include <intrins.h>
#include <stdio.h>
#define uchar unsigned char
#define uint unsigned int
#define lcd_out P1
#define adc_data_port P0
/***********端口定义********************************/
sbit rs=P2^0;
sbit rw=P2^1;
sbit e=P2^2;
sbit ADCCLK=P2^6;
sbit START=P2^3;
sbit EOC=P2^4;
sbit OE=P2^5;
sbit ADDA=P3^0;
sbit ADDB=P3^1;
sbit ADDC=P3^2;
/***********函数申明***********************************/
void check_busy(void);
void write_command(uchar com);
void write_data(uchar dat);
void LCD_initial(void);
void string(uchar ad ,uchar *s);
void dis_char(uchar ad, uchar input);
void display_data(uchar input);
void lcd_test(void);
void delay(uint);
void adc_change(void);
void adc_channel(bit ac,bit ab,bit aa);
void time_init();
uchar adc_data=0; //存放ADC转换结果
/***********主程序***********************/
void main(void)
{
LCD_initial(); //LCD1602 初始化
time_init();
while(1)
{
adc_channel(0,0,1);
adc_change();
display_data(adc_data);
delay(500);
}
}
//输入转换通道号
void adc_channel(bit ac,bit ab,bit aa)
{
ADDC=ac;
ADDB=ab;
ADDA=aa;
}
//开始转换
void adc_change(void)
{
OE=0;
START=0;
START=1;
START=0;
while(EOC==0);
adc_data_port=0x0ff;
OE=1;
adc_data=adc_data_port;
OE=0;
}
//定时器0控制ADC的CLK信号
void time_init(){
TMOD=0x01;
TH0=(65536-200)/256;
TL0=(65536-200)%256;
ET0=1;
EA=1;
TR0=1;
}
void timer0()interrupt 1
{ TH0=(65535-200)/256;
TL0=(65535-200)%256;
ADCCLK=~ADCCLK;
}
//LCD显示信息
void display_data(uchar input)
{
long voltage;
dis_char(0x0d,input/100+0x30);
dis_char(0x0e,input/10%10+0x30);
dis_char(0x0f,input%10+0x30);
voltage = input;
voltage = voltage*500/255;
dis_char(0x4a,voltage/100+0x30);
dis_char(0x4b,'.');
dis_char(0x4c,voltage/10%10+0x30);
dis_char(0x4d,voltage%10+0x30);
dis_char(0x4e,'V');
}
//1ms延时程序
void delay(uint j)
{
uchar i=250;
for(;j>0;j--)
{
while(--i);
i=249;
}
}
//查忙程序
void check_busy(void)
{
uchar dt;
do
{
dt=0xff;
e=0;
rs=0;
rw=1;
e=1;
dt=lcd_out;
}while(dt&0x80);
e=0;
}
//写控制指令
void write_command(uchar com)
{
check_busy();
e=0;
rs=0;
rw=0;
lcd_out=com;
e=1;
_nop_();
e=0;
delay(1);
}
//写数据指令
void write_data(uchar dat)
{
check_busy();
e=0;
rs=1;
rw=0;
lcd_out=dat;
e=1;
_nop_();
e=0;
delay(1);
}
//液晶屏初始化
void LCD_initial(void)
{
write_command(0x38);//8位总线,双行显示,5X7的点阵字符
write_command(0x0C);//开整体显示,光标关,无黑块
write_command(0x06);//光标右移
write_command(0x01);//清屏
delay(1);
string(0x00,"ADC0809 ch1="); //显示字符串
string(0x40,"Voltage = "); //显示字符串
}
//输出字符串
void string(uchar ad,uchar *s)
{
write_command(ad+0x80); //ad是显示位置信息
while(*s>0)
{
write_data(*s++);
delay(100);
}
}
// 输出字符
void dis_char(uchar ad, uchar input)
{
write_command(ad+0x80);
write_data(input);
delay(10);
}
3、利用定时器1的定时中断,实现ADC通道3每隔1s转换一次,并将模拟电压值送LCD1602显示。
编程思路:
大体思路和第二问差不多。所以这里只说不同点。
这里增加了一个定时器1控制通道3转换时间为1s。
1、首先对定时计数器1进行初始化,因为要控制通道3的转换时间为1s=200us*5000。
所以利用定时器1的方式2,这时一定要注意!!! 因为Timer1工作在方式2,Timer0工作在方式1,所以在选择模式的时候一定要一起配置,即TMOD=0x21(如果分开在各自的子函数里面初始化TMOD=0x20,TMOD=0x01就会出错)。溢出周期为200,设置初值TH1=0-200,TL1=0-200。然后PT1=0(低级),这是因为定时器0中断控制ADC的CLK信号,所以PT0=1(高级)。程序里面有两个中断,最好分下高低级。最后启动定时器1,打开中断分开关和中断总开关。
2、定时器1中断函数里面来进行counter的软件计数,中断一次counter++。转换在主程序里面做,避免中断耗时太长。
3、主函数main() 先配置T0,T1的模式,TMOD=0x21,然后对LCD1602、T0和T1进行初始化。在while循环里面,先输入通道数,然后对中断函数中counter进判断,如果counter==5000,就开始AD转换,并把counter清零,同时也把数字和电压清屏,在显示器上给人一种1s变化一次的感觉。最后调用显示函数 将数字和电压显示出来。
代码如下:
/** 功能描述: 程序运行后,实现ADC一个通道的数据采样,并将采样结果送LCD显示 **/
#include <reg52.h>
#include <intrins.h>
#include <stdio.h>
#define uchar unsigned char
#define uint unsigned int
#define lcd_out P1
#define adc_data_port P0
/***********端口定义********************************/
sbit rs=P2^0;
sbit rw=P2^1;
sbit e=P2^2;
sbit ADCCLK=P2^6;
sbit START=P2^3;
sbit EOC=P2^4;
sbit OE=P2^5;
sbit ADDA=P3^0;
sbit ADDB=P3^1;
sbit ADDC=P3^2;
/***********函数申明***********************************/
void check_busy(void);
void write_command(uchar com);
void write_data(uchar dat);
void LCD_initial(void);
void string(uchar ad ,uchar *s);
void dis_char(uchar ad, uchar input);
void display_data(uchar input);
void delay(uint);
void adc_change(void);
void adc_channel(uchar ch);
void time0_init();
void time1_init();
uchar adc_data=0; //存放ADC转换结果
uint counter=0;//软件计数timer1中断次数
uchar channel_num=0;//通道数
/***********主程序***********************/
void main(void)
{
TMOD=0x21;
LCD_initial(); //LCD1602 初始化
time0_init();
time1_init();
while(1)
{
channel_num=3;
adc_channel(channel_num);
if(counter>=5000){
counter=0;
string(0x0d," ");//数字清屏
string(0x4a," ");//电压清屏
adc_change(); //开始转换
}
display_data(adc_data);
}
}
//输入转换通道号
// set ADC channel
void adc_channel(uchar ch)
{
ch = ch%0x07;
ADDA=0;
if(ch&0x01)ADDA=1;
ADDB=0;
if(ch&0x02)ADDB=1;
ADDC=0;
if(ch&0x04)ADDC=1;
}
//开始转换
void adc_change(void)
{
OE=0;
START=0;
START=1;
START=0;
while(EOC==0);
adc_data_port=0x0ff;
OE=1;
adc_data=adc_data_port;
OE=0;
}
//定时器1控制通道3转换时间
void time1_init(){
TH1=0-200;
TL1=0-200;
PT1=0;
ET1=1;
EA=1;
TR1=1;
}
void timer1() interrupt 3
{
counter++;
}
//定时器0控制ADC的CLK信号
void time0_init(){
TH0=(65536-200)/256;
TL0=(65536-200)%256;
PT0=1;//设为高级
ET0=1;
EA=1;
TR0=1;
}
void timer0()interrupt 1
{ TH0=(65535-200)/256;
TL0=(65535-200)%256;
ADCCLK=~ADCCLK;
}
//LCD显示信息
void display_data(uchar input)
{
long voltage;
dis_char(0x0a,channel_num+0x30);//显示通道数
string(0x0b,"= ");
//显示数字
dis_char(0x0d,input/100+0x30);
dis_char(0x0e,input/10%10+0x30);
dis_char(0x0f,input%10+0x30);
voltage = input;
voltage = voltage*500/255;
//显示电压
dis_char(0x4a,voltage/100+0x30);
dis_char(0x4b,'.');
dis_char(0x4c,voltage/10%10+0x30);
dis_char(0x4d,voltage%10+0x30);
dis_char(0x4e,'V');
}
//1ms延时程序
void delay(uint j)
{
uchar i=250;
for(;j>0;j--)
{
while(--i);
i=249;
}
}
//查忙程序
void check_busy(void)
{
uchar dt;
do
{
dt=0xff;
e=0;
rs=0;
rw=1;
e=1;
dt=lcd_out;
}while(dt&0x80);
e=0;
}
//写控制指令
void write_command(uchar com)
{
check_busy();
e=0;
rs=0;
rw=0;
lcd_out=com;
e=1;
_nop_();
e=0;
delay(1);
}
//写数据指令
void write_data(uchar dat)
{
check_busy();
e=0;
rs=1;
rw=0;
lcd_out=dat;
e=1;
_nop_();
e=0;
delay(1);
}
//液晶屏初始化
void LCD_initial(void)
{
write_command(0x38);//8位总线,双行显示,5X7的点阵字符
write_command(0x0C);//开整体显示,光标关,无黑块
write_command(0x06);//光标右移
write_command(0x01);//清屏
delay(1);
string(0x00,"ADC0809 ch"); //显示字符串
string(0x40,"Voltage = "); //显示字符串
}
//输出字符串
void string(uchar ad,uchar *s)
{
write_command(ad+0x80); //ad是显示位置信息
while(*s>0)
{
write_data(*s++);
delay(100);
}
}
// 输出字符
void dis_char(uchar ad, uchar input)
{
write_command(ad+0x80);//ad是显示位置信息
write_data(input);
delay(10);
}