单片机课程设计报告书波形发生器

                

目录

1、课程设计目的......................................................................................................... 1

2、课程设计题目和实现目标..................................................................................... 1

3、设计方案................................................................................................................. 1

4、Proteus仿真原理图................................................................................................ 1

5、程序流程图............................................................................................................. 1

6、程序代码................................................................................................................. 1

7、调试总结................................................................................................................. 1

8、设计心得体会......................................................................................................... 1

9、参考文献................................................................................................................. 1

 


1、课程设计目的

《单片机原理及应用》课程设计是与《单片机原理及应用》课程相配套的实践教学环节。《单片机原理及应用》是一门实践性很强的专业基础课,通过课程设计,达到进一步理解单片机的硬件、软件和综合应用方面的知识,培养实践能力和综合应用能力,开拓学习积极性、主动性,学会灵活运用已经学过的知识,并能不断接受新的知识。培养大胆发明创造的设计理念,为今后就业打下良好的基础。

通过课程设计,掌握以下知识和技能:

1.单片机应用系统的总体方案的设计;

2.单片机应用系统的硬件设计;

3.单片机应用系统的软件程序设计;

4.单片机开发系统的应用和调试能力

    

2、课程设计题目和实现目标

   本次课程设计的题目是;制作一个波形发生器,产生单极性、幅度可调、周期可调的方波、锯齿波、三角波、正弦波信号,不同波形用不同符号显示在一个LED上,用一个LCD显示幅值和频率。

   本次课程设计的目标:设计一个波形发生器,带有四个按钮,分别是波形选择、增加频率、减少频率、调节幅度,并带有一个LCD和一个LED,LED用来显示波形的符号LCD用来显示频率、幅值。波形符号用1表示正弦波,2表示三角波,3表示方波,4表示锯齿波。频率的调节幅度是10HZ,幅值调节幅度分别是0.2V,0.02V,0.3V,0.4V。

 3、设计方案

   本次设计采用AT89C51及其外围扩展系统和PCF8591,软件方面主要是应用C语言设计程序。系统以AT89C51为核心,配置相应的外设及接口电路,用Keil C及Proteus等软件开发,用C语言编程,组成一个多功能信号发生器。用户通过按键选择输出实验室中经常使用到的几种基本波形:方波、锯齿波、正弦波和三角波。方波由AT89C51单片机将最大值和最小值输出给D/A进行转换,并由用户通过键盘选择波形周期。

 可采用单片机程序产生以上4种波形,并通过一片D/A转换器输出。另外,采用一片D/A转换器来控制前一片D/A转换器的参考电压,从而可以改变输出波形幅值。通过外接键盘来设定波形的类型、幅值和频率。

 

          总体方案结构图

a. 单片机的选择

AT89C51是美国ATMEL公司生产的低电压、高性能CMOS8位单片机,片内含4Kbytes的可反复擦写的只读程序存储器(PEROM)和128bytes的随机抽取数据

存储器(RAM),器件采用ATMEL公司的高密度、非易失性存储技术生产。兼容标准MCS-51指令系统,片内置通用8位中央处理器(CPU)和Flash存储单元,功能强大。AT89C51单片机可为您提供许多高性价比的应用场合,可灵活应用于各种控制领域。

AT89C51提供以下标准功能:4K字节Flash闪速存储器,128字节内部RAM,32个I/O口线,两个16位定时/计数器,一个5向量两级中断结构,一个全双工串行通信口,片内振荡器及时钟电路。同时,AT89C51可降至0Hz的静态逻辑操作,并支持两种软件可选的节电工作方式。空闲工作方式停止CPU的工作,但允许RAM,定时器/计数器串口通信及中断系统继续工作,掉电方式保存RAM中的内容,但振荡器停止工作并禁止其它所有部件工作直到下一个硬件复位。

AT89C51方框图

PCF8591是一个单片集成、单独供电、低功耗、8-bit CMOS数据获取器件。PCF8591具有4个模拟输入、1个模拟输出和1个串行I2C总线接口。PCF8591的3个地址引脚A0, A1和A2可用于硬件地址编程,允许在同个I2C总线上接入8个PCF8591器件,而无需额外的硬件。在PCF8591器件上输入输出的地址、控制和数据信号都是通过双线双向I2C总线以串行的方式进行传输。PCF8591的功能包括多路模拟输入、内置跟踪保持、8-bit模数转换和8-bit数模转换。PCF8591的最大转化速率由I2C总线的最大速率决定。

 

                   PCF8591原理图

                  

b.输入电路键盘的接口电路

