DAC简介

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

技术指标
1)分辨率
分辨率是DAC能够生成的模拟输出的精度,通常以比特数(位)来表示

DAC的分辨率是输入数字量的最低有效位(LSB)发生变化时,所对应的输出模拟量(电压或电流)的变化量。它反映了输出模拟量的最小变化值。分辨率与输入数字量的位数有确定的关系,可以表示成 FS /(2^n)。FS 表示满量程输入值,n 为二进制位数。对于 5V 的满量程,采用8位的 DAC 时,分辨率为 5V/256=19.5mV;当采用 12 位的 DAC 时,分辨率则为 5V/4096=1.22mV。显然,位数越多分辨率就越高

2)线性度
线性度(也称非线性误差)是实际转换特性曲线与理想直线特性之间的最大偏差。常以相对于满量程的百分数表示。如±1%是指实际输出值与理论值之差在满刻度的±1%以内。

3)绝对精度和相对精度
绝对精度(简称精度)是指在整个刻度范围内,任一输入数码所对应的模拟量实际输出值与理论值之间的最大误差。

4)建立时间
建立时间是指输入的数字量发生满刻度变化时,输出模拟信号达到满刻度值的±1/2LSB 所需的时间。

是描述 D/A 转换速率的一个动态指标。根据建立时间的长短,可以将 DAC 分成超高速(<1μS)、高速(10~1μS)、中速(100~10μS)、低速(≥100μS)几档。

DAC工作原理

T型电阻网络DA转换器

T型电阻网络DA转换器即是dac0832芯片内部结构(8位DA转换器)
在这里插入图片描述
在这里插入图片描述
反向放大器(下列式子还需添负号
在这里插入图片描述

  • V0是输出的模拟电压
  • VREF:参考电压,一般接在系统电源上,即参考电压是5v
  • D1、D2、D3、D4、D5、D6、D7是8位的输入数字量,通过给予高低电平控制每个位的接线(0或1),确定单片机给的数字量
  • 8位输入数字量将参考电压(VREF)进行256份等分
  • Rfb反馈电阻,接在运算放大器的负端,则运算放大器处在负反馈的状态下,可以使用虚短和虚断来分析电路。
  • 虚断和虚短可以查阅这篇博客————运算放大器详解
    电路分析:利用虚短概念,则v+和v-电压相等。而v+接地,则v-也接地,v+和v-对应的整条接线都是0v。

为什么要接地呢——为了不影响DA电阻网络的分压和分流(这样做可以保证接0或者接1都不会影响DA电路)

初高中我们学过并联电路

因此易得DA电路总阻值为R 电流:
I1=2I0
I2=2*I1

I7=2I6
实现了位权(二进制每一位)

PWM型DA转换器

概念
PWM(脉宽调制)是一种通过调整脉冲信号的占空比来实现对信号平均功率或能量的控制技术。它在电子领域中被广泛应用,特别是在控制系统、电源管理、电机驱动、通信和LED调光等方面。
在这里插入图片描述
(电压跟随器+滤波器)

PWM 的输出其实就是对外输出脉宽可调(即占空比调节)的方波信号,信号频率是由 T 的值决定,占空比由 C 的值决定。其示意图如图所示:
在这里插入图片描述
从上图中可以看到,PWM 输出频率是不变的,改变的是 C 的值,此值的改变将导致 PWM 输出信号占空比的改变。

  • 占空比其实就是一个周期内高电平时间与周期的比值
  • 频率的话可以使用 51 单片机的定时器确定。

在这里插入图片描述

  • VH:高电平电压

实现方式
硬件实现

比较器和计数器:典型的PWM生成电路使用计数器来生成周期性的计时信号,并使用比较器来控制脉冲的宽度。 脉冲宽度调制器(PWM Controller):一些现代控制器和微处理器集成了PWM模块,能够直接生成PWM信号,简化了设计和实现过程。

软件实现

在软件层面,可以使用编程语言如C语言或Python编写PWM生成算法,通过周期性的改变IO口的高低电平来模拟PWM信号。

本次实验使用PWM(软件模拟)

软件模拟PWM

在嵌入式系统或需要模拟PWM型DA转换的应用中,通常可以通过软件来模拟实现。PWM型的DA转换(数字到模拟转换)利用脉宽调制的原理,通过调节脉冲宽度的方式来控制输出电压的大小,从而模拟出连续变化的模拟信号。

基本思路:使用定时器中断模拟PWM信号

步骤
1)选择定时器和IO口
(通常情况下,interrupt 0 通常用于外部中断,例如外部中断(INT0),而 interrupt 1 则通常用于定时器中断,例如定时器0的溢出中断)

选择一个定时器作为PWM生成的计时器,并且选择一个IO口作为PWM的输出口。

在这里插入图片描述
选择为定时器 0 模式,工作方式 1
TMOD|=0X01;

IO口:从上图中可知,PWM 输出控制管脚接在单片机 P2.1 管脚上,DAC1 为 PWM 输出信号,将其连接一个 LED,这样可以通过指示灯的状态直观的反映出 PWM 输出电压值变化

