嵌入式开发学习(STC51-17-DAC数模转换)

内容

使DAC(PWM)模块上的指示灯DA1呈呼吸灯效果,由暗变亮再由亮变暗;

DAC介绍

简介

DAC(Digital to analog converter)即数字模拟转换器,它可以将数字信号转换为模拟信号,它的功能与ADC相反;

在常见的数字信号系统中,大部分传感器信号被转化成电压信号,而ADC把电压模拟信号转换成易于计算机存储、处理的数字编码,由计算机处理完成后,再由DAC输出电压模拟信号,该电压模拟信号常常用来驱动某些执行器件,使人类易于感知;如音频信号的采集及还原就是这样一个过程;

主要技术指标

分辨率

DAC的分辨率是输入数字量的最低有效位(LSB)发生变化时,所对应的输出模拟量(电压或电流)的变化量,它反映了输出模拟量的最小变化值;

分辨率与输入数字量的位数有确定的关系,可以表示成FS/(2^n),FS表示满量程输入值,n为二进制位数;

对于5V的满量程,采用8位的DAC时,分辨率为5V/256=19.5mV;当采用12位的DAC时,分辨率则为5V/4096=1.22mV;显然,位数越多分辨率就越高;

线性度

线性度(也称非线性误差)是实际转换特性曲线与理想直线特性之间的最大偏差;

常以相对于满量程的百分数表示,如±1%是指实际输出值与理论值之差在满刻度的±1%以内;

绝对精度和相对精度

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

绝对精度是由DAC的增益误差(当输入数码为全1时,实际输出值与理想输出值之差)、零点误差(数码输入为全0时,DAC的非零输出值)、非线性误差和噪声等引起的;

绝对精度(即最大误差)应小于1个LSB;

相对精度与绝对精度表示同一含义,用最大误差相对于满刻度的百分比表示;

建立时间

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

是描述D/A转换速率的一个动态指标;

根据建立时间的长短,可以将DAC分成超高速(<1μS)、高速(10~1μS)、中速(100~10μS)、低速(≥100μS)几档;

工作原理

其内部结构图如下所示:
嵌入式开发学习(STC51-17-DAC数模转换)_第1张图片
DAC输出电压计算公式:V0=Vref*z/256;

公式中的z表示单片机给的数字量;vref为参考电压,通常我们是接在系统电源上,即5V;数值256表示DAC精度为8位;

DAC主要由数字寄存器、模拟电子开关、位权网络、求和运算放大器和基准电压源(或恒流源)组成;

用存于数字寄存器的数字量的各位数码,分别控制对应位的模拟电子开关,使数码为1的位在位权网络上产生与其位权成正比的电流值,再由运算放大器对各电流值求和,并转换成电压值;

上述的模拟电子开关都分别接着一个分压的器件,比如说电阻;

模拟开关的个数取决于DAC的精度,N个电子开关就把基准电压分为N份(并不是平均分),而这些开关根据输入的二进制每一位数据对应开启或者关闭,把分压的器件上的电压引入输出电路中;

PWM介绍

简介

PWM是Pulse Width Modulation的缩写,中文意思就是脉冲宽度调制,简称脉宽调制;

将数字信号输出转换为模拟信号输出,需要相应的转换器,这种转换器我们称之为DAC数模转换器,但DAC专用芯片价格是比较高的,在实际应用中通常使用PWM技术来模拟DAC输出;

51开发板上集成了一个DAC(PWM)模块电路,因此我们可以通过单片机IO口生成PWM波来模拟DAC输出;

它是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,其控制简单、灵活和动态响应好等优点而成为电力电子技术最广泛应用的控制方式,其应用领域包括测量,通信,功率控制与变换,电动机控制、伺服控制、调光、开关电源,甚至某些音频放大器,因此学习PWM具有十分重要的现实意义;

原理

我们可以理解为,PWM是一种对模拟信号电平进行数字编码的方法;

通过高分辨率计数器的使用,方波的占空比调制,来对一个具体模拟信号的电平进行编码;

PWM信号仍然是数字的,因为在给定的任何时刻,满幅值的直流供电要么完全有(ON),要么完全无(OFF);

电压或电流源是以一种通(ON)或断(OFF)的重复脉冲序列被加到模拟负载上去的;通的时候即是直流供电被加到负载上的时候,断的时候即是供电被断开的时候;

只要带宽足够,任何模拟值都可以使用PWM进行编码;

PWM对应模拟信号的等效图,如下图所示:
嵌入式开发学习(STC51-17-DAC数模转换)_第2张图片
从图中可以看到,上图a是一个正弦波即模拟信号,b是一个数字脉冲波形即数字信号;

我们知道在计算机系统中只能识别是1和0,对于51单片机芯片,要么输出高电平(5V),要么输出低电平(0),假如要输出1.5V的电压,那么就必须通过相应的处理,比如PWM输出;

从上图也可以看到,只要保证数字信号脉宽足够就可以使用PWM进行编码,从而输出1.5V的电压;

