北邮小学期AVR单片机电子琴实验报告

小学期AVR单片机实验报告

实验题目: 基于ATmega16L单片机的电子琴设计

学生姓名:学渣

班    级:2012XXXXXX

班内序号:XX

学    号:2012XXXXXX

日    期:20##年9月30日

同组同学:学渣

                  

目录:

一、实验介绍 ......................................3 

1.1实验课题名称 .................................3

1.2实验平台 .....................................3

1.3实验课题关键字 ..............................3

1.4实验摘要 .....................................3

二、小组分工 ......................................3

三、基本题目训练——流水灯与数码管秒表计时器.......3

3.1实现功能......................................3

3.2程序代码分析..................................4

3.3实验结果图片...................................7

四、有关发声的基础知识 ............................7 

五、电子琴的设计与测试..............................9 

5.1设计过程 ......................................9 

5.2实验所需元器件.................................9 

5.3实验程序主要流程图.............................10

5.4实验原理及原理图...............................10 

5.4.1实验原理....................................10 

5.4.2原理图 .....................................11

5.5各个模块的设计与讲解............................11 

5.6程序源代码及程序分析 ..........................14 

5.7实验结果..................................... ..24

六、排错过程............................ .............26 

七、心得体会..........................................29 

八、参考文献..........................................32 

九、意见与建议........................................33

一、实验介绍:

1.1实验课题名称:基于ATmega16L单片机的电子琴设计

1.2实验平台:本实验所用平台为AVR Studio 4

1.3实验课题关键字:

ATmega16L型单片机 电子琴 键盘按键 LCD液晶显示屏

1.4实验摘要:

本实验设计的电子琴拥有可视化操作界面,能实现即时弹奏音乐、音乐播放、音乐变速、音乐变调,并可以进行任意长度录音(通过按键记录音阶)等功能

二、小组分工:

l  XXX负责电路硬件的连接和报告的撰写

l  XXX负责程序代码的编写

l  XXX负责资料的收集整理和查阅

三、基本题目训练——流水灯与数码管秒表计时器

3.1功能:八盏LED二极管按顺序依次循环点亮,实现流水灯的效果,同时两只数码管分别代表秒和十分之一秒,进行秒表计时,配有两个按键,实现计时过程中的暂停和继续,同时在按下暂停键的时候蜂鸣器会响一声。利用ATmega16的寄存器中断功能,实现流水灯和数码管秒表计时器在实验板上同时工作,并且互相独立不影响。

3.2程序代码分析:

#include<avr/io.h>

#include <avr/interrupt.h>

       char b[10]  =  {

              0b11111010,//0

              0b00110000,//1

              0b11011001,//2

              0b01111001,//3

              0b00110011,//4

              0b01101011,//5

              0b11101011,//6

              0b00111000,//7

              0b11111011,//8

              0b01111011,//9          

       };

       char a[10]  =  {

              0b11111010,//0

              0b00110000,//1

              0b11011001,//2

              0b01111001,//3

              0b00110011,//4

              0b01101011,//5

              0b11101011,//6

              0b00111000,//7

              0b11111011,//8

              0b01111011,//9          

       };

       volatile char temp;

int main(void)

{

       DDRA = 0xff;

       PORTA = 0b10000000;

DDRC = 0xff;

       DDRD = 0b11111011;

       DDRB = 0b11111011;

      

       TCNT0 = 55;

       PORTB = b[0];

       PORTD = a[0];

       PORTC |= (1 << 0);

       TCCR0 |= (1 << CS01);

int count1 = 0,count2 = 0,i;

      

MCUCR |= (1 << ISC00)|(1 << ISC01);//INT0上升沿触发

       GICR |= (1 << INT0);//使能INT0,INT1

       sei();//使能全局中断

      while(1)

       {

              for(i = 0;i<9000;i++)

              {

                  while(!(TIFR & (1<<TOV0)));

                     TCNT0=55;

              }

          count1++;

          if(count1!=10)

          PORTD = a[count1];

          else

          {

               count2++;

               count1 = 0;

               if (count2 == 10)

                    count2 = 0;

              temp = PORTA;

              PORTA = PORTA >> 1;

              if(temp & 0b00000001)

              {

                     PORTA = PORTA | 0b10000000;

              }

               PORTB = b[count2];

               PORTD = a[0];

               }

    }

}

