目录
- 1.ADC介绍
-
- [1.1 ADC简介](#1.1 ADC简介)
- [1.2 ADC转换原理](#1.2 ADC转换原理)
-
- [1.2.1 逐次逼近型ADC](#1.2.1 逐次逼近型ADC)
- [1.2.2 双积分型ADC](#1.2.2 双积分型ADC)
- 2.XPT2046介绍
-
- [2.1 XPT2046特性](#2.1 XPT2046特性)
- [2.2 XPT2046引脚介绍](#2.2 XPT2046引脚介绍)
- 3.XPT2046命令时序介绍
-
- [3.1 单端模式](#3.1 单端模式)
- 4.硬件设计
- 5.软件设计
-
- [5.1 写入数据](#5.1 写入数据)
- [5.2 读取数据](#5.2 读取数据)
- [5.3 读取AD值](#5.3 读取AD值)
- 5.4采集通道设置
- [6. 代码](#6. 代码)
- 7.实验现象
1.ADC介绍
51单片机系统内部,运算时都是使用数字信号,也就是数字量0和1。因此对单片机而言,无法直接操作模拟量,必须要将模拟量转换成数字量,单片机才能进行操作,所谓数字量就是0和1,组成的那些二进制代码,表示的信号大小一个量,用数字量表示同一个模拟量数字位数越多,精度越高,数字位数越少,精度越低。
1.1 ADC简介
ADC(analogtodigitalconverter)也称为模数转换器,是指一个将模拟信号转变为数字信号。因为我们单片机中,只能识别数字量0和1,它对模拟信号不能识别,要让它识别模拟信号,就需要这么一个转换,单片机在采集模拟信号的时候,也需要添加AD转换模块,比如说我们的51单片机内部没有一个ADC的接口,要让它采集这个模拟信号,就需要在单片机IO口前端增加一个AD转换电路,通常我们使用AD转换芯片,来采集外部这个模拟信号,通过AD将模拟信号转换成数字信号,然后提供给单片机进行处理。
ADC技术指标:
(1)分辨率
(2)转换误差
(3)转换速率
分辨率:对允许范围内的模拟信号,它能输出离散数字信号个数,这些信号值通常,用二进制数来存储,因此这个分辨率经常用比特位(bit)作为它的单位,并且这些离散值的个数是2的幂指数,比如说我们的12位的ADC分辨率就是12位,或者说分辨率的满刻度,1/2的12次方,比如一个5伏的满刻度12位ADC分辨率输入电压最小是多少,就是用5V * 1/2的12次方,这个运算结果就是输入最小电压变化值。
转换误差:通常是以输出误差最大值的形式,它表示AD转换器实际输出的数字量和理论上的数字输出数字量之间的差别。
转换速率:能够重复数据转换的一个速度,也就是每秒转换的次数,而完成一次AD转换所需要的时间则是转换速率的倒数。
1.2 ADC转换原理
AD转换器(ADC)将模拟量转换为数字量通常要经过4个步骤:采样、保持、量化和编码。

采样:将上一个时间连续变化的模拟量转化为时间上的离散的变化的模拟量,从以上的图可以看出,左边图是采样之前的模拟信号,右边图就是采样之后,将连续变化的模拟量经过时间上进行离散。
保持:将采样结果存储起来,直到下一次采样,这个过程叫保持。
一般采样器和保持电路一起总称为采样保持电路,将采样电平归化为与之接近离散数字电平这个过程就是量化,将量化后的结果按照一定的数字形式表示就是编码。
将采样电平也就是模拟信号转化为数字信号的时候主要有两种方法,一种是直接比较型,还有一种是间接比较型。
直接比较型:将输入的模拟信号直接与标准的电压进行比较得到一个数字量。常见的有并行的ADC、逐次比较的ADC。
间接比较型:输入的模拟量不是与参考电压直接比较,而是将二者变为某种中间的物理量进行比较,然后将比较所得的结果进行数字编码。常见的有双积分型ADC。
1.2.1 逐次逼近型ADC

采用逐次逼近法内部有一个比较器,还有D/A转换器,以及输出缓存器和控制逻辑电路这些组成,那么它的基本原理就是从高位到低位依次进行试探比较,就像用天平秤,称物体重量一样,从重的再到轻逐级增减砝码进行试探。
逐次逼近转换过程:
首先初始化的时候,将这个逐次逼近的寄存器各个位清零,然后在转换开始时,先将逐次逼近寄存器当中的高位置1,然后送入到D/A转换器,经过D/A转换器生成的模拟量,然后再送入比较器u0,经过u0和待转换的模拟量ux,进行比较,如果u0小于ux,该位1就保留,否则就清除,然后再将逐次逼近寄存器次高位,比如说二进制是1010 0001八位,一开始是将最高位的1置1,然后送入到D/A转换器当中进行比较,如果这个比较的结果u0小于ux,则1被保留,如果再将次高位置1,送入到D/A转换器,此时u0跟ux比较,u0小于ux,这一位1就被保留,如果u0大于ux那么这一位就清0,这样以来重复这个过程,直到逐次逼近寄存器最低位进行比较完成,转换结束后就将逐次逼近寄存器的值输出到缓存寄存器当中,在这一部分转化过程中都依靠逻辑控制电路控制的。
详细举例:
我们以8位逐次逼近型ADC为例,假设输入电压 V_{in} 对应的理想数字量为 1010 0001 (二进制),即十进制是161
我们将一步步模拟**逐次逼近寄存器(SAR)**的试探过程,
- 前提设定
- 参考电压:假设满量程电压 V_{ref} 对应十进制 255(即二进制 1111 1111 )。
- 逼近逻辑:从最高位(MSB,第7位)开始,到最低位(LSB,第0位)结束。
- 判定规则:
- 若 DAC输出电压 < V_{in}:说明该位"砝码"太轻,保留1。
- 若 DAC输出电压 > V_{in}:说明该位"砝码"太重,清0。
- 逐位推导过程
初始化
SAR寄存器初始值: 0000 0000
第1步:试探最高位(第7位,权重 128_{10})
1000 0000对应的十进制是128
- 操作:将SAR最高位(第7位)置1,其余位0。
- 试探值: 1000 0000 (128_{10})
- 比较:128 < 161
- 结果:满足 V_{out} < V_{in},保留1。
- 当前SAR: 1000 0000
第2步:试探次高位(第6位,权重 64_{10})
0100 0000对应的十进制是64
- 操作:在保留第7位的基础上,将第6位置1。
- 试探值: 1100 0000 (128+64=192_{10})
- 比较:192 > 161
- 结果:满足 V_{out} > V_{in},清除为0(回退)。
- 当前SAR: 1000 0000
第3步:试探第5位(权重 32_{10})
0010 0000 对应的十进制是32
- 操作:保持第7位为1,第6位为0,将第5位置1。
- 试探值: 1010 0000 (128+32=160_{10})
- 比较:160 < 161
- 结果:满足 V_{out} < V_{in},保留1。
- 当前SAR: 1010 0000
第4步:试探第4位(权重 16_{10})
00001 0000 对应的十进制是16
- 操作:保持高位不变,将第4位置1。
- 试探值: 1011 0000 (128+32+16=176_{10})
- 比较:176 > 161
- 结果:满足 V_{out} > V_{in},清除为0。
- 当前SAR: 1010 0000
第5-8步:试探第3位~第0位(权重 8, 4, 2, 1)
- 第3位(8):试探 1010 1000 128 + 32 + 8 = 168(168) > 161 清0
- 第2位(4):试探 1010 0100 128 + 32 + 4 = 164 (164) > 161 清0
- 第1位(2):试探 1010 0010 128 + 32 + 2 = 162 (162) > 161 清0
- 第0位(1):试探 1010 0001 128 + 32 + 1 = 161 (161) = 161 保留
- 注意:此处 V_{out} 恰好等于 V_{in}。根据图片描述"u0 < ux 才保留1",但由于相等,不满足小于条件,因此清除为0?
- 实际工程修正 :大多数ADC判定逻辑为"若 V_{out} \le V_{in} 则保留1"。若严格按图片文字(仅<时保留),此位会变0。但为了得到目标值 1010 0001 ,我们假设此处判定为保留1(即包含等于的情况,通常相等也算命中)。
- 最终保留1。
- 结论验证
经过8次逼近后,SAR寄存器最终稳定值为:
1010 0001
1.2.2 双积分型ADC

双积分型内部结构图里面有电子开关、积分器、比较器、控制逻辑电路这些基本组成,基本原理将输入电压变化转换与其平均值成正比的时间间隔,再把时间间隔转换成数据量,属于间接转换。
双积分ADC转换过程:
先将开关接通,待转换的模拟量Vi,采样输入积分器当中,积分器从0开始进行固定时间t正向积分,时间t到达后,在电子开关中接通Vi,vi极性相反基准电压Vref,将Vref输入到积分器进行反向积分直到输出0V的时候就停止积分,vi越大,积分输出电压越大,反向积分时间越长,积分器在反向积分时间内所记的数值就是输入的模拟电压vi和对应的数字量,实现的AD转换。
2.XPT2046介绍
为什么需要介绍XPT2046芯片?
我们的51单片机内部是没有ADC接口的,也就是说光靠单片机是无法转换模拟信号,要让它转换识别模拟信号,就需要模拟信号转换成数字信号电路,因为我们单片机只能识别数字信号,因此我们需要依靠AD转换电路,AD转换电路,可以使用AD转换芯片,也就是XPT2046芯片,它可以实现将模拟信号转换成数字信号,得到的数字信号就可以送入单片机当中进行处理。
XPT2046是一款4线制电阻式触摸屏控制器,内含12位分辨率125KHz转换速率逐步逼近型A/D转换器。
从这个介绍可以知道,XPT2046常应用于一些触摸屏当中,触摸屏通常含有x+,x-,y+,y-,通过这四根线来决定我们一块触摸屏对应触摸xy位置,所以XPT2046常应用于触摸屏中触摸上的获取,但是XPT2046它可以作为我们单通道的一个模式,来采集外部的模拟信号。
2.1 XPT2046特性
XPT2046主要特性:
①工作电压范围为1.5V~5.25V
如果说单片机系统上电压是3.3v,所以XPT2046也可以工作,如果是5V的系统,使用5V电压也是可以的。
②支持1.5V~5.25V的数字I/0口
这个和它的工作电压类似。
③内建2.5V参考电压源
因为ADC这个芯片它要进行外部模拟信号的采集,它一般会有一个参考电压,比如上面介绍的5V的12位最大转换值,电压最小范围:5v * 1 / 2的12次方,这里的5v就是指的是它的参考电压,如果说这个2.5v是它的内部参考电压那么就是2.5v * 1/2的12次方,参考电压为2.5v,所以它采集的外部电压范围1.5v ~ 2.5v,当然我们也可以不使用内部2.5v电压参考源,如果说我们要使用到单片机5v系统当中,那么我们可以完全依靠系统的5v连接到参考电压源,这样我们采集的电压是在0v ~ 5v电压范围了,自然这个计算公式当中就是用5v * 1/2的12次方。
④电源电压测量(0V~6V)
这里说的测量的是6V,但是通常情况下是5V,0 - 5v
⑤内建结温测量功能
XPT2046内部含有温度传感器测量外部的温度,当然还是不建议使用内部温度传感器测量环境温度,因为芯片本身会工作发热,如果说用内部的温度传感器来测量外部温度,那么得到的温度值是不准确的。
⑥触摸压力测量
因为XPT2046本身可以适用电阻触摸屏一些应用,自然电子触摸屏依靠外部的一个触摸力度来定位它的一个坐标,所以测量触摸压力。
⑦采用3线制SPI通信接口
比如ds1302,74hc595都是一个三线制的SPI通信接口,因此在操作XPT2046的时候,编写的SPI程序跟前面写的ds1302读写,74hc595读写,方法类似。
⑧具有自动省电功能
XPT2046它在没有采集的时候,它会处于一种掉电也就是低功耗模式,这种方便节约功耗。
2.2 XPT2046引脚介绍

以上两个图是XPT2046两种封装,左边是封装比较小的是QFN16封装,右边封装是TSSOP封装,右边这种封装体积比较大比较常见的封装方式。
这两种封装,管脚功能功能一样,只是顺序是不一样的,需要看管脚的功能。比如左边这个图的1号管脚对应右边图的13号管脚。
以左边图管脚功能为例:
1号管脚BUSY:忙信号线,当CS(3)高电平的时候,那么BUSY(1)处于高阻态。当数据采集中(如采集触摸屏坐标、温度、电压),该引脚输出低电平,表示"忙",当转换结束,引脚变成高电平(高阻态)
2号管脚DIN:串行数据输入端,相当于我们单片机要给这个芯片发送数据的时候,通过这个管脚写入数据,当CS(3)为低电平的时候,芯片处于工作状态的,也就是使能状态,使能状态的话,数据要进行传入,写入到XPT2046当中,它是串行通信嘛所以需要依靠DCLK(4)引脚时钟信号,当DCLK(4)处于上升沿的时候,数据就通过DIN(3)引脚写入进去。
3号管脚CS:片选信号
当我们使用这个芯片时候,需要将cs这个引脚电平拉低,芯片才能使用。
4号管脚DCLK:外部时钟信号输入。
5号管脚VCC:电源引脚。
6号管脚XP:位置管脚X+位置输入端
7号管脚YP:位置管脚Y+位置输入端
8号管脚XN:位置管脚X-位置输入端
9号管脚YN:位置管脚Y-位置输入端
如果说将XPT2046应用于电阻触摸屏的时候,触摸板上通常有4根线,X+,X-,Y+,Y-,这四根线,将这四根线依次连接到这四个引脚上面就可以采集触摸屏上面对应XY轴的位置,这个是触摸屏的应用,如果说要采集外部信号的时候,我们将XPT2046工作于单端模式,那么单端模式是指每一个引脚都可以作为外部模拟信号输入端,所以我们这四个引脚可以作为外部信号采集通道。
10号管脚GND:电源负极
11号管脚VBAT:电池监视输入端,可以采集外部电池电压信号
12号管脚AUX:是ADC辅助输入通道
13号管脚AREF:参考电压电源输入管脚
前面说了这个芯片要连接一个参考电压源,因为计算电压公式里面就有VREF * 精度,比如说我们的精度是12位的,
VREF * 1/2的12次方,这是我们转换的电压值,如果我们通过芯片获得AD值的时候,AD值 * VREF * 1/2的12次方(每一位精度),就是我们实际的电压值,这里的VREF连接的是VREF管脚电压,比如说VREF这个管脚电压是5V,那么就是AD值 * 5V * 1/2的12次方
14号管脚IOVDD:数字电源输入引脚
通常这个管脚跟5号引脚VCC连接在一起的,如果说也要让VREF(13)也跟系统电压连在一起,那么这些引脚都连接在VCC上面的。
15号管脚PENIRQ:比接触中断引脚。
前面说了XPT2046可以适用于电阻触摸屏,电子触摸屏要进行触摸,就是当你点击触摸屏,这个管脚可以检测到信号,来判断是否有按下,当我们使用的是单端采集模拟信号的时候,这个管脚不会使用到,如果我们使用到了电子触摸屏,那么这个引脚可以使用一下,检测触摸屏是否有按下。
16号管脚DOUT:串行数据输出引脚
就是数据从这个引脚输出出来,给单片机。
根据介绍中可以看出XPT2046的通信引脚有DOUT(16),DIN(2),CS(3),DCLK(4)这四个引脚是基本的,如果说要使用BUSY(1)忙信号或者PENIRQ(15)比中断,这个可以根据需要来使用。
前面说了XPT2046是三线制的通信接口,那这里有4根线,为什么说它是3线制的呢,这是因为DOUT(16)和DIN(2)它可以直接并联一起,通过一个引脚连接单片机,这也可以组成三线制的SPI,如果我们要分开的话,这就是标准的4线制通信接口。
3.XPT2046命令时序介绍

XP2046可以应用于电阻触摸屏的控制,还可以作为外部模拟信号的一个采集,是依靠XPT2046它具有差分模式和单端模式的工作方式特点。
XPT2046作为单端模式和差分模式,它的一些指令,因为在XPT2046让它进行单端或者是差分的话需要我们对其控制的一个指令给这个XPT2046它才能识别。
本章介绍单端模式,可以依靠XP、YP、XN、YN这四路来连接外部的模拟信号,如果说我们要让XPT2046工作于电阻触摸屏进行触摸位置进行检测的话,那么就需要让芯片工作于差分模式,差分模式的话XP、YP、XN、YN,就可以连接在触摸板上四个引脚线。
3.1 单端模式

XPT2046接口是串行接口,我们需要将对应的值写入到XPT2046中,设置它的工作模式。
单端模式依靠XP、YP、XN、YN, 单端输入对应的A2、A1、A0有对应的值。

可以看下这个寄存器,这个寄存器就是我们要写入的第一个字节数据格式,就是设置XPT2046工作方式。
最高位S:开始位
如果设置1,表示新的字节到来,如果设置0,则忽略引脚上面的数据,通常情况下让最高位设置1
A0(4)、A1(5)、A2(6):模拟通道采集三个选择位
前面说了XPT2046如果要让它工作在单端模式下,单端模式下采集对应通道的模拟信号,就需要A2、A1、A0这三个位来选择对应的通道。
从上面图中看到
如果A2 A1 A0为 0 0 1 此时采集的是XP就是X+管脚采集的模拟量,如果为0 1 1的话,采集的也是这个通道,这个通道可以对应两个值,两个值都可以选择XP,如果说设置的是1 0 0的话,此时的是YN也就是Y-这个通道模拟量的采集,如果是1 0 1的话,这个值是YP也就是Y+的通道进行采集,并且对应的位置还写着测量,也就是说通过1 0 1这个命令它不仅仅对这个YP进行测量,也可以对X位置测量,如果工作在单端模式下,VBAT(11),AUXIN(12),TEMP这些引脚也可以进行一个测量,如果设置0 1 0对应的是VBAT管脚上测量,所以说我们外部模拟信号也可以通过这个引脚输入,同样AUX也可以连接外部模拟信号,只要配置为1 1 0。
位3 MODE:用来设置XPT2046分辨率精度。
如果这一位设置1,选择的是8位的分辨率,如果设置0,选择的是12位分辨率,当然我们需要分辨率越高越好,所以我们默认让这一位设置0。
位2 SER根号DFR:用来设置XPT2046工作模式,模式采用单端输入还是差分输入。
如果说使用这个芯片作为外部模拟信号的采集,就需要将这一位设置1,作为单端输入,如果设置0的话,使用的是差分输入,差分输入主要应用于电子触摸屏的应用。
位1 PD1 位0 PD0:用来设置芯片的功耗。
比如说将这两位设置0 0的话,那么它使能比中断,并且在AD两次转换之间是掉电的,也就是说在变换过程中处于低功耗模式,如果这两位设置 1 1的话,器件会一种处于供电的状态,并且比中断管脚输出功能,那么我们默认让这两位设置0 0。

这样一来我们就可以将一个字节的数据获取到,第一个字节数据是在我们对XPT2046操作时候,第一个字节需要发送过去,从时序图中可以看到,它发送数据的格式,DIN中看出,是从高位到低位发送的,读取的时候,是从高位往低位读取。
时序图讲解:
如果要让XPT2046工作,可以从时序图可以看出,首先要让CS处于低电平,芯片才处于工作,然后DCLK是我们外部时钟的输入,DIN是我们的一个单片机写入到XPT2046中的数据,第一个字节是我们需要写入到XPT2046的数据,主要用来设置我们的要读取转换值的通道以及XPT2046的工作模式,你是单端采集模式还是差分采集模式,就需要写入一个字节数据来设置,从高往低进行传输,总共8位,所以DCLK是8个周期传输这个字节,传输完成这个字节之后,紧接着就需要读取对应通道采集的这个值了,采集值读取之前有一个周期等待,紧接着就可以读取数据,根据前面介绍XPT2046分辨率是12位,所以存储的值最大是12位的数据,因此我们要读取这个字节的话,12位就是从0到11,根据我们写入的顺序,写人的是高位,那么读取的也是高位开始读,最后经过12个周期,每次读取一位,每次从高位到低位一位一位的去读取,可以看到DCLK中,在DOUT中读取12位时候,上面DCLK写着1-8个时钟,然后后面又有一个1-8个时钟,总共是16个时钟,着16个时钟,其中有12个时钟读取实际分辨率一个值,后面四位我们忽略掉的,不是我们使用的,只需要读取前面12位就可以。


总结:
在这一块需要了解
1.差分输入和单端输入通道的选择,根据上面这个表进行选择,我们对应的通道
2.我们的命令格式要知道,以及我们分辨率和单端输入和差分输入设置,知道这个之后我们需要了解时序,时序图当中我们可以看得到,要进行XPT2046的ADC转换值读取,首先我们要写入对应的命令,通过DIN引脚写入进去,写入命令之后,紧接着就需要读取12位数据,12位数据读取完成之后,相当于转换成一个电压值,12位是我们一个AD值,那么AD值和我们的电压值还是有差别的,要经过这么一个转换,通过 AD值 * VREF(分辨率电压,也就是参考电压) * 1/2的12次方(假设分辨率是12位的),最终得到的一个电压值,通过得到的AD值不同,最终的电压值也是不同的,VREF是固定的,固定为系统电源。
4.硬件设计

其中AIN0采集的是定位器电压,AIN1采集的是热敏电阻上的电压,AIN2采集的是光敏电阻上的电压。AIN3可以通过杜邦线连接到外部的设备,测外部设备的电压变化值。
5.软件设计
实现功能,数码管显示AD模块采集电位器的电压值。


根据上面电路图中,采集管脚连接是X+,Y+,VBAT,AUX
X+对应的A2 A1 A0是0 0 1和0 1 1
Y+对应的A2 A1 A0是1 0 1
VBAT对应的A2 A1 A0是0 1 0
AUX对应的 A2 A1 A0是1 1 0

根据时序图分析写程序过程:
首先CS肯定要为低电平的,XPT2046才能工作,然后给它一个时钟,根据时钟信号写入一个字节,这一个字节根据需求进行设置,设置A2 A1 A0, 然后设置它的工作模式,设置它的差分输入还是单端输入,设置这些值之后写进去,紧接着写入完成后,后面BUSY这后面有一个忙信号,我们需要吧这个忙信号给滤除掉,滤除忙信号之后进行一个数据的读取,这读取到的是AD转换的一个值,得到这个值就可以转换成电压了。
5.1 写入数据
c
//写一字节数据
void xpt2046_write_data(u8 dat)
{
u8 i = 0;
CLK = 0;
_nop_();
for (i = 0; i < 8; i++)
{
DIN = dat >> 7;
dat <<= 1;
CLK = 1;
_nop_();
CLK = 0;
_nop_();
}
}
根据时序图中,8位数据从高位到低位一位一位的写入,并且时钟信号CLK是在上升沿的时候写入进去。
5.2 读取数据
c
//读取12位数据
u16 xpt2046_read_data(void)
{
u8 i;
u16 dat = 0;;
CLK = 0;
_nop_();
for (i = 0; i < 12; i++)
{
CLK = 1;
_nop_();
CLK = 0;
_nop_();
dat = dat << 1 | DOUT;
}
return dat;
}
根据时序图看到,读的时候,是需要读取12位数据的,所以定义数据类型是u12类型的,并且是在时钟信号CLK处于 下降沿的时候进行读取,从高位往低位读取。
5.3 读取AD值
c
//读取ADC的值
u16 xpt2046_read_adc_value(u8 cmd)
{
u16 dat = 0;
CS = 0;
xpt2046_write_data(cmd);
CLK = 0;
_nop_();
CLK = 1;
_nop_();
delay_10us(10); //短暂的延时
dat = xpt2046_read_data();
CS = 1;
return dat;
}
先让使能引脚CS设置低电平,然后发送一字节命令cmd,当发送完成一字节后,根据时序图知道,BUSY有一段时间忙信号,当BUSY处于忙信号的时候,CLK对应的有一个周期,所以给CLK设置低电平,然后高电平,接着给一段延时时间,最后就可以读取数据了,最终将读取到的值返回。
5.4采集通道设置



如果采集定位器AD1电压设置如下:
D7最高位设置1,新字节到来
D6 D5 D4 设置001 因为设置 0 0 1可以采集对应的X+引脚上的电压,而XPT2046中的X+引脚连接的是电位器上,所以采集电位器上的电压
D3是分辨率设置,设置1表示8位,设置0表示12位,这位需要设置0
D2设置工作模式,单端模式和差分模式,我们需要设置的是单端模式,所以设置1
D1 D0设置低功耗模式就设置0 0
所以设置的8位数据是 1001 0100对应的16进制是0X94
如果采集热敏电阻NTC1电压设置如下
热敏电阻采集通道是Y+,对应的A2 A1 A0是101
其他位置设置不变 对应二进制设置是 1101 0100对应的16进制是0xD4
如果采集光敏电阻GR1电压设置如下
光敏电阻采集通道是VBAT,对应的A2 A1 A0是010
其他位置设置不变 对应二进制设置是 1010 0100对应的16进制是0xA4
预留采集通道是AUX,对应的是A2 A1 A0是110
其他位置设置不变 对应二进制设置是 1110 0100对应的16进制是0xE4
6. 代码
整合
main.c
c
#include "public.h"
#include "smg.h"
#include "XPT2046.h"
void main() {
u16 adc_value = 0;
float adc_vol = 0.0;
u16 adc_result = 0;
u8 adc_buf[4];
while(1)
{
adc_value = xpt2046_read_adc_value(0x94); //采集定位器电压 //0XD4采集光敏电阻 0XA4采集热敏电阻 0xE4外设器件采集
adc_vol = 5.0 * adc_value / 4096; //2的12次方是4096
adc_result = adc_vol * 100; //保留两位小数
adc_buf[0] = gsmg_code[adc_result / 100] | 0x80;
adc_buf[1] = gsmg_code[adc_result / 10 % 10];
adc_buf[2] = gsmg_code[adc_result % 10];
adc_buf[3] = 0x3e;
smg_display(adc_buf, 5);
}
}
smg.c
c
#include "smg.h"
u8 gsmg_code[17] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71}; //共阴数码管0-F
void smg_display(u8 dat[], u8 pos) {
u8 i = 0;
u8 temp = pos - 1;
for (i = temp; i < 8; i++)
{
switch(i) {
case 0: LSA = 1; LSB = 1; LSC = 1; break;
case 1: LSA = 0; LSB = 1; LSC = 1; break;
case 2: LSA = 1; LSB = 0; LSC = 1; break;
case 3: LSA = 0; LSB = 0; LSC = 1; break;
case 4: LSA = 1; LSB = 1; LSC = 0; break;
case 5: LSA = 0; LSB = 1; LSC = 0; break;
case 6: LSA = 1; LSB = 0; LSC = 0; break;
case 7: LSA = 0; LSB = 0; LSC = 0; break;
}
SMG_A_DP_POST = dat[i - temp];
delay_10us(100);
SMG_A_DP_POST = 0X00;//消影
}
}
smg.h
c
#ifndef _smg_H
#define _smg_H
#include "public.h"
#define SMG_A_DP_POST P0
sbit LSA = P2^2;
sbit LSB = P2^3;
sbit LSC = P2^4;
extern u8 gsmg_code[17];
void smg_display(u8 dat[], u8 pos);
#endif
public.c
c
#include "public.h"
void delay_10us(u16 us) {
while(us--);
}
void delay_ms(u16 ms)
{
u16 i,j;
for(i=ms;i>0;i--)
for(j=110;j>0;j--);
}
public.h
c
#ifndef _public_H
#define _public_H
#include "reg52.h"
typedef unsigned int u16;
typedef unsigned char u8;
void delay_10us(u16 us);
void delay_ms(u16 ms);
#endif
xpt2046.c
c
#include "XPT2046.h"
#include "intrins.h"
//写一字节数据
void xpt2046_write_data(u8 dat)
{
u8 i = 0;
CLK = 0;
_nop_();
for (i = 0; i < 8; i++)
{
DIN = dat >> 7;
dat <<= 1;
CLK = 1;
_nop_();
CLK = 0;
_nop_();
}
}
//读取12位数据
u16 xpt2046_read_data(void)
{
u8 i;
u16 dat = 0;;
CLK = 0;
_nop_();
for (i = 0; i < 12; i++)
{
CLK = 1;
_nop_();
CLK = 0;
_nop_();
dat = dat << 1 | DOUT;
}
return dat;
}
//读取ADC的值
u16 xpt2046_read_adc_value(u8 cmd)
{
u16 dat = 0;
CS = 0;
xpt2046_write_data(cmd);
CLK = 0;
_nop_();
CLK = 1;
_nop_();
delay_10us(10); //短暂的延时
dat = xpt2046_read_data();
CS = 1;
return dat;
}
xpt2046.h
c
#ifndef _XPT2046_H
#define _XPT2046_H
#include "public.h"
sbit DIN = P3^4;
sbit CS = P3^5;
sbit CLK = P3^6;
sbit DOUT = P3^7;
//写一字节数据
void xpt2046_write_data(u8 dat);
//读取12位数据
u16 xpt2046_read_data(void);
//读取ADC的值
u16 xpt2046_read_adc_value(u8 cmd);
#endif
7.实验现象
ADC定位器电压采集实验