51——AD/DA转换学习总结

51单片机——AD\DA转换

前言:
AD/DA介绍

  1. AD和DA是模拟信号和数字信号之间的转换过程。
    AD,全称为模拟到数字(Analog-to-Digital),指的是将模拟信号转换为数字信号的过程。在AD转换中,模拟信号经过采样、量化和编码等步骤,被转换为离散的数字信号。这样可以更方便地处理和传输信号,在数字系统中进行数字信号的处理和分析。

  2. DA,全称为数字到模拟(Digital-to-Analog),指的是将数字信号转换为模拟信号的过程。在DA转换中,数字信号经过解码和重构处理,被转换为连续的模拟信号。这样可以将数字信号转换为模拟信号,使其能够被模拟系统接收和处理。

  3. AD和DA转换常见于各种电子设备和系统中,例如音频设备、通信系统、传感器等。AD转换可以将实际物理量(如声音、温度、光强)转换为数字形式进行处理,而DA转换则可以将数字信号转换为模拟形式输出到外部设备或环境中。

  4. AD和DA转换的准确性和性能对于系统的精度和稳定性至关重要,因此在设计和选择AD和DA转换器时需要考虑多个因素,如分辨率、采样率、信噪比、线性度等

一. A/D、D/A转换中的主要概念

位数: AD转换后转出来的数由几位二进制来表示。位数越多,越细腻,精度越高。例如:10位,代表可以将模拟量分成1024份,12位,代表可以将模拟量分成4096份;
量程:AD转换器可以接受的模拟量的范围。跟芯片制造有关系,如果超过量程,超量程部分可能会被阉割,重则将芯片烧毁;
分辨率:AD转换器转出来的二进制数,每一格表示多少。例如:10位,分辨率是1/1024,12位,分辨率是1/4096;
转换速率:就是完成一次A/D,D/A转换所耗费的时间。实际应用中,转换速率越快越好,随之成本会越高。适合项目使用即可;

举例说明:
条件:电压量程范围0-5V,A/D,D/A转换位数是10位,精度是0.01V
分辨率为:(5-0)/2exp(10)=0.00488V
例如一次A转换中数字量是1010101010,计算转换中模拟量的过程如下:
11010101010对应的十进制数是6822、数字量对应的模拟电压是682 * (分辨率)0.00488v = 3.33V

二. A/D,D/A转换在系统中存在的方式

CPU外部扩展专用A/D,D/A芯片;
CPU内部集成A/D,D/A模块(内部外设)

早期的单片机基本都是在外部扩展,新的高级一些的单片机是内部集成的;
51单片机内部没有集成,所以使用的是外部扩展,使用ET2046芯片,***并用SPI接口通信***

三. A/D,D/A转换在系统中存在的方式

A/D转换原理图及ET2046芯片介绍
1、原理图

et2046芯片电路图AIN0 – XP+, 对应右边感应器AD1滑动电位器。
AIN1 – YP+, 对应右边NTC1热敏传感器。
AIN2 – VBAT,对应右边GR1热敏传感器。
AIN3 – AUX,暂无对应

2、开发板接线

(1)、SPI接线(接线引脚可随意)

CS -> P0^0;  //SPI片选。
DI -> P0^1;   //SPI 输入;
CLK -> P0^2; //SPI时钟;
DO -> P0^3; //SPI输出;

(2)、模拟输入量

AIN0靠滑动变阻器分压变化;
AIN1靠热敏电阻NTC分压,温度变化电阻变化,导致分压变化;
AIN2靠光敏电阻分压。

四、ET2046芯片介绍

1、主要特点

工作电压范围为 1.5V~5.25V
支持 1.5V~5.25V 的数字 I/O 口
内建 2.5V 参考电压源
电源电压测量( 0V~6V)
内建结温测量功能
触摸压力测量
采用 3 线制 SPI 通信接口
具有自动省电功能

2、引脚功能说明
ET2046分为 单端模式差分模式

在使用VBAT、AUX和TEMP时使用单端模式
XP YP VBAT AUX

在作为触摸屏应用时使用差分模式

引脚

3、ET2046控制字介绍
根据ET2046的芯片,需要先向这个芯片发送一个控制字节,(类似于寻址的地址一样)。控制字节为8bit数据,各位功能分别表示如图:
控制字节及解析数据位高——低分别是:

(1)位8, 开始位:

1时表示一个新的控制字节的开始
为0时表示忽略PIN引脚上的数据

(2)位7 - 位 5,通道选择位: 参照单端模式输入配置图!数据如下:

AIN0 对应 +XP  数据为 : 001011	(没错!就是2个数据)
AIN1 对应  +YP 数据为 : 101
AIN2  对应  VBAT 数据为:010
AIN3 对应  AUX 数据为 : 110

(3)位4,MODE,12/8Bit转换分辨率选择位。

 18bit分辨率
 012bit分辨率

(4)位3,单端模式/差分模式选择位。

1为单端模式
 2为差分模式

(5)位2-位1,低功耗选择位。

11,总处于总处于供电状态
 为00,表示在低功率状态

五、ET2046芯片SPI时序分析

1、时序图
ET2046时序图

时序图分析:***类SPI时序,均为上升沿写入,下降沿读出***。
当cs由高电变低电时, 使能开始(类总开关)。
数据DIN要在DCLK时钟之前准备好(高位在前),
然后按照dclk时钟上升沿写入的规则,在每一个时钟周期上升沿
写入1位控制字节数据,8位数据。
完后用延时一段时间检测BUSY是否在忙,这里不用检测了,
直接延时一段时间+1个时钟周期用以代替BUSY的检测就行。

ET2046 —SPI写时序代码实现:

#include "reg51.h"
#include <intrins.h>

sbit CS   = P0^0;  // 管脚定义
sbit DIN  = P0^1;  
sbit SCLK = P0^2; 
sbit DOUT = P0^3;

#define u16 unsigned int
#define u8 unsigned char

u16 Read_AD_data(u8 dat)
{
	u8 i = 0;  //定义循环变量i
	u16 value = 0;  //用于保存读出的12位分辨率的数据
	CS = 0;
	SCLK = 0;
	// 向et2046写控制字节
	for(i=0; i<8; i++)
	{
		DIN = dat >> 7;
		SCLK = 0;
		SCLK = 1;     //数据 在上升沿读入
		dat <<= 1;
	} 
	for(i=10; i>0; i--);  //for循环用于延时,i=?要根据开发板的晶振选择!
	SCLK = 1;    // 这一个时钟周期用于抵消 BUSY
	_nop_();
	SCLK = 0;
	_nop_();
	//  读et2046数据
	for(i=0; i<12; i++)   // 控制字节的分辨率我们这里选择12bit,“0”
	{
		value = value << 1;  //  这里需要先一移位,12bit移到最高位需要循环n-次
		SCLK = 1;
		SCLK = 0;
		value = value | DOUT;  // DOUT读出的数据也是一位一位的读,
												// 因为一个时钟的下降沿数据被读出
	}
	CS = 1;
	return value;      //返回数据value;
}

程序实现——ad转换uart文本方式发送

/*********** ET2046-SPI时序 .c文件  *****************/

#include "ET2046.h"

/****** 读取et2406数据函数 ********/
u16 Read_Spi(u8 dat)	// 参数是一个局部变量
{
	u8 i = 0;
	u16 value = 0;

	CS  = 0;	  // 时序开始
	CLK = 0;
	for(i=0; i<8; i++)  //向ET2406芯片输入8位控制字节数据
	{
		DIN = dat >> 7; // 上升沿写入,锁存。下降沿读出,锁存,并且高位在先
		CLK = 0;
		CLK = 1;   // 数据在上升沿被写入,根据时序图来看。 	
		dat = dat << 1;
	}

   
	CLK = 0;
	for(i=0; i<6; i++);// 延时
    CLK = 1;	 // 延时一个周期消除BUSY
	_nop_();
	_nop_();
	CLK = 0;
	_nop_();
	_nop_();		

    /* 变量value | DOUT (dout是一个时钟输出一位数据) */
	for(i=0; i<12; i++)		 // 控制字节这里单端模式是12位分辨率选择模式,所以这里需要循环12次
	{
		value = value << 1;	  // 先读出的位为高位,每次向前位移一位让其一直为最高位	
		CLK = 1;
		CLK = 0;		// 数据在下降沿被读出
		value = value | DOUT;
	}
	CS = 1;	   // CS使能结束,拉高,结束整个过程。

	return value;
}

/*********** ET2046-SPI时序 .h文件  *****************/
#ifndef _ET2046_H_
#define _ET2046_H_

// 包含头文件
#include <reg51.h>
#include <intrins.h>

#ifndef u8
#define u8 unsigned char
#endif 

#ifndef u16
#define u16 unsigned int
#endif 

sbit CLK  = P1^0;
sbit CS   = P1^1;
sbit DIN  = P1^2;
sbit DOUT = P1^3;

u16 Read_Spi(u8 dat);

#endif

/*********** urat.c文件  *****************/

/*  串口函数	波特率4800  */

#include "uart.h"

void uart_init(void)	// 串口初始化函数
{
	SCON = 0x50;
	PCON = 0X80;	//波特率加倍  2400mhz加倍到4800,相应的TH1\TL1的计算就要改变 
					//波特率计算公式为:2的smod次方/32*晶振频率*10 000 000/12/(256-TH1)=4800mhz
	TMOD = 0x20;
	TH1 = 243;
	TL1 = 243;

	TR1 = 1;
//	EA = 1;
 
//	ES = 1;
}

void uart_send_byte(uchar a)  // 串口发送函数, uchar为发送一个字节数据
{
	SBUF = a;
	while (!TI);		
		TI = 0;
}


