51单片机(六)A/D和D/A

A/D和D/A工作原理和程序实现

  • 一、A/D
    • 1、ADC0804原理图
    • 2、ADC0804接线
    • 3、ADC0804时序图
    • 4、程序实现流程
    • 5、例题+分析
  • 二、D/A
    • 1、DAC0832原理图
    • 2、DAC0832接线
    • 3、DAC0832时序图
    • 4、程序实现流程
    • 5、例题+分析
  • 三、A/D与D/A综合例题

一、A/D

1、ADC0804原理图

51单片机(六)A/D和D/A_第1张图片

CS:片选信号输入端,低电平有效,CS引脚软件置0后,表示ADC0804被选中。
RD:读信号输入端,低电平输出端有效。
WR:写信号输入端,低电平启动AD转换。
INTR:转换完毕中断提供端,AD转换结束后,低电平表示本次转换已完成。
DB0~DB7:三态特性数字信号输出口,接单片机芯片P10 ~P17引脚
VCC:芯片电源5V输入。
CLKR:内部时钟发生器的外接电阻端,与CLK端配合可由芯片自身产生时钟脉冲,其频率计算方式是:fck=1/(1.1RC)。
CLKIN:时钟信号输入端。
Vin(+)、Vin(-):两个模拟信号输入端,可以接收单极性、双极性和差模输入信号。
AGND:模拟电源地线。
VREF/2:参考电平输入,决定量化单位。
DGND:数字电源地线。

2、ADC0804接线

上图中左边的引脚是直接或间接接单片机芯片引脚的,右边引脚连接电阻、电容、电感、电源来自成电路的。
CS:接U2锁存器

51单片机(六)A/D和D/A_第2张图片

U2锁存器在之前的实验中控制数码管的位选(我的板子6个数码管,所以锁存器只用到Q0~Q5,所以用Q7控制AD的片选CS)

51单片机(六)A/D和D/A_第3张图片RD:芯片引脚P3^7
WR:芯片引脚P3^6
WR控制启动AD,RD读人信号,都是低电平有效。程序开始时,先将WR软件置0,启动AD,再将RD软件置0,读入转换的数据。
INTR:悬空(未用中断法抓取)
DB0 ~ DB7:接芯片P10~ P17,与LED同接到P1,但是LED是通过锁存器U3接到芯片P1上的,在单片机上电后U3锁存器的DIOLA引脚(和U1的的段选位、U2的位选位是同一位置)置高电平,此时锁存器U3处于前第俩种工作状态下,要将该位软件置0,即让锁存器Q输出端为Q0,LED左右俩端都是高电平,此时LED被“锁住”。
CLKR与CLKIN:CLKIN是外部时钟输出端。可以通过外部时钟来控制AD,我的板子是CLKR与CLKIN外接电阻和电容形成RC振荡电路,输出为正弦波形(不懂RC振荡电路的朋友可以去看看《高频电子线路》)
Vin(+)、Vin(-):可以接入外部模拟信号,如测量一组声音信号、电流、电压信号等。我的板子上是直接接的电感电路和电位器

51单片机(六)A/D和D/A_第4张图片

通过转动电位器旋钮,Vin+引脚不断输入模拟信号,Vin-接模拟地。
VREF/2:通过电阻的分压,输入VCC/2。参考电压决定着量化单位。

ADC0804是8位ADC,为单端输出,输出编码范围是0~255,计算编码为:取样电压/VCC * 255(若为差分输出时,数字输出D的范围是-128 - +127,此时计算编码为:取样电压/VCC* 128=2* 取样电压/VCC*255 )。

3、ADC0804时序图

51单片机(六)A/D和D/A_第5张图片
51单片机(六)A/D和D/A_第6张图片

上图1是ADC0804启动转换时的时序图,图2是读取数据时的时序图(时序图之后会经常用到,不仅仅51,32、FPGA等也经常用到,学会查看时序图,一劳永逸)

从图一看到:AD启动后,先片选CS置0,随后WR置0,经过Tw(wr)时间后WR拉高。此时AD被启动(WR就是用来启动AD的),但是AD开始时处于“不忙”状态,大概1~8个时钟周期后开始工作(所以WR启动AD后需要延时几个时钟周期,再读取数据),INTR由于被悬空,所以INTR不需要考虑(没有使用中断法)

