普中科技单片机AD电压数模转换。STC89C52和XPT2046 芯片

这篇博客详细介绍了使用普中科技ES2.0开发板上的STC89C52单片机配合XPT2046芯片进行AD电压数模转换的过程。内容涵盖AD转换原理、XPT2046芯片的功能特性和工作原理,以及具体的代码实现,包括读取和显示转换值。在实践中遇到的AD值跳动问题和数值转换注意事项也进行了讨论。

普中科技ES2.0开发板学习日记

在这里插入图片描述


前言

使用单片机开发板进行AD电压数模转换。STC89C52和XPT2046 芯片。


一、AD数模转换的原理

数/模定义

模拟量:自然界连续变化的物理量。所谓连续,包含两个方面的含义;一方面从时间上来说,它是随时间连续变化的;另一方面从数值上来说,它的数值也是连续变化的。这种连续变化的物理量通常称为模拟量。

数字量:计算机中处理的是不连续变化的量,离散性的数字量。

AD转换的主要技术指标

1、分辨率
ADC的分辨率是指使输出数字量变化一个相邻数码所需输入模拟电压的变化量。用二进制表示,数值为1/2^n,**即八位分辨率为1/255,12位分辨率为1/4095。**举个例子,如果你的电压最大为5V,分辨率为8位,分辨率为255,那么你ad采样的数值显示为255,此时就是5V的满电压。如果是200,那么实际电压就是5/255*200V。
2、量化误差
ADC把模拟量变为数字量,用数字量近似表示模拟量,这个过程称为量化。量化误差是ADC的有限位数对模拟量进行量化而引起的误差。
3、偏移误差
偏移误差是指输入信号为零时,输出信号不为零的值,所以有时又称为零值误差。
4、满刻度误差
满刻度误差又称为增益误差。ADC的满刻度误差是指满刻度输出数码所对应的实际输入电压与理想输入电压之差。
5、线性度
线性度有时又称为非线性度,它是指转换器实际的转换特性与理想直线的最大偏差。
6、绝对精度
在一个转换器中,任何数码所对应的实际模拟量输入与理论模拟输入之差的最大值,称为绝对精度。
7、转换速率
ADC的转换速率是能够重复进行数据转换的速度,即每秒转换的次数。而完成一次A/D转换所需的时间(包括稳定时间),则是转换速率的倒数。

XPT2046 的介绍

1.功能介绍

XPT2046 是一个内含 12 位分辨率 125KHz 转换速率逐步逼近型 A/D 转换器。XPT2046 支持从 1.5V 到 5.25V 的低电压 I/O 接口。

2.主要特性

工作电压范围为 1.5V~5.25V
支持 1.5V~5.25V 的数字 I/O 口
内建 2.5V 参考电压源
电源电压测量(0V~6V)
内建结温测量功能
触摸压力测量
采用 3 线制 SPI 通信接口
具有自动省电功能

3.工作原理和图表(数据手册)

工作原理

数据手册中有说明,在使用ADC功能的时候配置为单端模式或差分模式;模式配置说明图如下:

在这里插入图片描述
在这里插入图片描述

通过观察开发板上面的ad模块我们可以知道,我们如果测量通道AIN2和3需要用到AUX引脚所以配置选择单端模式。

在这里插入图片描述
                                             控制字节的描述

通过上图和开发板AD模块原理图可以得到,我们如果使用12位分辨率单端模式测量AIN1(可调电阻),那么我们输入DIN端就要控制字命令寄存器值为0x94(1001 1000)或0xB4(1011 1000)。同理如果要检测转换热敏电阻模拟信号,控制字命令寄存器值为0XD4;要检测转换光敏电阻模拟信号,控制字命令寄存器值为0XA4;测转换AIN3通道上模拟信号,控制字命令寄存器值为0XE4.

在这里插入图片描述
                                             XPT2046时序图

二、XPT2046AD转换代码

1.写入部分

在这里插入图片描述

void SPI_Write(uchar dat)
{
	uchar i;
	CLK = 0;
	for(i=0; i<8; i++)
	{
		DIN = dat >> 7;  	//放置最高位
		dat <<= 1;
		CLK = 0;			//上升沿放置数据

		CLK = 1;

	}
}

2.读取数据

uint SPI_Read(void)
{
	uint i, dat=0;
	CLK = 0;
	for(i=0; i<12; i++)		//接收12位数据
	{
		dat <<= 1;

		CLK = 1;
		CLK = 0;

		dat |= DOUT;

	}
	return dat;	
}

3.读取通道转换的数据

uint Read_AD_Data(uchar cmd)
{
	uchar i;
	uint AD_Value;
	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;	
}

三.用数码管显示转换值

1.main.c

#include "reg52.h"			
#include"XPT2046.h"	

typedef unsigned int u16;	  //对数据类型进行声明定义
typedef unsigned char u8;

sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;

u8 disp[4];
u8 code smgduan[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};

/*******************************************************************************
* 函 数 名         : delay
* 函数功能		  : 延时函数,i=1时,大约延时10us
*******************************************************************************/
void delay(u16 i)
{
	while(i--);	
}

