
A(A,analog,模拟的,D,digital,数字的)
现实世界是模拟的,连续分布的,无法被分成有限份;
计算机世界是数字的,离散分布的,可以被分成有限份的
AD转换就是把一个物理量从模拟的转换成数字的。
想要计算机来实现现实世界
CPU是数字的【要准确的0V或者5V】
将差一点的电压转换为准确的二进制
所有的AD转换芯片内部都是用比较器来实现的。
使用除法
AD转换后转出来的二进制数由几位二进制来表示。【实际结果是一样大】
位数越多,越细腻。【精度越高】
AD转换器可以接受的模拟量的范围
精度==准确度
简单理解就是转出来有多准
【精度越小可靠率越高】
AD转换器转出来的二进制数,每一格表示多少
时间越短,速率越大
输入电压范围:0-5V,AD转换输出位数是10,精度是0.01V
量程:0-5V
分辨率:(5-0)/2exp(10)=0.00488V
比如一次AD转换后得到数据为:【1010101010】,对应电压值:1010101010-->十进制:682,电压=682*0.00488=3.328V,考虑精度后为=3.33V
https://www.semiee.com/file/ETEK/ETEK-ET2046.pdf




AIN0-AIN3:不能同时采集,同一时间只能采集一路

CLK接P1.0
CS接P1.1
DI接P1.2
DO接P1.3



AIN0靠滑动变阻器控制电压变化
AIN1靠热敏电阻NTC
AIN2靠光敏电阻
bit7:起始位【高表示开始传输】:1
bit6-bit4:决定采样哪一路(AIN1,AIN0,AINT2,AINT3):
AIN0:001/011 X+
AIN1:101 Y+
AIN2:010 VBAT
AIN3:110 VBAT
bit3:设置ADC精度:【1:使用bit8位】【0:使用bit12位--一般使用这个】:0
bit2:【1:表示用单端模式】【0:表示差分模式(触摸屏)】:1
bit1:power down模式使能,00表示使能
读AIN0:0b 1001 0100=0x94
读AIN1:0b 1101 0100=0xd4
读AIN2;0b 1010 0100=0xa4
读AIN3:0b 1110 0100=0xe4


AIN0:001/011 X+
AIN1:101 Y+
AIN2:010 VBAT
AIN3:110 VBAT

回顾SPI知识点:【单片机】13-实时时钟DS1302-CSDN博客
有CS,DCLK,I/O
之前DS1302(SPI)的时候也是这样
上升沿写入:当CLK为上升沿的时候,数据通过DI从SPI主设备写入到SPI从设备
下降沿读出:当CLK为下降沿的时候,数据通过DO从SPI从设备读入到SPI主设备
之前DS1302(SPI)的时候是低位在前


- /*******************************************************************************
- * 函 数 名 : xpt2046_wirte_data
- * 函数功能 : XPT2046写数据
- * 输 入 : dat:写入的数据
- * 输 出 : 无
- *******************************************************************************/
- void xpt2046_wirte_data(u8 dat)
- {
- u8 i;
-
- CLK = 0;//可以忽略的
- _nop_();
- for(i=0;i<8;i++)//循环8次,每次传输一位,共一个字节
- {
- //先准备好数据,在置CLK=0
- 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--);//延时等待转换结果,这个时候进行AD转换
- CLK = 1;//发送应该
- _nop_();
- CLK = 0;//发送一个时钟,清除BUSY
- _nop_();
- adc_value=xpt2046_read_data();
- CS = 1;//关闭XPT2046
- return adc_value;
- }
- /*******************************************************************************
- * 函 数 名 : main
- * 函数功能 : 主函数
- * 输 入 : 无
- * 输 出 : 无
- *******************************************************************************/
- void main()
- {
- u16 adc_value=0;
- float adc_vol;//ADC电压值
- u8 adc_buf[3];
-
- while(1)
- {
- //0x94:对应AINT0--0b 1001 1100
- 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);
- }
- }
- //AD value是12bit的,分2波出去【因为一次只能输出8位】
- void uart_send_advalue(u16 val){
- uart_send_byte((val>>8)&0xff); //高8位
- uart_send_byte(val&0xff); //低8位
- uart_send_byte(0);//分割符
- }