SIGNAL(SIG_INTERRUPT0)//INT0中断服务程序

{

  int count3 = 0,count4=0;

    while(!(PINB & (1<<2)))

  {

           count3++;

           if(count3 == 10)

            {

                     count4++;

                     count3 = 0;

                  temp = PORTA;

                     PORTA = PORTA >> 1;

                     if(temp & 0b00000001)

                     {

                            PORTA = PORTA | 0b10000000;

                     }

                }

                for(int i=0;i<9000;i++)

          {

          while(!(TIFR & (1<<TOV0)));

              TCNT0=55;

          }

       }

}

3.3实验结果:

四、有关发声的基础知识:

声波是振动产生的。频率即表示每秒钟振动的次数,采用CTC方式时AVR单片机通过特定的端口(PD4及PD5)输出一定频率的方波,TCCR1A设为比较匹配时OC1A/OC1B电平取反,TCCR1B的计数上限为OC1A,根据公式OCnA=f/2N(1+OCRnA)计算出7个频率音阶所需的OCR1A,则只需将喇叭接在PD4或PD5,通过程序控制端口输出特定频率的方波波形(发声使用正弦波最好,方波效果稍次但影响不大),喇叭就会发出七种不同的声音,依照人听觉分辨7个音阶分为三组,分别为高中,低音阶频率,经计算可得,当OCR1A=(500000/musicmem[i]-1)时,{131,147,165,175,196,220,247}存放低音阶频率,{262,294,330,349,392,440,494}存放中音阶频率,{524,588,660,698,784,880,988}则存放高音阶频率,所以需要定义三个数组存放各音阶的频率值。除了音符频率以外还需要音长,所以定义1个2位数组表示一段音乐,第一个表示频率,第二个表示音长,播放时先访问频率数组,使喇叭发声,之后访问音长数组,确定喇叭发声时间。而有了音符频率数组,只要再得到任意一首歌的简谱,就可以将其转化为两个数组的形式,由音符对应的频率得出频率数组,然后再根据每个音符的音长,将其通过乐曲的节拍和音符的拍数计算出音符持续时间即可得出音长数字。

五、电子琴的设计过程:

5.1设计过程:

5.2 实验所需元器件:

ATmega16L型单片机,JTAG下载器,扬声器,4*4矩阵键盘,1602LCD液晶屏,LM386,实验盒(内装剪刀、镊子、导线等用品)等

5.3实验程序主要流程图:

 5.4实验原理及原理图 :

5.4.1实验原理 :

以ATmega16单片机作为整个系统的控制中心,外加琴键控制模块、播放模块、显示模块,使制作的电子琴完成设想的功能。琴键控制模块为4*4矩阵键盘,可以通过按下不同的琴键弹奏出不同的音阶,每个音阶对应着不同的频率,一段音乐是由许多不同的音阶组成,这样我们就可以根据不同的频率组合得到我们想要的音乐,同时在录制模式下,还可以通过按不同的按键记录下不同的音阶,由此记录一段音乐。播放模块接收对应频率的方波,由此播放琴键弹奏的音阶以及播放预先存放在单片机里的音乐。显示模块显示出当前所处的模式。

5.4.2 原理图:

5.5各模块的设计与详解:

中央处理器——ATmega16:

 

实验中,PB0~PB7全部设置为输出,分配给LCD液晶显示屏D0~D7管脚;PA0~PA7连接4*4矩阵键盘的八个引脚;PD4、PD6和PD7设置为输出状态,分别连接到LCD显示屏的RS、R/W和E端口上;PD5置为输出状态,接到扬声器的一个管脚,扬声器的另一管脚接地;VCC为电源,向LCD显示屏供电;GND为公共接地。

