1.设计内容
本信号发生器主要由DDS、AT89C51、独立按键、LCD1602显示,以及TLC5615实现幅度控制等模块组成,通过按键给AT89C51发送频率控制字、相位控制字和幅度控制字,使其输出一定频率、相位差和幅度值可调的双路正弦波信号,经过四阶sallen-key低通滤波器后形成平滑的正弦波形。
2基本指标要求
内容;基于DDS原理实现幅度、频率和相位均可数控调整的双路正弦波发生器。
要求如下:
(1)频率调整范围:1Hz~1kHz,步进不大于1Hz(原始信号需5KHZ(含预留)以上满足采样定理,),步进不大于1Hz(频率分辨率最大是1HZ,通过设置相位累加器位数和频率控制字实现);
(2)Vpp调整范围:0.8V~5V(通过控制DAC的参考电压实现);
(3)同频正弦时相位可设置,分辨率不大于1°(两个同频率的正弦波的相移就是相位累加器的时刻差,时刻差越大则相移越大)。
3.总体设计方案
采用直接数字合成(Direct Digital Synthesizer)方案,DDS原理框图如下
DDS技术频率分辨率高、转换速度快、信号纯度高、相位可控、输出信号无电流脉冲叠加、输出可平稳过渡且相位可保持连续变化。
DDS技术中的波形存储器采用生成正弦数表代替。相位累加器采用定时器与中断配合代替。
4.硬件电路设计
4.1 单片机最小系统电路设计
4.1.1时钟电路:
本系统采用单片机内部方式产生时钟信号,用于外接一个12MHZ石英晶体振荡器(X1)和两个30pF微调电容(C1、C2),构成稳定的自激振荡器,其发出的脉冲直接送入内部的时钟电路。
4.1.2复位电路:
确定单片机工作的起始状态,完成单片机的启动过程。单片机系统的复位方式又上电自动复位和手动按键复位。本设计采用手动按键复位,该复位方式同样具有上电自动复位功能。
4.2 TLC5615硬件电路设计
4.2.1电路功能:
通过TLC的DIN端输入需要转换的电压值的二进制数字量,经过Vout=D(数字量)*Vref/1024计算得到输出的电压值,LM324运放输出口VREF2接到DAC0832,用于改变0832的参考电压。
4.2.2TLC5615电路设计:
U8(TLC5615),因其芯片本身工作电压上限为+5V,所以输出电压经过测试最多只能达到4.7V,无法达到题目0.8-5V的要求,所以将TLC的OUT口输出电压最大值设置为4.2V,并且通过R21与R3对5V电源的分压来补偿一个稳定的0.8V的输入信号,由图可知5v电源分在R3上电压即为0.8V.
4.2.3加法运算电路设计:
通过U9(同相加法运算电路)将两信号进行相加,以满足最大输出电压5V的要求,当R20=R4时,Vout=U1+U2.
4.3 DAC0832硬件电路设计
4.3.1电路功能:
通过程序控制CPU向D/A转换器送入随时间呈一定规律变化的数字量,则D/A转换器输出端就可以输出随时间按一定规律变化的波形。
4.3.2电路设计:
由TLC5615输出端控制VREF端参考电压的大小,采取单缓冲方式连接,转换结果通过IOUT1口输出。
4.4sallen-key四阶低通滤波器硬件电路设计
4.4.1电路功能;
将DAC0832模拟输出的不平整电压波形整成平滑的正弦波。
4.4.2电路设计:
该四阶低通滤波器是由两个二阶sallen-key滤波器级联而成,根据题目要求所产生的正弦波频率范围为1HZ-1KHZ,所以设定的截至频率需大于1KHZ,此处设定截止频率=33KHZ,增益K=1,品质因数.
4.5 人机接口电路设计
4.5.1按键电路
如图可见,按键采取多外部中断源电路连接,四个按键通过AND_4(四通道与门)连接到单片机的外中断INT0,只要其中任一按键按下即可产生低电平触发INTT0的中断。
①P34(SELECT_SIN)为通道选择按键,初始为通道0,按下按键后转变为通道1。
②P35(SELECT_ELE)为元素选择按键,初始为幅度,按下一次后转变为频率,按下第二次转变为相位。
③P36(RISE)为增加按键,在选择完通道与元素后,此按键便可以增加数字。
④P37(DECLINE)为减少按键,用法与P36一致。
注:幅度的步进值为0.1V,频率的步进值为1HZ,幅度的分辨率为1°。
4.5.2显示电路
如图,显示采用LCD1602液晶屏显示。
①第一行为从左到右依次是通道0的正弦波频率;通道1的正弦波频率,初始化值为1HZ。
②第二行从左到右依次是通道0的正弦波VPP;通道1的正弦波VPP;两通道正弦波频率相同时的相位差,其中VPP初始化值为0.8V,相位初始化值为0°。
4.6 整体电路设计图
5.系统软件设计
5.1 软件总体设计思路
本组设计采用四按键控制信号。SELECT_SIN为通道选择,通道有01;SELECT_ELE为元素选择,元素有02(元素0~2分别为信号的幅度、频率、相位);RISE为对应通道对应元素上升按键;DECLINE为对应通道对应元素下降按键。LCD1602显示屏第一行显示通道0通道1信号的频率,第二行显示通道0通道1信号的频率以及通道1的相位。
主程序初始化通道号、元素号及电压频率相位,先在LCD1602显示屏上显示电压频率相位初始值,用TLC5615输出稳定信号作为参考电压控制幅值,相位累加器和频率控制字结合控制频率以及相位,DAC0832最终输出元素对应值的信号,并打开外部按钮中断,死循环等待中断。
5.2 主程序流程设计
主函数初始化通道号元素号以及双路信号初始元素参数,调用子函数调整各元素参数,LCD1602初步显示输出双路信号幅度频率相位参数,DAC0832输出双路正弦信号,等待外部按键中断。
/******包含头文件******/
#include "DoubleSin.h"
/**********************************
全局变量
**********************************/
float freq0,freq1;//两信号的频率(设置精度为0.5Hz)
float Phase;//相位,通道1的相位,通道0不动
float Voltage0,Voltage1;//TLC5615输出电压模拟量
uint K0,K1;//频率控制字
unsigned char key;
unsigned char Sin,Ele;//Sin=0~1,Ele=0~2
/******************************************************
* 函 数 名 : main
* 功 能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************/
int main(void)
{
Sin=Ele=0; //初始化选择通道0和元素0(元素0是幅度,元素1是频率,元素2是相位)
/********************幅度********************/
Voltage0=Voltage1=4.2;//幅度初始化为0.8V,外部电路有加偏置0.8V
TLC5615(Voltage0,0);
TLC5615(Voltage1,1);
/********************频率********************/
freq0=freq1=500;//单位是Hz
FretoK(freq0,0);
FretoK(freq1,1);
/********************相位********************/
Phase=0;//通道1初始相位为0,和通道0没有相位差
PhasetoPtr();//相位算时差(相位点个数差)
/********************************************/
LCDInit();//初始化LCD1602
WriteCommandLCD(0x40,1);//准备写CGRAM
Voltage_display(Voltage0,0);
Voltage_display(Voltage1,1);
Frequence_display(freq0,0);
Frequence_display(freq1,1);
Phase_display(Phase);//先显示
DAC0832();//DAC输出信号
EA=1;
EX0=1;
IT0=1;
while(1);
}
void int0_ISR(void) interrupt 0 using 1
{
key=Read_Key();
if(key!=0xff)
{
switch(key)
{
case 1:Sin=Choose_Sin(Sin);
break;
case 2:Ele=Choose_Ele(Ele);
break;
case 3:Rise(Sin,Ele);
break;
case 4:Decline(Sin,Ele);
break;
}
}
}
5.3 DAC0832信号输出子程序设计
通过MATLAB得出三角函数波形相位值将其存入程序存储器作为波形存储器,使用AT89C52的定时器0,设置一个10KHz的时钟,并设定变量作为相位累加器和频率控制字,每次输出波形相位累加器清零,以频率控制字为步长抽样读取波形存储器内容通过DAC0832输出。
#include "DoubleSin.h"
/**********************************
全局变量
**********************************/
extern float freq0,freq1;//两信号的频率(设置精度为0.5Hz)
extern float Phase;//相位,通道1的相位,通道0不动
extern uint K0,K1;
/**********************************
正弦相位值
**********************************/
uchar code sin_ROM[256]={128,131,134,137,140,143,146,149,152,156,159,162,165,
168,171,174,176,179,182,185,188,191,193,196,199,201,204,206,209,211,213,216,
218,220,222,224,226,228,230,232,234,235,237,239,240,242,243,244,246,247,248,
249,250,251,251,252,253,253,254,254,254,255,255,255,255,255,255,255,254,254,
253,253,252,252,251,250,249,248,247,246,245,244,242,241,239,238,236,235,233,
231,229,227,225,223,221,219,217,215,212,210,207,205,202,200,197,195,192,189,
186,184,181,178,175,172,169,166,163,160,157,154,151,148,145,142,138,135,132,
129,126,123,120,117,113,110,107,104,101,98,95,92,89,86,83,80,77,74,71,69,66,
63,60,58,55,53,50,48,45,43,40,38,36,34,32,30,28,26,24,22,20,19,17,16,14,13,
11,10,9,8,7,6,5,4,3,3,2,2,1,1,0,0,0,0,0,0,0,1,1,1,2,2,3,4,4,5,6,7,8,9,11,12,
13,15,16,18,20,21,23,25,27,29,31,33,35,37,39,42,44,46,49,51,54,56,59,62,64,
67,70,73,76,79,81,84,87,90,93,96,99,103,106,109,112,115,118,121,124,127};
uint ptr0,ptr1;//各自通道读取ROM的位置
uchar ptr_real0,ptr_real1;//相位表中实际位置
/******************************************************
* 函 数 名 : DAC0832
* 功 能 : 调整正弦波频率
* 输 入 : 无
* 输 出 : 无
*******************************************************/
void DAC0832(void)//前两个是定时器次数,后两个是定时器TH、TL值
{
ptr0=0;//每次修改频率或相位后,重新输出波形
PhasetoPtr();//相位算时差(相位点个数差)
ptr_real0=(ptr0>>8)-1;
ptr_real1=(ptr1>>8)-1;
TMOD=0x02;
TL0=156;
TH0=156;
ET0=1;
EA=1;
TR0=1;
}
/******************************************************
* 函 数 名 : T0_ISR
* 功 能 : 定时器0
* 输 入 : 无
* 输 出 : 无
*******************************************************/
void T0_ISR(void) interrupt 1 using 1
{
ptr0=ptr0+K0;
if((ptr0>>8)>ptr_real0) P0=sin_ROM[ptr0>>8];
ptr_real0=ptr0>>8;
ptr1=ptr1+K1;
if((ptr1>>8)>ptr_real1) P1=sin_ROM[ptr1>>8];
ptr_real1=ptr1>>8;
}
/******************************************************
* 函 数 名 : FretoK
* 功 能 : 频率求得频率控制字
* 输 入 : 无
* 输 出 : 无
*******************************************************/
void FretoK(float freq,uchar select)
{
if(!select) K0=N*freq/fclk;
else K1=N*freq/fclk;
}
/******************************************************
* 函 数 名 : PhasetoT
* 功 能 : 相位差算出时间差(相位点差)
* 输 入 : 无
* 输 出 : 无
*******************************************************/
void PhasetoPtr()
{
ptr1=Phase/(2*pi)*N;//相位累加器加上相位对应点数
}
5.4 TLC5615参考电压输出子程序设计
按键修改幅值后,全局变量幅值改变,调用子函数根据幅值计算得数字量,调用给TLC5615传数字量的函数,通过TLC5615输出模拟电压作为DAC0832的参考电压
#include "DoubleSin.h"
/******************************************************
* 函 数 名 : TLC5615
* 功 能 : 给DAC0832输出参考电压
* 输 入 : 作为参考电压的电压值、通道号
* 输 出 : 无
*******************************************************/
void TLC5615(float Voltage,uchar select)
{
uint DAValue=0;
DAValue=num(Voltage); //输入需要转换的电压值
DA_Conver(DAValue,select); //调用DA_Conver函数
}
/******************************************************
* 函 数 名 : num
* 功 能 : 将输入的电压值转换为数字量
* 输 入 : 需转换的电压值
* 输 出 : 电压值的数字量
*******************************************************/
uint num(float V)
{
uint DA; //转换电压值的数字量
DA=V*1024/5; //V=DA*VREF/1024
return DA; //返回的电压值的数字量
}
/******************************************************
* 函 数 名 : DA_Conver
* 功 能 : 将数字量传进TLC5615输出模拟电压
* 输 入 : 电压的数字量、通道号
* 输 出 : 无
*******************************************************/
void DA_Conver(unsigned int d10,uchar select)
{
unsigned char i;
TLC5615CLK=0;
if(!select){ //select=0 选择为通道0时
TLC5615_CS0=0;
for(i=0;i<12;i++)
{
if(d10&0x0200) TLC5615DATA0=1; //从第10位开始发送数据 每发送完一位,左移一位
else TLC5615DATA0=0;
TLC5615CLK=1;
d10<<=1;
TLC5615CLK=0;
}
TLC5615_CS0=1;
}
else{ //select=0 选择为通道1时
TLC5615_CS1=0;
for(i=0;i<12;i++)
{
if(d10&0x0200) TLC5615DATA1=1; //从第10位开始发送数据 每发送完一位,左移一位
else TLC5615DATA1=0;
TLC5615CLK=1;
d10<<=1;
TLC5615CLK=0;
}
TLC5615_CS1=1;
}
}
5.5 中断服务子程序设计
主程序等待中断,按键按下,中断触发,调用读按键子函数,子函数内部消抖,返回按键值给中断服务子程序,选择调用对应按键子函数。
#include "DoubleSin.h"
/**********************************
全局变量
**********************************/
extern float freq0,freq1;//两信号的频率(设置精度为0.5Hz)
extern float Phase;//相位,通道1的相位,通道0不动
extern float Voltage0,Voltage1;//TLC5615输出电压模拟量
/******************************************************
* 函 数 名 : Read_Key
* 功 能 : 读按键是否按下
* 输 入 : 无
* 输 出 : 按键号
*******************************************************/
uchar Read_Key(void)
{
if(!Key1)
{
Delay_ms(10);
if(!Key1) return 1;
}
else if(!Key2)
{
Delay_ms(10);
if(!Key2) return 2;
}
else if(!Key3)
{
Delay_ms(10);
if(!Key3) return 3;
}
else if(!Key4)
{
Delay_ms(10);
if(!Key4) return 4;
}
return 0xff;
}
/******************************************************
* 函 数 名 : Choose_Sin
* 功 能 : 选择通道
* 输 入 : 前通道号
* 输 出 : 后通道号
*******************************************************/
uchar Choose_Sin(uchar Sin)
{
if(Sin==1) return 0;
else return 1;
}
/******************************************************
* 函 数 名 : Choose_Ele
* 功 能 : 选择元素
* 输 入 : 前元素号
* 输 出 : 后元素号
*******************************************************/
uchar Choose_Ele(uchar Ele)
{
if(Ele==2) return 0;
else return (Ele+1);
}
/******************************************************
* 函 数 名 : Rise
* 功 能 : 上升
* 输 入 : 通道号、元素号
* 输 出 : 无
*******************************************************/
void Rise(uchar Sin,uchar Ele)
{
TR0=0;
if(Ele==0) //选择元素0,幅度
{
if(Sin==0) //通道0
{
if(Voltage0<=4.1) Voltage0+=0.1;
Voltage_display(Voltage0,0);//先显示
TLC5615(Voltage0,0);
DAC0832();
}
else //通道1
{
if(Voltage1<=4.1) Voltage1+=0.1;
Voltage_display(Voltage1,1);//先显示
TLC5615(Voltage1,1);
DAC0832();
}
}
else if(Ele==1) //选择元素1,频率
{
if(freq0==freq1) Phase=0;//假设频率变化之前是相等的,则相位清零
Phase_display(Phase);//先显示
PhasetoPtr();
if(Sin==0) //通道0
{
if(freq0<=999.5) freq0+=0.5;//单位是Hz
Frequence_display(freq0,0);//先显示
FretoK(freq0,0);
DAC0832();
}
else //通道1
{
if(freq1<=999.5) freq1+=0.5;
Frequence_display(freq1,1);//先显示
FretoK(freq1,1);
DAC0832();
}
}
else //选择元素2,相位
{
if(freq0==freq1)
{
if(Phase<=2*pi*359/360) Phase+=2*pi/360;
Phase_display(Phase);//先显示
PhasetoPtr();
DAC0832();
}
}
}
/******************************************************
* 函 数 名 : Decline
* 功 能 : 下降
* 输 入 : 通道号、元素号
* 输 出 : 无
*******************************************************/
void Decline(uchar Sin,uchar Ele)
{
TR0=0;
if(Ele==0) //选择元素0,幅度
{
if(Sin==0) //通道0
{
if(Voltage0>=0.1) Voltage0-=0.1;
Voltage_display(Voltage0,0);//先显示
TLC5615(Voltage0,0);
DAC0832();
}
else //通道1
{
if(Voltage1>=0.1) Voltage1-=0.1;
Voltage_display(Voltage1,1);//先显示
TLC5615(Voltage1,1);
DAC0832();
}
}
else if(Ele==1) //选择元素1,频率
{
if(freq0==freq1) Phase=0;//假设频率变化之前是相等的,则相位清零
Phase_display(Phase);//先显示
PhasetoPtr();
if(Sin==0) //通道0
{
if(freq0>=1.5) freq0-=0.5;//单位是Hz
Frequence_display(freq0,0);//先显示
FretoK(freq0,0);
DAC0832();
}
else //通道1
{
if(freq1>=1.5) freq1-=0.5;
Frequence_display(freq1,1);//先显示
FretoK(freq1,1);
DAC0832();
}
}
else //选择元素2,相位
{
if(freq0==freq1)
{
if(Phase>=2*pi/360) Phase-=2*pi/360;
Phase_display(Phase);//先显示
PhasetoPtr();
DAC0832();
}
}
}
6.系统仿真结果显示
6.1示波器显示说明:
①ChannelA(黄色线条)为通道0未经过滤波电路前输出的正弦波形
②ChannelB(蓝色线条)为通道1未经过滤波电路前输出的正弦波形
③ChannelC(粉色线条)为通道0经过滤波电路后输出的正弦波形
③ChannelD(绿色线条)为通道1经过滤波电路后输出的正弦波形
四个通道其中竖轴每一小格的电压值都为1V,横轴每一小格时间值不固定,详情见keil仿真波形。
6.2 keil显示说明:
利用keil更直观的显示正弦波的频率值。
6.3显示情况
低频时配合调试情况:
6.3.1选择通道0,元素为幅度时,按下RISE按键至2.0V时显示情况:
6.3.2选择通道0,元素为频率时,按下RISE按键至10HZ时显示情况:
高频时显示情况:(因为此部分只为了观察高频时的波形的情况,所以幅度统一设定为5V)
6.3.3选择通道0,元素为频率时,按下RISE键至500HZ,切换通道1,元素为频率时,按下RISE键至800HZ。
6.3.4两通道高频相等时,元素为相位时,p=180°显示情况:
7.总结
本设计内容:基于DDS原理实现幅度、频率和相位均可数控调整的双路正弦波发生器。要求:
(1)频率调整范围:1Hz—1kHz,步进不大于1Hz;
(2)Vpp调整范围:0.8V~5V;
(3)同频正弦时相位可设置,分辨率不大于1°。
本组双路正弦波发生器设计为四按键控制以及LCD1602显示人机界面。
本设计完成的功能:
(1)双路信号幅度、频率的升降按键控制;
(2)当频率相等时,通道0相位不动,通道1相位可通过按键控制调整升降;
(3)当频率相等时,若通过按键调整频率,通道1相位自动清零;
(4)LCD1602实时显示双路正弦信号个元素(幅度、频率和相位)参数;