51单片机-AD/DA
一、运算放大器
- 运算放大器(简称:运放)是具有很高效的放大倍数的放大电路单元
- 运算放大器可构成的电路有:电压比较器、反向放大器、同向放大器、电压跟随器、加法器、积分器、微分器等
下面是四种运放电路和输出电压公式
-
电压比较器
-
反向放大器
-
同向放大器
-
电压跟随器
二、AD/DA介绍
- AD(Analog to Digital):模拟-数字转换,将模拟信号转换为计算机可以操作的数字信号,例如将模拟电压转换为数字量,可以应用在光敏、热敏、麦克风等等一些外设上
- DA(Digital to Analog):数字-模拟转换,将计算机输出的数字信号转换为模拟信号
2.1 AD/DA硬件电路模型
- AD硬件电路模型如下所示
- DA硬件电路模型如下所示
- AD转换通常有多个输入通道,用多路选择开关连接至AD转换器,以实现AD多路复用的目的,提高硬件利用率
- AD/DA与单片机数据传送可使用并行口或者串行口
- 可以将AD/DA模块直接集成在单片机内,这样直接写入/读出寄存器就可以进行AD/DA转换,单片机的IO口可以直接复用为AD/DA的通道
2.2 AD/DA工作原理图
2.2.1 AD工作原理图
下面两张图为一个AD转换模块内部原理图
IN0-IN7是8路输入通道,通过地址锁存确定哪路开关打开,经过逐次逼近最终得到数字量
具体的逐次逼近图如下所示
逐次逼近型:DAC的模拟电压是已知编码电压,VIN的量是未知编码电压(待测)的,两者同时输入到电压比较器(输出高低电平判断谁大谁小),不断比较DAC的输出电压和VIN,如果DAC大,就调小DAC数据,反之就增大DAC数据,通过不断调整DAC的模拟电压,直到DAC的输出电压无限接近外部通道输入的电压VIN,DAC的输入数据(SAR完成-二分法)就是外部电压的编码数据,即VIN的8位数字量,存到缓冲器,最终输出8位数字量
数字量公式如下
其中VIN是输入电压,VREF是参考电压,51系列单片机是5V,D7-D0是输出的八位数字量
2.2.2 DA工作原理图
下图为PWM型DA转换器,输出电压V0为(PWM占空比)×Vh
下图为DA模块内部原理图,输入数据经过DA模块,输出模拟电压值
下面为8位D/A转换器具体原理图
VREF就是我们输入的参考电压,Rfb是反馈电阻,D7-D0是当我们将开关拨到0或者1时的二进制数
2.3 AD/DA性能指标
- 分辨率:指AD/DA数字量的精细程度,通常用位数表示。例如在5v系统中,8位的AD将5V等分为256份,即数字量变化最小一个单位时,模拟量变化5V/256,所以位数越高,则分辨率越高
- 转换速度:表示AD/DA的最大采样/建立频率,通常用转换频率或者转换时间来表示,对于采样/输出高速信号,应该注意转换速度
三、AD应用-XPT2046
DA可以用PWM波去模拟,所以这里不用去特地了解,AD在日常开发过程中用的比较多,这里以XPT2406去应用一下AD
3.1 XPT2046介绍
- XPT2046是一款触摸屏控制器,这点我们无需理会,只需要知道它内置了12分辨率AD转换器
下图为XPT2046模块电路原理图,其含逐次逼近型AD转换模块
CS使能,DCLK串行时钟线,DIN是地址输入,DOUT是输出AD转换完成的数字量
AIN0、1、2连接在了传感器的模拟输出口
传感器的模拟量通过AINx进入AD模块,转换成数字量以后通过DOUT引脚输出给单片机
3.2 XPT2046时序介绍
CS非作为使能线,可以接多个设备进行片选,每一个芯片单独有一个CS非,同一时间如果想和哪个设备通信,则就给CS非0
DCLK是串行时钟线,一次转换需要24个时钟来完成,上升沿输入,下降沿输出;DIN是输入,在前8个时钟里,主要是对通信地址一些配置;DOUT主要是发送AD转换后的数字量,可以看出在后4位都是以0进行填充的
3.3 XPT2046代码编写
本次代码的编写,主要是对通信时序的编写,此通信协议主要分为两块,MCU先给XPT2046发送数据,随之XPT2046再给MCU发送转换好的数字量
下面给出XPT2046.c
#include <REGX52.H>
sbit XPT2046_CS = P3^5;
sbit XPT2046_DCLK = P3^6;
sbit XPT2046_DIN = P3^4;
sbit XPT2046_DOUT = P3^7;
/*
函数功能:XPT去读取指定传感器的模拟值,内置的AD模块会得到数字量发送给MCU
形式参数:命令字(读那个通道)
返回值:传感器数字量
*/
unsigned int XPT2046_ReadAD(unsigned char Command) //命令字(手册):起始位+地址(AD通道)+模式选择...
{
unsigned char i;
unsigned int ADValue = 0; //XPT2046发给MCU的数字量
XPT2046_DCLK = 0; //初始化
XPT2046_CS = 0; //使能
for(i = 0;i<8;i++) //MCU将前8位发送给XPT2046,以配置地址
{
XPT2046_DIN = Command&(0x80>>i); //高位在前
XPT2046_DCLK = 1;
XPT2046_DCLK = 0; //上升沿输入,MCU给芯片发
}
for(i = 0;i<16;i++) //后16位是XPT2046发送给MCU
{
XPT2046_DCLK = 1;
XPT2046_DCLK = 0;
if(XPT2046_DOUT){ADValue|=(0x8000>>i);} //下降沿输出,芯片给MCU发16位数据,DOUT上就是存的数字量
}
XPT2046_CS = 1;
if(Command&0x08) //8位分辨率
{
return ADValue>>8; //ADValue一共16位,低8位全是0,
}
else //12位分辨率
{
return ADValue>>4; //ADValue一共16位,低4位全是0
}
}
宏定义地址和分辨率
#define XPT2046_XP_8 0x9C
#define XPT2046_YP_8 0xDC
#define XPT2046_VBAT_8 0xAC
#define XPT2046_AUX_8 0xEC
#define XPT2046_XP_12 0x94
#define XPT2046_YP_12 0xD4
#define XPT2046_VBAT_12 0xA4
#define XPT2046_AUX_12 0xE4
上述代码中8和12分别代表分辨率,可以进行不同分辨率的显示
在LCD1602显示出转换后的数字量,给出main.c
#include <REGX52.H>
#include "Delayms.h"
#include "LCD1602.h"
#include "XPT2046.h"
unsigned int ADValue;
void main()
{
LCD_Init();
LCD_ShowString(1,1,"ADJ NTC RG"); //可变电阻 热敏电阻 光敏电阻
while(1)
{
ADValue = XPT2046_ReadAD(XPT2046_XP_8); //单片机发送地址给XPT读某一个传感器的模拟值,经过AD处理后返回数字量,发送给单片机
LCD_ShowNum(2,1,ADValue,3);
ADValue = XPT2046_ReadAD(XPT2046_YP_8);
LCD_ShowNum(2,6,ADValue,3);
ADValue = XPT2046_ReadAD(XPT2046_VBAT_8);
LCD_ShowNum(2,11,ADValue,3);
Delayms(10);
}
}