51单片机-实现DAC(PWM)数模转换PWM控制呼吸灯、直流电机实验教程

本章概述思维导图:

51单片机实现DAC数模转换实验教程

DAC简介

DAC(Digital-to-Analog Converter,数字模拟转换器)是将离散的数字信号转换为连续模拟信号的关键电子器件,广泛应用于音频处理、工业控制、通信系统等领域。其性能指标直接影响转换精度和动态响应。

DAC应用

ADC(模数转换器)和DAC(数模转换器)是模拟电路和数字电路之间的桥梁;


DAC核心参数

1、分辨率:表示模拟电压的最小增量,常用二进制位数表示,比如:8、12位等

数学表达:对于n位DAC,分辨率=1/2ⁿ。例如,8位DAC的分辨率为1/256≈0.39%, 12位DAC为1/4096≈0.024%。

物理意义:分辨率越高,DAC能区分的模拟信号变化越精细。例如,8位DAC在0-5V输出范围内,每增加1个LSB(最低有效位),输出电压增加约19.6mV(5V/255)。

影响因素:分辨率由DAC的位数决定,位数越多,分辨率越高,但电路复杂度和成本也相应增加。

2、建立时间:表示将一个数字量转换为稳定模拟信号所需的时间;

动态指标:建立时间是描述DAC转换速率的动态参数,直接影响系统响应速度。

分类:

超高速:<1μs(如12位DAC在100MHz时钟下工作)。

高速:1-10μs。

中速:10-100μs。

低速:≥100μs。

3、精度:转换器实际特性曲线与理想特性曲线之间的最大偏差,误差源:比例系统误差、失调 误差、非线性误差;原因:元件参数误差、基准电压不稳地、运算放大器零漂等;

4、线性度:线性度反映DAC实际转换特性曲线与理想直线之间的最大偏差,通常用满量程的 百分比或LSB表示。

理想特性:理想DAC的输出应与输入数字量呈严格线性关系。

实际偏差:由于电阻网络误差、运算放大器非线性等因素,实际输出会偏离理想直线。 例如,±1%线性度表示实际输出与理论值的偏差不超过满量程的±1%。

重要性:线性度直接影响DAC的精度,尤其在需要高保真输出的场景(如音频处理) 中至关重要。


DAC工作原理

DAC的核心是权电阻网络或电流/电压分配网络,通过将数字输入的每一位对应一个特定权重的模拟量(如电流或电压),再将所有位的模拟量叠加,得到最终的模拟输出。

DAC的电路实现方向多样,常见结构包括:

1、权电阻网络DAC、

2、 R-2R梯形电阻网络DAC

3、电流型DAC(如电流舵DAC)

4、 ∑-Δ(Sigma-Delta)DAC

本章介绍 R-2R梯形电阻网络DAC(分辨率8位),电路如图所示;

R-2R梯形电阻网络DAC

组成:

电阻网络结构:电路中只有两种阻值的电阻,即R和2R,符合R-2R梯形电阻网络的特 征。这种结构通过电阻的分压和叠加原理,将数字输入转换为模拟输出。

数字输入:图中有多个数字输入(D0 - D7),这些输入控制开关的连接状态,从而决定 电流的流向和大小,这是DAC的典型特征。

运算放大器:右侧的运算放大器用于将电流信号转换为电压信号,并可能提供缓冲和放 大功能,这是DAC中常见的输出级设计。

反馈电阻:运算放大器的反馈电阻(Rfb)用于设置增益和稳定输出,这也是DAC电路 的一部分。

结构:由R和2R两种电阻组成梯形网络,每位数字输入通过开关控制是否接入2R电阻支 路。

原理:利用电阻分压和叠加原理,将每位数字量转换为与权重成正比的电流,再经运放 转换为电压。

特点:

优点:电阻数量少(仅需2种阻值),对电阻精度要求较低,适合高位数DAC。

缺点:需精确匹配R和2R电阻,否则可能引入非线性误差。

DAC输出电压计算公式:

V0=VREF*Z/(2的n次方);

VREF:参考电压

Z:数字量

V0:输出模拟电压

N:数字量的位数(分辨率)


PWM简介

