51单片机应用开发---ADC详解(基于XPT2046 芯片)

实现目标

1、掌握ADC工作原理

2、掌握单片机驱动XPT2046 芯片

3、具体目标: 1、将单片机采集到的电压值在数码管显示; 2、将单片机采集到的电压值在LCD1602显示;3、将单片机采集到的电压值上传至上位机串口调试助手显示。


一、ADC概述

1.1 什么是ADC?

ADC(Analog to Digital Converter)即模数转换器,用来将模拟信号转换为数字信号。

A/D转换过程

1.2 ADC 的主要技术指标

1.2.1 分辨率

ADC 的分辨率是指对于允许范围内的模拟信号,它能输出离散数字信号值的 个数。这些信号值通常用二进制数来存储,因此分辨率经常用比特作为单位,且这些离散值的个数是 2 的幂指数。 例如:12 位 ADC 的分辨率就是 12 位,或者说分辨率为满刻度的 1/(2^12)。 一个 10V 满刻度的 12 位 ADC 能分辨输入电压变化最小值是 10V×1/(2 12 )=2.4mV。

1.2.2 转换误差

转换误差通常是以输出误差的最大值形式给出。它表示 A/D 转换器实际输出 的数字量和理论上的输出数字量之间的差别。常用最低有效位的倍数表示。例如 给出相对误差≤±LSB/2,这就表明实际输出的数字量和理论上应得到的输出数 字量之间的误差小于最低位的半个字。

1.2.3 转换速率

ADC 的转换速率是能够重复进行数据转换的速度,即每秒转换的次数。而完 成一次 A/D 转换所需的时间(包括稳定时间),则是转换速率的倒数。

1.3 ADC 转换原理

AD 转换器(ADC)将模拟量转换为数字量通常要经过 4 个步骤:采样、保持、 量化和编码。所谓采样即是将一个时间上连续变化的模拟量转换为时间上离散变 化的模拟量。如下图所示:

将采样结果存储起来,直到下次采样,这个过程叫做保持。一般采样器和保 持电路一起总称为采样保持电路。

将采样电平归化为与之接近的离散数字电平, PRECHIN 普中 www.prechin.net 336 这个过程叫做量化

将量化后的结果按照一定数制形式表示就是编码

将采样电 平(模拟值)转换为数字值时,主要有两类方法:直接比较型与间接比较型

  1. **直接比较型:**就是将输入模拟信号直接与标准的参考电压比较,从而得到 数字量。常见的有并行 ADC 和逐次比较型 ADC。
  2. **间接比较型:**输入模拟量不是直接与参考电压比较,而是将二者变为中间 的某种物理量在进行比较,然后将比较所得的结果进行数字编码。常见的有双积 分型 ADC。

二、XPT2046 芯片介绍

XPT2046 是一款 4 线制电阻式触摸屏控制器,内含 12 位分辨率 125KHz 转换速率逐步逼近型 A/D 转换器。XPT2046 支持从 1.5V 到 5.25V 的低电压 I/O 接口。XPT2046 能通过执行两次 A/D 转换查出被按的屏幕位置,除此之外, 还可以测量加在触摸屏上的压力。内部自带 2.5V 参考电压,可以作为辅助输入、 温度测量和电池监测之用,电池监测的电压范围可以从 0V 到 6V。XPT2046 片 内集成有一个温度传感器。在 2.7V 的典型工作状态下,关闭参考电压,功耗可 小于 0.75mW。XPT2046 采用微小的封装形式:TSSOP-16,QFN-16 和 VFBGA-48。 工作温度范围为-40℃~+85℃。与 ADS7846、TSC2046、AK4182A 完全兼容。

2.1主要特性

①工作电压范围为 1.5V~5.25V

②支持 1.5V~5.25V 的数字 I/O 口

③内建 2.5V 参考电压源

④电源电压测量( 0V~6V)

⑤内建结温测量功能

⑥触摸压力测量

⑦采用 3 线制 SPI 通信接口

⑧具有自动省电功能

2.2 芯片管脚说明

2.3 单端和差分模式输入配置

