基于DAC0832的程控衰减器
文章目录
- 基于DAC0832的程控衰减器
- 一、需求分析:
- 二、主要元器件:
- 三、设计流程
- 四、工作原理
- 4.1完整原理图
- 4.2仿真环境
- 4.3基准源 2.56V
- 4.4基准源 0.02V
- 4.5衰减电路
- 4.6放大电路
- 4.7模式切换
- 五、程序代码
- 六、相关资料下载
一、需求分析:
1、对输入信号Vin(2.56V直流)进行程控衰减,倍数为2~256倍,由按键控制
2、程控放大器模式,对输入信号(0.02V直流)进行程控放大
3、程控放大器模式,对输入信号(Vp-p=0.1V正弦信号)放大128倍
4、显示器(SMS0801)显示倍数和电压
5、通过程控方式,在衰减器和放大器两种模式下切换
二、主要元器件:
稳压源TL431;稳压二极管LM385;
数模转换DAC0832;模数转换ADS1115;
显示器SMS0801;运算放大器OP07;继电器
三、设计流程
四、工作原理
4.1完整原理图
①—2.56V基准源;②—0.02V基准源;③—衰减/放大电路;④—模式切换;⑤—DAC0832的输出状态、继电器输入的模拟;⑥显示的模拟
4.2仿真环境
在线仿真:电子森林 https://www.eetree.cn/war/circuitjs.html?lang=zh
离线仿真:Protues;
4.3基准源 2.56V
功能描述:
对应原理图模块①,采用TL431的恒压电路产生2.56V的稳定电压,使用可调电阻可以精确调节输出电压值Vo,又简化了阻值的计算,但对电路造成了后置影响,即在连接后续电路后输出电压会低于2.56V。
原理分析:
TL431的内部含有一个2.5V的基准电压,所以当在REF端引入输出反馈时,器件可以通过从阴极到阳极很宽范围的分流,控制输出电压。如上图所示,当R1和R2的阻值确定后,两者对Vo的分压引入反馈,若Vo增大,反馈量增大,TL431的分流也就增加,从而又导致Vo下降。显见,这个深度的负反馈电路必然在VI等于基准电压处稳定,此时Vo=(1+R1/R2)Vref。选择不同的R1和R2的值可以得到从2.5V到36V范围内的任意范围电压输出,特别地,当R1=R2时,Vo=5V。需要注意的是,在选择电阻时必须保证TL431工作的必要条件,就是通过阴极的电流要大于1mA。
[1]资料来源:TL431常用电路整理-电子发烧友网 http://m.elecfans.com/article/577647.html
4.4基准源 0.02V
功能描述:
对应原理图模块②,使用LM385制作输出电压约为1.2V的电压基准,经过一个运放构成的电压跟随器后(实现阻抗变换,保障信号衰减小),通过两个电阻分压实现0.02V的稳定输出。同上采用了可调电阻,虽然本次结果显示影响较小,但实际上采用阻值固定的电阻较好。
原理分析:
参考LM324的数据手册构建电压基准源,使用一个电阻器保证有大于Iz(min)=8uA的电流流入阴极。
4.5衰减电路
功能描述:
对应原理图模块③,DAC0832的8脚VREF作输入端,9脚RFB作反馈电阻,通过改变DAC0832的DI7~0口输出状态实现不同倍数的衰减。第二个运算放大器起输出电压反相的作用。
原理分析:
DAC0832是采用CMOS工艺制成的单片直流输出型8位数/模转换器。如图所示,它由倒T型R-2R电阻网络、模拟开关、运算放大器和参考电压VREF四大部分组成。
倒T型R-2R电阻网络:
可以等效为
网路特点:任何一个节点的三条分支的等效电阻都是2R,因此流进任何一个选定分支的电流为I=UR/3R。同时因为并联结构,流入其他节点时,电流会被平分。
以图中模拟A0=1为例,A0的电流为I=UR/(2R||2R+2R)=UR/3R,A1的电流就为I/2,A2的电流为I/4…以此类推,A7的电流为1/128,流入Rfb的电流为I/256,最终输出电压为Vo=I/256*Rfb。当Rfb=3R时,Vo=1/256UR。
运算放大器的输出模拟量Vo为:
4.6放大电路
功能描述:
对应原理图模块③,与衰减电路相似,8脚VREF作为反馈,9脚RFB作为输入端,改变DAC0832的DI7~0口输出状态实现不同倍数的放大。
原理分析:
电路原理与衰减相同,简言之是将输入、输出交换。
运算放大器的输出模拟量Vo为:
4.7模式切换
功能描述:
对应原理图模块④,电路上,通过继电器实现不同线路的切换;程序上,通过改变控制端口的输出电压,让继电器在跳转/未跳转两种状态之间切换。
原理分析:
对比衰减和放大电路,可以发现模式的切换只需要交换输入、输出端即可,因而采用继电器控制不同线路的选择,电路中使用三极管实现控制端口的电压变化影响继电器工作状态的效果。
五、程序代码
程序代码主要包括按键功能、液晶显示、AD采集三大部分。
/********************
*主程序代码参考(C51)
*********************/
#include <reg52.h>
#include <ads1115.h>sbit CLKPIN = P3^0; //CLK对应单片机引脚
sbit DIPIN = P3^1; //DI对应单片机引脚
sbit KEY_1 = P1^2; //按键1减小倍数
sbit KEY_2 = P1^3; //按键2增加倍数
sbit KEY_3 = P1^4; //按键3切换模式
sbit KEY_4 = P1^5; //按键4置空sbit P1_6 = P1^6; // P1.6、1.7 控制继电器的工作状态
sbit P1_7 = P^7;//以下Num1_Ram至Num8_Ram为数字1至8在演示程序中的RAM地址预定义
#define NUMBERS 8 //显示数字的个数
unsigned char Num1_Ram; //数字1
unsigned char Num2_Ram; //数字2
unsigned char Num3_Ram; //数字3
unsigned char Num4_Ram; //数字4
unsigned char Num5_Ram; //数字5
unsigned char Num6_Ram; //数字6
unsigned char Num7_Ram; //数字7
unsigned char Num8_Ram; //数字8//数码笔段顺序: D76543210
// ABCDEFGX
unsigned char code NUMCODETAB[]={0xFC,0x60,0xDA,0xF2, //数字0,1,2,30x66,0xB6,0xBE,0xE0, //数字4,5,6,70xFE,0xF6,0x00,0x02, //数字8,9,字符空格,字符-0x9C,0xCE,0x9E,0x8E}; //字符C,字符P,字符E,字符Funsigned char KeySta[4] ={1,1,1,1}; //按键的当前状态unsigned int ValVoltage(unsigned char n); //ADC读取函数
void delay(unsigned int xms); //软件延时函数,单位1ms
void ConfigTimer0(unsigned int ms); //定时器0配置函数
void ShowNumber(unsigned int num,unsigned int value); //数码管显示函数
void KeyScan(); //按键扫描函数
void KeyDriver(); //按键功能驱动函数//SMS0801B的函数定义
void decodetolcd(void); //液晶显示控制器译码子程序
void transram(void); //LCD显示刷新子程序
void transbyte(unsigned char d); //送1字节数据到液晶显示控制器子程序
void transbit(bit d); //送1位数据到液晶显示控制器子程序unsigned char T0RH =0; // T0重载值的高字节
unsigned char T0RL =0; // T0重载值的低字节
unsigned int shift =32; // DAC当前的输出状态,即衰减/放大的倍数,初始为256/32=8倍
unsigned int adcValue; // ADC1115获取的电压值//--------------------------------------------------------------------------------------------------void main()
{ EA = 1; //使能总中断ConfigTimer0(10); //配置T0定时10msConfige1115(); //配置AD寄存器delay(5);
// P1_6=0; //设置P1.6、P1.7的初始状态,需根据硬件电路作改动
// P1_7=0;while (1){KeyDriver(); // 调用按键驱动函数 delay(100);P2=shift; // 将倍数发送至DACadcValue=ValVoltage(100); // ADS1115采集电压信息ShowNumber(shift,adcValue); //显示放大倍数、电压值,SMS0801本质上是8个数码管 }
}//--------------------------------------------------------------------------------------------------
/*延时函数,单位1ms*/
void delay(unsigned int xms)
{unsigned int i,j;for(i=xms;i>0;i--)for(j=30;j>0;j--);
}//----------------------------------------------------------------------
/*按键驱动函数*/
void KeyDriver()
{unsigned char i;for(i=0;i<4;i++) // 循环检测4个按键{if(KeySta[i]==0) // 检测按键动作{if(i==0) // 减小键,DAC数值增加,倍数减小{while(KeySta[i]==0) //等待按键释放,实现长按功能{if(shift<255) shift++;delay(500); //延时0.5s,约每隔0.5s加1ShowNumber(shift,adcValue); }}else if (i == 1) // 增大键,DAC数值减小,倍数增加{while(KeySta[i]==0) //等待按键释放,实现长按功能{if(shift>1) shift--; //延时0.5s,约每隔0.5s减1delay(500);ShowNumber(shift,adcValue); }}else if (i == 2) //模式转换按键,P2.7,P2.6输出反转{while(KeySta[i]==0) delay(1000);P1_6 = ~P1_6;P1_7 = ~P1_7;}}}
}//----------------------------------------------------------------------
/*显示函数*/
void ShowNumber(unsigned int num,unsigned int val)
{signed char i;unsigned char buf[8];//倍数显示//num = 256/num;for(i=7; i>=5 ;i--) //把长整型数转换为8位十进制数组{ buf[i] = num%10; //个位存入buf[7],十位存入buf[6],百位存入buf[5]num = num/10;}for(i=5;i<=7;i++) //高位0不显示{if(buf[i]==0)buf[i]=10;else break;}//电压显示val = val*187.5/1000000*100; //对ADS1115采集的数据进行换算,// *100 的作用是将小数化为所需整数,以便显示,如2.56变为256for(i=3; i>=0 ;i--) //把长整型数转换为8位十进制数组{buf[i] = val%10;val = val/10;}Num1_Ram=buf[0]; //第1位Num2_Ram=buf[1]; //第2位,输出该位时点亮小数点Num3_Ram=buf[2]; //第3位Num4_Ram=buf[3]; //第4位Num5_Ram=buf[4]; //第5位,显示'-'Num6_Ram=buf[5]; //第6位Num7_Ram=buf[6]; //第7位Num8_Ram=buf[7]; //第8位transram();delay(300);
}//----------------------------------------------------------------------
/*ADC1115读取电压值*/
unsigned int ValVoltage(unsigned char n)
{unsigned long int idata sum=0;unsigned int idata val;unsigned char idata i;val=read1115();val=read1115();for(i=0;i<n;i++){sum += read1115();}val = sum/n;return val;
}//----------------------------------------------------------------------
/*液晶刷新*/
void transram(void) //LCD显示刷新子程序
{ decodetolcd(); //按地址映射表字节顺序,所有数据译码后送//LCD液晶显示控制器
}void decodetolcd(void) //按地址映射表字节顺序,所有数据译码后送
{ unsigned char buf; //LCD液晶显示控制器子程序
//LCDBUF+0 缓冲区第1个字节译码buf=0x00; //当前译码数据置初始值//需译码的BIT为ABCDEFGZ位buf|=NUMCODETAB[Num1_Ram]&0xFE; //第1个8字的ABCDEFG笔段译码 transbyte(buf); //将当前译码数据送LCD液晶显示控制器//LCDBUF+1 缓冲区第2个字节译码buf=0x00; //当前译码数据置初始值//需译码的BIT为ABCDEFGZ位buf|=NUMCODETAB[Num2_Ram]&0xFE; //第2个8字的ABCDEFG笔段译码buf+=0x01; //显示小数点transbyte(buf); //将当前译码数据送LCD液晶显示控制器//LCDBUF+2 缓冲区第3个字节译码buf=0x00; //当前译码数据置初始值//需译码的BIT为ABCDEFGZ位buf|=NUMCODETAB[Num3_Ram]&0xFE; //第3个8字的ABCDEFG笔段译码transbyte(buf); //将当前译码数据送LCD液晶显示控制器//LCDBUF+3 缓冲区第4个字节译码buf=0x00; //当前译码数据置初始值//需译码的BIT为ABCDEFGZ位buf|=NUMCODETAB[Num4_Ram]&0xFE; //第4个8字的ABCDEFG笔段译码transbyte(buf); //将当前译码数据送LCD液晶显示控制器//LCDBUF+4 缓冲区第5个字节译码buf=0x02; //当前译码数据置初始值,第5位一直为'-'transbyte(buf); //将当前译码数据送LCD液晶显示控制器//LCDBUF+5 缓冲区第6个字节译码buf=0x00; //当前译码数据置初始值//需译码的BIT为ABCDEFGZ位buf|=NUMCODETAB[Num6_Ram]&0xFE; //第6个8字的ABCDEFG笔段译码 transbyte(buf); //将当前译码数据送LCD液晶显示控制器//LCDBUF+6 缓冲区第7个字节译码buf=0x00; //当前译码数据置初始值//需译码的BIT为ABCDEFGZ位buf|=NUMCODETAB[Num7_Ram]&0xFE; //第7个8字的ABCDEFG笔段译码 transbyte(buf); //将当前译码数据送LCD液晶显示控制器//LCDBUF+7 缓冲区第8个字节译码buf=0x00; //当前译码数据置初始值//需译码的BIT为ABCDEFGX位buf|=NUMCODETAB[Num8_Ram]&0xFE; //第8个8字的ABCDEFG笔段译码transbyte(buf); //将当前译码数据送LCD液晶显示控制器
}//----------------------------------------------------------------------
/*字节数据传输*/
void transbyte(unsigned char d) //送1字节数据到液晶显示控制器子程序
{ unsigned char i;for(i=0;i<8;i++){ if((d&0x1) == 0x1)transbit(1);elsetransbit(0);d>>=1; //从低到高位送字节位数据到液晶显示控制器}
}//----------------------------------------------------------------------
/*位数据传输*/
void transbit(bit d) //送1位数据到液晶显示控制器子程序
{ DIPIN = d; //先送数据到数据口线DI_nop_();_nop_();CLKPIN = 1; //再使时钟口线发一个负脉冲_nop_();_nop_();CLKPIN = 0;_nop_();_nop_();CLKPIN = 1;
}//----------------------------------------------------------------------
/*按键扫描*/
void KeyScan()
{unsigned char i;static unsigned char keybuf[4] = {0xFF,0xFF,0xFF,0xFF};// 将一行按键值移入缓冲区keybuf[0] = (keybuf[0] << 1) | KEY_1;keybuf[1] = (keybuf[1] << 1) | KEY_2;keybuf[2] = (keybuf[2] << 1) | KEY_3;keybuf[3] = (keybuf[3] << 1) | KEY_4;//消抖后更新按键状态for(i=0;i<4;i++){if((keybuf[i]&0x0F) == 0x00){ // 连续4次扫描值为0,认为按键已稳定的按下KeySta[i] = 0;}else if ((keybuf[i])&0x0F == 0x0F){KeySta[i] = 1;}}
}//----------------------------------------------------------------------
/*定时器配置*/
void ConfigTimer0(unsigned int ms)
{unsigned long tmp; //临时变量tmp = 11059200 / 12; // 定时器计数频率tmp = (tmp*ms)/1000; // 计算所需的计数值tmp = 65536 - tmp; // 计算定时器重载值tmp = tmp +12;T0RH = (unsigned char)(tmp >>8);T0RL = (unsigned char)tmp;TMOD &= 0xF0; // 清零T0的控制位TMOD |= 0x01; // 配置T0为模式1TH0 = T0RH; // 加载T0重载值TL0 = T0RL;ET0 = 1; // 使能T0中断TR0 = 1; // 启动T0
}//----------------------------------------------------------------------
/* 定时器0中断服务函数*/
void InterruptTimer0() interrupt 1
{ TH0 = T0RH; // 重新加载重载值TL0 = T0RL;KeyScan(); // 调用按键扫描函数
}
六、相关资料下载
https://download.csdn.net/download/AriesPIG/20466303