2)初始化定时器

设置定时器的工作模式和时钟源,使其能够按照一定的频率产生中断。 编写PWM生成函数:

TMOD|=0X01; //选择为定时器 0 模式,工作方式 1
TH0 = gtim_h; //定时初值设置
TL0 = gtim_l;
ET0=1;//打开定时器 0 中断允许
EA=1;//打开总中断
TR0=1;//打开定时器

3)在定时器的中断服务函数中编写PWM生成的逻辑

根据需要,可以采用简单的计数方式或者更复杂的算法来生成不同占空比的PWM信号。

void pwm(void) interrupt 1 //对应定时器 0 中断函数
{
static u16 time=0;
TH0 = gtim_h; //定时初值设置
TL0 = gtim_l;
time++;
if(time>=gtim_scale)//PWM 周期=定时器初值*gtim_scale,重新开始计数
	time=0;
if(time<=gduty)//占空比
	PWM=1;
else
	PWM=0;
}
  • pwm_init 函数有 4 个入口参数,tim_h 和 tim_l 为定时器定时初值,即进入中断时间;
  • tim_scale 参数为 PWM 的周期倍数,使用该值乘以定时器初值可得
    出 PWM 的周期
  • time 自增,表示经过的计数时间。
  • 判断 time 是否超过了 gtim_scale,即PWM的周期,如果超过则重新开始计数。
  • 根据 time 和 gduty 控制PWM输出 PWM 的高低电平。

4)设置输出电压

根据PWM信号的占空比,计算相应的输出电压值。通常,占空比为50%时输出电压会达到输入电压的一半,以此类推。

pwm_set_duty_cycle(duty);//设置占空比
void pwm_set_duty_cycle(u8 duty)
{
	gduty=duty;//使用全局变量接收,方便中断函数使用占空比gduty
}

5)实现模拟输出

使用PWM信号控制IO口的高低电平,从而通过RC滤波电路或者直接连接负载,产生模拟的电压输出。

if(time<=gduty)//占空比
	PWM=1;
else
	PWM=0;

呼吸灯实验

使用DAC(PWM)模块电路
在这里插入图片描述

  • LM358 芯片与这些电容电阻构成了一个跟随电路,即输入是多少, 输出即为多大电压,输出电压范围是 0-5V。
  • 输出信号由 J52 端子的 DAC1 引出,在其端子上还有一个 AIN3 脚,它是上一章介绍 ADC 时的外部模拟信号输入通道。
  • 如果使用短接片将 DAC1 和 AIN3 短接,这样就可以使用 XPT2046 芯片采集检测PWM 输出信号

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

代码文件
pwm.c

#include "pwm.h"

u8 gtim_h=0;//定时器初值高位
u8 gtim_l=0;//定时器初值低位
u8 gduty=0;//保存PWM占空比
u8 gtim_scale=0;//保存PWM周期=定时器初值*gtim_scale


//初始化PWM
void pwm_init(u8 tim_h,u8 tim_l, u16 tim_scale,u8 duty)
{
 	//全局变量接收数据
	gtim_h=tim_h;
	gtim_l=tim_l;
	gduty=duty;
	gtim_scale=tim_scale;

	//定时器中断内容
	TMOD|=0x01;//选择定时器0模式,工作方式1
	//给定时器初值
	TH0=gtim_h;
	TL0=gtim_l;
	ET0=1;//开启定时器0中断允许
	EA=1;//打开总中断
	TR0=1;//打开定时器
}

//设置占空比
void pwm_set_duty_cycle(u8 duty)
{
 	gduty=duty;
}

//定时器中断函数

void pwm(void) interrupt 1   //定时器0中断
{
	//静态变量,用来保存PWM周期内的计数
 	static u16 time=0;

	//定时初值设置
	TH0=gtim_h;
	TL0=gtim_l;
	
	time++;

	if(time>=gtim_scale) time=0;//超过PWM周期重置time
	//根据占空比设置高低电平
	if(time<=gduty) PWM=1;
	else PWM=0;
}

main.c

#include "public.h"
#include "pwm.h"

//PWM模拟呼吸灯

void main()
{
 	u8 dir=0;//用于切换方向(递增/递减)

	u8 duty=0;//占空比从0开始
	//当到达一定值切换方向,占空比最大能到 100,但到达 70 左右再递增,
//肉眼也分辨不出亮度变化

	pwm_init(0XFF,0XF6,100,0);//定时时间为 0.01ms,PWM 周期是
//100*0.01ms=1ms,占空比为 0%
	while(1)
	{
		if(dir==0)//递增
		{
		 	duty++;
			if(duty==70) dir=1;
		}					
		else 
		{
		 	duty--;
			if(duty==0) dir=0;
		}
		pwm_set_duty_cycle(duty);//设置占空比
		delay_ms(1);//短暂延时,使呼吸灯肉眼看着更流畅
	}
}

实验现象
DAC(PWM)模块上的指示灯 DA1 呈呼吸灯效果,由暗变亮再由亮变暗。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