三种ADC的原理就默认都懂了~
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)
公式怎么来的?
图中的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 d2d1d0→u0
不是一个位对应一个电压值,而是一个二进制数对应一个电压值。
\;
\;
\;
一个二进制对应一个电压值,可是电压连续变换。那么从一个二进制跳到另一个二进制之间不就必然出现其他二进制吗???
我估计,(没错,又是估计)
数字转模拟的数字量间不能间隔别的,因为模拟信号是一直连着的,所以需要转换的数字量也需要是一串在模拟上对应连续的二进制。
\;
\;
\;
转换精度和分辨率关系不大,分辨率大的精度不一定大。即分辨率小数点后位数再多,精度也可能不会有提高。
分辨率与测量的刻度有关,而精度是值接近真实值的程度。
例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是二进制位数 2n−11,n是二进制位数
转换误差:满刻度FSR(Full Scale Range)的百分比,或者m倍的最低有效位LSB(Least Significant Bit)—— m表示输入是0…01时,输出电压与绝对误差的比值。
\;
\;
\;
PWM(Pulse width modulation,脉冲宽度调制) 就是更改数字信号高电平的占空比。
等 效 电 压 V ‾ = ∫ a b V d t T 等效电压\overline{V} = \frac{ \int_a^b{V}dt}{T} 等效电压V=T∫abVdt
呼吸灯的意思是灯的亮度逐渐变化,像在呼吸一样。这通过PWM实现,在一个周期里,使高电平(如果高电平让灯亮的话)占空比越来越小,那么灯就会越来越暗。
连续原理图如下,左边的电路就是DAC(Digtal to Analog Convertor)由PWM得到的仍然是数字信号需要经过外围DAC电路得到模拟信号。
(PWM实质可能是给电容充电的时间长度的改变)
#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电路:逐次逼近型、双积分型、并联比较型。
三种ADC原理都很简单,如果仔细推敲的话!三个的速度是逐次递增的——毫米级、微秒级、纳秒级。
XPT2046原理图如下。单片机上有AIN0、AIN1、AIN2、AIN3这4个通道,分别是接电位器、温敏电阻、光敏电阻、无。外部模拟信号可以由这四个输入被转换成数字信号。
XPT2046控制字使用
一个命令8位,需要逐个从DIN端口输入。
MODE一般都选单端输入。毕竟只需要测一个量。
因为AIN0接电位器,而AIN0对应XP(X+)。那么 A 2 A 1 A 0 A_2A_1A_0 A2A1A0为001、011都行。
那么数据该从哪头开始呢?
从左到右,首先CS端给个零。
然后DIN输入端开始传一位。
DCLK给个上升沿后,DIN开始传下一位。
… \dots …
控制字8位传完后,等一个周期。消除BUSY这段时间。
然后从DOUT读一位数据。
每次DCLK给下降沿后,就读取一位。
… \dots …
接收12位后CS端置位。
#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;
}
}
显示结果: