北邮课程设计-简易信号发生器
课程设计
题目:简易数字信号发生器
学院:电子工程学院 专业:光电信息科学与工程 班级:
一、 课程设计要求
以msp430单片机为核心,通过一个DA (数字模拟)转换芯片,将单片机输出的方波、三角波、正弦波(数字信号)转换为模拟信号输出。提供芯片:msp430G2553/msp430f5529、DAC0832、REF102、LM384、OP07。 1.基本要求
(1)供电电压VDD= 5V~12V; (2)信号频率:5~500Hz(可调) ;
(3)输出信号电压可调范围:≥0.5*VDD ,直流偏移可调:≥0.5*VDD ; (4)完成输出信号切换;
(5)方波占空比:平滑可调20%~80%;
(6)通带内正弦波峰峰值稳定度误差:≤±10%(负载1K ); (7)提交设计报告。 2.发挥部分
(1) 信号频率:5~2000Hz(可调) ;
(2) 多通道同时输出同频正弦波,方波,三角波。(频率可调); (3) 输出频率与幅度可调的正弦波与余弦波,相位误差≤±5度; (4) 自由发挥。
二、 系统硬件和软件框图
1、系统硬件框图
图1 系统硬件框图
2、软件框图
图2 系统软件框图
各函数的作用和相互关系:
在主函数中首先对系统的时钟、I /O 口、定时器进行了初始化。初始化完毕,开启总中断。接着进入循环等待定时器中断子程序的执行。按键中断函数主要对三个菜单按键的动作进行处理。这三个菜单按键可以实现波形的切换,频率的加减,信号的使能输出。在按键中断函数中修改的信号频率大小和波形的种类这两个参数会被主函数和定时器中断函数调用。
三、 硬件系统设计
1、 方案论证与选择 方案一:
利用DAC 0832的11号管脚做为信号输出端,8号管脚输入基准电压,将其输出的电流信号再转换为电压信号进行检测调试。 方案二:
利用DAC 0832的8号管脚做为信号输出端,11号管脚输入基准电压,其输出直接为电压信号,可以直接利用示波器进行检测调试。
综上,方案二省去了电流信号转电压信号的过程,实行起来更加简便。所以最终采用方案二。 2、硬件电路系统设计
系统主控部分采用MSP 430G 2452 LaunchPad 开发板;外围电路主要包括DAC 模块、放大电路、电源及按键模块。
(1)DAC 模块:DAC 模块采用的是DAC 0832。DAC 0832是8分辨率的D /A 转换集成芯片。与微处理器完全兼容。这个DA 芯片以其价格低廉、接口简单、转换控制容易等优点,在单片机应用系统中得到广泛的应用。其主要特性如下:
* 分辨率为8位; * 电流稳定时间1us ;
* 可单缓冲、双缓冲或直接数字输入; * 只需在满量程下调整其线性度; * 单一电源供电(+5V ~+15V );
* 低功耗,20mW
其引脚功能如下:
* D 0~D 7:8位数据输入线,TTL 电平,有效时间应大于90ns (否则锁存器的数据会出错) ;
* ILE :数据锁存允许控制信号输入线,高电平有效; * CS :片选信号输入线(选通数据锁存器),低电平有效;
* WR 1:数据锁存器写选通输入线,负脉冲(脉宽应大于500ns )有效。由ILE 、CS 、WR 1的逻辑组合产生LE 1,当LE 1为高电平时,数据锁存器状态随输入数据线变换,LE 1的负跳变时将输入数据锁存;
* XFER :数据传输控制信号输入线,低电平有效,负脉冲(脉宽应大于500ns )有效;
* WR 2:DAC 寄存器选通输入线,负脉冲(脉宽应大于500ns )有效。由WR 2、XFER 的逻辑组合产生LE 2,当LE 2为高电平时,DAC 寄存器的输出随寄存器的输入而变化,LE 2的负跳变时将数据锁存器的内容打入DAC 寄存器并开始D /A 转换。
* IOUT 1:电流输出端1,其值随DAC 寄存器的内容线性变化; * IOUT 2:电流输出端2,其值与IOUT 1值之和为一常数;
* Rfb :反馈信号输入线,改变Rfb 端外接电阻值可调整转换满量程精度; * Vcc :电源输入端,Vcc 的范围为+5V ~+15V ;
* VREF :基准电压输入线,VREF 的范围为-10V ~+10V ; * AGND :模拟信号地; * DGND :数字信号地。 DAC 电路原理图如下:
(2)放大模块:放大电路模块只要采用的是LM 358运算放大器。LM 358内部包括有两个独立的、高增益、内部频率补偿的双运算放大器,适合于电源电压范围很宽的单电源使用,也适用于双电源工作模式。
其主要特性如下: * 内部频率补偿
* 直流电压增益高(约100dB ) * 单位增益频带宽(约1MHz )
* 源电压范围宽:单电源(3—30V ) ; 双电源(±1.5一±15V ) * 低功耗电流,适合于电池供电;低输入偏流 * 低输入失调电压和失调电流 * 共模输入电压范围宽,包括接地 * 差模输入电压范围宽,等于电源电压范围
我们采用的是单电源同相放大。同时为了给信号加入偏置电压,采用了加法器的设计。
LM 358引脚图及引脚功能如图所示。
放大电路原理图如下:
(3)电源模块:实验中用到的电压主要有12V ,5V ,3.5V
①12V :12V 电压主要是给DAC 0832,REF 102以及LM 358提供工作电压,其来源是由稳压电源直接产生的。
②5V :5V 电压主要是做为基准电压源,其产生来源是REF 102与运放OP 07搭建产生的。
其电路图如图所示
③3.5V :其作用主要是给DAC 0832的19管脚置于高电平。其来源是msp430G2553的VCC 管脚产生的。 3、 总电路原理图
4、焊接的万能板
四、 软件系统设计
1、程序主流程
如图8所示为程序主流程图:
2、程序设计
系统程序主要分为以下几个模块:初始化程序、主程序、输出程序和中断函数。
2.1初始化程序
初始化程序主要完成的引脚的配置
void IO _init (void ) {
P 2DIR =0x 0f ; //P 2的低四位作为输出 }
P 1DIR =0xf 0;
P 1REN |=BIT 1+BIT 2+BIT 3; P 1IE |=0x 0E ; P 1IES |=0x 0E ;
2.2主程序
主程序要负责总体程序管理功能以及AD 使能。
int main (void ) {
WDTCTL = WDTPW + WDTHOLD; IO_init(); DCO_init(); ADC_init();
_enable_interrupts(); Timer_A0_init(); while (1) {
ADC10CTL0 |= ENC + ADC10SC; } //return 0;
}
2.3 按键中断程序
主要完成按键的读取,以及相应的输出量赋值。
#pragma vector = PORT1_VECTOR __interruptvoidPORT1_ISR(void ) { }
void P1_IODect(void ) {
unsignedint Push_Key=0;
//-----排除输出IO 的干扰后,锁定唯一被触发的中断标志位----- Push_Key=P1IFG&(~P1DIR);
//-----延时一段时间,避开机械抖动区域----- __delay_cycles(10000); if ((P1IN&Push_Key)==0)
//消抖延时
//----判断按键状态是否与延时前一致-----
//如果该次按键确实有效
//-----启用Port1事件检测函数----- P1_IODect();
//检测通过,则会调用事件处理函数 //退出中断前必须手动清除IO 口中断
P1IFG=0;
标志
{
//----判断具体哪个IO 被按下,调用该IO 的事件处理函数----- switch (Push_Key){
// case BIT0: P10_Onclick();
break;
case BIT1: { WaveSelect+=5; tt=1;
switch (WaveSelect) {
case 5: break ; case 10: break ; case 15: break ; // case 20: break;
default : WaveSelect=5;break ;
} }; break ;
case BIT2:
{ if (tt==1) {
num=num+1;
Tccr0=keynum[num];
}
if (num==100)num=1;
} break ;
case BIT3:
{ if (tt==1) {
num=num-1;
Tccr0=keynum[num];
}
if (num==0)num=99;
}
break ;
default :
break ;
default }
}
}
2.4输出程序
使用TimerA 0计数器进行输出使能
//任何情况下均加上
#pragma vector =TIMER0_A0_VECTOR
__interruptvoidTimer_A0(void ) //CCIFG中断被响应后,该标志位自动清零
{
if (WaveSelect==5) { if (j
write_dac(tcoutm) ; //三角波产生上升段
tcoutm = tcoutm+8;
}
else
ttcout=1;
TA0CCR0 =Tccr0;
}
if (ttcout==1)
{
if (tcoutm>0)
{
write_dac(tcoutm) ;
tcoutm = tcoutm-8;
}
else
ttcout=0;
TA0CCR0 =Tccr0;
}
if (WaveSelect==15)
{
if (ttcout==0)
{
if (tcoutn
{
write_dac(high) ;
tcoutn+=4;
}
else
ttcout=1;
TA0CCR0 =Tccr0;
}
if (ttcout==1)
{
if (tcoutn
{
write_dac(low) ;
tcoutn+=4;
} //三角波产生下降段 //方波产生上升段 //方波产生下降段 }
tcoutn=0; ttcout=0; TA0CCR0 =Tccr0; } } }
3、各模块之间关系
五、 实现功能说明
1、实现的基本要求
(1)供电电压VDD= 5V~12V;
(2)信号频率:5~500Hz(可调) ;
(3)输出信号电压可调范围:≥0.5*VDD ,直流偏移可调:≥0.5*VDD ;
(4)完成输出信号切换;
(5)方波占空比:平滑可调20%~80%;
(6)通带内正弦波峰峰值稳定度误差:≤±10%(负载1K );
2、实现的提高要求
(1) 信号频率:5~2000Hz(可调) ;
(2) 自由发挥,实现了频率的平滑可调。
六、 程序调试与运行结果
1、切换波形
产生的正弦波,三角波,方波,锯齿波。如图:
2、频率范围可调
输出频率范围5~2000Hz 可调。如图:
3、输出信号范围与直流偏置可调范围
输出信号电压可调范围:> 0.5Vdd
直流偏移可调:> 0.5Vdd
4、方波占空比可调
平滑可调20%~80%
七、 故障及问题分析
1、程序烧制后,将单片机连接到电路中后,波形不稳定,只有当按住P 1.3键时,波形才会稳定。
解决方法:经过几次的不断程序调试后,问题一直存在。后来我们单独搭了个外设按键来代替P 1.3按键,发现是因为单片机的P 1.3按键出了故障,从而导致波形无法稳定。
2、正弦波频率无法达到500Hz 以上。
解决方法:经过查询各种资料以及不断的学习,发现是我们在程序中所取的采样点过多,从而导致波形频率无法继续上升。从而通过减少采样点的个数,使得最后波形频率达到了2000 Hz 。
3、给波形添加直流偏置时遇到的阻碍。
解决方法:为了给正弦波添加直流偏置我们采用了加法器。但对于负载电阻的选取遇到了一定的麻烦。若电阻过小,会导致直流偏置改变非常小,要想达到预想的直流偏置大小,只能加入非常大的电压,这对电路是非常不利的。
若负载
电阻过大,虽然能满足直流偏置的要求,但电路的放大倍数会非常小,甚至会导致波形反而缩小。所以通过我们不断的调试,最后发现当负载电阻为33K 时不仅满足波形的放大,也满足直流偏置的要求。
八、 本次课程设计中的创新点
本次课程设计我们采用了全新的电路设计,利用了DAC 0832直接输出电压值,省去了将电流转换为电压的步骤,不仅电路更加简便,波形输出也更加稳定。在电路的焊接,调试上也更加简单。
也是在老师的鼓励下,使我们的电路从一开始的设计上就独辟蹊径,在后续的实验中也顺利地能完成此次课程设计。
九、 总结与反思
此次课程设计综合了我们在大学本科学习到的很多知识,包括微机原理与接口技术,程序设计语言,模拟电子电路,数字电子电路等,不仅是对大学知识的综合考验,也是对于大学本科所学知识的一次深层次运用,加强了我们对于知识更加系统,更加深入的理解与运用。
在对于电路的设计方面,也是考验和锻炼我们去学会怎么阅读器件的说明手册,怎么从中去筛选我们所需的重要信息。也使得我们更加具备了专业所需的基础本领。在电路的焊接方面,也是对于焊接技术的再次锻炼。
通过此次课程设计,收获颇多,无论是对于学习还是对于生活都是一次收获。对于电路的分模块调试,分模块焊接;对于代码的不断调试修改,电路的不断改进,都是我们从中得到的进步与收获。
但此次课程设计仍旧存在一些遗憾,比如我们没有完成波形的三路同时输出。我们也将继续反思,继续学习。
同时也感谢老师在整个过程中对于我们的支持和鼓励,使我们顺利地完成了此次课程设计。
附源代码:
/*
* main.c
*/
#include
#include
#define uint unsigned int
#define uchar unsigned char
#define ulong unsigned long
#define SID BIT4
#define SCLK BIT3
//#define CS BIT5
//#define CS_SET P2OUT|=BIT4
//#define CS_CLC P2OUT&=~BIT4
//#define WR_SET P2OUT|=BIT5
//#define WR_CLC P2OUT&=~BIT5
uint i=0;
uint k=0;
uint j=0;
uint tt=0;
uint Tccr0=160; //待定
uint data0;
uchar tcout=0;
uchar tcoutm=0;
uchar tcoutn=0;
uchar high=255;
uchar low=0;
uint ttcout=0;
uint num=89;
uint data=0;
uint h_time=100;
//uint h_fre=167;
uint WaveSelect=5;
ulong a=0;
uint keynum[100]={
50000,16000,8000,5333,4000,3200,2667,2286,2000,1778,1600,1455
,1333,1231,1143,1067,1000,889,842,800,762,727,696,667
,640,615,593,571,552,516,500,485,471,457,432,421
,410,400,390,381,372,364,356,348,340,333,327,320,314,308
,302,296,291,281,276,271,267,262,258,254,250,246,242
,239,235,232,229,225,222,219,216,213,211,208,203,200
,198,195,193,190,188,186,182,180,178,176,172,170
,168,167,165,163,162,155,150,145,134,130,126,115};
//延时程序
#define CPU_F ((double)16000000)
#define delay_us(x) __delay_cycles((long)(CPU_F*(double)x/1000000.0)) #define delay_ms(x) __delay_cycles((long)(CPU_F*(double)x/1000.0))
//static unsigned char temp=0xaa; //定时器
uchar
sindata[64]={71.18,77.29,83.29,89.11,94.70,100.00,104.97,109.55,113.70,
117.38,120.56,123.20,125.29,126.79,127.70,128.00,127.70,126.79,125.29,123.20,
120.56,117.38,113.70,109.55,104.97,100.00,94.70,89.11,83.29,77.29,71.18,65.00,
58.82,52.71,46.71,40.89,35.30,30.00,25.03,20.45,16.30,12.62,9.44,6.80,4.71,3.21,
2.30,2.00,2.30,3.21,4.71,6.80,9.44,12.62,16.30,20.45,25.03,30.00,35.30, 40.89,46.71,52.71,58.82,65.00};
/*{66.98,68.96,70.93,72.90,74.86,76.81,78.74,80.67,82.58,84.47,86.34,88.19,
90.02,91.82,93.60,95.35,97.07,98.76,100.41,102.03,103.61,105.16,106.66, 108.13,109.55,
110.93,112.26,113.54,114.78,115.97,117.11,118.19,119.23,120.21,121.13,122.00,122.82,123.58,
124.28,124.92,125.50,126.02,126.48,126.88,127.22,127.50,127.72,127.88,127.97,128.00,127.97,
127.88,127.72,127.50,127.22,126.88,126.48,126.02,125.50,124.92,124.28,123.58,122.82,122.00,
121.13,120.21,119.23,118.19,117.11,115.97,114.78,113.54,112.26,110.93,109.55,108.13,106.66,
105.16,103.61,102.03,100.41,98.76,97.07,95.35,93.60,91.82,90.02,88.19,8
6.34,84.47,82.58,
80.67,78.74,76.81,74.86,72.90,70.93,68.96,66.98,65.00,63.02,61.04,59.07,57.10,55.14,53.19,
51.26,49.33,47.42,45.53,43.66,41.81,39.98,38.18,36.40,34.65,32.93,31.24,29.59,27.97,26.39,24.84,
23.34,21.87,20.45,19.07,17.74,16.46,15.22,14.03,12.89,11.81,10.77,9.79,
8.87,8.00,7.18,6.42,5.72,
5.08,4.50,3.98,3.52,3.12,2.78,2.50,2.28,2.12,2.03,2.00,2.03,2.12,2.28,2.50,2.78,3.12,3.52,3.98,
4.50,5.08,5.72,6.42,7.18,8.00,8.87,9.79,10.77,11.81,12.89,14.03,15.22,1
6.46,17.74,19.07,20.45,21.87,
23.34,24.84,26.39,27.97,29.59,31.24,32.93,34.65,36.40,38.18,39.98,41.81,43.66,45.53,47.42,
49.33,51.26,53.19,55.14,57.10,59.07,61.04,63.02,65.00
};*/
uchar count[200]={
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,
0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,
0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,
0x3d,0x3e,0x3f,0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0x50,
0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f,0x60,0x61,0x62,0x63,0x64,
0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,
0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0
x87,0x88,0x89,0x8a,0x8b,0x8c,
0x8d,0x8e,0x8f,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0
x9b,0x9c,0x9d,0x9e,0x9f,0xa0,
0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0
xaf,0xb0,0xb1,0xb2,0xb3,0xb4,
0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf,0xc0,0xc1,0xc2,0
xc3,0xc4,0xc5,0xc6,0xc7, };
void P1_IODect(void);
/****************************************************/ //IO初始化
/****************************************************/ void IO_init(void) { }
/****************************************************/ //时钟初始化
/****************************************************/
void DCO_init(void) {
P2DIR=0x0f; //P2的低四位作为输出 P1DIR=0xf0;
P1REN |=BIT1+BIT2+BIT3; P1IE |=0x0E; P1IES|=0x0E;
BCSCTL1 = CALBC1_16MHZ; //设定cpu 时钟DCO 频率为16MHz DCOCTL = CALDCO_16MHZ;
BCSCTL2|=SELM_1+DIVM_0; //SMCLK选择16MHz ,不分频 BCSCTL2&=~SELS; }
/****************************************************/ //ADC初始化
/****************************************************/
void ADC_init(void) { }
/****************************************************/ //定时器A 初始化
/****************************************************/
void Timer_A0_init(void) {
TA0CTL|=TASSEL_2+MC_1; //TA时钟源选择SMCLK ,连续/*ADC10CTL0=ADC10SHT_2 + ADC10ON + ADC10IE; ADC10CTL1=INCH_0; //通道2 ADC10CTL0 |=ENC; //使能*/ ADC10CTL0&=~ENC;
ADC10CTL0 |= ADC10ON +ADC10SHT_0 + SREF_0 +ADC10IE; ADC10CTL1 |= INCH_0+CONSEQ_0; ADC10AE0|=0x00;
//ADC10CTL0 |= ENC + ADC10SC; _EINT();
增计数模式 TA0CCR0=167;
TA0CCTL0|=CCIE; //打开比较模块0中断
_EINT(); }
/****************************************************/ //ADC中断服务程序
/****************************************************/
#pragma vector=ADC10_VECTOR __interrupt void ADC10(void) { }
/****************************************************/ //输出程序
/****************************************************/ void write_dac(uchar da) {
P1OUT=da; //P1P2组合输出 P2OUT=da; delay_us(1); }
}*/
h_time=data0*15/128+40; //TA0CCR0=data0*45+115;
data0=ADC10MEM; //获取外部输入的电压 /*
if(100
data=(uint)(data0/2-50); h_time=180-data;
/****************************************************/ //定时器中断服务程序
/****************************************************/ #pragma vector =TIMER0_A0_VECTOR
__interrupt void Timer_A0(void) //CCIFG中断被响应后,该标志位自动清零 { if(WaveSelect==5) { if(j
write_dac(sindata[j]) ; //
j++;
} else
j=0;
TA0CCR0 =Tccr0;
}
if(WaveSelect==10) {
if(ttcout==0)
{
if(tcoutm
{
write_dac(tcoutm) ; // tcoutm = tcoutm+2; } else
ttcout=1;
TA0CCR0 =Tccr0;
}
if(ttcout==1)
正弦波产生 三角波产生上升段
if(tcoutm>0) { write_dac(tcoutm) ; //三角波产生下降段
tcoutm = tcoutm-2;
} else
ttcout=0;
TA0CCR0 =Tccr0;
}
if(WaveSelect==15) { if(ttcout==0) {
if(tcoutn
{
write_dac(high) ; // tcoutn++; } else
ttcout=1; TA0CCR0 =Tccr0;
}
if(ttcout==1) { if(tcoutn
}
方波产生上升段方波产生下降段
}
tcoutn=0; ttcout=0;
TA0CCR0 =Tccr0;
}
}
}
#pragma vector = PORT1_VECTOR __interrupt void PORT1_ISR(void) {
//-----启用Port1事件检测函数----- P1_IODect();
//检测通过,则会调用事件处理函数 //退出中断前必须手动清除IO 口中
P1IFG=0;
断标志 }
/****************************************************************************************************** * 名称:P1_IODect()
* 功能:判断具体引发中断的IO ,并调用相应IO 的中断事件处理函数 * 入口参数:无 * 出口参数:无
* 说明:该函数兼容所有8个IO 的检测,请根据实际输入IO 激活“检测代码”。 *
本例中,仅有P1.3被用作输入IO ,所以其他7个IO 的“检测代码”没
有被“激活”。 * 范例:无
******************************************************************************************************/
void P1_IODect(void) {
unsigned int Push_Key=0;
//-----排除输出IO 的干扰后,锁定唯一被触发的中断标志位----- Push_Key=P1IFG&(~P1DIR);
//-----延时一段时间,避开机械抖动区域----- __delay_cycles(10000);
//消抖延时
//----判断按键状态是否与延时前一致----- if((P1IN&Push_Key)==0)
//如果该次按键确实有效
{
//----判断具体哪个IO 被按下,调用该IO 的事件处理函数----- switch(Push_Key){
break;
// case BIT0: P10_Onclick();
case BIT1:
{
WaveSelect+=5;
tt=1;
switch(WaveSelect) {
case 5: break; case 10: break; case 15: break; // case 20: break;
default: WaveSelect=5;break; } break;
};
case BIT2:
{
if(tt==1)
{
num=num+1;
Tccr0=keynum[num]; }
if(num==100)num=0;
break;
}
case BIT3:
{
if(tt==1)
{
num=num-1;
Tccr0=keynum[num]; }
if(num==0)num=99; }
break;
P14_Onclick(); P15_Onclick(); P16_Onclick(); P17_Onclick();
break; break; break; break;
break;
//任何情况下均加上
// case BIT4: // case BIT5: // case BIT6: // case BIT7:
default:
default
}
} }
/***************************************************/
/******************************************************/ int main(void) {
// Stop watchdog timer to prevent time out reset WDTCTL = WDTPW + WDTHOLD; IO_init(); DCO_init(); ADC_init();
_enable_interrupts();
Timer_A0_init(); while(1) {
ADC10CTL0 |= ENC + ADC10SC;
}
//return 0; }