C51:数模模数转换---------我的理解多少有点问题

前言

三种ADC的原理就默认都懂了~


数模模数转换:DAC

DA硬件上讲比较容易,就是通过运算放大器把二进制数(一系列的稳定的高低电平)转换成不稳定的、连续变换的波。

比如下面这个图,可以得到 u 0 u_0 u0的通式就是
u 0 = − U R E F 2 3 ( d 2 2 2 + d 1 2 1 + d 0 2 0 ) u_0 = - \frac{U_{REF}}{2^3}(d_22^2 + d_12^1 + d_02^0) u0=23UREF(d222+d121+d020)

C51:数模模数转换---------我的理解多少有点问题_第1张图片
公式怎么来的?
图中的MOS都是绝缘栅型、N沟道、增强型管,在 U g s > 0 U_{gs}>0 Ugs>0时有可以导通。所以在每个管子的 d ? d_? d处,如果给1,即高电平,那么该管子导通,看做漏级与栅极之间短路。

上面这个公式就表明了简单的DA转换原理。
d 2 d 1 d 0 → u 0 d_2d_1d_0 \rightarrow u_0 d2d1d0u0

不是一个位对应一个电压值,而是一个二进制数对应一个电压值。

  • 另外,上面式子中前面的系数 − U R E F 2 3 -\frac{U_{REF}}{2^3} 23UREF是其比例系数、单位电压,也是最小输出电压的增量。
       \;
       \;
       \;

数模转换器结构

C51:数模模数转换---------我的理解多少有点问题_第2张图片

   \;
   \;
   \;

一个二进制对应一个电压值,可是电压连续变换。那么从一个二进制跳到另一个二进制之间不就必然出现其他二进制吗???
我估计,(没错,又是估计)
数字转模拟的数字量间不能间隔别的,因为模拟信号是一直连着的,所以需要转换的数字量也需要是一串在模拟上对应连续的二进制。

   \;
   \;
   \;


转换精度和分辨率关系不大,分辨率大的精度不一定大。即分辨率小数点后位数再多,精度也可能不会有提高。
分辨率与测量的刻度有关,而精度是值接近真实值的程度。

例1:一把1米的软尺,有1000个刻度,分辩率1毫米(分辩率0.1%),用标准尺量下绝对误差5毫米,精度0.5%。如果能把尺拉长20毫米,此时绝对误差25毫米,精度降为2.5%, 可是尺还是1000个刻度,其分辨率还是1毫米(0.1%).

例2:两杆称来称真重1克的物体, 一杆的结果为1.03克, 另一杆的结果为0.8333333333333333333333333克,
哪个准呢?

转换精度与转换误差有关。误差越大,精度越小。精度值越小,精度越高。

分辨率: 1 2 n − 1 , n 是 二 进 制 位 数 \frac{1}{2^n - 1} ,n是二进制位数 2n11n

转换误差:满刻度FSR(Full Scale Range)的百分比,或者m倍的最低有效位LSB(Least Significant Bit)—— m表示输入是0…01时,输出电压与绝对误差的比值。

   \;
   \;
   \;


DA-PWM呼吸灯

PWM(Pulse width modulation,脉冲宽度调制) 就是更改数字信号高电平的占空比。
等 效 电 压 V ‾ = ∫ a b V d t T 等效电压\overline{V} = \frac{ \int_a^b{V}dt}{T} V=TabVdt
C51:数模模数转换---------我的理解多少有点问题_第3张图片

呼吸灯的意思是灯的亮度逐渐变化,像在呼吸一样。这通过PWM实现,在一个周期里,使高电平(如果高电平让灯亮的话)占空比越来越小,那么灯就会越来越暗。

连续原理图如下,左边的电路就是DAC(Digtal to Analog Convertor)由PWM得到的仍然是数字信号需要经过外围DAC电路得到模拟信号。
(PWM实质可能是给电容充电的时间长度的改变)
C51:数模模数转换---------我的理解多少有点问题_第4张图片

#include "reg52.h"		

//DAC input port
sbit PWM=P2^1;
bit DIR;


unsigned int count,value,timer1;

void Timer1Init()
{
	TMOD|=0X10;//T1 mode1

	TH1 = 0xFF; 
	TL1 = 0xff;  
		
	ET1=1;//open T1 interrupt
	EA=1;//open whole interrupt
	TR1=1;//start T1		
}



void main(){	
	Timer1Init();  
	DIR=1;
	value=0;
	count=0;
	while(1)	{
		if(count>100){ 
		//保持亮多久,亮几次才能达到视觉暂留效果 
		//Led显示中常常需要这样的变量,像多加一个循环一样延长暂留的时间好被看清楚

			count=0;
			if(DIR==1){//bright more
			  if(++value>=1000)
			  	DIR=0;
			}
			else{//bright less
			  if(--value<=0)
			  	DIR=1;
			}

		}


				
		if(timer1>1000)/// 
			timer1=0;
		if(timer1 

显示效果:

数模模数转换:ADC

  • 数模模数转换的操作也是操作器件,不是纯在软件上的。我的单片机上只有一个XPT2046,所以使用时我只写这个芯片的使用(话说我不是一直这样吗)。

三种ADC电路:逐次逼近型、双积分型、并联比较型。
三种ADC原理都很简单,如果仔细推敲的话!三个的速度是逐次递增的——毫米级、微秒级、纳秒级。

XPT2046的简单介绍

XPT2046原理图如下。单片机上有AIN0、AIN1、AIN2、AIN3这4个通道,分别是接电位器、温敏电阻、光敏电阻、无。外部模拟信号可以由这四个输入被转换成数字信号。
C51:数模模数转换---------我的理解多少有点问题_第5张图片

XPT2046引脚图如下。需要用到的就是框里4个口。
C51:数模模数转换---------我的理解多少有点问题_第6张图片

XPT2046控制字使用
一个命令8位,需要逐个从DIN端口输入。
C51:数模模数转换---------我的理解多少有点问题_第7张图片
MODE一般都选单端输入。毕竟只需要测一个量。
因为AIN0接电位器,而AIN0对应XP(X+)。那么 A 2 A 1 A 0 A_2A_1A_0 A2A1A0为001、011都行。

C51:数模模数转换---------我的理解多少有点问题_第8张图片

那么数据该从哪头开始呢?
从左到右,首先CS端给个零。
然后DIN输入端开始传一位。
DCLK给个上升沿后,DIN开始传下一位。
… \dots
控制字8位传完后,等一个周期。消除BUSY这段时间。
然后从DOUT读一位数据。
每次DCLK给下降沿后,就读取一位。
… \dots
接收12位后CS端置位。
C51:数模模数转换---------我的理解多少有点问题_第9张图片

读取电位器电压

#include"reg52.h"
#include"intrins.h"



sbit DIN=P3^4;//输入
sbit CS=P3^5;//片选 
sbit CLK=P3^6;//avoid the conflict 外部时钟
sbit DOUT=P3^7; //输出


sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;
unsigned char leds[4];
unsigned char code table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};


unsigned int XPT_2046_Read(unsigned char _cmd);
unsigned int SPI_Read();
void SPI_Send(unsigned char _cmd);

void delay(unsigned int i);
void show();


void main(){
	while(1)show();
}

void delay(unsigned int i){
	while(i--);
}
void show(){
	
	unsigned int temp,j;
	static  int i=0;
	if(++i>=50){
		i=0;
		temp=XPT_2046_Read(0x94);// or 0xB4
	}


	leds[0] = table[ temp%10 ];
	leds[1]= table[ temp/10%10  ];
	leds[2] = table[ temp/100%10 ];
	leds[3] = table[ temp/1000 ];
	
	
	for(j=0;j<4;j++){
		switch(j){
			case 0:
				LSC=0,LSB=0,LSA=0;
				break;
			case 1:
				LSC=0,LSB=0,LSA=1;
				break;
			case 2:
				LSC=0,LSB=1,LSA=0;
				break;
			case 3:
				LSC=0,LSB=1,LSA=1;
				break;
		}

		P0=leds[j];
		delay(100);
		P0=0;
	
	}

}

unsigned int XPT_2046_Read(unsigned char _cmd){

	unsigned char i;
	unsigned int var_AD;
	CLK=0;
	CS=0;
	SPI_Send(_cmd);
	for(i=6;i>0;i--);

	CLK=1;//清除busy
	_nop_();
	_nop_();
	CLK=0;
	_nop_();
	_nop_();

	var_AD=SPI_Read();
	CS=1;
	return var_AD;

}
unsigned int SPI_Read(){

	unsigned int value=0,i;
	CLK=0;
	for(i=0;i<12;i++){ //接收12位的数据
		value<<=1;
		CLK=1;
		CLK=0;
		value|=DOUT;
	}
	return value;

}
void SPI_Send(unsigned char _cmd){

	unsigned char i;
	CLK=0;
	for(i=0;i<8;i++){
		DIN=_cmd>>7;
		_cmd<<=1;
		CLK=0;//上升沿发送
		CLK=1;
	}

}

显示结果:

  • 很简单的代码,折磨了我两三个小时。以前也有这样的错误,不过没注意,这次可难受死我了——在定义变量时,比如i,就不要定义成int型。要定义成unsigned char型。

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