并行比较型
优点:转换速度快,内含寄存器,无需附加采样-保持电路。
缺点:需要很多电压比较器和触发器,高分辨率较难制造。
反馈比较型
分为计数型和逐次比较型,计数型又称为双积分型。
其中,计数型且转换速度不高,转换时间长,但价格便宜;逐次比较型使用对分搜索法,因此转换速度要比计数型快很多。(ADC0804和XPT2046都属于逐次比较型)
分辨率为A/D转换器对输入信号的分辨能力,其计算公式如下,其中n为A/D转换器的位数:
分 辨 率 = 1 2 n 分辨率=\frac{1}{2^{n}} 分辨率=2n1
当A/D转换器支持输入负电压时,第一位代表正负,则分辨率的计算公式为:
分 辨 率 = 1 2 n − 1 分辨率=\frac{1}{2^{n-1}} 分辨率=2n−11
可以求得模拟电压的最小值如下, 满刻度为输入的模拟电压最大值:
变 化 最 小 值 = 满 刻 度 × 分 辨 率 变化最小值=满刻度\times 分辨率 变化最小值=满刻度×分辨率
转换误差表示A/D转换器实际输出的数字量与理论输出量的区别。分为量化误差、偏移误差和满刻度误差。其中偏移误差和满刻度误差合称为非线性误差。
转换精度为实际模拟量输入与理论模拟输入之差的最大值。对于A/D转换器而言,可以在每个阶梯的水平中点进行测量,它包括了所有误差。
即完成一次A/D转换所需要的时间(包括稳定时间)。而转换时间的倒数为转换速率,即每秒转换的次数。
ADC0804是逐次比较型A/D转换器,共有20个引脚,分辨率为8位,输入电压范围为0-5V,模数转换时间大约100us。价格便宜,应用广泛。
引脚 | 名称 | 解释 |
---|---|---|
1 | C S ‾ \overline{CS} CS | 片选信号输入端,当输入低电平,表明A/D被选中 |
2 | R D ‾ \overline{RD} RD | 读信号输入端,低电平输出端有效 |
3 | W R ‾ \overline{WR} WR | 写信号输入端,低电平启用A/D转换 |
4 | CLK | 时钟信号输入端 |
5 | I N T R ‾ \overline{INTR} INTR | A/D转换结束信号,低电平表示转换完成 |
6-7 | VIN(+),VIN(-) | 两模拟信号输入端,可接收单极性,双极性和差模输入信号 |
8 | AGND | 模拟信号地 |
9 | VREF/2 | 参考电平输入端,决定量化单位 |
10 | DGND | 数字信号地 |
11-18 | DB7-DB0 | 具有三态特性的数字信号输出口 |
19 | CLKR | 内部时钟发生器外接电阻端 |
20 | VCC(VREF) | 芯片输入电源,为5V |
#include
#include
typedef unsigned int u16;
typedef unsigned char u8;
sbit dula = P2^6; //U1锁存端
sbit wela = P2^7; //U2锁存端
sbit adwr = P3^6; //A/D的WR端口
sbit adrd = P3^7; //A/D的RD端口
u8 code table[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};//显示0~9的值
u8 bai,shi,ge; //用于显示AD转换后的值
void AD_Init(); //A/D初始化函数
void AD_read(); //A/D读取函数
void delay(u16); //延时函数
void display(u8,u8,u8); //数码管显示函数
void main()
{
AD_Init();
while(1)
{
AD_read();
}
}
void AD_Init() //A/D初始化函数
{
wela=1;
P0=0x7f; //置CS片选为0,启动ADC0804
wela=0;
}
void AD_read() //A/D读取函数
{
u8 i,adval; //循环变量i,读取变量adval
adwr=1; //WR口先为高电平
_nop_(); //_nop_函数位于intrins.h头文件中,作用是延时一个机器周期的时间
adwr=0; //延时一段时间后,WR口置低
_nop_(); //又延时一段时间后
adwr=1; //WR口拉高 ADC0804被启动
//由于连续读取数据,因此CS片选不必要反复操作
//由于INTR由硬件控制,因此也不需要操作
for(i=10;i>0;i--) //TX-1C实验板AID工作频率较低,所以启动转换要多留点时间用来转换
display(bai,shi,ge);//把显示部分放这里的原因也是为了延长转换时间
P1=0xff; //读取P1口之前先给其写全1
adrd=1; //先使RD拉高
_nop_(); //延时一段时间
adrd=0; //RD拉低
_nop_(); //再延时一段时间
adval=P1; //AD数据读取赋给P1口
adrd=1; //马上把RD拉高
bai=adval/100; //分出百,十和个位
shi=adval% 100/10;
ge=adval% 10;
}
void delay(u16 ms) //延时函数
{
u16 i,j;
for(i=ms;i>0;i--)
for(j=110;j>0;j--);
}
void display(u8 bai,u8 shi,u8 ge) //数码管显示函数
{
/*百位*/
dula=1;
P0=table[bai];
dula=0;
P0=0xff;
wela=1;
P0=0x7e;
wela=0;
delay(5);
/*十位*/
dula=1;
P0=table[shi];
dula=0;
P0=0xff;
wela=1;
P0=0x7d;
wela=0;
delay(5);
/*个位*/
dula=1;
P0=table[ge];
dula=0;
P0=0xff;
wela=1;
P0=0x7b;
wela=0;
delay(5);
}
总结:代码是在郭天祥书中摘取后加入自己的想法进行的修改,做注释,增加可读性同时使每一步与时序图相对应。但由于博主手头没有相应的硬件,无法测试代码可行性。仅帮助理解ADC0804转换的过程。
XPT2046本是电阻触控屏控制器,由于触摸控制器需要测量电压,因此可以作为A/D转换器使用。采用了逐次比较型A/D转换器,共有16个引脚,分辨率为12位,输入电压范围为0-6V。
引脚 | 名称 | 解释 |
---|---|---|
1 | BUSY | 忙时信号线,当 C S ‾ \overline{CS} CS为高电平时为高阻状态 |
2 | DIN | 串行数据输入端。当 C S ‾ \overline{CS} CS为低电平时,数据在DCLK上升沿锁存进来 |
3 | C S ‾ \overline{CS} CS | 片选信号。控制转换时序和使能串行输入输出寄存器,高电平时ADC掉电 |
4 | DCLK | 外部时钟信号输入端 |
5 | VCC | 电源输入端 |
6-9 | XP,YP,XN,YN | 信号输入端 |
10 | GND | 接地端 |
11 | VBAT | 电池监视输入端 |
12 | AUX | ADC辅助输入通道 |
13 | VREF | 参考电压输入/输出 |
14 | IOVDD | 数字电源输入端 |
15 | P E N I R Q ‾ \overline{PENIRQ} PENIRQ | 笔接触中断引脚 |
16 | DOUT | 串行数据输出端。数据在DCLK的下降沿移出,当 C S ‾ \overline{CS} CS高电平时为高阻状态 |
位序号 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
---|---|---|---|---|---|---|---|---|
位符号 | S | A2 | A1 | A0 | MODE | S E R D F R SER\sqrt{DFR} SERDFR | PD1 | PD0 |
位序号 | 名称 | 解释 |
---|---|---|
7 | S | 开始位。S=1表示新的控制字节到来,S=0表示忽略PIN引脚数据 |
6-4 | A2-A0 | 通道选择位 |
3 | MODE | MODE=1为8位转换分辨率,MODE=0为12位转换分辨率 |
2 | S E R D F R SER\sqrt{DFR} SERDFR | S E R D F R SER\sqrt{DFR} SERDFR=1为单端输入方式, S E R D F R SER\sqrt{DFR} SERDFR=0为差分输入方式 |
1-0 | PD1-PD0 | 低功率模式选择位,一般采用低功率模式,赋值00 |
注:
由于博主使用的是普中单片机开发板,且XPT2046仅作为AD转换器使用,因此仅使用单端模式,控制A0,A1,A2三个位即可,其余位较为固定。
控制位 | A2 | A1 | A0 | 输入端口 | 外部设备 |
---|---|---|---|---|---|
0x94/0xB4 | 0 | 0/1 | 1 | X+ | 电位器 |
0xD4 | 1 | 0 | 1 | Y+ | 热敏电阻 |
0xA4 | 0 | 1 | 0 | VBAT | 光敏电阻 |
0xE4 | 1 | 1 | 0 | AUX | 外部信号 |
#include
#include
typedef unsigned int u16;
typedef unsigned char u8;
/*AD转换器的IO口设置*/
sbit DOUT = P3^7; //输出
sbit CLK = P3^6; //时钟
sbit DIN = P3^4; //输入
sbit CS = P3^5; //片选
/*数码管的IO口设置*/
sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;
u8 time; //控制显示
u8 disp[4]; //存放数据的数组
u8 code smgduan[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};//数码管显示表
void datapros(); //数据处理函数
u16 SPI_Read(void); //采用SPI协议读取数据
void SPI_Write(u8 dat); //写入数据
u16 Read_AD_Data(u8 cmd); //读取AD
void delay(u16); //延时函数
void DigDisplay(); //数码管显示函数
void main()
{
while(1)
{
datapros(); //数据处理函数
DigDisplay();//数码管显示函数
}
}
void datapros() //数据处理函数
{
u16 temp;
if(time==50) //每隔一段时间显示一次,时间约为time循环50次
{
time=0;
temp = Read_AD_Data(0x94); //写入电位器端口,修改参数可以更换模式
}
time++;
disp[0]=smgduan[temp/1000]; //千位
disp[1]=smgduan[temp%1000/100]; //百位
disp[2]=smgduan[temp%1000%100/10]; //十位
disp[3]=smgduan[temp%1000%100%10]; //个位
}
void SPI_Write(u8 dat) //使用SPI写入数据
{
u8 i;
CLK = 0; //时钟信号拉低
for(i=0; i<8; i++)
{
DIN = dat >> 7; //放置最高位,DIN为控制寄存器
dat <<= 1; //每次写入移位
CLK = 0; //时钟信号上升沿放置数据
CLK = 1;
}
}
u16 SPI_Read(void) //使用SPI读出数据
{
u8 i;
u16 dat=0;
CLK = 0; //时钟信号拉低
for(i=0; i<12; i++) //接收12位数据
{
dat <<= 1; //每次写入移位
CLK = 1;
CLK = 0; //时钟信号下降沿读取数据
dat |= DOUT; //由DOUT接口写入数据
}
return dat;
}
u16 Read_AD_Data(u8 cmd) //读AD数据
{
u8 i; //循环变量
u16 AD_Value; //存放读出的AD数据
CLK = 0; //时钟信号拉低
CS = 0; //片选电位拉低
SPI_Write(cmd); //写数据
for(i=6; i>0; i--); //延时等待转换结果
CLK = 1; //发送一个时钟周期,清除BUSY
_nop_(); //延时一个时钟周期
_nop_(); //延时一个时钟周期
CLK = 0; //时钟信号拉低
_nop_(); //延时一个时钟周期
_nop_(); //延时一个时钟周期
AD_Value=SPI_Read(); //读出模拟值
CS = 1; //片选电位拉高
return AD_Value; //返回读出的值
}
void delay(u16 i) //延时函数 i=1延时10us
{
while(i--);
}
void DigDisplay() //数码管显示函数
{
u8 i,j;
for(i=0;i<4;i++)
{
switch(i) //位选,选择点亮的数码管
{
case(0):
LSA=0;LSB=0;LSC=0; break;//显示第0位
case(1):
LSA=1;LSB=0;LSC=0; break;//显示第1位
case(2):
LSA=0;LSB=1;LSC=0; break;//显示第2位
case(3):
LSA=1;LSB=1;LSC=0; break;//显示第3位
}
P0=disp[3-i]; //发送数据
delay(100); //间隔一段时间扫描
P0=0x00; //消隐
}
}
总结:代码是在普中科技A/D转换例程改写的。与ADC0804不同的是,由于XPT2046功能较多,使用了控制寄存器,且对比前面的郭天祥的板子,普中的板子没有使用锁存器,所以数据要一位一位的写入/读取。