(1)什么是AD转换?A(A,analog,模拟的,D,digital,数字的)
现实世界是模拟的,连续分布的,无法被分成有限份;
计算机世界是数字的,离散分布的,是可以被分成有限份的;
AD转换就是把一个物理量从模拟的转换成数字的。
(2)AD转换的意义?
(3)什么情况下需要AD转换。
(1)比较器,所有的AD转换芯片内部都是用比较器来实现的。
(2)和10进制转2进制有点像。
(1)位数,AD转换后转出来的二进制数由几位二进制来表示。位数越多,越细腻。
(2)量程,AD转换器可以接受的模拟量的范围。
(3)精度,简单理解就是转出来到底有多准。
(4)分辨率,AD转换器转出来的二进制数,每一格表示多少。
(5)转换速率(转换时间)。
举个栗子:
输入电压范围0-5V,AD转换输出位数是10,精度是0.01V,则:量程为0-5V,
分辨率为:(5-0)/2exp(10)=0.00488V
譬如一次AD转换后得到的数据是1010101010,则对应的电压值为:3.328V,考虑精度后为3.33V
(1)CPU外部扩展专用AD芯片。
(2)CPU内部集成AD模块(内部外设)。
(1)SPI接口。
CLK接P1.0,CS接P1.1,DI接P1.2,DQ接P1.3.
(2)3种模拟电压变化原理:
AIN0靠滑动变阻器控制电压变化,AIN1靠热敏电阻NTC,AIN2靠光敏电阻GR。
ET2046的控制字:
bit7:1
bit6-4:决定采样的是哪一路(AIN0、AIN1、AIN2、AIN3)
AIN0:001/011 X+
AIN1:101 Y+
AIN2:010 VBAT
AIN3:110 AUX
bit3:设置采样位数。0表示12bit,1表示8bit,一般都用12bit
bit2:为1表示用单端模式,为0表示差分模式。此处用单端模式
bit1-0:power down模式使能,00表示使能。
读AIN0:0b10010100 = 0x94 (实验下0xb4行不行)
读AIN1:0b11010100 = 0xD4
读AIN2:0b10100100 = 0xA4
读AIN3:0b11100100 = 0xE4
(1)SPI变种。
(2)上升沿写入下降沿读出(之前DS1302的时候也是这样的)。
(3)读写都是高位在前(之前DS1302的时候是低位在前的)。
(4)注意写和读的交接点。
(1)写指令。
(2)读12位数字值。
(3)写+读形成一个完整采样周期。
#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
_nop_();
_nop_();
CLK = 0;
_nop_();
_nop_();
AD_Value=SPI_Read();
CS = 1;
return AD_Value;
}
(1)参考例程写代码。
(2)添加串口输出调试。
(3)实践。
电位器做模拟量输入源
热敏电阻做模拟量输入源
光敏电阻做模拟量输入源
(4)串口直接显示电压值。
关键点:
直接显示电压值,而不是采样AD值;
以文本方式显示,而不是十六进制方式。
ET2046.h
#ifndef __XPT2046_H_
#define __XPT2046_H_
//---包含头文件---//
#include
#include
//---重定义关键词---//
#ifndef uchar
#define uchar unsigned char
#endif
#ifndef uint
#define uint unsigned int
#endif
#ifndef ulong
#define ulong unsigned long
#endif
//---定义使用的IO口---//
sbit CLK = P1^0; //时钟
sbit CS = P1^1; //片选
sbit DIN = P1^2; //输入
sbit DOUT = P1^3; //输出
uint Read_AD_Data(uchar cmd);
#endif
ET2046.c
#include "ET2046.h"
uint Read_AD_Data(uchar cmd)
{
uchar i;
uint AD_Value = 0; // 局部变量的初始化非常重要
CLK = 0;
CS = 0;
for(i=0; i<8; i++)
{
DIN = cmd >> 7; //放置最高位
cmd <<= 1;
CLK = 0; //上升沿放置数据
CLK = 1;
}
for(i=6; i>0; i--); //延时等待转换结果
CLK = 1; //发送一个时钟周期,清除BUSY
_nop_();
_nop_();
CLK = 0;
_nop_();
_nop_();
for(i=0; i<12; i++) //接收12位数据
{
AD_Value <<= 1;
CLK = 1;
CLK = 0;
AD_Value |= DOUT;
}
CS = 1;
return AD_Value;
}
uart.h
#ifndef __UART_H__
#define __UART_H__
#include
void uart_init(void);
void uart_send_byte(unsigned char c);
void uart_send_text(unsigned char c);
void uart_send_text2(unsigned int c);
#endif
uart.c
#include "uart.h"
// 串口设置为: 波特率9600、数据位8、停止位1、奇偶校验无
// 使用的晶振是11.0592MHz的,注意12MHz和24MHz的不行
void uart_init(void)
{
// 波特率9600
SCON = 0x50; // 串口工作在模式1(8位串口)、允许接收
PCON = 0x00; // 波特率不加倍
// 通信波特率相关的设置
TMOD = 0x20; // 设置T1为模式2
TH1 = 253;
TL1 = 253; // 8位自动重装,意思就是TH1用完了之后下一个周期TL1会
// 自动重装到TH1去
TR1 = 1; // 开启T1让它开始工作
// ES = 1;
// EA = 1;
}
// 通过串口发送1个字节出去
void uart_send_byte(unsigned char c)
{
// 第1步,发送一个字节
SBUF = c;
// 第2步,先确认串口发送部分没有在忙
while (!TI);
// 第3步,软件复位TI标志位
TI = 0;
}
// 以文本方式发送c过去,意思就是要串口助手用文本方式来查看,看到的是
// 这个数字本身
void uart_send_text(unsigned char c)
{
// 思路就是把c以十进制方式显示的几个数字,挨个变成文本发送出去
unsigned char i;
// 先计算得出c的最高位,然后发出去
i = c / 100;
uart_send_byte(i + 48);
// 然后计算次高位
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');
}
// 因为我们知道电压值不会超过5000,所以只考虑显示1万以内的数值
// 这种方式限制能限制的最大值是9999
void uart_send_text2(unsigned int c)
{
// 思路就是把c以十进制方式显示的几个数字,挨个变成文本发送出去
unsigned char i;
i = c / 1000;
uart_send_byte(i + '0');
uart_send_byte('.');
// 先计算得出c的最高位,然后发出去
c = c % 1000;
i = c / 100;
uart_send_byte(i + 48);
// 然后计算次高位
c = c % 100;
i = c / 10;
uart_send_byte(i + 48);
// 然后计算个位
c = c % 10;
i = c;
uart_send_byte(i + 48);
uart_send_byte('V');
// 发送一个换行
uart_send_byte('\r');
uart_send_byte('\n');
}
main.c
#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 // 外部输入的电压值
// AD value是12bit的,分2拨发出去
void uart_send_advalue(uint val)
{
uart_send_byte((val >> 8) & 0xff); // 高8位
uart_send_byte(val & 0xff); // 低8位
uart_send_byte(0);
}
void delay1s(void) //误差 0us
{
unsigned char a,b,c;
for(c=167;c>0;c--)
for(b=171;b>0;b--)
for(a=16;a>0;a--);
_nop_(); //if Keil,require use intrins.h
}
// 思路:用51单片机来计算,直接算出最终的电压值,然后通过串口发送显示
// 第1步:先去算出电压值(val/4096)*5000mV =1.22*val(mV)
// 第2步:串口发送出去显示。显示方法1,用二进制发送出去显示
// 方法2:用文本方式去显示.把算出来要发送的数值,转成对应的ASCII码发送
// 给串口去显示
// 以文本方式直接打印电压值
void uart_send_voltage(uint val)
{
// 先将AD值val换算成mV为单位的电压值
float index = 1.22;
float voltage = index * val;
uint vol_display = (uint)voltage;
uart_send_text2(vol_display);
}
void main(void)
{
uint val = 0;
uart_init();
// uart_send_voltage(3241);
// while (1);
while (1)
{
// val = Read_AD_Data(CMD_READ_AIN0); // 滑动变阻器,测试ok
// val = Read_AD_Data(CMD_READ_AIN1); // NTC,测试ok
val = Read_AD_Data(CMD_READ_AIN2); // 光敏电阻,测试ok
//uart_send_advalue(val);
uart_send_voltage(val);
delay1s();
}
}
(1)何为DA转换。
(2)DA转换的意义和用途(函数信号发生器)。
(3)DA转换的原理。
(1)运算放大器(放大+隔离)。
(2)PWM数字信号。
(3)其实不接LM358,直接用IO口连接LED实验现象也一样的。
(4)真正的DA一般是专用芯片或者CPU内置模块,给数字值输出平滑模拟量。
(5)这勉强算演示DA。
/**************************************************************************************
* DA-PWM输出实验 *
实现现象: 具体接线操作请参考视频
下载程序后DA模块上的指示灯呈呼吸灯效果,由暗变亮再由亮变暗
注意事项:要将AD/DA模块上的短接片将OUT和LED短接,板子丝印上有。
***************************************************************************************/
#include "reg52.h" //此文件中定义了单片机的一些特殊功能寄存器
typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;
//--定义使用的IO口--//
sbit PWM=P2^1;
bit DIR;
//--定义一个全局变量--//
u16 count,value,timer1;
/*******************************************************************************
* 函 数 名 : Timer1Init
* 函数功能 : 定时器1初始化
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void Timer1Init()
{
TMOD|=0X10;//选择为定时器1模式,工作方式1,仅用TR1打开启动。
TH1 = 0xFF;
TL1 = 0xff; //1us
ET1=1;//打开定时器1中断允许
EA=1;//打开总中断
TR1=1;//打开定时器
}
/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void main()
{
Timer1Init(); //定时器1初始化
while(1)
{
if(count>100)
{
count=0;
if(DIR==1) //DIR控制增加或减小
{
value++;
}
if(DIR==0)
{
value--;
}
}
if(value==1000)
{
DIR=0;
}
if(value==0)
{
DIR=1;
}
if(timer1>1000) //PWM周期为1000*1us
{
timer1=0;
}
if(timer1
{
PWM=1;
}
else
{
PWM=0;
}
}
}
/*******************************************************************************
* 函 数 名 : Time1
* 函数功能 : 定时器1的中断函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void Time1(void) interrupt 3 //3 为定时器1的中断号 1 定时器0的中断号 0 外部中断1 2 外部中断2 4 串口中断
{
TH1 = 0xFF;
TL1 = 0xff; //1us
timer1++;
count++;
}