在单片机控制系统中,为了实现人对系统的操纵控制及向系统输入参数,都需要为系统设置按键或键盘。键盘是一组按键的集合。键盘所使用的按键一般都是具有一对常开触点的按键开关,平时不按键时,触点处于断开(开路)状态,当按下按键时,触点才处于闭合(短路)状态,而当按键被松开后,触点又处于断开状态。

C. 各部分组成

(1)控制部分 

 由单片机AT89C51作为系统的主核心,包括四个按钮,按钮用来选择波形、调节频

率和幅值。

  

(2)转换部分   由PCF8591组成,作为D/A转换的芯片;

(3)显示部分   由一片LCD 和一片LED组成,LCD用来显示频率和幅值,LED用来显示数字“1,2,3,4”,数字1代表着此时输出的波形是正弦波,数字2代表输出波形是三角波,数字3代表输出是方波,数字4代表输出是锯齿波。

             

 

4、Proteus仿真原理图 

                 正弦波与仿真电路图

               三角波与仿真电路图

               方波与仿真电路图

       

                   锯齿波与仿真电路图

5.程序流程图

6、程序代码    

 #include<reg52.h>

#define  uchar unsigned char

#define  uint  unsigned int

#include <I2C.H>

#define  PCF8591 0x90    //PCF8591 地址

sbit key=P3^2;

uchar wavecount;    //'抽点'计数

uchar THtemp,TLtemp;//传递频率的中间变量

//uint T_temp;

uchar judge=1;      //在方波输出函数中用于简单判别作用

uchar waveform;         //当其为0、1、2,3时,分别代表三种波

uint total_freq; //总频率

uchar code freq_unit[4]={10,10,10,10}; //三种波的频率单位

uchar idata wavefreq[4]={1,1,1,1};   //给每种波定义一个数组单元,用于存放单位频率的个数

uchar idata lcd_hang1[16]={"             V"};

uchar idata lcd_hang2[16]={"f=    Hz  .  V"};

uint volt;//电压

uchar ADdata;

uchar k,p;

/*******************************************************************

DAC 变换, 转化函数              

*******************************************************************/

bit DACconversion(unsigned char sla,unsigned char c,  unsigned char Val)

{

   Start_I2c();              //启动总线

   SendByte(sla);            //发送器件地址

   if(ack==0)return(0);

   SendByte(c);              //发送控制字节

   if(ack==0)return(0);

   SendByte(Val);            //发送DAC的数值 

   if(ack==0)return(0);

   Stop_I2c();               //结束总线

   return(1);

}

/*******************************************************************

ADC发送字节[命令]数据函数              

*******************************************************************/

bit ISendByte(unsigned char sla,unsigned char c)

{

   Start_I2c();              //启动总线

   SendByte(sla);            //发送器件地址

   if(ack==0)return(0);

   SendByte(c);              //发送数据

   if(ack==0)return(0);

   Stop_I2c();               //结束总线

   return(1);

}

/*******************************************************************

ADC读字节数据函数              

*******************************************************************/

unsigned char IRcvByte(unsigned char sla)

{  unsigned char c;

   Start_I2c();          //启动总线

   SendByte(sla+1);      //发送器件地址

   if(ack==0)return(0);

   c=RcvByte();          //读取数据0

   Ack_I2c(1);           //发送非就答位

   Stop_I2c();           //结束总线

   return(c);

}

/***********这两组数组很重要,需要根据波形来调试,选择合适的值,使输出波形达到频率要求************/

uchar code waveTH[]={

    0xfd,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,

    0xfd,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,

    0xec,0xf6,0xf9,0xfb,0xfc,0xfc,0xfd,0xfd,0xfd,0xfe};

uchar code waveTL[]={

    0x06,0x8a,0x10,0x4e,0x78,0x93,0xa8,0xb3,0xbe,0xc6, //正弦波频率调整中间值

    0xac,0xde,0x48,0x7a,0x99,0xaf,0xbb,0xc8,0xd0,0xde,   //三角波频率调整中间值

    0x88,0x50,0x90,0x32,0x34,0xbe,0x4a,0xa3,0xe5,0x2c};

/*************************************************************************************************/

uchar code triangle_tab[]={ //每隔数字8,采取一次  三角波

    0x00,0x08,0x10,0x18,0x20,0x28,0x30,0x38,0x40,0x48,0x50,0x58,0x60,0x68,0x70,0x78,

    0x80,0x88,0x90,0x98,0xa0,0xa8,0xb0,0xb8,0xc0,0xc8,0xd0,0xd8,0xe0,0xe8,0xf0,0xf8,0xff,

    0xf8,0xf0,0xe8,0xe0,0xd8,0xd0,0xc8,0xc0,0xb8,0xb0,0xa8,0xa0,0x98,0x90,0x88,0x80,

    0x78,0x70,0x68,0x60,0x58,0x50,0x48,0x40,0x38,0x30,0x28,0x20,0x18,0x10,0x08,0x00};