X、Y、Z、VBAT、Temp 和 AUX 模拟信号经过片内 的控制寄存器选择后进入 ADC,ADC 可以配置为单端或差分模式。选择 VBAT、Temp 和 AUX 时应该配置为单端模式;作为触摸屏应用时,应该配置为差分模式,这可 有效消除由于驱动开关的寄生电阻及外部的干扰带来的测量误差,提高转换精度。

2.4 XPT2046 芯片时序分析

XPT2046 数据接口是串行接口,其典型工作时序如下图所示,图中展示的信号来自带有基本串行接口的单片机或数据信号处理器。处理器和转换器之间的的 通信需要 8 个时钟周期,可采用 SPI、SSI 和 Microwire 等同步串行接口。一 次完整的转换需要 24 个串行同步时钟(DCLK)来完成。

前 8 个时钟用来通过 DIN 引脚输入控制字节。当转换器获取有关下一次转 换的足够信息后,接着根据获得的信息设置输入多路选择器和参考源输入,并进 入采样模式,如果需要,将启动触摸面板驱动器。3 个多时钟周期后,控制字节 设置完成,转换器进入转换状态。这时,输入采样-保持器进入保持状态,触摸 面板驱动器停止工作(单端工作模式)。接着的 12 个时钟周期将完成真正的模 数转换。如果是度量比率转换方式(SER/DFR=0),驱动器在转换过程中将一直 工作,第 13 个时钟将输出转换结果的最后一位。剩下的 3 个多时钟周期将用 来完成被转换器忽略的最后字节(DOUT 置低)

在对 XPT2046 进行控制时,控制字节由 DIN 输入的控制字命令格式如下所 示:

三、原理图设计

从上图中可知,XPT2046 芯片的控制管脚接至单片机 P3.4~P3.7 管脚上,XPT2046 芯片的ADC 输入转换通道分别接入了 AD1 电位器、 NTC1 热敏传感器、GR1 光敏传感器,还有一个外接通道 AIN3 接在 DAC(PWM)模块的 J52 端子上供外部模拟信号检测。

注意:上图中 XPT2046 的 DOUT 脚与 DS18B20 温度传感器均连接到单片机的 P3.7,因此该两个外设资源不能同时使用,可以分时复用。

四、程序设计

4.1电位器电压采集(数码管显示)

实现的功能是:数码管上显示 AD 模块采集电位器的电压值。 程序框架如下: (1)编写数码管显示功能 (2)编写 ADC 转换函数 (3)编写主函数

(1)public.h 文件

cpp 复制代码
#ifndef _public_H
#define _public_H

#include "reg52.h"

typedef unsigned int u16;	//对系统默认数据类型进行重定义
typedef unsigned char u8;
typedef unsigned long u32;

void delay_10us(u16 ten_us);
void delay_ms(u16 ms);

#endif

(2)public.c 文件

cpp 复制代码
#include "public.h"

/*******************************************************************************
* 函 数 名       : delay_10us
* 函数功能		 : 延时函数,ten_us=1时,大约延时10us
* 输    入       : ten_us
* 输    出    	 : 无
*******************************************************************************/
void delay_10us(u16 ten_us)
{
	while(ten_us--);	
}

/*******************************************************************************
* 函 数 名       : delay_ms
* 函数功能		 : ms延时函数,ms=1时,大约延时1ms
* 输    入       : ms:ms延时时间
* 输    出    	 : 无
*******************************************************************************/
void delay_ms(u16 ms)
{
	u16 i,j;
	for(i=ms;i>0;i--)
		for(j=110;j>0;j--);
}

(3)xpt2046.h 文件

cs 复制代码
#ifndef _xpt2046_H
#define _xpt2046_H

#include "public.h"

//管脚定义
sbit DOUT = P3^7;	  //输出
sbit CLK  = P3^6;	  //时钟
sbit DIN  = P3^4;	  //输入
sbit CS   = P3^5;	  //片选


//函数声明
u16 xpt2046_read_adc_value(u8 cmd);

#endif

(4)xpt2046.c 文件

cpp 复制代码
#include "xpt2046.h"
#include "intrins.h"

