51单片机STC89C52RC——15.1 AD/DA(模数数模)

目的/效果

1 LCD1602 显示 可调电阻、光敏电阻、热敏电阻值(AD)

2 模拟信号控制LED明暗(DA)

一,STC单片机模块

二,AD/DA

2.1 AD/DA 介绍

AD(Analog to Digital):模拟-数字转换,将模拟信号转换为计算机可操作的数字信号

DA(Digital to Analog):数字-模拟转换,将计算机输出的数字信号转换为模拟信号

AD/DA转换打开了计算机与模拟信号的大门,极大的提高了计算机系统的应用范围,也为模拟信号数字化处理提供了可能。

2.1.1 扬声器(麦克风)

扬声器(麦克风),像我们电脑里存的数字音乐就是数字信号,怎么才能将它输出到扬声器进行播放呢?就需要DA将这个数字信号代表的电压转换成实际的电压,形成一个连续的电压波形,再送给麦克风,这样就可以听到数字信号储存的音乐了。

2.1.2 麦克风

麦克风,也可以通过这种电阻分压的形式,将人说话的声音信号转换为一个电压信号,然后通过电压采集,那数字系统就可以采集到这个声音信号了。

2.1.3 热敏电阻

热敏电阻,阻值会随着温度的变化而变化。如果我们把上图的光敏电阻换成热敏电阻,也就可以获取热敏电阻这一点的电压,从而可以知道温度的高低。

2.1.4 光敏电阻

光敏电阻,阻值会随着光线的变化而变化。

2.2 电路模型

模拟量和数字量是成正比的,比如说模拟量是0V,则数字量就是0;如果模拟量是5V,则数字量就是255;如果模拟量是2.5V,则数字量就是255/2;

AD/DA与单片机数据传送可使用并口(速度快、原理简单),也可使用串口(接线少、使用方便)

可将AD/DA模块直接集成在单片机内,这样直接写入/读出寄存器就可进行AD/DA转换,单片机的IO口可直接复用为AD/DA的通道。

2.2.1 AD 装换

AD转换通常有多个输入通道,用多路选择开关连接至AD转换器,以实现AD多路复用的目的,提高硬件利用率

2.2.2 DA 装换

DA的应用一般可以用PWM来代替,所以DA的使用没有AD广泛。

2.3 运算放大器

运算放大器(简称"运放")是具有很高放大倍数的放大电路单元。内部集成了差分放大器、电压放大器、功率放大器三级放大电路,是一个性能完备、功能强大的通用放大电路单元,由于其应用十分广泛,现已作为基本的电路元件出现在电路图中

运算放大器可构成的电路有:电压比较器、反相放大器、同相放大器、电压跟随器、加法器、积分器、微分器等

运算放大器电路的分析方法:虚短、虚断(负反馈条件下)

2.3.1 运放原理

我们这里简单理解下面的电路,

V+,V- 这两个是电源,上正下负,分别可以接5V和-5V,也可以分别接5V和地,一般来说这种接法是比较多。

Un、Up 是输入

Uout 是输出

他的特性如下图

运放的内部工作我们可以认为里面是一个开关,这个开关可以打到VCC即5V的一边,也可以打到GND即0V的一边。

那这个开关什么时候打到VCC这边或GND的一边呢?

这取决于Up和Un的差值。

当Up>Un时,(开关打到VCC)则输出电压就接近于正电源电压Uout=5V

当Up<Un时,(开关打到GND)则输出电压就接近于负电源电压Uout=0V

当Up和Un差不多大时,则输出电压=A(Up-Un)+2.5

A是运放的放大倍率,运放的固有参数,我们一开始并不需要太关注这个参数,只需要知道一般来说A都非常大,可以达到几十万,也就是说这根线会非常陡峭,或者说这部分的宽度非常窄。Up和Un相差几十微伏或者更小时,才会进入这个区间

以上理论知识,下面开始看运放的第一个实际应用。

运放LM358

运放作为一个电压比较器来使用,我们常见的运放LM358。

只需要用到其中一个就可以了,给它接好电源和地,然后Up这边用信号发生器给它输入一个正弦波,Un这边通过这个可调电阻,让它从0V到5V开始变化。​​​​​​

给光敏电阻光照时,Un>Up,运放就输出低电平.

没有光照时,Un<Up,运放就输出高电平.

那么运放作为一个电压比较器来使用时,我们发现这个实际应用中我们主要用到的是运放的这两个区间:

也就是运放的非线性区或者叫饱和区

而运放真正的核心其实是在这一个狭窄的区间里,也就是线性区

但是这个区间实在是太狭窄了,为了能够用到这个区间,需要引入一个概念:负反馈

负反馈

这么说,有了负反馈,运算放大器才能被称为是运算放大器,否则只能被称为放大器。

下面我们来看一下这张图

将Un和输出接在一起,问Un电压是多少?

此时Un大概等于Un=2.9999x

因为只要Un和Up不相等,或者说差的很多,那么输出电压就一定会变成5V或者0V,进而通过这个路径反馈回来,不断修正Un的电压,直到它和Up电压差不多为止。

也就是会对应到这条线上的某一个点,这就是所谓的负反馈。

负反馈的过程

因为当Up>Un时,Uout要从Un上升到5V,上升到3.1V左右时,此时 Uout=Un>Up,Uout又要下降到0,但在下降到2.9V时,Uout=Un<Up,于是Uout又要上升。所以Uout会在 Up 附近震荡,且越来越接近Up。 如果初学者想不明白负反馈的过程也没关系,

我们只需要记住一个非常重要的结论: 当给运放引入负反馈,也就是把输出和Un接在一起时,Un就约等于Up。同时又因为Up和Un差的实在太少了,只相差几十微伏不到,所以我们就认为Up等于Un了。

这里的负反馈不一定是完全短接,通过一个电阻电容连接都是可以的。然后在这些电路中输入输出的关系分别是这样子的(如图中的关系式),你会发现第一个电路实现了比例的运算,第二个电路实现了加法的运算,而这个电路实现了积分的运算。

同向发放大器

假如说流过负载的电流有1A,那么此时Rs这一端大概会产生一个10mV的电压。 这个电压实在太小了,以至于我们没有办法很好的测量到,所以此时运放的这部分电路就派上用处了。假设此时R1是49千欧姆,R2是1千欧姆,那么这个运放就可以把Rs上的电压放大50倍输出。这样我们就可以很方便的通过测量输出电压。反推负载上的电流了。

Uout=(1+R1/R2)Uin=(1+49k/1k)10mV=500mV

运放的两个主要应用场景。 一个作为比较器使用,一个是作为比例放大器来使用

电压比较器
反向放大器
电压跟随器

虚短状态:负反馈状态下负极输入的电压和正极输入的电压是基本相等的。

虚断状态:由于输入阻抗比较大,电流既不从负极输入端流入也不流出。

2.4 DA

2.4.1 DA原理

我们知道了当Rfb=R时的输出电压,那么如果Rfb不等于R呢?电压的表达式怎么来的呢?我们知道这其实是一个我们前面讲过的反向放大器模型。如果我们能算出这一点的电流,那么电流通过反馈电阻分压,就可以得出输出电压了。

由上图分析:两个2R并联,流过两个2R的电流为I0, I1=2I0;阻值则为R。在与经过I的R串联。

同理:

I2=2I1, 阻值2R;

I3=2*I2,总阻值2R

......

I=2*I7,总阻值2R;线路中的总阻值为R;I=Vref/R;

所以,I=2*I7=4*I6=8*I5=16*I4=32*I3=64*I2=128*I1=256*I0;I0=Verf/(256*R);

所以我们可以将电路的I分为256份。I0=I/256;

我们在分析简单的,单D0闭合1时,数字信号为 0000 0001(0x01=1) ,电路电流如下图

电流I02=I-I0=255*I0;

同理可得:当D7-D0 输入 1000 0000(0x80=128) 时I02=I6+I5+I4+I3+I2+I1+I0=I7=128*I0;

D7-D0 0001 0001=0x11=17 ,I02=17*I0;

2.4.2 DAC(PWM)模块 (脉冲宽度调制)

输出电压 Vout=(PWM占空比)×Vh;

2.4.2.1 什么是PWM

​脉冲宽度调制(PWM),是英文"Pulse Width Modulation"的缩写,简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,广泛应用在从测量、通信到功率控制与变换的许多领域中。 ​

2.4.2.2 PWM的频率

是指1秒钟内信号从高电平到低电平再回到高电平的次数(一个周期);

也就是说一秒钟PWM有多少个周期

单位: Hz

表示方式: 50Hz 100Hz

2.4.2.3 PWM的周期

T=1/f

周期=1/频率

50Hz = 20ms 一个周期

如果频率为50Hz ,也就是说一个周期是20ms 那么一秒钟就有 50次PWM周期

2.4.2.4 占空比

是一个脉冲周期内,高电平的时间与整个周期时间的比例

单位: % (0%-100%)

表示方式:20%

周期为T
T1为高电平时间
T2 为低电平时间

假设周期T为 1s 那么频率就是 1Hz 那么高电平时间0.5s ,低电平时间0.5s 总的占空比就是 0.5 /1 =50%

控制LED的通断时间比例,占空比。如下图

输出数字量 (D7~D0)=V_IN/V_REF×256...... 结果取整

其实以上的工作原理是利用的二分法查找这种方法

比如,输入一个在0V~5V范围的模拟电压,我们首先让DAC产生一个2.5V的中间值和这个未知电压进行比较,如果这个未知电压比2.5V小的话,我们就把DAC调成(0+2.5)/2=1.25V再进行对比,如果比未知电压大的话,我们就把ADC调成(1.25+2.5)/2的电压和它对比,如此循环,最终8次对比就可以和未知电压的值接近了。

根据这个例子,我们其实是将DAC的8位数据从左往右,就是将第一位置1,第二位置1......这样开始和未知电压进行对比。那么比如首先把DAC的2.5V,即1000 0000(也即128)和未知电压进行对比,如果未知量比这个数小,那么我们把第一位置0,第二位置1,即0100 0000(表示64,也即1.25V),如果未知量比这个数大,那么这个1就保留,再把第三个置1,即0110 0000(表示96,也即(1.25+2.5)/2)再比较......依次判断8位,最终这8位数据取出来就是表示我们的未知电压。

2.6 AD/DA芯片

我们主要介绍ADC0809和DA0832,这两个芯片是比较老旧的,但是它比较经典。所以我们主要了解它们的原理,实际应用的话还是可以用现在新的一些开发板上的AD、DA芯片。

2.6.1 DA0832

2.6.2 ADC0809

2.6.3 XPT2046

2.6.3.1 简介

XPT2046是一款4线制电阻式触摸屏控制器,内含12位分辨率125KHz转换速率逐步逼近型A/D转换器。XPT2046支持从1.5V到5.25V的低电压I/0接口。XPT2046能通过执行两次A/D转换查出被按的屏幕位置,除此之外,还可以测量加在触摸屏上的压力。内部自带⒉.5V参考电压,可以作为辅助输入、温度测量和电池监测之用,电池监测的电压范围可以从OV到6V。XPT2046片内集成有一个温度传感器。在2.7V的典型工作状态下,关闭参考电压,功耗可小于0.75mW。XPT2046采用微小的封装形式:TSSOP-16,QFN-16和VFBGA一48。工作温度范围为-40℃~~+85℃。与 ADS7846、TSC2046、AK4182A完全兼容。

2.6.3.2 电路图

单端模式输入配置 如下表

差分模式输入配置

2.6.3.3 时序图

24时钟周期转换时序

16时钟转换时序

详细时序如下

准备读数据
bash 复制代码
	XPT2046_CS=0;//低电平
	Delay_ms(1);
	XPT2046_DCLK=0;//低电平
	Delay_ms(1);
写入地址
bash 复制代码
	//发地址
	for(i=0;i<8;i++)
	{
		XPT2046_DIN=Addr&(0x80>>i);    
		XPT2046_DCLK=1;//高电平    
		Delay_ms(1);
		XPT2046_DCLK=0;//低电平
		Delay_ms(1);
	}
接收数据
bash 复制代码
	//收数据
	for(i=0;i<12;i++)
	{
		AD_Value=AD_Value<<1;
		Delay_ms(1);
		XPT2046_DCLK=1;//高电平
		Delay_ms(1);
		XPT2046_DCLK=0;//置低电平,下降沿输出数据
		Delay_ms(1);
		AD_Value=AD_Value|XPT2046_DOUT;//取数,转换  
	}
重置

只需要将CS置高电平

bash 复制代码
	//重置
	XPT2046_CS=1;
2.6.3.4 读取数据完整代码
bash 复制代码
/**
 * 函    数:读取信号值
 * 参    数:Addr 信号值地址
 * 返 回 值:无
 */
int XPT2046_ReadAD(unsigned char Addr)
{
	int AD_Value,i;
	XPT2046_CS=0;//低电平
	Delay_ms(1);
	XPT2046_DCLK=0;//低电平
	Delay_ms(1);
  
	//发地址
	for(i=0;i<8;i++)
	{
		XPT2046_DIN=Addr&(0x80>>i);    
		XPT2046_DCLK=1;//高电平    
		Delay_ms(1);
		XPT2046_DCLK=0;//低电平
		Delay_ms(1);
	}
	//收数据
	for(i=0;i<12;i++)
	{
		AD_Value=AD_Value<<1;
		Delay_ms(1);
		XPT2046_DCLK=1;//高电平
		Delay_ms(1);
		XPT2046_DCLK=0;//置低电平,下降沿输出数据
		Delay_ms(1);
		AD_Value=AD_Value|XPT2046_DOUT;//取数,转换  
	}
	//重置
	XPT2046_CS=1;//
	return AD_Value;
}

三,创建Keil项目

详细参考:51单片机STC89C52RC------创建Keil项目-CSDN博客

四,代码

项目完整代码请参考《STC89C52RC: 51单片机学习资料、代码 - Gitee.com

main.c

bash 复制代码
#include <REGX52.H>

#include "XPT2046.h"
#include "Delay.h"
#include "lcd1602.h"
#include "timer0.h"
unsigned int Counter,Compare=90,
  Model=0;// 模式切换  0-AD(模数),1-DA(数模)
/**
 * 函    数:显示可调电位器的值
 * 参    数:无
 * 返 回 值:无
 */
void ShowDianWei()
{
	int  AIN0;
	float ADC_VOL;

	AIN0=XPT2046_ReadAD(0x94);
	//LCD_ShowBinNum(1,1,AIN0,12);
	ADC_VOL=(5.0*AIN0/4096)*10;
	LCD_ShowNum(2,1,((int)ADC_VOL)/10,1);
	LCD_ShowString(2,2,".");
	LCD_ShowNum(2,3,((int)ADC_VOL)%10,1);
	LCD_ShowNum(2,4,((int)(ADC_VOL*10))%10,1);
	LCD_ShowNum(2,5,((int)(ADC_VOL*100))%10,1);
}
/**
 * 函    数:显示光敏电阻的值
 * 参    数:无
 * 返 回 值:无
 */
void ShowGuangMin()
{
	int  AIN1;
	AIN1=XPT2046_ReadAD(0xA4);
	//LCD_ShowBinNum(1,1,AIN1,12);
	LCD_ShowNum(2,7,AIN1/1000,1);
	LCD_ShowNum(2,8,AIN1%1000/100,1);
	LCD_ShowNum(2,9,AIN1%100/10,1);
	LCD_ShowNum(2,10,AIN1%10,1);
  
}
/**
 * 函    数:显示热敏电阻的值
 * 参    数:无
 * 返 回 值:无
 */
void ShowReMin()
{
	int  AIN2;

	AIN2=XPT2046_ReadAD(0xD4);
	//LCD_ShowBinNum(1,1,AIN2,12);
	LCD_ShowNum(2,12,AIN2/1000,1);
	LCD_ShowNum(2,13,AIN2%1000/100,1);
	LCD_ShowNum(2,14,AIN2%1000%100/10,1);
	LCD_ShowNum(2,15,AIN2%1000%100%10,1);
}
/**
 * 函    数: 数字型号 转 模拟信号
 * 参    数:无
 * 返 回 值:无
 */
void SetDA()
{
	int i=0;
	for(i=0;i<100;i++)
	{
		Compare=i;
		Delay_ms(5);
	}

	for(i=100;i>0;i--)
	{
		Compare=i;
		Delay_ms(5);
	}
}
/**
 * 函    数:主函数
 * 参    数:无
 * 返 回 值:无
 */
void main()
{
	if(Model==1) {
		Timer0_Init();//初始化定时器 
	}
	LCD_Init();
	while(1)
	{
		switch(Model)
		{
			case 0:
			{
				//依次显示 电位器   光敏   热敏
				LCD_ShowString(1,1," DW    GM   RM");
				ShowDianWei();//电位器
				Delay_ms(10);
				ShowGuangMin();//光敏电阻
				Delay_ms(10);
				ShowReMin();//热敏电阻
				Delay_ms(10); 
				break;
			}
			case 1:
			{      
				LCD_ShowString(1,1,"DA            ");
				LCD_ShowString(2,1,"                ");
				SetDA();
				break;
			}

		}
	}  
	while(1)
	{
	}
}

 /**
  * 函    数:定时器中断函数
  * 参    数:无
  * 返 回 值:无
  */
void Timer0_Routine() interrupt 1
{
	TL0 = 0x9C;			//设置定时初值
	TH0 = 0xFF;			//设置定时初值
	Counter++;
	Counter%=100;
	if(Counter>=Compare)
	{
		P2_1=1;//熄灭
	}
	else {
		P2_1=0;//点亮
	}
}

五,代码编译、下载到51单片机

代码编译请参考

51单片机STC89C52RC------代码编译-CSDN博客

代码下载请参考

《51单片机STC89C52RC------STCAI-ISP代码下载-CSDN博客

相关推荐
yutian06062 小时前
Keil MDK下载程序后MCU自动重启设置
单片机·嵌入式硬件·keil
析木不会编程5 小时前
【小白51单片机专用教程】protues仿真独立按键控制LED
单片机·嵌入式硬件·51单片机
枯无穷肉9 小时前
stm32制作CAN适配器4--WinUsb的使用
stm32·单片机·嵌入式硬件
不过四级不改名6779 小时前
基于HAL库的stm32的can收发实验
stm32·单片机·嵌入式硬件
嵌入式大圣10 小时前
单片机UDP数据透传
单片机·嵌入式硬件·udp
云山工作室10 小时前
基于单片机的视力保护及身姿矫正器设计(论文+源码)
stm32·单片机·嵌入式硬件·毕业设计·毕设
嵌入式-老费10 小时前
基于海思soc的智能产品开发(mcu读保护的设置)
单片机·嵌入式硬件
qq_3975623112 小时前
MPU6050 , 设置内部低通滤波器,对于输出数据的影响。(简单实验)
单片机
liyinuo201712 小时前
嵌入式(单片机方向)面试题总结
嵌入式硬件·设计模式·面试·设计规范
艺术家天选13 小时前
STM32点亮LED灯
stm32·单片机·嵌入式硬件