从图二看到:INTR置低后被拉高(中断法时,现在不考虑),CS置0(AD转换时,都需要选中ADC0804,所以CS可以一直置0,表示ADC0804一直处于选中状态),选中ADC0804,此时开始读数据,RD置0,开始读数据(WR与RD都是低电平有效),从图2可以看到RD置0后还要经过Tacc后才有数据流。

4、程序实现流程

从上面的总结得出程序流程:

进入程序后,①CS软件置0——>②WR软件置0,启动AD——>③延时8个时钟周期以上(具体多少,可调试)——>④RD置0,开始读数据——>⑤延时几个时钟周期后(可调试)开始读取单片机芯片P1寄存器的数据

5、例题+分析

例:当拧动实验板A/D旁边的电位器时,数码管前三位以十进制方式动态显示A/D转换后的数字量
分析:
1)锁住LED
U3锁存器DIOLA端接芯片的P25引脚

51单片机(六)A/D和D/A_第7张图片

程序开始需要将DIOLA软件置0

DIOLA=0;

2)片选AD
U2锁存器的Q7引脚输出为0,即D7输入脚为0,即芯片P07为0

wela=1;
P0=0x7f;//01111111  片选CSAD
wela=0;

3)启动AD

adwr=0;//启动AD
_nop_();//一个时钟周期
adwr=1;

4)AD数据读取

adrd=0;
_nop_();

5)数码管显示

根据时序图,启动AD后,有1~8个时钟周期+内部Tc,所以在AD读取数据之前进行要延时8个周期以上的时间,可以直接使用_nop_();延时一个机器周期,即12个时钟周期,也可以将数码管显示函数插在启动、读取之间,这样的话,执行数码管显示所需要的时间刚好可以代替出现有效数据之前延时。而且AD数据处理需要一定的时间,所以适当延长数码管显示的时间。如下程序:

for( i=10;i>0;i--)
display_time(a,b,c);
void display_time(uint bai ,uint shi,uint ge)
{

	dula=1;
	P0=table[bai];
	dula=0;
	P0=0xff;
	
	wela=1;
	P0=0x7e;//数码管1
	wela=0;
	delay_ms(5);
	
	dula=1;
	P0=table[shi];
	dula=0;
	P0=0xff;
	
	wela=1;
	P0=0x7d;//数码管2
	wela=0;
	delay_ms(5);
	
	dula=1;
	P0=table[ge];
	dula=0;
	P0=0xff;
	
	wela=1;
	P0=0x7b;//数码管3
	wela=0;
	delay_ms(5);
}

每执行一次数码管实验,至少需要15ms,即至少需要15000个机器周期,A/D转换大约在100us,所以循环十次,即数码管显示需要执行150us,既给充足时间让AD转换又不用担心启动AD后因为延时不够而无法出现有效数据。

完整程序:

#include 
#include 
#define uint unsigned int
#define uchar unsigned char
uchar code table[]={
0x3f,0x06,0x5b,0x4f,
0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,
0x39,0x5e,0x79,0x71
};
sbit DIOLA=P2^5;
sbit dula=P2^6;
sbit wela=P2^7;
sbit adwr=P3^6;
sbit adrd=P3^7;
uint i,a,b,c,adval;
void delay_ms(uint);
void display_time(uint,uint,uint);

void main()
{
	DIOLA=0;
	wela=1;
	P0=0x7f;//01111111  片选CSAD
	wela=0;
	while(1)
	{
		adwr=0;//启动AD
		_nop_();//一个时钟周期
		adwr=1;
		for( i=10;i>0;i--)
		display_time(a,b,c);
		adrd=0;
		_nop_();
		
		adval=P1;
		adrd=1;
		a=adval/100;
		b=adval%100/10;
		c=adval%10;
	}

}