/*******************************************************************************
* 函 数 名       : xpt2046_wirte_data
* 函数功能		 : XPT2046写数据
* 输    入       : dat:写入的数据
* 输    出    	 : 无
*******************************************************************************/
void xpt2046_wirte_data(u8 dat)
{
	u8 i;

	CLK = 0;
	_nop_();
	for(i=0;i<8;i++)//循环8次,每次传输一位,共一个字节
	{
		DIN = dat >> 7;//先传高位再传低位
		dat <<= 1;//将低位移到高位
		CLK = 0;//CLK由低到高产生一个上升沿,从而写入数据
		_nop_();	
		CLK = 1;
		_nop_();
	}
}

/*******************************************************************************
* 函 数 名       : xpt2046_read_data
* 函数功能		 : XPT2046读数据
* 输    入       : 无
* 输    出    	 : XPT2046返回12位数据
*******************************************************************************/
u16	xpt2046_read_data(void)
{
	u8 i;
	u16 dat=0;

	CLK = 0;
	_nop_();
	for(i=0;i<12;i++)//循环12次,每次读取一位,大于一个字节数,所以返回值类型是u16
	{
		dat <<= 1;
		CLK = 1;
		_nop_();
		CLK = 0; //CLK由高到低产生一个下降沿,从而读取数据
		_nop_();
		dat |= DOUT;//先读取高位,再读取低位。	
	}
	return dat;	
}

/*******************************************************************************
* 函 数 名       : xpt2046_read_adc_value
* 函数功能		 : XPT2046读AD数据
* 输    入       : cmd:指令
* 输    出    	 : XPT2046返回AD值
*******************************************************************************/
u16 xpt2046_read_adc_value(u8 cmd)
{
	u8 i;
	u16 adc_value=0;

	CLK = 0;//先拉低时钟
	CS  = 0;//使能XPT2046
	xpt2046_wirte_data(cmd);//发送命令字
	for(i=6; i>0; i--);//延时等待转换结果
	CLK = 1;
	_nop_();
	CLK = 0;//发送一个时钟,清除BUSY
	_nop_();
	adc_value=xpt2046_read_data();
	CS = 1;//关闭XPT2046
	return adc_value;
}

(5)smg.h 文件

cpp 复制代码
#ifndef _smg_H
#define _smg_H

#include "public.h"


#define SMG_A_DP_PORT	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

(6)smg.c 文件

cpp 复制代码
#include "smg.h"

//共阴极数码管显示0~F的段码数据
u8 gsmg_code[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
				0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};

/*******************************************************************************
* 函 数 名       : smg_display
* 函数功能		 : 动态数码管显示
* 输    入       : dat:要显示的数据
				   pos:从左开始第几个位置开始显示,范围1-8
* 输    出    	 : 无
*******************************************************************************/
void smg_display(u8 dat[],u8 pos)
{
	u8 i=0;
	u8 pos_temp=pos-1;

	for(i=pos_temp;i<8;i++)
	{
	   	switch(i)//位选
		{
			case 0: LSC=1;LSB=1;LSA=1;break;
			case 1: LSC=1;LSB=1;LSA=0;break;
			case 2: LSC=1;LSB=0;LSA=1;break;
			case 3: LSC=1;LSB=0;LSA=0;break;
			case 4: LSC=0;LSB=1;LSA=1;break;
			case 5: LSC=0;LSB=1;LSA=0;break;
			case 6: LSC=0;LSB=0;LSA=1;break;
			case 7: LSC=0;LSB=0;LSA=0;break;
		}
		SMG_A_DP_PORT=dat[i-pos_temp];//传送段选数据
		delay_10us(100);//延时一段时间,等待显示稳定
		SMG_A_DP_PORT=0x00;//消音
	}
}

(7)main.c 文件

cpp 复制代码
/**************************************************************************************
实验名称:ADC模数转换实验--电位器电压采集	
实验现象:下载程序后,数码管上显示AD模块采集电位器的电压值																			  
***************************************************************************************/
#include "public.h"
#include "smg.h"
#include "xpt2046.h"