琴键控制模块——4*4矩阵键盘:

工作原理:按键设置在行、列线交点上,行、列线分别连接到按键开关的两端。行线通过上拉电阻接到+5V 电源上。无按键按下时,行线处于高电平的状态,而当有按键按下时,行线电平由与此行线相连的列线的电平决定。

显示模块——LCD液晶显示屏:

引脚详解:

第1脚:VSS为地电源。 

第2脚:VDD接5V正电源。 

第3脚:VL为液晶显示器对比度调整端,接正电源时对比度最弱,接地时对比度最高,对比度过高时会产生“鬼影”,使用时可以通过一个10K的电位器调整对比度。 

第4脚:RS为寄存器选择,高电平时选择数据寄存器、低电平时选择指令寄存器。

第5脚:R/W为读写信号线,高电平时进行读操作,低电平时进行写操作。当RS和R/W共同为低电平时可以写入指令或者显示地址,当RS为低电平R/W为高电平时可以读忙信号,当RS为高电平R/W为低电平时可以写入数据。 

第6脚:E端为使能端,当E端由高电平跳变成低电平时,液晶模块执行命令。

第7~14脚:D0~D7为8位双向数据线。 

第15脚:背光源正极。

第16脚:背光源负极。

播放模块——扬声器:

原先以为单片机本身的电源电压不足以驱动扬声器工作,所以设计了有LM386的功放模块,后来在实际操作的过程中测试发现,单片机本身的电源足以驱动扬声器播放音乐音调,因此舍弃了LM386功放模块的设置。

5.6程序源代码及程序分析:

#include<avr/io.h>

#include<avr/interrupt.h>

#pragma data:code

#define uchar unsigned char

#define uint unsigned int

const uchar table[]="HELLO WELCOME!";

volatile uint num=0,count=0;

volatile uint Mode=0,list=2,pause=0,aim=0,sure=0,del=0;

volatile int state=0, S=1;                           //定义全局变量,S为变速变量

const uint Mode_Data[16]={0,440,494,523,587,659,698,784,880,998,1046,1156,1318,1396,1568};//存放声音的频率

//音高对应定时器初始化数值(低la~高la+休止符),cpu频率1MHz,用8分频

const uchar tone[]={0x00,0x8E,0x7E,0x77,0x6A,0x5E,0x59,0x4F,0x47,0x3F,0x3B,0x35,0x2F,0x2C,0x27,0x23,0X19,0X15};

uint Ssong[10][2]={{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}};

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

void delay_us(int n)        //微妙级延时函数;

{

       while(n--)

       {

   asm("nop");//自动延时一个时钟周期什么也不做

       }

}

void delay_ms(int n)   //毫秒级延时函数;

{

 int m=14500*n;

 while(m--)

 {

      asm("nop");

 }

}

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

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

屏幕显示函数

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

void write_com(uchar com)

{

 PORTD&=~(1<<4);      //低电平指令模式。高电平数据;

 PORTD&=~(1<<6);      //低电平写;

 PORTB=com;

 PORTD|=(1<<7);       //高电平使能

 delay_ms(1);

 PORTD&=~(1<<7);       

}

void write_dat(uchar dat)

{    

 PORTD|=(1<<4);

 PORTD&=~(1<<6);

 PORTB=dat;

 PORTD|=(1<<7);

 delay_ms(1);

 PORTD&=~(1<<7);

}

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

*       音乐产生函数   *

* 功能:输出频率为x的方波*

* 范围:x:100-20000Hz,0:不发声   *

*    

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

void sound (int i)         

{

      TIMSK|=(1<<2);

       sei();        

       if(i&&i<17)                  //按了音符键了,且在0到F键这16个发音键上,才发声

       {           

        OCR1A=tone[i];

        TCCR1A=0X50;                  //控制寄存器,选择可翻转功能,产生方波;

   TCCR1B=0X0a;

        PORTD=(1<<5);

       }

      else

        { 

          TCCR1A = 0x00;                        //频率为0,休止符,不发声

          TCCR1B=0X00;

          OCR1A=0;

        }

}

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

