不要白嫖,不要瞎抄程序,因为我在试图教会你。
如果没有用过proteus 没关系,看这个教程新手快速上手proteus实现仿真画pcb
ADC0809是美国国家半导体公司生产的CMOS工艺8通道,8位逐次逼近式A/D模数转换器。其内部有一个8通道多路开关,它可以根据地址码锁存译码后的信号,只选通8路模拟输入信号中的一个进行A/D转换。仅在单片机初学应用设计中较为常见。
ADC0809是CMOS单片型逐次逼近式A/D转换器,它由8路模拟开关、地址锁存与译码器、比较器、8位开关树型A/D转换器、逐次逼近寄存器、逻辑控制和定时电路组成。
首先输入3位地址,并使ALE=1,将地址存入地址锁存器中。此地址经译码选通8路模拟输入之一到比较器。START上升沿将逐次逼近寄存器复位。下降沿启动 A/D转换,之后EOC输出信号变低,指示转换正在进行。直到A/D转换完成,EOC变为高电平,指示A/D转换结束,结果数据已存入锁存器,这个信号可用作中断申请。当OE输入高电平 时,输出三态门打开,转换结果的数字量输出到数据总线上。
转换数据的传送 A/D转换后得到的数据应及时传送给单片机进行处理。数据传送的关键问题是如何确认A/D转换的完成,因为只有确认完成后,才能进行传送。为此可采用下述三种方式。
(1)定时传送方式
对于一种A/D转换器来说,转换时间作为一项技术指标是已知的和固定的。例如ADC0809转换时间为128μs,相当于6MHz的MCS-51单片机共64个机器周期。可据此设计一个延时子程序,A/D转换启动后即调用此子程序,延迟时间一到,转换肯定已经完成了,接着就可进行数据传送。
(2)查询方式
A/D转换芯片有表明转换完成的状态信号,例如ADC0809的EOC端。因此可以用查询方式,测试EOC的状态,即可确认转换是否完成,并接着进行数据传送。
(3)中断方式
把表明转换完成的状态信号(EOC)作为中断请求信号,以中断方式进行数据传送。
不管使用上述哪种方式,只要一旦确定转换完成,即可通过指令进行数据传送。首先送出口地址并以信号有效时,OE信号即有效,把转换数据送上数据总线,供单片机接受。
我们要实现多通道采集,所以外部模拟量就通一些可变变阻或者电压源来代替模拟输入,我们显示数值使用lcd显示,再采用按键来切换通道,达到我们的目的。
我们这块采用了可调的滑动变阻器来模拟电压源,为清楚的看到电压值的差别,我们特意加了电压表来显示数值和lcd显示的数值来对比,比较显示误差
我们lcd显示就是可以显示的东西能多一点比如可以显示一些字母注释,可以更清楚的看到数值。但是需要注意的是连接P0口的时候一定要加上拉电阻。加一个排阻也是一个不错的选择
首先要有定义的引脚和一些定义的字符串数组
sbit RS=P1^3;
sbit RW=P1^4;
sbit E=P1^5;
sbit BF =P0^7;
sbit key1= P1^6;
sbit key2= P1^7;
unsigned char code Str[]={"Vorlt="};
unsigned char code digit[10]={"0123456789"}; //定义字符数组显示数字
其次就是lcd的显示函数,对其进行了封装,只需要一开始进行初始化就行,然后调用display(x)函数就可以在指定要位置写上数字,也有一些写字符串符号小数点的函数,调用即可。
bit BusyTest(void)
{
bit result;
RS=0; //根据规定,RS为低电平,RW为高电平时,可以读状态
RW=1;
E=1; //E=1,才允许读写
_nop_();
_nop_(); //空操作两个机器周期,给硬件反应时间
result=BF; //将忙碌标志电平赋给result
E=0; //将E恢复低电平
return result;
}
//函数功能:将模式设置指令或显示地址写入液晶模块 入口参数:dictate
void WriteInstruction (unsigned char dictate)
{
while(BusyTest()==1); //如果忙就等待
RS=0; //根据规定,RS和R/W同时为低电平时,可以写入指令
RW=0;
E=0; //E置低电平(根据表8-6,写指令时,E为高脉冲, // 就是让E从0到1发生正跳变,所以应先置"0"
_nop_();
_nop_(); //空操作两个机器周期,给硬件反应时间
P0=dictate; //将数据送入P0口,即写入指令或地址
_nop_();
_nop_(); //空操作两个机器周期,给硬件反应时间
E=1; //E置高电平
_nop_();
_nop_(); //空操作两个机器周期,给硬件反应时间
E=0; //当E由高电平跳变成低电平时,液晶模块开始执行命令
}
//函数功能:指定字符显示的实际地址 入口参数:x
void WriteAddress(unsigned char x)
{
WriteInstruction(x|0x80); //显示位置的确定方法规定为"80H+地址码x"
}
//函数功能:将数据(字符的标准ASCII码)写入液晶模块 入口参数:y(为字符常量)
void WriteData(unsigned char y)
{
while(BusyTest()==1);
RS=1; //RS为高电平,RW为低电平时,可以写入数据
RW=0;
E=0; //E置低电平(根据表8-6,写指令时,E为高脉冲,
// 就是让E从0到1发生正跳变,所以应先置"0"
P0=y; //将数据送入P0口,即将数据写入液晶模块
_nop_();
_nop_(); //空操作四个机器周期,给硬件反应时间
E=1; //E置高电平
_nop_();
_nop_(); //空操作四个机器周期,给硬件反应时间
E=0; //当E由高电平跳变成低电平时,液晶模块开始执行命令
}
//函数功能:对LCD的显示模式进行初始化设置
void LcdInitiate(void)
{
delaynms(15); //延时15ms,首次写指令时应给LCD一段较长的反应时间
WriteInstruction(0x38); //显示模式设置:16×2显示,5×7点阵,8位数据接口
delaynms(5); //延时5ms ,给硬件一点反应时间
WriteInstruction(0x38);
delaynms(5); //延时5ms ,给硬件一点反应时间
WriteInstruction(0x38); //连续三次,确保初始化成功
delaynms(5); //延时5ms ,给硬件一点反应时间
WriteInstruction(0x0c); //显示模式设置:显示开,无光标,光标不闪烁
delaynms(5); //延时5ms ,给硬件一点反应时间
WriteInstruction(0x06); //显示模式设置:光标右移,字符不移
delaynms(5); //延时5ms ,给硬件一点反应时间
WriteInstruction(0x01); //清屏幕指令,将以前的显示内容清除
delaynms(5); //延时5ms ,给硬件一点反应时间
}
//函数功能:显示电压符号
void display_volt(void)
{
unsigned char i;
WriteAddress(0x00); //写显示地址,将在第2行第1列开始显示
i = 0; //从第一个字符开始显示
while(Str[i] != '\0') //只要没有写到结束标志,就继续写
{
WriteData(Str[i]); //将字符常量写入LCD
i++; //指向下一个字符
}
}
//函数功能:显示电压的小数点
void display_dot(void)
{
WriteAddress(0x08); //写显示地址,将在第1行第10列开始显示
WriteData('.'); //将小数点的字符常量写入LCD
}
//函数功能:显示电压的单位(V)
void display_V(void)
{
WriteAddress(0x0b); //写显示地址,将在第2行第13列开始显示
WriteData('v');
}
//函数功能:显示电压的整数部分 入口参数:x
void display1(unsigned char x)
{
WriteAddress(0x07); //写显示地址,将在第2行第7列开始显示
WriteData(digit[x]); //将百位数字的字符常量写入LCD
}
//函数功能:显示电压的小数数部分 入口参数:x
void display2(unsigned char x)
{
unsigned char i,j;
i=x/10; //取十位(小数点后第一位)
j=x%10; //取个位(小数点后第二位)
WriteAddress(0x09); //写显示地址,将在第1行第11列开始显示
WriteData(digit[i]); //将小数部分的第一位数字字符常量写入LCD
WriteAddress(0x0a);
WriteData(digit[j]); //将小数部分的第一位数字字符常量写入LCD
}
这就是本次采集的核心部分,利用多通道的输入去实现多通道的采集,因为采集是要把模拟量转换为数字量的过程。所以需要对out口的传输到单片机芯片的数字量进行处理显示。但是这里有一个要注意的点就是out1是高位,而out7是地位,也就是说在连接口引脚的时候iuot7对应的是p2.0低位口。在仿真的时候由于proteus中没有ADC0809的moudle ,在运行时报错,我们就用一个功能几乎一样的ADC0808来代替仿真。
这块也是要先定义它的引脚和变量,进行全局定义
unsigned char dat,results,n;
sbit CLK=P3^3;
sbit A_C=P1^2;
sbit A_B=P1^1;
sbit A_A=P1^0;
sbit EOC = P3^1;
sbit OE = P3^0;
sbit START = P3^2 ;
//初始化ad
void InitUart(void)
{
SCON=0x50; //设置中断模式
TMOD=0x22;
PCON=0x00;
TL1=0xfd;
TH1=0xfd;
TI=1;
TR1 =1;
n=0;
}
//定时器中断
void Timer0_INT() interrupt 1
{
CLK =!CLK;
}
按键的主要作用就是切换通道进行通道选择。
首先就得定义按键。然后将按键选择的函数进行封装,减少主函数中的代码量就可以更加清楚的观察到主函数的功能。
sbit key1= P1^6;
sbit key2= P1^7;
void Choose(unsigned char x)
{
switch(x){
case 0: A_A=0; A_B=0; A_C=0; break ;
case 1: A_A=1; A_B=0; A_C=0; break ;
case 2: A_A=0; A_B=1; A_C=0; break ;
case 3: A_A=1; A_B=1; A_C=0; break ;
case 4: A_A=0; A_B=0; A_C=1; break ;
case 5: A_A=1; A_B=0; A_C=1; break ;
case 6: A_A=0; A_B=1; A_C=1; break ;
case 7: A_A=1; A_B=1; A_C=1; break ;
}
}
前面是每部分的概述和程序的实现,主程序才是对这些模块封装的函数的调用和使用。去实现我们想要的功能。
void main(void)
{
unsigned char Int,Dec;
LcdInitiate(); //将液晶初始化
InitUart();
TH0 = 0x14;
TL0 = 0x00;
ET0 =1;TR0 =1 ; EA =1 ;
while(1)
{
delaynms(5);
Choose(n); //选择通道
START=0;
START=1;
START=0;//当低电平来时开始读取数据
while(EOC==0);
OE=1;
dat=P2; //将数据赋给P2
OE=0;
display_volt(); //显示电压字符串
Int = dat/51;
Dec = (dat-Int*51)*100 /51 ; //处理数据
display1(Int);
display_dot();//分别显示个位和小数位和小数点
display2(Dec);
display_V(); //显示电压值符号
if(key1==0)
{
delay1ms(); //消抖
if(key1==0)
{
n++; //通道++
}
while(!key1);
}
if(key2==0)
{
delay1ms();//消抖
if(key2==0)
{
n--; //通道--
}
while(!key2);
}
}
}
本次实验使用ADC0809模拟了八通道模拟量的采集的过程,我们使用了电压源来模拟通道输入,利用AD转换成数字量,再到lcd上进行显示的一个过程,我们实现了按键切换通道的功能,了解熟悉了ADC0809芯片的使用,如何去完成的数据的转换和采集。更多的让我们可以在软件上实现对硬件的编程的使用。让书上的原理得到实践和验证。