51单片机学习笔记——AD转换

一、AD转换器的分类

直接A/D转换器
  • 并行比较型

    优点:转换速度快,内含寄存器,无需附加采样-保持电路。
    缺点:需要很多电压比较器和触发器,高分辨率较难制造。

  • 反馈比较型

    分为计数型和逐次比较型,计数型又称为双积分型。
    其中,计数型且转换速度不高,转换时间长,但价格便宜;逐次比较型使用对分搜索法,因此转换速度要比计数型快很多。(ADC0804和XPT2046都属于逐次比较型)

间接A/D转换器
  • V-T(电压-时间)
  • V-F(电压-频率)

二、A/D转换器主要参数指标

1.分辨率

分辨率为A/D转换器对输入信号的分辨能力,其计算公式如下,其中n为A/D转换器的位数:

当A/D转换器支持输入负电压时,第一位代表正负,则分辨率的计算公式为:

可以求得模拟电压的最小值如下, 满刻度为输入的模拟电压最大值:

2.转换误差

转换误差表示A/D转换器实际输出的数字量与理论输出量的区别。分为量化误差、偏移误差和满刻度误差。其中偏移误差和满刻度误差合称为非线性误差。

3.转换精度

转换精度为实际模拟量输入与理论模拟输入之差的最大值。对于A/D转换器而言,可以在每个阶梯的水平中点进行测量,它包括了所有误差。

4.转换时间

即完成一次A/D转换所需要的时间(包括稳定时间)。而转换时间的倒数为转换速率,即每秒转换的次数。

三、ADC0804

概况

ADC0804是逐次比较型A/D转换器,共有20个引脚,分辨率为8位,输入电压范围为0-5V,模数转换时间大约100us。价格便宜,应用广泛。

1.引脚详情

ADC0804管脚图

引脚 名称 解释
1 片选信号输入端,当输入低电平,表明A/D被选中
2 读信号输入端,低电平输出端有效
3 写信号输入端,低电平启用A/D转换
4 CLK 时钟信号输入端
5 A/D转换结束信号,低电平表示转换完成
6-7 VIN(+),VIN(-) 两模拟信号输入端,可接收单极性,双极性和差模输入信号
8 AGND 模拟信号地
9 VREF/2 参考电平输入端,决定量化单位
10 DGND 数字信号地
11-18 DB7-DB0 具有三态特性的数字信号输出口
19 CLKR 内部时钟发生器外接电阻端
20 VCC(VREF) 芯片输入电源,为5V
2.时序图
  • 启动时序图
    启动时序图
  • 读取时序图
    读取时序图
3.例程:电位器A/D转换
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#include<reg52.h>
#include<intrins.h>
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

概况

XPT2046本是电阻触控屏控制器,由于触摸控制器需要测量电压,因此可以作为A/D转换器使用。采用了逐次比较型A/D转换器,共有16个引脚,分辨率为12位,输入电压范围为0-6V。

1.引脚图与控制寄存器
  • 引脚图为QFN-16封装方式

QFN-16封装

引脚 名称 解释
1 BUSY 忙时信号线,当为高电平时为高阻状态
2 DIN 串行数据输入端。当为低电平时,数据在DCLK上升沿锁存进来
3 片选信号。控制转换时序和使能串行输入输出寄存器,高电平时ADC掉电
4 DCLK 外部时钟信号输入端
5 VCC 电源输入端
6-9 XP,YP,XN,YN 信号输入端
10 GND 接地端
11 VBAT 电池监视输入端
12 AUX ADC辅助输入通道
13 VREF 参考电压输入/输出
14 IOVDD 数字电源输入端
15 笔接触中断引脚
16 DOUT 串行数据输出端。数据在DCLK的下降沿移出,当高电平时为高阻状态
  • 控制寄存器
位序号 D7 D6 D5 D4 D3 D2 D1 D0
位符号 S A2 A1 A0 MODE PD1 PD0
位序号 名称 解释
7 S 开始位。S=1表示新的控制字节到来,S=0表示忽略PIN引脚数据
6-4 A2-A0 通道选择位
3 MODE MODE=1为8位转换分辨率,MODE=0为12位转换分辨率
2 =1为单端输入方式,=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 外部信号
2.时序图

工作时序

3.例程:电位器A/D转换
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#include<reg52.h>
#include<intrins.h>
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功能较多,使用了控制寄存器,且对比前面的郭天祥的板子,普中的板子没有使用锁存器,所以数据要一位一位的写入/读取。


51单片机学习笔记——AD转换
http://blog.mingxuan.xin/2020/05/13/20200513/
作者
Obscure
发布于
2020年5月13日
许可协议