51单片机DAC数模转换

目录

  • 1.DAC介绍
    • [1.1 DAC简介](#1.1 DAC简介)
    • [1.2 DAC主要技术指标](#1.2 DAC主要技术指标)
    • [1.3 DAC工作原理](#1.3 DAC工作原理)
  • 2.PWM介绍
  • [3. 硬件设计](#3. 硬件设计)
  • 4.软件设计
    • [4.1 PWM代码编写](#4.1 PWM代码编写)
    • [4.2 main函数编写](#4.2 main函数编写)
    • 4.3整合代码
  • 5.实验现象

1.DAC介绍

DAC(Digitaltoanalogconverter)即数字模拟转换器,它可以将数字信号转换为模拟信号。

1.1 DAC简介

它的功能和ADC功能相反的,ADC是将模拟信号转换成数字信号,在常见的数字信号系统当中,大部分传感器信号会被转换成电压信号,ADC就是将电压模拟信号转换成易于计算机存储处理的数字编码,由计算机处理完成之后,再由DAC输出电压模拟信号,那么这个电压模拟信号常常是用来驱动某些执行器件,让人类易于感知,比如音频信号的一个采集和还原就是这样的一个过程,首先它采集声音的模拟信号,经过内部的处理器处理,处理之后再通过DAC输出一个模拟信号,这是ADC和DAC结合的一个过程。

1.2 DAC主要技术指标

(1)分辨率

它是输入数字量最低有效位发生变化的时候,所对应的输出模拟量的变化,它反映的是输出模拟量最小变化值,比如我们有一个8位的DAC,参考电压是5V,当DAC最小精度5 / 2的8次方,这个8就是DAC的位数,这个5表示满量程的输出,假设DAC参考电压是5V,它就可以输出0V- 5V模拟信号,最大值是5V,最小量程就是5 / 2的8次方伏特,大约是19.5mV。

那么如何指定对应输出电压?

可以通过单片机输出数字量,假设输出数字量是一个字节,8位嘛,假设这个字节是0X3F,那么就是将这个字节数据0x3f * 19.5就是对应的模拟电压值,这个有点类似于ADC的转换。

假设我们分辨率越高,假设是12位的,5 / 2的12次方,分母越大对应的精度越高,2的12次方是4096,5/4096大约是1.22mv,所以可以知道分辨率越高,它对应的值越小。

(2)线性度

实际转换的特性曲线与理想直线特性之间的最大偏差,其实就是实际转换的特性曲线与理想状态的差别。

(3)绝对精度和相对精度

绝对精度是指在整个刻度范围内任意输入数码所对应的模拟量实际输出值与理论值之间最大误差。

相对精度是指与绝对精度同一个含义,它是用最大误差,相对于满刻度百分比来表示,它这里还有零点误差,非线性误差,噪声等等。

(4)建立时间

输入的数字量发生满刻度变化的时候,输出模拟变化达到满刻度值它所需要的时间,它是一个动态的指标参数,根据建立的时间长短可以可以将DAC分成几种等级,超高速、高速、中速和低速这几档,通常情况下主要应用于DAC时候主要应用分辨率和建立的时间。

1.3 DAC工作原理

DAC输出电压计算公式:VO=Vrefz/256

DAC主要由数字寄存器D0 ~ D7,一共8位,还有模拟电子开关,以及位权网络,以及求和运算放大器,VREF基准电压源组成,这个是DAC内部构造。
用于存储,存储寄存器数字量各个量的位码,分别是从D0 ~ D7,用来控制对应的电子模拟开关,那么这个开关控制了,如果开关打到1的时候,对应的位权网络上产生的与其位权成正比的电流值,输入到运算放大器经过求和,因为每一个位都控制了电子开关,通过这个电子开关,如果导通,可以将所有对应的电流汇总输入到运算放大器,最终转换成电压值V0。
内部结构对应的计算关系:DAC输出电压计算公式:VO=Vref
z/256,这个VREF是基准电压也是参考电压,可以根据这个参考电压值输出的电压有一定的范围,比如我们的参考电压VREF是5V,对应的输出V0就是0 ~ 5V范围,如果参考电压是3.3V,对应的输出电压V0就是0 ~ 3.3V范围,所以我们要输出模拟电压值V0,重要的参考源是VREF,所以这个VREF根据需求选择电压,比如我们的51单片机开发板,VREF参考电压源直接绑定到系统电压5V上,所以VREF就是等于5V。

Z表示的是单片机给的数字量,也就是D0 ~ D7这8位一个字节的数据,假设是0X01,那么这个Z就是1,这里的Z对应的是10进制数,我们0X01对应的是16进制数,所以需要将D0~D7表示的16进制数转换成10进制数,但是在实际编程当中,会自动转换成10进制,所以直接将这个数字量给Z就可以了。

最后除以256,这个256表示的是DAC的分辨率,也就是2的8次方,因为它是8位的,所以是2的8次方,也就是256。

以上就是它是计算关系,所以我们需要控制单片机的数字量,就可以输出对应的电压值,这个DAC电压计算公式,内部就构就是通过单片机数字量控制对应的电子开关,控制了电子开关对应的电流,就可以放到运算放大器当中,从而输出对应的电压。

如果位数不同对应的电子开关,位权都不同,如果是8位的,它这里位权也是有8个的,电阻不一定是平均分配的,如果我们是12位的DAC,对应的也就是12个电子开关,对应的位权也是12个,所以位数不同,对应的电子开关和位权也是不同。

2.PWM介绍

PWM是PulseWidthModulation的缩写,中文意思就是脉冲宽度调制,简称脉宽调制。

它是利用微处理器,数字输出来来对模拟电子开关进行控制一种非常有效的技术,它的控制非常简单,灵活,动态响应好,那么它这些优点就称为了电力电子技术应用最为广泛的控制方式。

PWM它广泛应用于测量,通信,功率控制与变换,像电机控制,伺服控制,还有调光,开关电源,这些基本能够见到PWM的应用。

PWM其实是对模拟信号电平数字编码一种方法,通过高分辨率计数器的使用,方波的占空比调制,用来对具体模拟信号电平进行编码,PWM信号依然是数字量,虽然说用PWM模拟DAC的输出,DAC输出是一个模拟信号,PWM信号仍然是数字的,因为在给定的任何时刻,满幅值的直流供电要么就是有,要么就是没有,电压或者电流源是一种通路或者是断路,重复脉冲序列被加载到负载当中,通路的时候直流电被加到负载上,那么断路的时候,就断开,只要带宽足够,任何模拟值都是可以通过PWM来模拟。

从这张图中,左边上面坐标图是模拟信号图,下面坐标图是数字信号图,我们知道在计算机系统当中只能识别1和0,对于51单片机来说要么输出高电平5V,要么输出低电平0V,假如我要输出1.5V的话,那么就需要通过相应的处理,比如这里所说的PWM输出。

从下面坐标图中知道,阴影部分高电平在一个周期所持续的时间,低电平在一个周期所持续的时间,从上面模拟信号坐标图看到,电压越小,下面数字信号坐标图阴影部分越窄,也就是高电平持续的时间越少,电压越大,下面数字信号坐标图阴影部分越宽,也就是高电平持续的时间越多,可以看到周期时间是不变的,唯一变化的是占空比,高电平与低电平所持续的时间发生了变化。

占空比:在相同的周期之内,高电平的所占据整个周期的时间。

PWM输出其实就是对外输出脉宽可调,也就是占空比可调的一个方波信号,信号的频率是由T来决定的,从图中可以知道周期始终是不变的,唯一变化的是占空比,占空比就是高电平占据整个周期的比例,我们的占空比改变对应的电压输出就会改变,所以可以通过PWM来模拟DAC的功能。

对于51单片机来说,这个周期我们可以通过定时器来产生固定的一个周期,占空比可以通过在一个周期之内,假设这个周期是1ms,划分了100份,每一份就是0.01个ms进行采样,可以通过占空比的调节可以改变DAC电压,只要我们带宽足够,可以通过PWM输出任意模拟电压值。

PCF8591这个芯片采用IIC通信,只需要对相应命令寄存器操作,操作之后可以直接对这个芯片的通道来输出一个模拟信号,这种方式是直接通过DAC的芯片。

DAC0832比较传统的DAC的芯片,它也可以通过程序编写,来输出模拟信号。

3. 硬件设计

首先通过单片机通过对P21输出一个PWM波,PWM波经过RC电路,然后LM358构成的一个电路是一个跟随电路,跟随电路就是,输入是什么,输出也就是什么值,所以可以通过P21引脚输入PWM波进来,那么输出的DAC对应的模拟信号的输出,在DAC输出那里接了一个发光二极管,通过发光二极管的亮度变化可以知道DAC电压的改变,所以可以通过指示灯状态来观察DAC输出的情况。

而右边J52有两个端子,1号端子就是DAC控制电压范围变化,2号端子可以通过ADC采集电压变化的端子,如果连接在一起,那么可以通过2号端子采集1号端子电压的变化范围了。

4.软件设计

本章所要实现的功能是:DAC(PWM)模块上的指示灯DA1呈呼吸灯效果,由暗变亮再由亮变暗。

4.1 PWM代码编写

c 复制代码
u8 gtime_h = 0;
u8 gtime_l = 0;
u8 gtime_scale = 0;
u8 gdult = 0;

//初始化
void pwm_init(u8 time_h, u8 time_l, u8 time_scale, u8 dult) 
{
	gtime_h = time_h;		   //设定时间
	gtime_l = time_l;
	gtime_scale	= time_scale;  //设定周期时间 比如定时器设置时间是0.01ms,如果time_scale是100
	 						   //那么一个周期时间是1ms,它还有一个作用,将一个周期时间划分100份
							   //每份是0.01ms

	gdult = dult;			   //用来设置高电平与低电平持续的时间

	TMOD|=0X01;//选择为定时器0模式,工作方式1
	TH0=gtime_h;
	TL0=gtime_l;
	ET0=1;//打开定时器0中断允许
	EA=1;//打开总中断
	TR0=1;//打开定时器	
}

//修改占空比
void pwm_set_dult_cycle(u8 dult) 
{
	gdult = dult;//修改占空比
}

void pwm() interrupt 1 {

	static u16 time = 0;

	TH0=gtime_h;
	TL0=gtime_l;
	
	time++;

	if (time >= gtime_scale) //设定周期
		time = 0;

	if (time <= dult) PWM = 1; //通过gtime_scale控制高低电平持续时间
	else PWM = 0;
}

首先初始化函数,初始化设置定时器0,定时0.1ms, pwm_init函数有4个参数,第一个,第二个参数用来设置定时的时间,设置的初始值,定时0.1ms,晶振频率是11.0592MHZ,然后参数三和参数四,参数三是设定周期时间,比如我定时器设置的定时时间是0.01ms,如果参数三设置100,那么一个周期内定时1ms,也可以理解成,一个周期 = 高电平持续时间 + 低电平持续时间一共是1ms,而参数3就是将1ms划分100份,每一份是0.01ms,参数四设置占空比,就是在一个周期持续的时间是1ms,高电平持续的时间持续多久用参数四控制,比如参数4设置10,并且一个周期持续时间是1ms,高电平持续时间就是0.01 * 10就是0.1ms,通过参数4设置

4.2 main函数编写

c 复制代码
#include "public.h"
#include "pwm.h"	  

void main() 
{
	u8 time_h = 0XFF;
	u8 time_l = 0XF6;    //定时0.01ms 晶振频率11.0592MHZ
	u8 time_scale = 100; //周期1ms 0.01 * 100就是1ms
	u8 dult = 0;		 //如果dult是10,高电平持续时间是0.01 * 10 = 0.1ms
						 //在一个周期1ms时间内,高电平不断持续的时间在0.1ms
	u8 dir = 0;
	u8 duty = 0;

	pwm_init(time_h, time_l, time_scale, dult);
	while(1) 
	{
		if (dir == 0) 
		{
			duty++;
			if (duty == 90) dir = 1;
		}
		else 
		{
			duty--;
			if (duty == 0) dir = 0;
		}
		pwm_set_dult_cycle(duty);
		delay_ms(1);	  
	}
}

既然高电平持续的时间是通过参数四控制,那么我就在主函数不断改变参数四的值,比如定时是0.01ms,设定周期是0.01 * 100就是1ms,如果让高电平持续的时间没有达到0.01 * 90 = 0.9ms的话,那么高电平持续的时间不断+1,如果达到了,就不断增加低电平的持续时间,减少高电平持续的时间,低电平持续时间是整个周期时间就是1ms,就不断增加高电平持续时间到90,最终的效果是呼吸灯,慢慢的变亮,慢慢的变暗的效果。

4.3整合代码

main.c

c 复制代码
#include "public.h"
#include "pwm.h"	  

void main() 
{
	u8 time_h = 0XFF;
	u8 time_l = 0XF6;    //定时0.01ms 晶振频率11.0592MHZ
	u8 time_scale = 100; //周期1ms 0.01 * 100就是1ms
	u8 dult = 0;		 //如果dult是10,高电平持续时间是0.01 * 10 = 0.1ms
						 //在一个周期1ms时间内,高电平不断持续的时间在0.1ms
	u8 dir = 0;
	u8 duty = 0;

	pwm_init(time_h, time_l, time_scale, dult);
	while(1) 
	{
		if (dir == 0) 
		{
			duty++;
			if (duty == 90) dir = 1;
		}
		else 
		{
			duty--;
			if (duty == 0) dir = 0;
		}
		pwm_set_dult_cycle(duty);
		delay_ms(1);	  
	}
}

pwm.c

c 复制代码
#include "pwm.h"

u8 gtime_h = 0;
u8 gtime_l = 0;
u8 gtime_scale = 0;
u8 gdult = 0;

//初始化
void pwm_init(u8 time_h, u8 time_l, u8 time_scale, u8 dult) 
{
	gtime_h = time_h;		   //设定时间
	gtime_l = time_l;
	gtime_scale	= time_scale;  //设定周期时间 比如定时器设置时间是0.01ms,如果time_scale是100
	 						   //那么一个周期时间是1ms,它还有一个作用,将一个周期时间划分100份
							   //每份是0.01ms

	gdult = dult;			   //用来设置高电平与低电平持续的时间

	TMOD|=0X01;//选择为定时器0模式,工作方式1
	TH0=gtime_h;
	TL0=gtime_l;
	ET0=1;//打开定时器0中断允许
	EA=1;//打开总中断
	TR0=1;//打开定时器	
}

//修改占空比
void pwm_set_dult_cycle(u8 dult) 
{
	gdult = dult;//修改占空比
}

void pwm() interrupt 1 {

	static u16 time = 0;

	TH0=gtime_h;
	TL0=gtime_l;
	
	time++;

	if (time >= gtime_scale) //设定周期
		time = 0;

	if (time <= dult) PWM = 1; //通过gtime_scale控制高低电平持续时间
	else PWM = 0;
}

pwm.h

c 复制代码
#ifndef _pwm_H
#define _pwm_H

#include "public.h"

sbit PWM = P2^1;
//初始化
void pwm_init(u8 time_h, u8 time_l, u8 time_scale, u8 dult);

//修改占空比
void pwm_set_dult_cycle(u8 dult);

#endif

public.c

c 复制代码
#include "public.h"
void delay_10us(u16 us) {
	while(us--);
}

void delay_ms(u16 ms)
{
	u16 i,j;
	for(i=ms;i>0;i--)
		for(j=110;j>0;j--);
}

public.h

c 复制代码
#ifndef _public_H
#define _public_H

#include "reg52.h"

typedef unsigned int u16;
typedef unsigned char u8;

void delay_10us(u16 us);
void delay_ms(u16 ms);

#endif

5.实验现象

PWM实验现象

相关推荐
项目題供诗2 小时前
51单片机入门-LED点阵屏(九)
单片机·嵌入式硬件·51单片机
恶魔泡泡糖2 小时前
51单片机ADC模数转换
单片机·嵌入式硬件·51单片机
项目題供诗2 小时前
51单片机入门-DS1302时钟(十)
单片机·嵌入式硬件·51单片机
单片机设计星球2 小时前
51单片机的【智能台灯】仿真设计
单片机·嵌入式硬件·51单片机
智者知已应修善业2 小时前
【51单片机8位密码锁】2023-2-22
c语言·经验分享·笔记·单片机·嵌入式硬件·算法·51单片机
weiyvyy2 小时前
机器人嵌入式开发趋势-开源生态与标准化
人工智能·嵌入式硬件·机器人·开源·信息化
weiyvyy2 小时前
无人机嵌入式开发实战-飞控系统原理与架构
人工智能·嵌入式硬件·机器人·无人机
深圳市九鼎创展科技2 小时前
国产高性能 AIoT 核心板!九鼎创展 Z3576 核心板全面解析(基于瑞芯微 RK3576)
大数据·linux·人工智能·嵌入式硬件·ubuntu
猫猫的小茶馆2 小时前
【Linux 驱动开发】STM32MP1 + GT911 触摸显示系统开发笔记
linux·arm开发·驱动开发·stm32·单片机·嵌入式硬件·mcu