弹奏函数

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

void play(int n)         //参数为键盘上的数字;              

{    

       write_com(0X80+0X40+n);

       write_dat(16);

       sound(n);                           //弹奏音符;

       delay_ms(1);

       write_dat(20);

  TCCR1A=0x00; 

       TCCR1B=0X00;                        //中断,为下一个节拍做准备;

       PORTD|=(1<<5);                      //PD5作为输出管脚,接扬声器;

}

//___________________________________________

void INI_POTR(void)

{

       DDRD|=0xff;

       DDRB=0xff;   

       PORTD=(1<<5);

       PORTD&=~(1<<7);

       DDRA=0xff;                         //A口作为键盘输入口;

       DDRB=0xff;    

}

//_____________________________________

void INN_DEVICE(void)

{

       MCUCR=0X00;                        //  中断寄存器置零;          

}

//____________________________________

//检测键盘是否被按下的函数

uint isKeyPress()   

{

       DDRA=0xff;       //初始设置端口均为输出

       PORTA=0xf0;      //设置端口的初值为11110000

       DDRA=0xf0;       //将低四位设为输入

       if(PINA == 0xf0)   //当没有按下时,PINA仍然为11110000

       {

              DDRA=0xff;

              return 0;       //返回0,代表未按下

       }

       else             //当按下后,PINA不再是11110000

       {

              DDRA=0xff;

              return 1;    //返回1,代表按下了

       }    

}

//键盘输入检测函数;

//_______________________________

int key_in(void)                   

{  

       DDRA = 0xff;              //先全部设置为输出,再将后四位置为输入

       PORTA = 0xf0;             //设置高低电平

       DDRA = 0xf0;

       uint i = 0,j = 16,Key = 17;      

       unsigned char temp1,temp2;

      

       if(isKeyPress())

       {

              temp1 = PINA;

              temp1 &= 0x0f;                                 //只关心低四位的情况

              switch(temp1)

              {

                     case 0b00000001:

                            j = 0;

                            break;

                     case 0b00000010:

                            j = 1;

                            break;

                     case 0b00000100:

                            j = 2;

                            break;

                     case 0b00001000:

                            j = 3;

                            break;

              }

        

              temp2 = PINA;

              temp2 &= 0x0f;            //去抖动

              if(temp1 != temp2)

                     return 17;

              DDRA = 0xff;

              PORTA= 0x0f;

              DDRA = 0x0f;

              temp1 = PINA;

              temp1 &= 0xf0;                                        //只关心高四位的情况

              switch(temp1)

              {

                     case 0b00010000:

                            i = 0;

                            break;

                     case 0b00100000:

                            i = 1;

                            break;

                     case 0b01000000:

                            i = 2;

                            break;

                     case 0b10000000:

                            i = 3;

                            break;

              }

      

              temp2 = PINA;

              temp2 &= 0xf0;

              if(temp1 != temp2)

              return 17;

              Key = i*4 + j+1;//     

              return Key; 

       }  

}

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

音乐播放函数;

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

void m_sound(uint a)

{  

        int m=(62500/a)-1;                          //发声原理

        OCR1A=m;

        TCCR1A=0X50;                 //控制寄存器,选择可翻转功能,产生方波;

   TCCR1B=0X0a;

}

//**********************************播放函数;

void music_play(uint a[][2])

