注:DAC芯片贵,一般用PWM代替DAC
1.DAC介绍:
1.分辨率:指输入数字量的最低有效位(LSB)发生变化时,所对应的输出模拟量(电压或电流)的变化量。它反映了输出模拟量的最小变化值。
分辨率与输入数字量的位数有确定的关系,可以表示成FS /(2^n)。FS表示满量程输入值,n为二进制位数。对于5V的满量程,采用8位的DAC时,分辨率为5V/256=19.5mV;当采用12位的DAC时,分辨率则为5V/4096=1.22mV。显然,位数越多分辨率就越高。
2.线性度(也称非线性误差):实际转换特性曲线与理想直线特性之间的最大偏差。常以相对于满量程的百分数表示。如±1%是指实际输出值与理论值之差在满刻度的±1%以内。
3.绝对精度和相对精度
绝对精度(简称精度):指在整个刻度范围内,任一输入数码所对应的模拟量实际输出值与理论值之间的最大误差。绝对精度是由DAC的增益误差(当输入数码为全1时,实际输出值与理想输出值之差)、零点误差(数码输入为全0时,DAC的非零输出值)、非线性误差和噪声等引起的。绝对精度(即最大误差)应小于1个LSB。
相对精度:与绝对精度表示同一含义,用最大误差相对于满刻度的百分比表示。
4.建立时间:指输入的数字量发生满刻度变化时,输出模拟信号达到满刻度值的±1/2LSB所需的时间。是描述D/A转换速率的一个动态指标。
电流输出型DAC的建立时间短。电压输出型DAC的建立时间主要决定于运算放大器的响应时间。根据建立时间的长短,可以将DAC分成超高速(<1μS)、高速(10~1μS)、中速(100~10μS)、低速(≥100μS)几档。
数字寄存器+模拟电子开关+分压电阻网络+求和运算放大器+电压源
注:Vref是参考电压,是已知的。如,Vref为5V时,V0为0~5V
Z表示单片机所给数字量,即D7~D0对应的数据,如0x01,z为1
256为2的8次方
2.PWM介绍
通过微处理器的数字输出来对开关控制的一种方式。控制简单,灵活,动态响应好。
PWM是对模拟信号进行数字编码的方式 。
a为模拟信号,b为数字信号。
单片机只能识别0和1,即0V和5V,如果想让单片机实现如1.6V等数据,可通过脉宽,及占空比来实现。(占空比:一个周期内,高电平所占据的时间)
右图的T是周期,c是占空比。
PWM+RC电路=低配版DAC
3.硬件设计:
A2-A4版 A5-A7版(控制管脚单独引出)
通过PWM模拟DAC芯片。
通过单片机PWM控制P21改变呼吸灯的亮暗。
J52引出,目的是方便外部设备的使用
4.软件设计:
每一个周期内,都是从0-->1的瞬间开始。占空比增加,"1"的长度增加。
原始代码如下:
main部分:
#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);//短暂延时,让呼吸灯有一个流畅的效果 } }
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 //tim_h:定时器高8位 //tim_l:定时器低8位 //tim_scale:PWM周期倍数:定时器初值*tim_scale //duty:PWM占空比(要小于等于tim_scale) 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;//打开定时器 } //PWM占空比(要小于等于tim_scale) void pwm_set_duty_cycle(u8 duty) { gduty=duty; } 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.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