uchar code san_tab[]={0x00,0x08,0x10,0x18,0x20,0x28,0x30,0x38,0x40,0x48,0x50,0x58,0x60,0x68,0x70,0x78,

    0x80,0x88,0x90,0x98,0xa0,0xa8,0xb0,0xb8,0xc0,0xc8,0xd0,0xd8,0xe0,0xe8,0xf0,0xf8,0xff};

uchar code sine_tab[256]={

    //输出电压从0到最大值(正弦波1/4部分)

    0x80,0x83,0x86,0x89,0x8d,0x90,0x93,0x96,0x99,0x9c,0x9f,0xa2,0xa5,0xa8,0xab,0xae,0xb1,0xb4,0xb7,0xba,0xbc,

    0xbf,0xc2,0xc5,0xc7,0xca,0xcc,0xcf,0xd1,0xd4,0xd6,0xd8,0xda,0xdd,0xdf,0xe1,0xe3,0xe5,0xe7,0xe9,0xea,0xec,

    0xee,0xef,0xf1,0xf2,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfd,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,

    //输出电压从最大值到0(正弦波1/4部分)

    0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xfd,0xfd,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7,0xf6,0xf5,0xf4,0xf2,0xf1,0xef,

    0xee,0xec,0xea,0xe9,0xe7,0xe5,0xe3,0xe1,0xde,0xdd,0xda,0xd8,0xd6,0xd4,0xd1,0xcf,0xcc,0xca,0xc7,0xc5,0xc2,

0xbf,0xbc,0xba,0xb7,0xb4,0xb1,0xae,0xab,0xa8,0xa5,0xa2,0x9f,0x9c,0x99 ,0x96,0x93,0x90,0x8d,0x89,0x86,0x83,0x80,

    //输出电压从0到最小值(正弦波1/4部分)

   0x80,0x7c,0x79,0x76,0x72,0x6f,0x6c,0x69,0x66,0x63,0x60,0x5d,0x5a,0x57,0x55,0x51,0x4e,0x4c,0x48,0x45,0x43,

  0x40,0x3d,0x3a,0x38,0x35,0x33,0x30,0x2e,0x2b,0x29,0x27,0x25,0x22,0x20,0x1e,0x1c,0x1a,0x18,0x16 ,0x15,0x13,

0x11,0x10,0x0e,0x0d,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x02,0x01,0x00,0x00,0x00,0x00,0x00,0x00,

    //输出电压从最小值到0(正弦波1/4部分)

0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x02 ,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0d,0x0e,0x10,

  0x11,0x13,0x15 ,0x16,0x18,0x1a,0x1c,0x1e,0x20,0x22,0x25,0x27,0x29,0x2b,0x2e,0x30,0x33,0x35,0x38,0x3a,0x3d,

0x40,0x43,0x45,0x48,0x4c,0x4e,0x51,0x55,0x57,0x5a,0x5d,0x60,0x63,0x66 ,0x69,0x6c,0x6f,0x72,0x76,0x79,0x7c,0x80};

void delay(uchar z)  

{

    uint x,y;

    for(x=z;x>0;x--)

    for(y=110;y>0;y--);

}

void triangle_out()  //三角波输出

   // DAdata=triangle_tab[wavecount++];DA 数据

    DACconversion(PCF8591,0x40,triangle_tab[wavecount++] ); //DAC   数模转换

    if(wavecount>64) wavecount=0;

}

void sine_out() //正弦波输出

         DACconversion(PCF8591,0x40,sine_tab[wavecount++]);  //DAdata=sine_tab[wavecount++];

    if(wavecount>256) wavecount=0;

}

void square_out()  //方波输出

    judge=~judge;

    if(judge==1) DACconversion(PCF8591,0x40,0xff);

    else      DACconversion(PCF8591,0x40,0x00);

}

void san_out()//

{

   DACconversion(PCF8591,0x40,san_tab[wavecount++]);  //DAdata=sine_tab[wavecount++];

   if(wavecount>32) wavecount=0;

}

/************1602液晶的相关函数*************/

#define lcd_ports P1

sbit rs=P2^2;

sbit rw=P2^3;

sbit lcden=P2^4;

void write_com(uchar com)

{

    rs=0;      //置零,表示写指令

    lcden=0;

    lcd_ports=com;

    delay(5);

    lcden=1;

    delay(5);

    lcden=0;      

}