{  

       const char Mtable[]="Music Mode";

        write_com(0X01);                //清屏;

        for(int i=0;i<5;i++)

        {

             write_dat(Mtable[i]);

        }

   int i=0;

   char tem=aim,T=1;

   while((a[i][1]!=0)&&(tem==aim)&&state)   

   {      

        //A 返回 B 暂停 F 模式 C 上一曲你 D 播放  E 下一曲;

        num=key_in();

           switch(num)

           {

                                   case 16:

                            state=0;                                           // F STATE = 0弹奏

                            break;                             

                                   case 15:

                            aim=1;                                           //下一首E

                           break;

                                   case 14:

                            pause=1;               //播放D

                            break;

                                   case 13:

                            aim=0;                                          ///上一首C

                            break;

                                   case 12:

                            pause=0;                        ///暂停B

                            break;

                                   case 11:

                            T=2;                                  //变速,1代表正常速度A

                            break;

                                   case 10:

                            T=1;                                   //慢速0

                            break;

                                   case 9:                           //快速9

                            T=0;

                            break;

                                   case 5:                             //5  降调

                            S=0.5;

                            break;

                                   case 6:                             //6 正常调

                            S=1;

                            break;

                                   default:

                            break;                

              }    

              while(a[i][0]&&pause)

              {

                     num=key_in();

                     if(num==12)

                      {

                        pause=0;

                      }

                     else

                     {

                            if( S == 0.5)

                            m_sound(a[i][0]*S);

                            else        

                  m_sound(a[i][0]);

                                      

                            if(T==2)                   //加减速

                  delay_ms(a[i][1]-100);  

                            else if(T==0)

                            delay_ms(a[i][1]+100);

                            else

                            delay_ms(a[i][1]);

                            i++;

                            TCCR1A=0x00; 

                  TCCR1B=0X00;

                     }

              }                                       //下一个音符;

         pause=0;

    TCCR1A=0x00; 

              TCCR1B=0X00;                        //中断,为下一个节拍做准备;                                                   

}

   if(a[i][1]==0)

       {

              aim = (++aim) % list; 

       }

}

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

录制音乐函数;

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

void record(void)

{   

       write_com(0X01);                                                    //清屏;

  const uchar R_table[]="Recording Mode";

       for(int i=0;i<9;i++)

       {

   write_dat(R_table[i]);

       }

    pause=1;

       while(pause)

       {

              uint Skey = key_in();

         if((Skey>0)&&(Skey<11))                     //开始录制

         {

              int i=0;//count=0;                                                        //先清零计数器    

              while(i<10)      

                     {    

                            Skey=key_in();

                            OCR1A=0;

                            TCCR1A=0x00; 

                            TCCR1B=0X00;                        

                            if((Skey>0)&&(Skey<11))

                            {

                                   play(Skey);

                                   Ssong[i][0]=tone[Skey];

                                   Ssong[i][1]=600;         

                                   i++; 

                                   delay_ms(20);

                            }

                            if(Skey==14)                                                                     //录制完成,按D键结束录制

                            {

        i=10;

                            }    

                                         

                     }

                     pause=0;                     //借助 PAUSE 来标记一下什么时候退出while

              }

       }

}

//***************************歌曲数据

uint music_data[][2]=

{

{440,400},{440,400},{659,400},{659,400},{740,400},{740,400},{659,800},

{587,400},{587,400},{554,400},{554,400},{494,400},{494,400},{440,800},

{659,400},{659,400},{587,400},{587,400},{554,400},{554,400},{497,800},

{659,400},{659,400},{587,400},{587,400},{554,400},{554,400},{497,800},

{440,400},{440,400},{659,400},{659,400},{740,400},{740,400},{659,800},

{587,400},{587,400},{554,400},{554,400},{494,400},{494,400},{440,800},

{0,0}

};                  

//  abcdefg

uint music_1[][2]=

 {{262,400},{294,400},{330,400},{262,400},{262,400},  //乐谱

{294,400},{330,400},{262,400},{330,400},{349,400},

{392,800},{330,400},{349,400},{392,800},{392,300},

{440,100},{392,300},{349,100},{262,400},{392,400},

{440,300},{392,100},{349,300},{330,100},{262,400},

{294,400},{196,400},{262,400},{294,800},{196,400},

{262,400},{294,800},{0,0}};

//music_1

int main(void)