PWM的输出其实就是对外输出脉宽可调(即占空比调节)的方波信号,信号频率是由T的值决定,占空比由C的值决定;

其示意图如图所示:
嵌入式开发学习(STC51-17-DAC数模转换)_第3张图片
从上图中可以看到,PWM输出频率是不变的,改变的是C的值,此值的改变将导致PWM输出信号占空比的改变;

占空比其实就是一个周期内高电平时间与周期的比值;

而频率的话可以使用51单片机的定时器确定;

原理图

嵌入式开发学习(STC51-17-DAC数模转换)_第4张图片
从上图中可知,PWM控制管脚接在单片机P2.1管脚上,DAC1为PWM信号输出管脚;

DAC1连接了一个LED灯即DA1,这样就可以通过指示灯DA1的状态直观的反映出PWM输出电压值变化;

输出信号由J52端子的DAC1引出,在其端子上还有一个AIN3脚,它就是ADC的外部模拟信号输入通道;

如果使用短接片将DAC1和AIN3短接,这样就可以使用XPT2046芯片采集检测PWM输出信号;

思路

通过调节占空比,即操作PWM控制管脚/p21管脚,使其输出对应的模拟信号,从而使DA1灯亮度呼吸变化;

通过定时器中断来确定电位变化的周期和占空比;

设置一个循环,使占空比不断变化,变化不能太快,也不能太慢,最好能直观看到灯光亮度的呼吸变化;

编码

User

main.c

/*
 * @Description: 使指示灯DA1呈呼吸灯效果,由暗变亮再由亮变暗
 */
#include "public.h"
#include "pwm.h"

void main()
{

	u8 dir = 0; // 默认为0
	u8 duty = 0;

	pwm_init(0XFF, 0XF6, 100, 0); // 定时时间为0.01ms,PWM周期是100*0.01ms=1ms,占空比为0%

	while (1) // 通过循环的方式不断改变占空比
	{

		if (dir == 0) // 当dir为递增方向
		{
			duty++; // 占空比递增
			if (duty == 70)
				dir = 1; // 当到达一定值切换方向,占空比最大能到100,但到达70左右再递减,肉眼也分辨不出亮度变化
		}
		else
		{
			duty--;
			if (duty == 0)
				dir = 0; // 当到达一定值切换方向
		}
		pwm_set_duty_cycle(duty); // 设置占空比
		delay_ms(1);			  // 短暂延时,让呼吸灯有一个流畅的效果
	}
}

Public

public.h

#ifndef _public_H
#define _public_H

#include "reg52.h"

typedef unsigned int u16; // 对系统默认数据类型进行重定义
typedef unsigned char u8;

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

#endif

public.c

#include "public.h"

/**
 * @description: 延时函数,ten_us=1时,大约延时10us
 * @param {u16} ten_us 延时倍数
 * @return {*}
 */
void delay_10us(u16 ten_us)
{
	while (ten_us--)
		;
}

/**ms延时函数,ms=1时,大约延时1ms***
 * @param {u16} ms 延时倍数
 * @return {*}
 */
void delay_ms(u16 ms)
{
	u16 i, j;
	for (i = ms; i > 0; i--)
		for (j = 110; j > 0; j--)
			;
}

App/pwm

pwm.h

#ifndef _pwm_H
#define _pwm_H

#include "public.h"

// 管脚定义
sbit PWM = P2 ^ 1;

// 变量声明
extern u8 gtim_scale;

// 函数声明
void pwm_init(u8 tim_h, u8 tim_l, u16 tim_scale, u8 duty);
void pwm_set_duty_cycle(u8 duty);

#endif

pwm.c

#include "pwm.h"

// 全局变量定义
u8 gtim_h = 0;	   // 保存定时器初值高8位
u8 gtim_l = 0;	   // 保存定时器初值低8位
u8 gduty = 0;	   // 保存PWM占空比
u8 gtim_scale = 0; // 保存PWM周期=定时器初值*tim_scale

/**
 * @description: PWM初始化函数(通过定时器中断实现)
 * @param {u8} tim_h 定时器高8位
 * @param {u8} tim_l 定时器低8位
 * @param {u16} tim_scale 周期倍数=定时器初值*tim_scale
 * @param {u8} duty 占空比(要小于等于tim_scale)
 * @return {*}
 */
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; // 打开定时器
}

/**
 * @description: PWM设置占空比
 * @param {u8} duty PWM占空比(要小于等于tim_scale)
 * @return {*}
 */
void pwm_set_duty_cycle(u8 duty)
{
	gduty = duty;
}

/**
 * @description: 定时器0中断函数
 * @return {*}
 */
void pwm(void) interrupt 1 // 中断号必须对应上
{
	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;
}

编译和结果

按F7编译,无错误,生成.hex文件,使用pz-isp将hex文件下载到单片机

结果:指示灯DA1以一定周期明暗变化;
嵌入式开发学习(STC51-17-DAC数模转换)_第5张图片

你可能感兴趣的:(嵌入式开发学习,学习,51单片机)