void write_date(uchar date)

{

    rs=1;      //置1,表示写数据(在指令所指的地方写数据)

    lcden=0;

    lcd_ports=date;

    delay(5);

    lcden=1;

    delay(5);

    lcden=0;      

}

void disp_lcd(uchar addr,uchar *temp1)

{

    uchar num;

    write_com(addr);

    delay(1);   //延时一会儿???

    for(num=0;num<16;num++)

    {

           write_date(temp1[num]);//或者这样写write_date(*(temp1+num));

           delay(1);

    }

}

void init_lcd()

{

    //uchar num;

    lcden=0;  //可有可无

    rw=0;  //初始化一定要设置为零,表示写数据

    write_com(0x38);  //使液晶显示点阵,为下面做准备

    write_com(0x0c);  //初始设置

    write_com(0x06);  //初始设置

    write_com(0x01);  //清零

    write_com(0x80);  //使指针指向第一行第一格

  //  disp_lcd(0x80,&lcd_hang1[4*16]);  //在第一行显示

    //disp_lcd(0xc0,&lcd_hang1[4*16]);  //在第二行显示

}

/********************1602液晶函数声明结束*********************/

void main()

{

    uchar i=0;

    init_lcd();

    waveform=0; 

    P0=0x00;

    TMOD=0x01;  //设置定时器0为16位工作方式

    IT0=1;      //设置外部中断0为下降沿触发

    ET0=1;      //开定时器中断

    EX0=1;

    EA=1;

    k=100;

    while(1)

    { 

       // disp_lcd(0xc0,lcd_hang2);  //在第一行显示

       // disp_lcd(0xc0,lcd_hang2);  //在第二行显示

           };

     

void timer0() interrupt 1

{

    TH0=THtemp;

    TL0=TLtemp;           

    if(waveform==0)    

       { P0=0xf9;

         p=10;

         sine_out();

       }

    else if(waveform==1)

         {

           P0=0xa4;

              p=1;

           triangle_out();

         }

    else if(waveform==2)

           {

            P0=0xb0;

                  p=15;

            square_out();

           }

    else if(waveform==3)

           {

            P0=0x99;

                  p=20;

            san_out();

                  }

}

//void AD() interrupt 2

//{}

void key_int0() interrupt 0

{

    uchar keytemp;

    EA=0; TR0=0;     //关总中断与定时器

    delay(5);       //延时够吗

    if(key==0)       //确实有按键按下而引发中断

    {

           keytemp=P3&0xf0; //获取P3口高四位的值

           switch(keytemp)

           {

               case 0xe0:    //选择波形

                       waveform++;

                          if(waveform>3) waveform=0;

                          break;

               case 0xd0:  //频率按规定单位依次增加

                       wavefreq[waveform]++;

                          if(wavefreq[waveform]>10) wavefreq[waveform]=1; // /*这边要用“>10”,因为它比“=11”可靠

                          break;                                                                        //   性更高,使加数有个上限,不会一直加下去*/

               case 0xb0:  //频率按规定单位依次衰减

                       wavefreq[waveform]--;

                          if(wavefreq[waveform]<1) wavefreq[waveform]=10; //这边要用“<1”,因为它比“=0”可靠  性吒

                          break;

            case 0x70:    //调节输出波形幅度 PO口数据

                       k=k+p; //调节输出幅度       

                          break;

           }

        THtemp=waveTH[waveform*10+(wavefreq[waveform]-1)]; //方括号中选取第几个数后,并把该值赋给T_temp

           TLtemp=waveTL[waveform*10+(wavefreq[waveform]-1)];

        total_freq= wavefreq[waveform] * freq_unit[waveform]; //求输出频率(个数*单位)

        lcd_hang2[5]=total_freq%10+0x30;                 //在液晶中显示个位,(0x30 在液晶显示中表示数字0)

        total_freq/=10; lcd_hang2[4]=total_freq%10+0x30; //在液晶中显示时十位

        total_freq/=10; lcd_hang2[3]=total_freq%10+0x30; //在液晶中显示时百位

        total_freq/=10; lcd_hang2[2]=total_freq%10+0x30; //在液晶中显示时千位

      //  ISendByte(PCF8591,0x41);

        //ADdata=IRcvByte(PCF8591)*2;  //ADC0 模数转换1                   

        volt=20*k; 

        volt/=10;lcd_hang2[12]=volt%10+0x30;

           volt/=10;lcd_hang2[11]=volt%10+0x30;

        volt/=10;lcd_hang2[9]=volt%10+0x30;  

  //      disp_lcd(0x80,lcd_hang1);  //在第一行显示

        disp_lcd(0xc0,lcd_hang2);  //在第二行显示

    

       

    }

    wavecount=0; //'抽点'计数清零

    while(!key);

    EA=1; TR0=1;     //开启总中断与定时器

}

7、调试总结  

一个单片机应用系统经过方案论证、硬件设计、软件的编制,通常不可能一次成功,不可避免地要出现一些错误,这就要靠调试来解决。调试的次序一般是先易后难,后面的调试应尽可能采用以前已调好的电路,各单元电路都调好以后,再进行系统总调。

对于本波形发生器,其硬件电路的调试可以按如下步骤进行

1)硬件电路的总体检查