// 这里需要写一个计算电压值“输出”的函数, 发送的方式是以文本的方式发送,需要对应ASSIIC码表
// 表中十进制显示数字0开始,是从48开始的!
void uart_send_voltage(uint a)  // uint a的范围在65536  2的16次方
{
	uchar i = 0;
	
	i = a / 1000;    // 高位计算(千位)
	uart_send_byte(i + '0');  // ‘0’的意思就是+48
	
	uart_send_byte('.');// mv转化v 小数点需要向前进位3个位
	
	a = a % 1000;   // 次高位的计算(百位)
	i = a / 100;
	uart_send_byte(i + 48);
	
	a = a % 100;   // 次低位的计算(十位)
	i = a / 10;
	uart_send_byte(i + 48);
	
	i = a % 10;    // 低位的计算(个位)
	uart_send_byte(i + 48);
	
	uart_send_byte('V'); //  增加‘电压’ 字节符号的显示
	uart_send_byte('\r');//   '\r'+'\n'  = 空格
	uart_send_byte('\n');
}
/*********** urat.h文件  *****************/

#ifndef _uart_H_
#define _uart_H_

#include "reg51.h"

#ifndef uchar
#define uchar unsigned char
#endif

#ifndef uint
#define uint unsigned int
#endif

void uart_init(void);
void uart_send_byte(uchar a);
void uart_send_voltage(uint a);

#endif
/*********** main.c文件  *****************/
#include "ET2046.h"
#include "uart.h"

#define u16 unsigned int
#define u8 unsigned char

#define AD1		0X94
#define NTC1		0XD4
#define GR1		0XA4
 

// 延时函数用于间隔发送的时间
void delay1s(void)   //误差 0us
{
    unsigned char a,b,c;
    for(c=167;c>0;c--)
					//for(b=71;b>0;b--)
        for(b=171;b>0;b--)
            for(a=16;a>0;a--);
    _nop_();  //if Keil,require use intrins.h
}

/*  
定义uart函数发送前8位和后8位读取的et2046数据
void uart_send_davalue(u16 val)
{
		uart_send_byte( (val >> 8) & 0xff );
		uart_send_byte( val & 0xff );
		//uart_send_byte( 0 );
}
*/

//  定义一个函数计算电压值!
void AD_voltage_value(u16 dat)
{
	// 先计算分辨率、(dat/4096)*5000mv=1.22*dat(mv) 
	float resolution_ratio = 1.22;
	// 计算电压值(mv)
	float voltage = resolution_ratio * dat;
	// 用于存储voltage电压值的强制类型转换  舍弃小数部分的整数值
	u16 vol_display = (u16)voltage;   
	
	// 用文本发送函数发送出去   调用void uart_send_voltage(uint vol)函数
	uart_send_voltage(vol_display);
}

//实验刚开始是 滑动变阻器的电压值随着转动,一直显示整数的电压值,考虑时电压值函数计算出来问题
// bug在于uart_send_voltage(vol_display);函数内的变量赋值,更改后ad显示正常1.xxx样式的读数


// 主函数;主程序调用;
void main(void)
{
	u16 val = 0;
	
	uart_init();
	while(1)
	{
		//val = Read_Spi(AD1);   // 滑动变阻——电压
		//val = Read_Spi(NTC1);		 // 热敏电阻
		val = Read_Spi(GR1);   // 光敏电阻
		
//uart_send_davalue(val);
		AD_voltage_value(val);
		delay1s();
	}	
}	



/*      ad转换总结
	1.(ad写入)根据et2046芯片时序设计程序,其中时序部分类似spi时序,但注意是高位在前(读/写)。时序前部分为向et2046写入“8bit控制字节”数据分别对应
		滑动变阻器,光敏电阻以及热敏电阻,相对的控制字节(16进制)的数据也会不一样,芯片资料都会查到。

	2.(读出)读出时高位在前,程序设计上与写入程序为一个程序。最后需要返回一个value数值。

	3.串口通信的加入  选用4800mhz(2400加倍至4800)。函数部分需要加入一个《发送文本》的函数,作用是
		计算ad转换出的数据并按照“千百十个位”的方式依次发送出来。(计算出的是十进制数,这里需要对应ASSIIC码表,当需要输出数字时,
		ASSIIC码表显示是在第48位显示为数字0,所以void uart_send_voltage(uint a)函数在计算“千百十个”位 时需要 +48).

	4.main函数的编写,main函数中需要注意!需要定义一个函数来计算电压值!!!!   如本文中的void AD_voltage_value(u16 dat)函数。
	(5000mv需要unsigned int变量类型来定义,其最大为65536,满足实际应用)。根据公式(dat/4096)*5000mv=1.22*dat(mv)计算出电压值,
		然后调用void uart_send_voltage(uint a)将uint类型数据传入被调函数中然后串口发送十进制电压值在串口助手上。
	
*/
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值