void display_time(uint bai ,uint shi,uint ge)
{

	dula=1;
	P0=table[bai];
	dula=0;
	P0=0xff;
	
	wela=1;
	P0=0x7e;//数码管1
	wela=0;
	delay_ms(5);
	
	dula=1;
	P0=table[shi];
	dula=0;
	P0=0xff;
	
	wela=1;
	P0=0x7d;//数码管2
	wela=0;
	delay_ms(5);
	
	dula=1;
	P0=table[ge];
	dula=0;
	P0=0xff;
	
	wela=1;
	P0=0x7b;//数码管3
	wela=0;
	delay_ms(5);
}

void delay_ms(uint ms)
{
	uint i,j;
	for(i=ms;i>0;i--)
	for(j=110;j>0;j--);
}

二、D/A

1、DAC0832原理图

51单片机(六)A/D和D/A_第8张图片
CS:片选信号输入线(选通数据锁存器),低电平有效;
WR1: 数据锁存器写选通输入线,负脉冲(脉宽应大于500ns)有效。当CS=0、ILE=1、WR1有效时,DI0~DI7状态被锁存到输入寄存器
WR2: DAC寄存器选通输入线,负脉冲(脉宽应大于500ns)有效。当XFER=0且WR2有效时,输入寄存器的状态被传到DAC寄存器中。
ILE: 数据锁存允许控制信号输入线,高电平有效。
XFER:数据传输控制信号输入线,低电平有效。
DI0~DI7:8位数据输入线,TTL电平,有效时间应大于90ns(否则锁存器的数据会出错);
GND:AGND和DGND,模拟信号地和数字信号地。模拟地为模拟信号与基准电源参考地;数字地为工作电源地与数字逻辑地(最好在基准电源处共地)
VDD:即Vcc,电源输入端,Vcc的范围为+5V~+15V;
VREF:基准电压输入线,VREF的范围为-10V~+10V;
RFB:反馈信号输入线,改变Rfb端外接电阻值可调整转换满量程精度(我的板子中此端与Iout1接有一个15Ω的电阻)
IOUT1:电流输出端1,其值随DAC寄存器的内容线性变化;当输入全为1时电流最大。
IOUT2:电流输出端2,其值与IOUT1值之和为一常数。

2、DAC0832接线

51单片机(六)A/D和D/A_第9张图片

CS:接P3^2。
WR:DAC0832与ADC0804的WR一样,也是接在芯片的P36引脚。
DI0~DI7:接芯片P00 ~P07引脚。

51单片机(六)A/D和D/A_第10张图片

ILE接VCC,片选CS引脚为0时选中DAC0832,当WR输入有效时,7DI0~DI7引脚的状态被锁存到输入寄存器。即片选后,只需WR写入有效,就能将D0 ~D7的输入数据读到输入锁存器中。

DAC0832内部结构图:(来自百度百科)

51单片机(六)A/D和D/A_第11张图片

XFER接地,所以当WR2输入有效时,输入锁存器的状态被送到DAC寄存器中。根据本板子的原理图,WR2直接接地(本板子用的是“直通连接方式”)DI0~DI7的数据经过WR1有效控制后,直接进入DAC寄存器,然后再进行D/A转换。若想连续转换,只需连续改变数字输入端DI0 ~DI7的数字信号

VCC供电,与VREF都接5V,IOUT1接15KΩ电阻后,与LED相连,所以可以通过LED的亮、暗变换情况,判断出输出的是否为模拟信号。(当然这是实验板为了方便显示,有条件的话可以外接示波器观察波形)

3、DAC0832时序图

查看DAC0832的芯片资料文档:

51单片机(六)A/D和D/A_第12张图片

根据上面的时序图得出,当CS置低电平后,再将WR置低,此时数据位才有数据,即选中AD0832后,WR将数据位写进来(类似0804,WR是启动0804).此时数据位有数据,并开始D/A转换。由于转换需要一定的时间,所以经过Ts的时间后才有稳定的输出

4、程序实现流程

从上面的总结得出程序流程:
进入程序后,①CS软件置0——>②WR软件置0,启动DAC0832——>③延时Ts时间后读取输出(Ts时间可调试)

5、例题+分析

例:用单片机控制DAC0832芯片输出,让发光二极管D12由灭均匀变亮再由最亮均匀熄灭。最亮和最暗时蜂鸣器分别报警一次,完成整个周期时间控制在5s左右,并且循环变化。
分析:
1)DA的数据输入为P0,所以为了不影响数码管,将U1、U2锁存器“锁住”

	dula=0;
	wela=0;

