参考教程:[16-1] AD/DA_哔哩哔哩_bilibili
温馨提示:本篇大多数原理可以不必深究,底层已经将复杂的部分封装好了,写代码的时候照着手册写就可以了。
1、AD/DA介绍:
(1)AD(Analog to Digital):模拟-数字转换,将模拟信号转换为计算机可操作的数字信号。(根据前面的学习,单片机看起来只能读取引脚当前是处于高电平还是低电平,并不能读取引脚的实际电压值,但是借助ADC模数转换器,单片机可以直接获取引脚的电压值,而不仅仅是单调的1/0)
(2)DA(Digital to Analog):数字-模拟转换,将计算机输出的数字信号转换为模拟信号。(根据前面的学习,单片机看起来只能将引脚置为高电平或是低电平,也就是说只能提供数字信号,但是借助PWM调制,单片机可以给引脚提供模拟信号,而不仅仅是单调的1/0)
(3)AD/DA转换打开了计算机与模拟信号的大门,极大的提高了计算机系统的应用范围,也为模拟信号数字化处理提供了可能。
2、硬件电路模型:
(1)AD转换通常有多个输入通道,用多路选择开关连接至AD转换器,以实现AD多路复用的目的,提高硬件利用率。
(2)AD/DA与单片机数据传送可使用并口(速度快、原理简单),也可使用串口(接线少、使用方便)。
(3)可将AD/DA模块直接集成在单片机内,这样直接写入/读出寄存器就可进行AD/DA转换,单片机的IO口可直接复用为AD/DA的通道。
3、运算放大器:
(1)运算放大器(简称“运放”)是具有很高放大倍数的放大电路单元。内部集成了差分放大器、电压放大器、功率放大器三级放大电路,是一个性能完备、功能强大的通用放大电路单元,由于其应用十分广泛,现已作为基本的电路元件出现在电路图中。
(2)运算放大器可构成的电路有:电压比较器、反相放大器、同相放大器、电压跟随器、加法器、积分器、微分器等。
(3)运算放大器电路的分析方法:虚短、虚断(负反馈条件下)。
(4)四种运放电路:
①电压比较器:
②反向放大器:
③同向放大器:
④电压跟随器:
4、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。工作温度范围为-40C~+85C。与ADS7846、TSC2046、AK4182A完全兼容。
5、XPT2046的时序及命令字:(重点,与写程序密切相关)
(1)给XPT2046发送命令字,XPT2046收到命令后通过DOUT将相应管脚的模拟信号转换为数字信号,然后将数字信号格式的数据一位一位传到主机,不过通过DOUT传到主机的二进制数据为了凑够两个字节,低8位或低4位(取决于分辨率)会使用0填充,软件需要用位移运算符处理一下再转换成十进制数据。
(2)8位命令字的含义:
(3)单端模式输入配置:
(4)性能指标:
①分辨率:指AD/DA数字量的精细程度,通常用位数表示。例如,对于5V电源系统来说,8位的AD可将5V等分为256份,即数字量变化最小一个单位时,模拟量变化5V/256=0.01953125V,所以,8位AD的电压分辨率为0.01953125V,AD/DA的位数越高,分辨率就越高。
②转换速度:表示AD/DA的最大采样/建立频率,通常用转换频率或者转换时间来表示,对于采样/输出高速信号,应注意AD/DA的转换速度。
6、AD模数转换:
(1)该项目需要用到液晶屏模块的代码以及延时函数。
(2)添加缺失的代码文件,然后进行编译,按照主函数中的注释进行调试。
①XPT2046.h文件:
#ifndef __XPT2046_H__
#define __XPT2046_H__
//命令字宏定义(需要对照手册)
//全部选择单端输入方式,2位为1
//XP就是X+,YP就是Y+(对应开发板原理图)
//8位转换分辨率
#define XPT2046_XP_8 0x9C //1001 1100(1000 1100也可以)
#define XPT2046_YP_8 0xDC //1101 1100
#define XPT2046_VBAT_8 0xAC //1010 1100
#define XPT2046_AUX_8 0xEC //1110 1100
//12位转换分辨率
#define XPT2046_XP_12 0x94 //1001 0100(1000 0100也可以)
#define XPT2046_YP_12 0xD4 //1101 0100
#define XPT2046_VBAT_12 0xA4 //1010 0100
#define XPT2046_AUX_12 0xE4 //1110 0100
unsigned int XPT2046_ReadAD(unsigned char Command);
#endif
②XPT2046.c文件:
#include <REGX52.H>
#include "Delay.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; //初始时DCLK为0
XPT2046_CS = 0; //初始时CS取反为1,所以CS为0
for(i = 0; i < 8; i++) //写入8位命令
{
XPT2046_DIN = Command & (0x80 >> i); //把命令一位一位地写在DIN上
XPT2046_DCLK = 1; //DCLK上升沿写入一位命令
XPT2046_DCLK = 0; //为下一个上升沿做准备
}
Delay(10);
for(i = 0; i < 16; i++) //读DOUT的16位数据
{
XPT2046_DCLK = 1; //DCLK上升沿,AD往DOUT上发一位数据
XPT2046_DCLK = 0; //为下一个上升沿做准备
if(XPT2046_DOUT)
{
ADVAlue |= (0x8000 >> i); //把DOUT上的数据一位一位地读进来
}
}
Delay(10);
XPT2046_CS = 1; //读过程结束,CS置为1
//DOUT传来的二进制数据为了凑够两个字节,低8位或低4位会使用0填充(Zero Filled)
//所以得到的二进制数据需要把低位的0去掉才能转换为十进制数据
if(Command & 0x08) //判断命令字的第三位
return ADVAlue >> 8; //选择8位转换分辨率的处理
else
return ADVAlue >> 4; //选择12位转换分辨率的处理
}
③main.c文件:
#include <REGX52.H>
#include "LCD1602.h"
#include "Delay.h"
#include "XPT2046.h"
unsigned int ADValue = 0;
void main()
{
LCD_Init();
LCD_ShowString(1,1,"ADJ NTC RG");
while(1)
{
/*
ADValue = XPT2046_ReadAD(XPT2046_XP_12); //读取X+的电压
//转动AD/DA模块的电位器
//理论上选择8位转换分辨率最大值为0255,12位转换分辨率最大值为4095
LCD_ShowNum(2,1,ADValue,4);
*/
ADValue = XPT2046_ReadAD(XPT2046_XP_8); //读取X+的电压
LCD_ShowNum(2,1,ADValue,3);
ADValue = XPT2046_ReadAD(XPT2046_YP_8); //读取Y+的电压
LCD_ShowNum(2,6,ADValue,3);
ADValue = XPT2046_ReadAD(XPT2046_VBAT_8); //读取VABT的电压
LCD_ShowNum(2,11,ADValue,3);
Delay(10);
}
}
7、DA数模转换:
(1)该项目就是之前实现过的呼吸灯,需要延时函数,然后在main.c文件中添加如下代码,进行编译。
#include <REGX52.H>
#include "Timer0.h"
#include "Delay.h"
sbit DA = P2^1; //LED2接收模拟信号
unsigned char Counter, Compare, i;
void main()
{
Timer0_Init();
Compare = 0;
while(1)
{
//本例就是之前实现过的呼吸灯
//在主函数中不断改变自设的比较值,这样定时器0写出来的方波脉冲占空比会不断变化,从而实现PWM调制(数模转换),产生模拟信号
for(i=0;i<100;i++)
{
Compare = i;
Delay(10);
}
for(i=100;i>0;i--)
{
Compare = i;
Delay(10);
}
}
}
void Timer0_Routine() interrupt 1 //CPU响应中断后执行的函数
{
Counter++;
Counter %= 100;
if(Counter < Compare) //计数器和自设比较值比较
{
DA = 1; //计数器低于比较值,输出高电平
}
else
{
DA = 0; //计数器高于比较值,输出低电平
}
//每次中断结束都要重置计数单元
TL0 = 0xA4; //设置定时初值
TH0 = 0xFF; //设置定时初值
}
(2)将生成的.hex文件下载到开发板中,观察第二盏LED灯的现象。