目录
一、A/D转换的介绍
AD转换即模拟信号转换为数字信号的过程,全称为模拟-数字转换(Analog-to-Digital Conversion)。在电子、通信和控制领域等方面,模拟信号是人能直接识别的形式,如音频、视频、温度、压力、力度等,但是它们需要经过处理(传输、存储、处理)时必须转换成数字信号,以便于数字设备进行数字化处理,如滤波、放大、压缩、编码等。因此, AD转换是将模拟信号通过一个特定的电路处理,将其转化为数字信号的过程,换言之,就是将连续时间上(analog)的信号转化为离散时间上(digital)的信号的过程。这个数字信号可以用计算机进行处理、传输、存储。常见的AD转换器包括逐次逼近型、窗口型、积分型等。在很多电子产品中,如手机、数码相机、音响设备等,都使用到了AD转换器。AD转换(Analog-to-Digital Conversion)的意义在于将模拟信号转换为数字信号,以便于数字系统的处理和分析。在实际应用中,例如传感器采集、仪器控制、音频处理等领域,模拟信号是无法直接处理和存储的,必须先进行AD转换将其转换为数字信号。因此,AD转换是数字系统中非常重要的一环,同时也是电子工程技术中的基础知识之一。
51单片机的ADC(Analog to Digital Converter)模块,可以将模拟信号转换为相应的数字信号,方便单片机进行数字运算和处理。在单片机应用中,ADC模块广泛应用于模拟量采集、传感器信号处理、电压检测等方面。51单片机的ADC模块通常有8位或10位的分辨率,采样速度也有快、中、慢三种模式,可以根据实际应用需求进行设置。
ADC模块工作流程如下:
1. 选定通道:通过寄存器配置选择要转换的模拟信号通道。
2. 启动转换:设置ADC启动位或者转换启动模式,让ADC开始转换模拟信号。
3. 转换过程:在启动转换后,ADC从模拟信号输入端口采样模拟信号,将其转化为一个数字量,通过数据总线传输给单片机。
4. 数据获取:单片机通过数据总线获取ADC转换后的数字数据,进行处理。
5. 中断处理:ADC转换结果可能通过中断的方式向单片机发送,单片机在中断服务程序中获取ADC数据。
不同的单片机的ADC模块是不同的,在使用前需要仔细阅读相关的芯片手册和数据手册,并根据实际情况进行配置和调试。
二、基于XPT2046的AD转换(SPI通信)
在使用XPT2046之前,我们首先要了解什么是SPI通信协议:
SPI(Serial Peripheral Interface)是一种同步串行通信协议,其使用了4根线实现全双工的通信。SPI协议包含四个时钟信号:SCK(时钟线)、MOSI(主机输出从机输入线)、MISO(主机输入从机输出线)和SS(片选线)。
在SPI通信中,主机通过SS信号对从机进行选择,然后通过SCK时钟信号同步发送和接收数据。主机和从机的数据传输是通过MOSI和MISO两根线进行的。
SPI通信的协议由以下几个方面组成:
1. 传输时序:SPI通信需要对时钟信号进行同步控制,通信方按同样的时钟转换数据,即轮流传输数据位。
2. 数据长度:可以按照需要设定数据位的个数。SPI通信每次传输的数据最大位数由从机还是主机确定,通常在一个字节到数千个字节之间。
3. 传输数据的时钟极性和相位:SPI通信的时钟极性和相位是可以设定的,以确保不能出错。通常SPI设备的数据都在沿时刻传输给SPI总线(也许是上升沿或下降沿),并且总线上下降沿总是发生在每个字节或数据位之间。
4. 确定状态:SPI通信中还需要一个确定通讯状态的控制信号,它可以是用于连接器的一个独立引脚或者SPI数据流中的一个特定字节,通常SPI设备的数据流中,最后一个字节会包含控制选项和CRC等特殊字节。
SPI通信的流程如下:
1. 由主设备发起传输请求,选择从设备并拉低片选(CS)信号;
2. 主设备通过时钟信号(SCK)来同步数据传输,将数据通过MOSI(Master Output Slave Input)引脚传给从设备;
3. 从设备在下降沿时读取MOSI引脚上的数据,并将自己的应答数据通过MISO(Master Input Slave Output)引脚返回给主设备;
4. 主设备在下降沿时读取MISO引脚上的数据,等待从设备的应答,然后拉高片选信号,传输完成。
信号和数据的传输是在时钟信号的同步下完成的,可以通过SPI的时钟极性和相位来确定数据的读取和传输顺序。SPI通信的时序图如下图所示:
而XPT2046是基于SPI通信的一种设备,对于XPT2406来说,它的时序图为:
参照时序图我们便可以编写XPT2046的AD转换函数:
#include <REGX52.H>
sbit XPT2046_CS = P3^5;
sbit XPT2046_DCLK = P3^6;
sbit XPT2046_DIN = P3^4;
sbit XPT2046_DOUT = P3^7;
unsigned int XPT2046_ReadAD(unsigned char Command)
{
unsigned char i;
unsigned int ADValue = 0;
XPT2046_DCLK = 0;
XPT2046_CS = 0;
for(i = 0;i < 8;i++)
{
XPT2046_DIN = Command & (0x80 >> i);
XPT2046_DCLK = 1;
XPT2046_DCLK = 0;
}
for(i = 0;i < 16;i++)
{
XPT2046_DCLK = 1;
XPT2046_DCLK = 0;
if(XPT2046_DOUT){ADValue |= (0x8000 >> i);}
}
XPT2046_CS = 1;
if(Command & 0x08)
{
return (ADValue >> 8);
}
else
{
return (ADValue >> 4);
}
}
需要注意的是这里要判断是8分辨模式还是12分辨模式,8分辨模式需要将读取到的数据右移八位,12位模式要右移4位,因为我们读取到的数据是16位的二进制数据。
#ifndef __XPT2046__H__
#define __XPT2046__H__
#define XPT2406_XP_8 0x9C//0x8C//8分辨率模式
#define XPT2406_YP_8 0xDC
#define XPT2406_VBAT_8 0xAC
#define XPT2406_AUX_8 0xEC
#define XPT2406_XP_12 0x94//0x84//12分辨率模式
#define XPT2406_YP_12 0xD4
#define XPT2406_VBAT_12 0xA4
#define XPT2406_AUX_12 0xE4
unsigned int XPT2046_ReadAD(unsigned char Command);
#endif
有了这个函数,我们就可以读取任意通道的模拟量并将模拟量转化为数字量:
#include <REGX52.H>
#include "Nixie.h"
#include "XPT2046.h"
#include "Delay.h"
unsigned int ADValue;
void main()
{
while(1)
{
// ADValue = XPT2046_ReadAD(XPT2406_XP_8);//读滑动电阻
// Nixie(1,ADValue/100);
// Nixie(2,ADValue/10%10);
// Nixie(3,ADValue%10);
// ADValue = XPT2046_ReadAD(XPT2406_YP_8);//读热敏电阻
// Nixie(1,ADValue/100);
// Nixie(2,ADValue/10%10);
// Nixie(3,ADValue%10);
ADValue = XPT2046_ReadAD(XPT2406_VBAT_8);//读光敏电阻
Nixie(1,ADValue/100);
Nixie(2,ADValue/10%10);
Nixie(3,ADValue%10);
Delay(5);
}
}
三、基于PCF8591的A/D转换(IIC通信)
PCF8591使用的IIC通信协议为另一种通信协议:
IIC(Inter-Integrated Circuit)通信协议,也被称作I2C(Inter-IC)通信协议。它是一种串行通信协议,由飞利浦公司(现已更名为恩智浦公司)在1980年代开发,用于各种数字芯片之间进行通信。IIC通信协议只需要两根线,即SDA(数据线)和SCL(时钟线),即可实现多个芯片之间的通信。
在IIC通信协议中,总线上通信的设备分为两类,一类是主设备,一类是从设备。主设备是控制总线,发送IIC通信命令和数据的设备。从设备只能在主设备允许的情况下回复命令和数据。
IIC通信协议的传输方式是字节传输,每个字节都由8个比特位组成。数据传输时,从设备需要在时钟线的上升沿之前准备好数据,主设备正好在时钟线上升沿时读取数据。通过控制位来表示数据的读写方向,实现数据的传输和通信。
I2C通信的基本流程:
1. 主设备发送起始信号(Start signal):主设备发送一个低电平脉冲信号,告诉所有从设备I2C总线现在被占用了。
2. 主设备发送从设备地址和读写模式:主设备将目标从设备的地址发送到I2C总线上,告诉从设备他想要读还是写。
3. 从设备响应:目标从设备响应主设备,并发送一个“应答信号”(ACK)回来。
4. 主设备发送要读/写的数据:主设备将要写入从设备或要读出来的数据发送到I2C总线上。
5. 从设备响应:从设备发送一个ACK回复主设备,确认数据已经处理。
6. 如果主设备想再进行读/写数据操作,重复步骤4和5。
7. 主设备发送停止信号(Stop signal):主设备发送一个高电平脉冲信号,告诉所有从设备I2C总线现在空闲了。
有了这个流程我们是不是可以用它来编写我们需要的读写和转换程序:
#include "PCF8591.h"
/**
* @brief AD转换
* @param Commend 通道
* @retval Value 转换后的值
*/
unsigned char ADGet(unsigned char Commend)
{
unsigned char Value;
IIC_Start();
IIC_SendByte(0x90);
IIC_WaitAck();
IIC_SendByte(Commend);
IIC_WaitAck();
IIC_Stop();
IIC_Start();
IIC_SendByte(0x91);
IIC_WaitAck();
Value = IIC_RecByte();
IIC_SendAck(1);
IIC_Stop();
return Value;
}
/**
* @brief DA转换
* @param Commend
* @retval 无
*/
//void DAGet(unsigned char Commend)
//{
// IIC_Start();
// IIC_SendByte(0x90);
// IIC_WaitAck();
// IIC_SendByte(0x40);
// IIC_WaitAck();
// IIC_SendByte(Commend);
// IIC_WaitAck();
// IIC_Stop();
//}
PCF8591的地址字节为:
1 | 0 | 0 | 1 | A2 | A1 | A0 | R/W |
其中前四位为固定位,后四位为可编程部分,对照原理图可知,A2、A1、A0接地,则这三位为0,如图:
因为首先我们要读取通道的模拟量,所以PCF8591的地址字节为1001 0000既0x90;
PCF的控制字节为:
D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
控制字节控制着PCF所有的功能
其中:
D3、D7:固定为0;
D1、D0:4路AD通道编号,AIN0~AIN3,00、01、10、11分别对应通道0、1、2、3;
D2:自动增量标志位,1表示在采集完0通道数据后继续采集通道1、2、3;单路采集时置0即可。
D4,D5:模拟量输入模式选择:00、01、10、11分别表示:四路单端输入、三路差分输入、单端与差分配合输入、两路差分输入。
D6:DA转换使能控制位:1表示DA转换,0表示AD转换。
所以我们的主函数要怎么写呢
#include <STC15F2K60S2.H>
#include "PCF8591.h"
#include "buzzer.h"
#include "Nixie.h"
#include "Delay.h"
void main()
{
unsigned char Value = 0;
Buzzer_Init();
while(1)
{
Value = ADGet(AIN3);
Nixie_C(1,Value/100);
Nixie_C(2,Value/10%10);
Nixie_C(3,Value%10);
}
}
#ifndef __PCF8591__H__
#define __PCF8591__H__
#include "iic.h"
#define AIN0 0x00
#define AIN1 0x01 //光敏电阻
#define AIN2 0x02
#define AIN3 0x03 //滑动变阻
unsigned char ADGet(unsigned char Commend);
#endif
四、内部集成AD转换器
STC15系列单片机内部集成有高速10位AD转换器,只需要对相关寄存器进行配置,就可以采集AD转换数据,我们参照STC15单片机的手册:
参照这些寄存器我们来编写函数来采集通道7的模拟量:
void ADC_init()
{
P1ASF |= P1ASF_7; //选择通道7
ADC_RES = 0; //ADC_RES[7:0]存放高八位数据,ADC_RESL存放低两位数据
ADC_CONTR = 0x80;//打开电源
Delay(1);
}
unsigned int ADC_GetResult(unsigned char ch)
{
unsigned int ADC_Value;
ADC_CONTR = 0x88|ch;//打开电源选择要采集的模拟量通道
Delay(1);
while(!(ADC_CONTR&ADC_FLAG));//等待转换完成
ADC_CONTR = 0x80;//清楚转换完成标志位
ADC_Value = ADC_RES;//ADC_RES[7:0]存放高八位数据
ADC_Value = (ADC_Value << 2) | ADC_RESL;//ADC_RESL存放低两位数据并将数据合并
return ADC_Value;
}
采集数据后转化并显示在LCD1602上:
#include <STC15F2K60S2.H>
#include "LCD1602.h"
#include "ADGET.h"
#include "Delay.h"
unsigned int ADCTemp;
unsigned int ADCVoltage;
void main()
{
unsigned char temp = 0;
IO_init();
ADC_init();
LCD_Init();
LCD_ShowString(1,1,"ADJ");
while(1)
{
ADCTemp = ADC_GetResult(0x07);
ADCVoltage = (unsigned int)(((ADCTemp*5)/1024.000)*1000);
temp=ADCTemp/1000; LCD_ShowChar(2,1,temp+'0');
temp=(ADCTemp%1000)/100; LCD_ShowChar(2,2,temp+'0');
temp=(ADCTemp%100)/10; LCD_ShowChar(2,3,temp+'0');
temp=ADCTemp%10; LCD_ShowChar(2,4,temp+'0');
temp=ADCVoltage/1000; LCD_ShowChar(2,9,temp+'0');
LCD_ShowChar(2,10,'.');
temp=(ADCVoltage%1000)/100; LCD_ShowChar(2,11,temp+'0');
temp=(ADCVoltage%100)/10; LCD_ShowChar(2,12,temp+'0');
temp=ADCVoltage%10; LCD_ShowChar(2,13,temp+'0');
LCD_ShowChar(2,14,'V');
Delay(10);
}
}
五、总结
本文着重介绍了使用XPT2046、PCF8591、内部集成AD转换器进行AD转换,介绍过程中,对SPI通信协议和IIC通信协议进行了简单的介绍和使用方法的说明。
转自:https://blog.csdn.net/weixin_73497355/article/details/131144917