{

  INI_POTR();                  //初始化 

  write_com(0X38);        //显示光标等;

  write_com(0X01);        //清屏;

  write_com(0X0f);         //打开光标;

  write_com(0X06);

  write_com(0X80+0X02);

  for(int i=0;i<8;i++)

  {

   write_dat(table[i]);         

  }

  write_com(0X80+0X11);

  delay_ms(5); 

  while(1)

  {

              num = key_in();

            

         if(num==16)                         //模式选择

    {

                if(state==1)

                     state=0;

                     else

                     state=1;

         }                                              

                                                                                                                                    //播放模式

               if(state==1)

               {

                    (state==1)

                    {

                           switchaim()

                           {

                                  case 0:

                                   music_play(music_data);

                                break;

                              case 1:

                                   music_play(Ssong);

                                          break;

                                   default:

                                          break;

                            }while

                            delay_ms(10);

                     }

              }

              else if(num==11)                          //录制模式;

              {

                     record();

                     delay_ms(10);

              }    

         else

              {    

                     write_com(0X01);       

                const uchar Ptable[]="playing Mode";

                     for(int i=0;i<7;i++)

                     {

                            write_dat(Ptable[i]);

                     }

          

                     while((num!=16)&&(num!=11))

                     {

                            play(num);                                                       //弹奏模式

                            num=key_in();   

                     }                           

              }

       }     

}

5.7实验结果:

实现了最初设想的功能,所设计的电子琴可以进行即兴弹奏、录制音乐、播放音乐以及音乐变速。测试图片如下:

接通电源后,显示屏以每次显示一个字符的形式显示出“HELLO WELCOM!”的欢迎文字,接着自动进入弹奏模式,并在LCD的第一行显示出“Playing Mode”的文字,用户每次按下按键都会在LCD的第二行的相应位置出现对应按键顺序的光标闪烁。在弹奏模式下,按下F键会进入播放模式,LCD显示出“Music Mode”字样,此时按下D键是播放/继续,按下B键是暂停,C键和E键分别代表上一首和下一首,9键则是将音乐速度调整为慢速,再次按下F键会回到弹奏模式。在弹奏模式下,按下A键会进入录制模式,此时LCD显示“Recording Mode”字样,可以使用1~0十个按键进行录音,想要听听录制好的音乐,可以切换到播放模式下进行欣赏。

六、排错过程:

l  基础题目训练阶段:

1.  硬件连接好之后,在进行编程的过程中发现端口不够用,因为PB3和PD3这两个能产生中断的端口必须空出来作为暂停和开始按键的接口,于是我重新布线,将数码管显示小数点的右下角的引脚改接到PC0,同时在代码中将此端口设置为高电平输出,这样就解决了该问题。

2.  当我们简单地把流水灯和计时器代码合并在一起然后编译运行时,却发现流水灯在工作,计时器却停止了工作。经过仔细排查,发现简单地将代码合并会导致单片机陷入流水灯的工作循环,而没有进入计时器的工作进程。经过一番思考,我们将流水灯的代码加以修改写入计时器的工作循环中,解决了计时器不工作的问题。

3.  但是新问题又出现了,我们发现按下计时器的暂停按钮后,会导致流水灯也停止工作。经过小组的讨论,我们决定在中断程序函数里面加上流水灯工作的代码并加以修改使其能够在中断函数里面运行。经过多次调试,终于实现了计时器的计时、暂停、继续功能,并且流水灯能够一直工作不受计时器的暂停影响。

4.  在调整计时器计时精读的过程中,我们发现本实验中使用的ATmega16频率不是16MHz,也不是1MHz,为了尽可能的实现秒表计时,试验了很多的数字组合,最后达到了30秒误差1秒的精确度,当然还可以达到更高的精确度。

l  电子琴设计与实现阶段:

1.  在硬件连接的时候,没想到LCD液晶显示屏的每根管脚都需要连接,导致在进行程序下载验证的时候,显示屏总是不能显示出应有的文字,后来参考了相关文档资料发现是V0这个管脚也必须接地才可以,解决了问题。

2.  在安插矩阵键盘的时候,费了很多功夫,首先,不知道哪根管脚是行线哪根管脚是列线,其次不知道所连接的端口的输出输入模式应该怎么设置,导致设计过程受阻,后来观看了视频并查阅相关资料解决了此问题。