2)单元电路调试

3)波形输出电路调试

(1)在D/A转换中,为了改变每个输出波形的幅值,需要通过一个可调滑动变阻器来改变初值,进而通过PCF8591转换成相应的模拟量输出。 (2)最后,利用PROTUES和KEIL软件对所编写的主程序进行联调仿真,通过PROTUES中的模拟示波器观察到仿真结果。首先,通过按按键1,看是否有出现所需要的4种波,通过示波器观察;其次,通过按按键2和3,观察是否有相应的增加和减少频率,结果通过LCD来显示;最后通过按按键4来调节幅值。

8、设计心得体会

     课程设计是培养学生综合运用所学知识,发现,提出,分析和解决实际问题,锻炼实践能力的重要环节,是对学生实际工作能力的具体训练和考察过程.随着科学技术发展的日新日异,单片机已经成为当今计算机应用中空前活跃的领域, 在生活中可以说得是无处不在。因此作为二十一世纪的大学来说掌握单片机的开发技术是十分重要的。

在实训过程中遇到相当多的难题 。一开始大家都是一头雾水,不知道从哪里下手,而且知识严重匮乏,大家只好分工合作,纷纷查找各种资料,并且成立了一个讨论小组,共同来研究这次的课设。经过两周的努力和合作终于把这次的课设完成了。可谓苦尽甘来。

   通过这次单片机课程设计,我不仅加深了对单片机理论的理解,将理论很好地应用到实际当中去,而且我还学会了如何去培养我们坚持不懈和合作精神,让我们不断超越自己。课堂上没有见过真正的单片机 ,只是从理论上去理解 ,枯燥乏味,这次的实训 ,让我大开眼界,提高了单片机知识的理解和运用水平 ,从选题到定稿,从理论到实践,在整整两星期的日子里,可以学到很多很多的的东西,同时不仅可以巩固了以前所学过的知识,而且学到了很多在书本上所没有学到过的知识。通过这次课程设计我也发现了自身存在的不足之处,虽然感觉理论上已经掌握,但在运用到实践的过程中仍有意想不到的困惑,经过一番努力才得以解决。

   

    我认为这个收获应该说是相当大的。一开始我们从参考书上找来了课题,但是毕竟是参考书,做到后来发现很多程序都是不完整的,这让我们伤透了脑筋。看着别的小组都弄得有模有样了,可是我们连一个课题都还没有定好。好不容易又找到了课题,可是结果还是很不尽人意。程序接线什么的都弄好了,调试也没有问题,可是就是无法达到预期想要的结果。参考书毕竟只是一个参考,设计这种东西最后还是要靠自己动脑筋。然后我们大家一起齐心协力,从平时做的实验﹑老师上课的举例﹑书本上的知识以及老师的辅导和其他同学的帮助下终于完成了。另外在课程设计的过程中,当我们碰到不明白的问题时,指导老师总是耐心的讲解,给我们的设计以极大的帮助,使我们获益匪浅。因此非常感谢老师的教导。通过这次设计,我懂得了学习的重要性,了解到理论知识与实践相结合的重要意义,学会了坚持、耐心和努力,这将为自己今后的学习和工作做出了最好的榜样。我觉得作为一名工程专业的学生,这次课程设计是很有意义的。更重要的是如何把自己平时所学的东西应用到实际中。虽然自己对于这门课懂的并不多,很多基础的东西都还没有很好的掌握,觉得很难,也没有很有效的办法通过自身去理解,但是靠着这一个多礼拜的“学习”,在小组同学的帮助和讲解下,渐渐对这门课逐渐产生了些许的兴趣,自己开始主动学习并逐步从基础慢慢开始弄懂它。

     最后再次感谢老师和同学们的帮助。

 

9、参考文献  

参考文献

 [1] 肖看,李群芳。单片机原理、接口及应用——嵌入式系统技术基础(第二版)。清华大学出版社2010.9

[2] 竺可桢.物理学.北京:科学出版社,1997.56-60.

 

相关推荐