- #include"ET2046.h"
-
- /*******************************************************************************
- * 函 数 名 : 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
-
- //写入数据
- for(i=0;i<8;i++)//循环8次,每次传输一位,共一个字节
- {
- //先准备好数据,在置CLK=0
- DIN = cmd >> 7;//先传高位再传低位
- cmd <<= 1;//将低位移到高位
- CLK = 0;//CLK由低到高产生一个上升沿,从而写入数据
- _nop_();
- CLK = 1;
- _nop_();
- }
-
-
- for(i=6; i>0; i--);//延时等待转换结果,这个时候进行AD转换
- CLK = 1;//发送应该
- _nop_();
- CLK = 0;//发送一个时钟,清除BUSY
- _nop_();
-
-
- for(i=0;i<12;i++)//循环12次,每次读取一位,大于一个字节数,所以返回值类型是u16
- {
- adc_value <<= 1;
- CLK = 1;
- _nop_();
- CLK = 0; //CLK由高到低产生一个下降沿,从而读取数据
- _nop_();
- adc_value |= DOUT;//先读取高位,再读取低位。
- }
-
-
-
- CS = 1;//关闭ET2046
- return adc_value;
- }
- #ifndef _xpt2046_H
- #define _xpt2046_H
-
-
- #include "reg51.h"
- #include "intrins.h"
-
- typedef unsigned int u16; //对系统默认数据类型进行重定义
- typedef unsigned char u8;
- typedef unsigned long u32;
-
-
-
- //管脚定义
- sbit DOUT = P1^3; //输出
- sbit CLK = P1^0; //时钟
- sbit DIN = P1^2; //输入
- sbit CS = P1^1; //片选
-
-
- //函数声明
- u16 xpt2046_read_adc_value(u8 cmd);
-
- #endif
- #include"ET2046.h"
- #include"uart.h"
-
- #define CMD_READ_AIN0 0x94 //滑动变阻器
- #define CMD_READ_AIN1 0xD4 //NTC--热敏电阻
- #define CMD_READ_AIN2 0xA4 //GR1--光敏电阻
- #define CMD_READ_AIN3 0xE4 //外部输入的电压值
-
-
- void Delay400000us() //@11.0592MHz
- {
- unsigned char i, j, k;
-
- _nop_();
- _nop_();
- i = 17;
- j = 208;
- k = 27;
- do
- {
- do
- {
- while (--k);
- } while (--j);
- } while (--i);
- }
-
-
-
- //AD value是12bit的,分2波出去【因为一次只能输出8位】
- void uart_send_advalue(u16 val){
- uart_send_byte((val>>8)&0xff); //高8位
- uart_send_byte(val&0xff); //低8位
- uart_send_byte(0);//分割符
- }
-
-
- void main(){
-
- //注意:定义一个变量要记得初始化,要不然后面可能出现问题
- u16 val=0;//12bit数值
-
- uart_init();
-
- while(1){
- val=xpt2046_read_adc_value(CMD_READ_AIN0);
-
- uart_send_advalue(val);
-
- Delay400000us();
- }
- }
(1)直接显示电压值,而不是采样AD值
(2)以文本方式显示,而不是十六进制方式
- //以文本方式发送c过去,意思就是要串口助手用文本方式来查看,看到的是
- //这个数字本身
- void uart_send_text(unsigned char c){
- //思路就是把c以十进制方式显示的几个数字,挨个变成文本发出去
- unsigned char i;//因为c是unsigned char 范围是0-255
-
- //先计算得出c的最高位,然后发出去
- i=c/100;
- uart_send_byte(i+48);//+48是对应ASCII
-
- //计算次高位
- c=c%100;
- i=c/10;
- uart_send_byte(i+48);
-
- //计算个位
- c=c%10;
- i=c;
- uart_send_byte(i+48);
-
- //发送一个换行
- uart_send_byte('\r');
- uart_send_byte('\n');
- }
因为我们这里的电压是5V,对应5000mV,明显上面的0-255不在范围内,所以我们要求传入的是unsigned int c,【0-2的16次方】才足够
- //因为这个函数的范围是unsigned char---->是2的8次方
- //但是我们最大电压值为:5V----5000mV,所以我们这里要使用unsigned int
- //以文本方式发送c过去,意思就是要串口助手用文本方式来查看,看到的是
- //这个数字本身
- void uart_send_text2(unsigned int c){
- //思路就是把c以十进制方式显示的几个数字,挨个变成文本发出去
- unsigned char i;//因为c是unsigned char 范围是0-255
-
- //因为我们知道电压值不会超过5000mV,所以只考虑显示1万以内的数据
- //先计算得出c的最高位,然后发出去
- i=c/1000;
- uart_send_byte(i+48);//+48是对应ASCII
-
- c=c%1000;
- i=c/100;
- uart_send_byte(i+48);//+48是对应ASCII
-
- //计算次高位
- c=c%100;
- i=c/10;
- uart_send_byte(i+48);
-
- //计算个位
- c=c%10;
- i=c;
- uart_send_byte(i+48);
-
- //发送一个换行
- uart_send_byte('\r');
- uart_send_byte('\n');
- }
将数字转换为模拟的
为了让数字量转换成模拟量,必须将每一位代码按其权重的大小转换为相应的模拟量,然后再把这些模拟量相加。
放大作用:将数字信号-----》模拟信号
隔离作用:防止输出信号影响输入信号

当输入的PWM数字信号一直为1,则输出的模拟信号一直为高电压
如果输入的PWM数字信号一直为0,则输出的模拟信号一直为低电压
关键点:取决于输入的PWM信号的高低电平所占的时间。【连续变化的模拟量】
其实不接LM358,直接用IO口连接LED实现现象也一样。(说明灯的亮度只与PWM输入的电压值的大小有关)
真正的DA一般是专用芯片或者CPU内置模块,给数字值输出平滑模拟量