3.  在初步完成电子琴的弹奏功能程序时,我们开始对单片机进行测试,发现在按下数次按钮后单片机就会卡死,我们仔细检查了弹奏功能的代码,经过多次排查,我们小组最终将键盘检测函数key_in()和弹奏函数play()两条语句的执行顺序交换,以达到比较好的逻辑顺序效果,提高了程序的稳定性,把多次按下按钮会导致单片机卡死的问题给基本解决了。

4.  播放音乐功能也是我们设想的一个重要功能。刚开始,按照我们的思路去实现该功能时,在测试时播放音乐是实现了,但是暂停和继续功能都没能够实现。我们小组对代码进行重新检查,检查出几个比较严重的逻辑错误。我们重新调整播放音乐函数的判断语句和循环结构,并加入一个pause变量来标志暂停状态,在按下暂停按钮时将pause标志为0,使程序退出播放循环。经过一番努力,我们顺利完成了暂停播放和继续播放的功能。

5.  录制音乐功能的实现是整个实验过程里面最为困难的一环。我们预先设想的实现方法是每按下一个按键,将对应音阶的数字和节拍存入数组中,然后录制完成后自动播放录制的音乐。但是,我们实际测试时发现录制的音乐并没有能自动播放。经过小组讨论,我们决定将录制好的音乐在播放音乐模式里面播放,根据这个思路,我们将录制的音乐对应的数组用播放音乐函数进行播放,成功地实现了录制音乐并播放的功能。

七、心得体会:

为期9天的单片机小学期圆满结束,不得不说,我从中学到了很多知识,从一个对单片机一窍不通的小白,蜕变成了一个熟悉单片机各个端口的作用和使用方法、懂得如何编程实现相应功能的技术人员。

在这九天中,我们通过观看老师给的视频资料和文档,加上查阅的资料,独立完成了所布置的任务。由于有C++的基础,加上有硬件的端口以及相应设备的使用说明文档的帮助,因此在我看来,在AVR Studio上进行C语言的编程从逻辑上比C++更容易理解,所需要的只是在编程时将端口的配置和逻辑函数结合到一起,实现编写的函数对单片机端口以及内部中断的控制。

第一天刚接到流水灯和数码管秒表计时器的任务的时候,内心真的十分激动,心想:实现了这个不就实现了街边广告牌上滚动的文字了么!但是一看到ATmega16芯片时还是傻了眼,心想:这么多的管脚都该怎么用啊?“视频里有”,同学告诉我,于是我就开始认真地研究起老师给的单片机教学视频和它的技术文档。由此发现,只需要将单片机的端口设置好,再套上一个循环结构,就可以实现流水灯的功能了。说做就做,于是最初的流水灯就实现了,可以一个一个依次循环点亮。那时真是好高兴!在做数码管的时候,由于不知道数码管的显示与管脚之间的关系,于是我先创建了一个数组,数组中的元素都是8位二进制数,其中只有一位是1,其余位都是0,如下:

char a[8]  =  {

             0b00000001,

             0b00000010,

             0b00000100,

             0b00001000,

             0b00010000,

             0b00100000,

             0b01000000,

             0b10000000       

      };

然后我采用一个循环,将端口PORTX每次等于一个a[i],单步运行,找出了数码管的显示与管脚之间的对应关系。这样,之后的工作就轻松许多,只需要在该亮的位置将对应的PORTX端口置为1,就可以显示出自己想要的数字了。流水灯和数码管秒表计时器都分别完成了,最后只需将它们合在一起共同工作即可。不过这个过程也不是一帆风顺的,因为中断的概念我刚开始觉得很难理解,也不知道该将中断的有关函数放在代码的哪个位置,因此反复纠结反复研究视频和技术文档,持续了一段时间。后来我和同学决定将两份流水灯的程序,一份放在主程序main中,和秒表计时器的程序放在一起,即实现两者同时工作,且流水灯的闪烁间隔是一秒,另一份放在中断服务程序中SIGNAL()中,即实现按下按钮进入中断服务程序后,秒表计时器暂停工作,而流水依旧在依次循环闪烁。由此实现了流水灯和秒表计时器的独立工作,互不影响。