2)进入程序,打开片选,要连续读取数据,所以CS与WR要一直工作

    dacs=0;//片选
	dawr=0;//启动

3)数据输入端输入数字信号,所以只需设置一个值,让其递增、递减。再不停的将该值输入到P0寄存器即可,然后该值为255时,蜂鸣器响,并开始递减,该值为0是,蜂鸣器响,并且开始递增。
完整程序:

include <reg52.h>
#define uint unsigned int
#define uchar unsigned char
sbit dula=P2^6;
sbit wela=P2^7;
sbit dacs=P3^2;
sbit dawr=P3^6;
sbit beep=P2^3;
void delay_ms(uint);
void main()
{
	uint val,flag;
	dula=0;
	wela=0;
	dacs=0;//片选
	dawr=0;//启动
	while(1)
	{
		if(flag==0)
		{
			val+=5;
			P0=val;
			if(val==255)
			{
				flag=1;
				beep=0;
				delay_ms(100);
				beep=1;
			}
		delay_ms(50);//为了达到5s一个周期,满足题的设定
		}
		else
		{
			val-=5;
			P0=val;
			if(val==0)
			{
				flag=0;
				beep=0;
				delay_ms(100);
				beep=1;
			}
			delay_ms(50);//为了达到5s一个周期,满足题的设定	
		}
	}
}

void delay_ms(uint ms)
{
	uint i,j;
	for(i=ms;i>0;i--)
	for(j=110;j>0;j--);
}

三、A/D与D/A综合例题

例:拧动实验板A/D旁边的电位器,使产生的模拟信号通过ADC0804,产生数字信号,并在数码管上显示,同时将该数字信号传给DAC0832,来控制D12的亮暗,并且当输出的数值在100-120、200-230、20-50之间时蜂鸣器发声

完整程序:

#include 
#include 
#define uint unsigned int
#define uchar unsigned char
uchar code table[]={
0x3f,0x06,0x5b,0x4f,
0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,
0x39,0x5e,0x79,0x71
};
sbit DIOLA=P2^5;
sbit dula=P2^6;
sbit wela=P2^7;
sbit wr=P3^6;//A/D与D/A共用,所以wr不能打开,每次需要wr置0来启动A/D
sbit adrd=P3^7;
sbit dacs=P3^2;
sbit beep=P2^3;
uint i,a,b,c,adval;
void delay_ms(uint);
void display_time(uint,uint,uint);
void main()
{
	DIOLA=0;//锁住锁存器U3,防止流水灯乱跑
	wela=1;
	P0=0x7f;//01111111  AD片选CSAD
	wela=0;
	dacs=0;//DA片选
	while(1)
	{
		wr=0;//启动AD
		_nop_();//一个时钟周期
		wr=1;
		for( i=10;i>0;i--)
		display_time(a,b,c);
		adrd=0;
		_nop_();	
		adval=P1;
		adrd=1;
		a=adval/100;
		b=adval%100/10;
		c=adval%10;
		
		wr=0;
		P0=adval;
		if((adval>20&&adval<50)||(adval>100&&adval<120)||(adval>200&&adval<230))
			beep=0;
		else
			beep=1;
		wr=1;
	}

}
void display_time(uint bai ,uint shi,uint ge)
{

	dula=1;
	P0=table[bai];
	dula=0;
	P0=0xff;
	
	wela=1;
	P0=0x7e;//数码管1
	wela=0;
	delay_ms(5);
	
	dula=1;
	P0=table[shi];
	dula=0;
	P0=0xff;
	
	wela=1;
	P0=0x7d;//数码管2
	wela=0;
	delay_ms(5);
	
	dula=1;
	P0=table[ge];
	dula=0;
	P0=0xff;
	
	wela=1;
	P0=0x7b;//数码管3
	wela=0;
	delay_ms(5);
}

void delay_ms(uint ms)
{
	uint i,j;
	for(i=ms;i>0;i--)
	for(j=110;j>0;j--);
}

你可能感兴趣的:(51单片机)