/*******************************************************************************
* 函 数 名       : main
* 函数功能		 : 主函数
* 输    入       : 无
* 输    出    	 : 无
*******************************************************************************/
void main()
{	
	u16 adc_value=0;
	float adc_vol;//ADC电压值
	u8 adc_buf[3];

	while(1)
	{				
		adc_value=xpt2046_read_adc_value(0x94);//测量电位器
		adc_vol=5.0*adc_value/4096;//将读取的AD值转换为电压
		adc_value=adc_vol*10;//放大10倍,即保留小数点后一位
		adc_buf[0]=gsmg_code[adc_value/10]|0x80;
		adc_buf[1]=gsmg_code[adc_value%10];
	   	adc_buf[2]=0x3e;//显示单位V
		smg_display(adc_buf,6);		
	}		
}
/**************************************************************************************
深圳市普中科技有限公司(PRECHIN 普中)
技术支持:www.prechin.net
PRECHIN
 普中

实验名称:ADC模数转换实验--电位器电压采集
接线说明:	
实验现象:下载程序后,数码管上显示AD模块采集电位器的电压值
注意事项:																				  
***************************************************************************************/
#include "public.h"
#include "smg.h"
#include "xpt2046.h"


/*******************************************************************************
* 函 数 名       : main
* 函数功能		 : 主函数
* 输    入       : 无
* 输    出    	 : 无
*******************************************************************************/
void main()
{	
	u16 adc_value=0;
	float adc_vol;//ADC电压值
	u8 adc_buf[3];

	while(1)
	{				
		adc_value=xpt2046_read_adc_value(0x94);//测量电位器
		adc_vol=5.0*adc_value/4096;//将读取的AD值转换为电压
		adc_value=adc_vol*10;//放大10倍,即保留小数点后一位
		adc_buf[0]=gsmg_code[adc_value/10]|0x80;
		adc_buf[1]=gsmg_code[adc_value%10];
	   	adc_buf[2]=0x3e;//显示单位V
		smg_display(adc_buf,6);		
	}		
}

4.2 电位器电压采集(LCD1602显示)

实现的功能是:LCD1602上显示 AD 模块采集电位器的电压值。 程序框架如下: (1)编写LCD1602显示功能 (2)编写 ADC 转换函数 (3)编写主函数

4.3 电位器电压采集(串口打印)

实现的功能是:串口打印 AD 模块采集电位器的电压值。 程序框架如下: (1)编写串口打印程序 (2)编写 ADC 转换函数 (3)编写主函数

五、实验效果

5.1 AD采样值数码管显示

说明: 调节电位器 AD1 时,数码管上显示的电压值也将变化

5.2 AD采样值LCD1602上显示

六、仿真实现


总结

相关推荐
黄交大彭于晏28 分钟前
stm32 hal库 keil5工程编译烧录配置
stm32·单片机·嵌入式硬件
陌夏微秋1 小时前
元器件选型与参数13 电源的分类-线性电源参数 RT9013 AMS1117 PCB布局布线
arm开发·单片机·嵌入式硬件·智能手机·51单片机·硬件工程
码猩2 小时前
C#高速串口通讯方案winform实例
开发语言·单片机·c#
ADI_OP3 小时前
用System Workbench for STM32做MCU控制ADI DSP的开发,如何导入工程
stm32·单片机·嵌入式硬件·dsp开发·adi dsp中文资料·adi音频dsp·adi dsp开发教程
程序员buddha3 小时前
普中51单片机——LED流水灯模块
单片机·嵌入式硬件·51单片机
不能只会打代码4 小时前
51单片机从入门到精通:理论与实践指南综合应用——实战篇(九)
单片机·嵌入式硬件·51单片机
程序员-Queen6 小时前
DeviceIoControl超时后如何处理
stm32·单片机·嵌入式硬件
工匠Sola6 小时前
STM32基于HAL库的串口接收中断触发机制和适用场景
stm32·单片机·嵌入式硬件
Q23553688728 小时前
SL6115替代MT7201C+MT7202内置60V场效应管降压恒流芯片
stm32·单片机·嵌入式硬件
银科院-计算机与人工智能8 小时前
单片机的中断系统
笔记·单片机·嵌入式硬件