完成了基础题目的训练任务,我信心满满,迎来了我的下一个任务——电子琴。刚开始选题的时候觉得电子琴蛮有趣的,可以弹奏可以录音可以播放,而且原理看起来也比较简单。但是其实不是这样的,拿到元器件的时候,我发现,LCD液晶显示屏有16个管脚!矩阵键盘有8个管脚!但是有了前面对付ATmega16芯片的经验,我沉住气,查阅了有关LCD液晶显示屏和矩阵键盘的技术文档,以及一些经验之作,初步定下了键盘接到A端口,LCD的8个数据口接到B端口,D端口用于LCD的读写控制使能端以及扬声器的输入端。至此,硬件的连接工作就完成了,根据硬件的连接,我们开始了程序的编写。键盘检测函数、播放函数、液晶显示函数,弹奏功能,录制功能……一步一步,一个完整的系统渐渐完善。由于有硬件,因此我们编写的程序可以马上下载到板子里验证效果,并及时修改,大大方便了我们排错和系统优化。期间由于JTAG下载器和AVR Studio总是出现连接失败的情况,浪费了我们许多宝贵的时间,个人总结原因是驱动可能用了一段时间会失效,解决办法是重新启动计算机,并将WINAVR这个环境卸载后再次装上,并且是装到与上一次不同的盘中,同时编译AVR程序的时候先接上JTAG下载器,试验了许多次,这个方法很管用,之后就没有出现该问题了。最终,电子琴的功能基本完成,并且达标,能实现弹奏、播放、录制、变速等最初设想的功能,但是可能还有一些小BUG没有调出来,导致电子琴在运行的过程中有不稳定的现象,也不能排除是板子的问题。

最终,单片机小学期圆满结束了!9天的时间,完全的自己动手实验,不仅增加了自己对单片机的理解,提高了自己的编程能力,而且在这个过程中,我觉得团队的合作至关重要,自己连接的电路或者自己编写的程序,在自己看来都是对的,但是旁观者清,队友能很容易地帮你找出其中的错误;在和队友的讨论中,我能了解对方的想法和思路,多种想法和思路的碰撞能让我收获更多新的东西;组内的分工能让每个人都尽到自己的责任,发挥自己所长。

总之,这次单片机小学期是一次难忘的过程,一次丰富知识、提升技能的历程,也激起了我对单片机的兴趣,我今后会多多研究单片机,争取更上一层楼!

八、参考文献:

1.  AVR C库函数介绍.pdf

2.  AVR C语言开发入门指导.pdf

3.  AVR单片机原理及应用.pdf

4.  AVR高速嵌入式单片机原理及应用(修订版).pdf

5.  深入浅出AVR单片机.pdf

6.  1602液晶说明.pdf

7.  AVR系统板说明.doc

8.  cn_mega16-16L.pdf

9.  EN_TC1602.pdf

10.      字符手册.pdf

11.      百度百科《乐理》

12.      AVR单片机软硬件设计视频教程-入门篇-第二讲-AVR硬件电路设计教程

13.      AVR单片机软硬件设计视频教程-入门篇-第三讲-AVR开发基础知识

14.      AVR单片机软硬件设计视频教程-入门篇-第四讲-C语言的流水灯验证

15.      AVR单片机软硬件设计视频教程-入门篇-第五讲-按键与数码管的程序设计

16.      AVR单片机软硬件设计视频教程-入门篇-第六讲-中断与定时器

九、意见与建议:

1.  建议老师能稍微讲解一下实验室AVR软件的用法(视频中讲的是没有JTAG下载器的,与实验室不同,刚开始还以为是板子有问题);

2.  实验室的元器件老旧,个别元器件特别是单片机芯片有问题之后,调BUG好久都不知道错在哪里,建议更新。

相关推荐