PWM(Pulse Width Modulation)是一种通过调节脉冲信号的宽度来控制能量输出的技术。它通过在固定的时间周期内改变高电平和低电平的时间比例(即占空比)来实现对输出功率或电压的控制。

PWM工作原理

占空比:占空比是指高电平时间在一个周期中所占的比例。通过改变占空比,可以调整输出 信号的平均电压或功率。

频率:PWM信号的频率是指每秒钟的周期数。频率的选择取决于具体应用,过低的频率可 能导致输出波动明显,而过高的频率可能增加开关损耗。频率可以用定时器来确定

PWM的输出其实就是对外输出脉冲宽度可调(即占空比调节)的方波信号,信号频率是由T的值决定,占空比由C的值决定。其示意如图所示:


PWM应用领域

电机控制:通过调节电机的输入电压,控制电机的速度和转矩。

电源管理:在开关电源中,用于调节输出电压。

LED调光:控制LED的亮度。

音频放大:某些D类音频放大器使用PWM技术。


DAC(数模转换器)中的PWM基本概念

PWM可以用于实现简易的DAC功能。通过低通滤波器对PWM信号进行滤波,可以获得与占空比成比例的模拟电压输出。这种方式常用于低成本或资源受限的应用中。

PWM对应模拟信号的等效图:

从图中可以看到,图a是一个正弦波模拟信号,图b是一个数字脉冲波形数字信号。在计算机系统中只能识别是1和0,对于51单片机MCU要么输出高电平(5V),要么输出低电平(0V),假如要输出1.5V电压,那么就必须通过相应处理,处理方式为:保证数字信号脉宽足够就可以使用PWM脉冲宽度调制进行编码,从而输出1.5V电压。


DAC(数模转换器)中的PWM工作原理

PWM DAC的核心原理是利用PWM信号的占空比变化来控制模拟输出电压。具体来说:

PWM信号生成:PWM信号由微控制器或专用PWM生成器产生,具有固定的频率和可变的 占空比。占空比是指在一个周期内,高电平(脉冲宽度)所占的比例;

占空比与模拟电压的关系:通过改变PWM信号的占空比,可以控制输出电压的平均值。当 PWM信号的频率足够高时,低通滤波器可以将PWM信号转换 为相对平滑的直流电压,这个直流电压的幅度与PWM信号的占 空比成正比。

低通滤波器的作用:低通滤波器用于滤除PWM信号中的高频成分,保留直流分量,从而得 到平滑的模拟电压输出。


DAC(PWM)数模转换实验硬件电路原理图分析

该电路图展示了一个基于PWM(脉冲宽度调制)的DAC(数模转换器)电路,使用了LM358运算放大器以及其他电子元件。下面详细介绍电路的组成:

PWM输入(P21):PWM信号输入点,来自51单片机的P21管脚。

滤波网络(R31和C32):由电阻R31(51KΩ)和电容C32(104,即0.1μF)组成,用于对输入的PWM信号进行初步滤波,平滑脉冲信号。

运算放大器(U15A - LM358):用于进一步处理和放大滤波后的信号。LM358是一个双运算放大器,这里只使用了其中一个。

反馈网络(R29和C29):由电阻R29(470R)和电容C29组成,用于设置运算放大器的频率响应和稳定性。

增益设置电阻(R28和R30):电阻R28(470R)和R30(330R)用于设置运算放大器的增 益。

输出级(R33和R32):电阻R33(4.7KΩ)和R32(470R)组成输出级,用于调整输出信号 的幅度和阻抗匹配。

输出指示(DA1 - LED):一个LED灯,用于显示输出信号的存在或状态。

DAC接口(J52 - DAC1 ):一个接口,用于将处理后的模拟信号输出到其他电路或设备。

AIN3:外部模拟信号输入通道


DAC(PWM)数模转换实验软件编程设计

本章所实现的实验任务两个;

1、 DAC(PWM)模块上的LED指示灯呈现呼吸灯效果,由暗变亮再由亮变暗。

2、实现PWM对直流电机调速

实现DAC(PWM)模块呼吸灯效果步骤:

第一步:设定时器0定时函数

形参介绍:

timer_H,timer_L为定时器定时模式1的定时时间

PWM_cycle为PWM一个周期的分段次数;假设为100说明定时到达100次为PWM的一个周期

duty_cycle为PWM一个周期的占空比设置

cpp 复制代码
u8 Gtimer_H=0;
u8 Gtimer_l=0;
u8 GPWM_cycle=0;
u8 Gduty_cycle=0;
void PWM_DAC_LED_init(u8 timer_H,u8 timer_l,u8 PWM_cycle,u8 duty_cycle)	  //形参介绍:timer_H,timer_l为定时器计数添入值,PWM_cycle为PWM1个周期的段数,duty_cycle为调节占空比参数
{
	Gtimer_H=timer_H;
	Gtimer_l=timer_l;
	GPWM_cycle=PWM_cycle;
	Gduty_cycle=duty_cycle;
	TMOD|=0x01;			//配置定时器为定时器模式,,模式1
	TH0=timer_H;		//定时计数值添入,定时时间为0.01ms
	TL0=timer_l;
	ET0=1;				//开启定时器中断
	EA=1;				//开启总中断
	TR0=1;				//开启定时器
}

第二步:定时器0中断服务函数,实现PWM的生成

cpp 复制代码
void PWM_DAC_timer_ISR(void) interrupt 1
{
	static u8 cnt=0;
	cnt++;
	TH0=Gtimer_H;		//定时初始数值添入 清空计数值
	TL0=Gtimer_l;
	if(cnt==GPWM_cycle)		  //到达PWM一个周期,重新开始计数
	{
		cnt=0;
	}
	if(cnt<=Gduty_cycle)	  //在PWM一个周期内当计数周期小于我们设置的占空比时,PWM输出1
	{
		PWM=1;
	}
	else PWM=0;				  //在PWM一个周期内当计数周期大于我们设置的占空比时,PWM输出0	

}

第三步:创建更改PWM占空比函数

cpp 复制代码
void set_duty_cycle(u8 duty_cycle)	//设置占空比函数
{
	 Gduty_cycle=duty_cycle;
}

第四步:主函数实现

cpp 复制代码
#include "TYPEDEF.h"   						//u8、u16定义
#include "reg52.h"							//STC89C52RC
#include "Delay.h"							//延时
#include "PWM.h"							//定时器PWM
int main()
{
	u8 direction=0;						//PWM一个周期内的方向
	u8 duty_cycle=0;					//占空比值
	PWM_DAC_LED_init(0xff,0xf6,100,0);	//PWM生成初始话函数:定时器定时为0xfff6(0.01ms);PWM一个周期为100*0.01ms=1ms;因为是初始话函数所以占空比为0
	while(1)
	{   
		if(direction==0)   				//方向0低电平,占空比++
		{
			duty_cycle++;
			if(duty_cycle==70)			//加到峰值时,改变方向位
			{
				direction=1;
			}	
		}
		else if(direction==1)
		{
			duty_cycle--;
			if(duty_cycle==0)			//减到0时,改变方向位
			{
				direction=0;
			}
		}
		set_duty_cycle(duty_cycle);		//设置占空比  
		Delay_ms(1);					//延时一点时间,方便明显查看 
	}
}	 

实现PWM对直流电机调速步骤(直流电机驱动和按键检测章节在前面文章有详细介绍小伙伴们有疑问可以前去观看,本章着重讲解PWM)

第一步:设定时器0定时函数

形参介绍:

timer_H,timer_L为定时器定时模式1的定时时间

PWM_cycle为PWM一个周期的分段次数;假设为100说明定时到达100次为PWM的一个周期

duty_cycle为PWM一个周期的占空比设置

cpp 复制代码
u8 Gtimer_H=0;
u8 Gtimer_l=0;
u8 GPWM_cycle=0;
u8 Gduty_cycle=0;
void PWM_DAC_LED_init(u8 timer_H,u8 timer_l,u8 PWM_cycle,u8 duty_cycle)	  //形参介绍:timer_H,timer_l为定时器计数添入值,PWM_cycle为PWM1个周期的段数,duty_cycle为调节占空比参数
{
	Gtimer_H=timer_H;
	Gtimer_l=timer_l;
	GPWM_cycle=PWM_cycle;
	Gduty_cycle=duty_cycle;
	TMOD|=0x01;			//配置定时器为定时器模式,,模式1
	TH0=timer_H;		//定时计数值添入,定时时间为0.01ms
	TL0=timer_l;
	ET0=1;				//开启定时器中断
	EA=1;				//开启总中断
	TR0=1;				//开启定时器
}