/*******************************************************************************
* 函数名         :datapros()
* 函数功能		 :数据处理函数
* 输入           : 无
* 输出         	 : 无
*******************************************************************************/
void datapros()
{
	u16 temp;
	static u8 i;
	if(i==50)
	{
		i=0;
		temp = Read_AD_Data(0x94);		//   AIN0 电位器
	}
	i++;
	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];		
}


/*******************************************************************************
* 函数名         :DigDisplay()
* 函数功能		 :数码管显示函数
* 输入           : 无
* 输出         	 : 无
*******************************************************************************/
void DigDisplay()
{
	u8 i;
	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;//消隐
	}		
}

/*******************************************************************************
* 函 数 名       : main
* 函数功能		 : 主函数
* 输    入       : 无
* 输    出    	 : 无
*******************************************************************************/
void main()
{	
	while(1)
	{
		datapros();	 //数据处理函数
		DigDisplay();//数码管显示函数		
	}		
}
	

2.XPT2046.h头文件

#ifndef	  __XPT2046_H_
#define   __XPT2046_H_

//---包含头文件---//
#include<reg52.h>
#include<intrins.h>

//---重定义关键词---//
#ifndef uchar
#define uchar unsigned char
#endif

#ifndef uint
#define uint  unsigned int
#endif

#ifndef ulong
#define ulong  unsigned long
#endif

//---定义使用的IO口---//
sbit DOUT = P3^7;	  //输出
sbit CLK  = P3^6;	  //时钟
sbit DIN  = P3^4;	  //输入
sbit CS   = P3^5;	  //片选

uint Read_AD_Data(uchar cmd);
uint SPI_Read(void);
void SPI_Write(uchar dat);

#endif

2.XPT2046.c

#include"XPT2046.h"

/****************************************************************************
*函数名:SPI_Write
*输  入:dat:写入数据
*输  出:无
*功  能:使用SPI写入数据
****************************************************************************/

void SPI_Write(uchar dat)
{
	uchar i;
	CLK = 0;
	for(i=0; i<8; i++)
	{
		DIN = dat >> 7;  	//放置最高位
		dat <<= 1;
		CLK = 0;			//上升沿放置数据

		CLK = 1;

	}
}
/****************************************************************************
*函数名:SPI_Read
*输  入:无
*输  出:dat:读取 到的数据
*功  能:使用SPI读取数据
****************************************************************************/

uint SPI_Read(void)
{
	uint i, dat=0;
	CLK = 0;
	for(i=0; i<12; i++)		//接收12位数据
	{
		dat <<= 1;

		CLK = 1;
		CLK = 0;

		dat |= DOUT;

	}
	return dat;	
}

/****************************************************************************
*函数名:Read_AD_Data
*输  入:cmd:读取的X或者Y
*输  出:endValue:最终信号处理后返回的值
*功  能:读取触摸数据
****************************************************************************/
uint Read_AD_Data(uchar cmd)
{
	uchar i;
	uint AD_Value;
	CLK = 0;
	CS  = 0;
	SPI_Write(cmd);
	for(i=6; i>0; i--); 	//延时等待转换结果
	CLK = 1;	  //发送一个时钟周期,清除BUSY BUSY=0
	_nop_();
	_nop_();
	CLK = 0;
	_nop_();
	_nop_();
	AD_Value=SPI_Read();
	CS = 1;
	return AD_Value;	
}

注意 !!! 这里12位分辨率转换数码管显示的值是0-4095,如果单片机供电5V,那么实际电压值为 显示值 /4096*5

总结问题

1.在测试中AD值会跳动,上网查看了一下可能是adc采样波动,如果是这样可以采用滤波法,但我试了一下,效果不太理想,我再研究研究。
2.如果想把读取到的ad值转换为实际电压值,需要 AD值/4096*5. 注意在数据转换的过程中注意变量的数据类型,我刚开始转换的时候没有注意这个问题,数据一旦溢出数码管就没有显示,或者显示混乱。可能要用到浮点数据类型float.参考代码如下(注意定义的变量的数据类型使用):

void display_V()
{
    int temp=0;   //电压实际值的1000倍
	int ad_value;   //读取AD值
	float index = 1.222;  //1.22=4096/5000 
	float voltage = 0;
	
	static u8 i;
	if(i==50)                        //隔一段时间读取  
	{  
        i=0;
		EA=0;            //关中断
		ad_value =  Read_AD_Data(0x94);      //通道1AIN0
		EA=1;            //开中断
	}
	   
      voltage =  index * ad_value;
	  temp = (int)voltage;
	  time = temp/10;
    i++;
	
	SMG_dat[7]=0x3e;
	SMG_dat[6]=0x00;
	SMG_dat[5]=0x00;
	SMG_dat[4]=0x00;
	
//	SMG_dat[3]=0x00;
//	SMG_dat[2]=SMG_duanma[temp/1000]|0x80;
//	SMG_dat[1]=SMG_duanma[temp%1000/100];
//	SMG_dat[0]=SMG_duanma[temp%100/10];
	
	SMG_dat[3]=SMG_duanma[temp/1000]|0x80;
	SMG_dat[2]=SMG_duanma[temp%1000/100];
	SMG_dat[1]=SMG_duanma[temp%100/10];
	SMG_dat[0]=SMG_duanma[temp%10];
}

评论 7
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值