第二步:定时器0中断服务函数,实现PWM的生成

cpp 复制代码
void PWM_DAC_timer_ISR(void) interrupt 1
{
	static u8 cnt=0;
	cnt++;
	TH0=Gtimer_H;		//定时初始数值添入 清空计数值
	TL0=Gtimer_l;
	if(cnt==GPWM_cycle)		  //到达PWM一个周期,重新开始计数
	{
		cnt=0;
	}
	if(cnt<=Gduty_cycle)	  //在PWM一个周期内当计数周期小于我们设置的占空比时,PWM输出1
	{
		PWM_motor=1;          //输出高电平ULN2003芯片输出0,驱动直流电机
	}
	else //在PWM一个周期内当计数周期大于我们设置的占空比时,PWM输出0
    {
        PWM_motor=0;	    	//输出低电平ULN2003芯片断开状态,停止直流电机		  
    |	

}

第三步:创建更改PWM占空比函数

cpp 复制代码
void set_duty_cycle(u8 duty_cycle)	//设置占空比函数
{
	 Gduty_cycle=duty_cycle;
}

第四步:主函数实现PWM控制直流电机

cpp 复制代码
#include "TYPEDEF.h"   						//u8、u16定义
#include "reg52.h"							//STC89C52RC
#include "KEY.h"							//按键
#include "motor.h"							//电机
#include "PWM.h"							//定时器PWM
int main()
{
	u8 key_set=0;			//独立按键初始化函数
	u8 mode=0;
	PWM_DAC_LED_init(0xff,0xA3,100,0);	//PWM生成初始话函数:定时器定时为0xffA3(0.1ms);PWM一个周期为100*0.01ms=10ms;因为是初始话函数所以占空比为0
	while(1)
	{   
		key_set=KEY_detect(0);
		if(key_set==1)
		{
			mode++;
			if(mode==4)
			{
				mode=0;
			}
			if(mode==1){set_duty_cycle(30);}
			else if(mode==2){set_duty_cycle(60);}
			else if(mode==3){set_duty_cycle(90);}
			else if(mode==4){set_duty_cycle(0);}		
	    }
	}
}	 

制作不易!喜欢的小伙伴给个小赞赞!喜欢我的小伙伴点个关注!有不懂的地方和需要的资源随时问我哟!

相关推荐
阿川!12 小时前
嵌入式软件--STM32 SPI通信(上)
stm32·单片机·嵌入式硬件
芯岭13 小时前
内含32位MCU的无线收发芯片XL2422
单片机·嵌入式硬件·信息与通信·射频工程
宫瑾13 小时前
STM32USB学习
stm32·嵌入式硬件·学习
1379号监听员_14 小时前
嵌入式软件架构--按键消息队列3(测试)
开发语言·stm32·单片机·嵌入式硬件·架构
迎風吹頭髮14 小时前
Linux服务器编程实践57-功能强大的网络信息函数getaddrinfo:支持IPv4与IPv6
单片机·嵌入式硬件
GilgameshJSS16 小时前
STM32H743-ARM例程26-TCP_CLIENT
c语言·arm开发·stm32·单片机·tcp/ip
清风66666617 小时前
基于单片机的开尔文电路电阻测量WIFI上传设计
单片机·嵌入式硬件·毕业设计·课程设计
奋斗的牛马19 小时前
FPGA—ZYNQ学习Helloward(二)
单片机·嵌入式硬件·学习·fpga开发
我先去打把游戏先1 天前
ESP32学习笔记(基于IDF):ESP32连接MQTT服务器
服务器·笔记·单片机·嵌入式硬件·学习·esp32
CiLerLinux1 天前
第一章 FreeRTOS简介
单片机·嵌入式硬件·物联网·gnu