简介:开源讲解51单片机可练手项目——电子可调音乐闹钟,项目鸣谢B站up主江协科技,卡农简谱来自B站up主蓝色浪漫曲。这个项目还是很适合刚入门单片机(有一定基础的51单片机知识)的朋友去复刻学习的。此项目已通过实际测验,完全具备替代普通闹钟的功能。
一,硬件部分
所用开发板为普中51-实验板,所用单片机为STC89C52RC,A2开发板原理图,师从B站up主
江协科技,推荐新手小白入门单片机去看他的视频。也很推荐看完他的视频再来看我的文章,我文章没讲的一些细节都可以在他的视频里找到。
二,代码部分
因为模块化的代码很多,不方便一个一个复制上传代码,所以我把代码压缩包放到这个文章上了,有需要的朋友可以下载。
三,讲解和经验部分
先叠甲,个人经验,仅供参考。
验证项目的可行性
(当然,在我们有想法去做一个项目时,我们必须先确定该项目是否具有可行性,就好比我这个项目,我是一开始确认过了每个模块之间所用单片机I/O口不会有冲突才着手去做的,假如一开始就从硬件方面上不可行的话,那么你做到最后也会失败)
先来说说我对整个项目实现的框架:
1,先做显示和按键。(这是最基础的,你如果一开始没做这一步,你后面都无法去调试,所以我们无论做什么项目,都要先把用于调试的部分给做好,像数码管显示或LCD_1602显示屏)
2,加入时钟部分。
3,加入闹钟部分。
4,加入音乐部分。
接下来我会说明怎样一步一步来实现这个项目。模块我们可以不写,但一定要看的懂且会用,模块不是万能的,我们在有些情况下是需要修改底层的,cv工程师只是个调侃,想当高手还是老老实实打基础吧。
这个项目我是真一步步去做的,代码也是一步步调试的,就是做第一部分时没有太去想其他部分,这个思路我觉得更适合小白,但是效率肯定不如一开始就构思好所有部分之间的连接,就是做第一部分也给其他部分留好接口,熟练后还是用第二种思路吧。
1,显示和按键部分:
这一部分要用到:
Nixie数码管模块,此模块用于显示时间,所用到的引脚为P2^3,P2^2,P2^4。
Delay延时函数模块,此模块用于数码管的动态数显。
MatrixKey矩阵键盘模块,此模块用于我们和单片机的交互,所用到的引脚为P1寄存器。
Timer1定时器1模块,此模块用于数码管的闪烁。
首先让我们先做好矩阵按键和数码管的匹配。因为我们要显示"-"字符,up主给出的数码管模块里不包含该字符,所以我们要先修改数码管模块,从A2开发板原理图上可以看出当P0=0x40时,我们可以让数码管显示"-"字符,然后把它放到供数码管显示用的字符数组里。
unsigned int NixieTable[18]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71,0x00,0x40};//供数码管显示用的数组
之后我们把他们和闪烁一起做了,用上了定时器1做闪烁,定时器1以0.5s为周期让标志flag做翻转,Select和Flag双重判断做闪烁。到此我们第一部分就做完了。主文件如下:
#include <REGX52.H>
#include "DELAY.H"
#include "Nixie.H"
#include "MatrixKey.H"
#include "Timer1.H"
unsigned char KeyNum;//瞬间键码值
unsigned char ClockNum[3]={10,42,30};//暂代时间数组
char Select;//数码管的位选,Select>=0 && Select<=2
unsigned char Flag;//数码管闪烁标志
void main()
{
Timer1_Init();
P2_5=0;
while(1)
{
KeyNum=MatrixKey();//获取按键键码
if(KeyNum==1)//按键为S1时,选择位右移
{
Select++;
if(Select>2){Select=0;}
}
if(KeyNum==2)//按键为S2时,选择位左移
{
Select--;
if(Select<0){Select=2;}
}
//闪烁判断
if(Select==0 && Flag==1){Nixie(1,16);Nixie(2,16);}
else {Nixie(1,ClockNum[0]/10);Nixie(2,ClockNum[0]%10);}
if(Select==1 && Flag==1){Nixie(4,16);Nixie(5,16);}
else {Nixie(4,ClockNum[1]/10);Nixie(5,ClockNum[1]%10);}
if(Select==2 && Flag==1){Nixie(7,16);Nixie(8,16);}
else {Nixie(7,ClockNum[2]/10);Nixie(8,ClockNum[2]%10);}
Nixie(3,17);
Nixie(6,17);
}
}
void Timer1_Routine() interrupt 3
{
static unsigned int T1Count;
TL1 = 0x18; //设置定时初值
TH1 = 0xFC; //设置定时初值
T1Count++;
if(T1Count>=500)
{
T1Count=0;
Flag=!Flag;
}
}
以下是我犯的一些错误:
1.寄存器只能用16进制写入,其他会有问题。
2.按键检测和反馈不太熟练(重复读取键码值会出错),在那边耽误了一点时间
3.我一开始也没想到用双重判断交替显示空字符和时间数组去做闪烁,而是通过暂存值把原字符从时间数组里取出来,然后空字符塞到时间数组里去,循环往复去做显示。我还给暂存值做了个数组,还真花了我不少时间。(苦笑)
4.我还碰上了数组越界的问题,即时钟读取时数据丢失,浪费了半天的时间。
5.本来还考虑了Delay函数和定时器的冲突,后面发现其实没关系(对我们这个项目来说)
6.如果加入了定时器初始化,但是没有使用定时器函数的话,程序也会出问题。
2,加入时钟部分:
这一部分除了要用以上模块外,还要加上以下模块:
DS1302时钟模块,此模块用于单片机去读写DS1302中存取的准确时间,所用的引脚为P3^4,P3^5,P3^6。
首先,在我们把DS1302模块添加进来后,我们就要用它里面内置的时间数组去替换掉我们之前测试用的时间数组,然后我们再优化一下按键的结构,定义一个变量MODE,代表我们现在的两种模式,时钟显示模式和时钟设置模式,由我们的S5按键去切换模式,S1负责选择位的右移,S2负责选择位的左移,S3负责被选择数的递增,S4负责被选择数的递减。别忘了把越界判断给做一下。关于蜂鸣器要说几点,控制蜂鸣器的引脚为1时有些情况下蜂鸣器会乱叫,且无源蜂鸣器最好不要一直通电,所以我们在不使用蜂鸣器时最好不给它通电。主文件如下:
#include <REGX52.H>
#include "DELAY.H"
#include "Nixie.H"
#include "MatrixKey.H"
#include "DS1302.H"
#include "Timer1.H"
unsigned char MODE;
unsigned char KeyNum;//瞬间键码值
char Select;//数码管的位选(当前选择的数码管),Select>=0 && Select<=2
unsigned char Flag;//数码管闪烁标志
void TimeShow(void)
{
DS1302_ReadTime();
Nixie(1,DS1302_Time[3]/10);//显示时
Nixie(2,DS1302_Time[3]%10);
Nixie(3,17);
Nixie(4,DS1302_Time[4]/10);//显示分
Nixie(5,DS1302_Time[4]%10);
Nixie(6,17);
Nixie(7,DS1302_Time[5]/10);//显示秒
Nixie(8,DS1302_Time[5]%10);
}
void TimeSet(void)
{
// KeyNum=MatrixKey();
//选择设置1
if(KeyNum==1)
{
Select++;
if(Select>2){Select=0;}
}
//选择设置2
if(KeyNum==2)
{
Select--;
if(Select<0){Select=2;}
}
//按键时间加
if(KeyNum==3)
{
if(Select+3==3)
{
DS1302_Time[Select+3]++;
if(DS1302_Time[Select+3]>24)
{
DS1302_Time[Select+3]=0;
}
}
if(Select+3==4)
{
DS1302_Time[Select+3]++;
if(DS1302_Time[Select+3]>60)
{
DS1302_Time[Select+3]=0;
}
}
if(Select+3==5)
{
DS1302_Time[Select+3]++;
if(DS1302_Time[Select+3]>60)
{
DS1302_Time[Select+3]=0;
}
}
}
//按键时间减
if(KeyNum==4)
{
if(Select+3==3)
{
DS1302_Time[Select+3]--;
if(DS1302_Time[Select+3]<0)
{
DS1302_Time[Select+3]=24;
}
}
if(Select+3==4)
{
DS1302_Time[Select+3]--;
if(DS1302_Time[Select+3]<0)
{
DS1302_Time[Select+3]=60;
}
}
if(Select+3==5)
{
DS1302_Time[Select+3]--;
if(DS1302_Time[Select+3]<0)
{
DS1302_Time[Select+3]=60;
}
}
}
//闪烁判断
if(Select==0 && Flag==1){Nixie(1,16);Nixie(2,16);}
else {Nixie(1,DS1302_Time[3]/10);Nixie(2,DS1302_Time[3]%10);}
if(Select==1 && Flag==1){Nixie(4,16);Nixie(5,16);}
else {Nixie(4,DS1302_Time[4]/10);Nixie(5,DS1302_Time[4]%10);}
if(Select==2 && Flag==1){Nixie(7,16);Nixie(8,16);}
else {Nixie(7,DS1302_Time[5]/10);Nixie(8,DS1302_Time[5]%10);}
Nixie(3,17);
Nixie(6,17);
}
void main()
{
DS1302_Init();
Timer1_Init();
P2_5=0;
while(1)
{
KeyNum=MatrixKey();
if(KeyNum==5)
{
MODE=!MODE;DS1302_SetTime();
}
if(MODE==1){TimeShow();}
if(MODE==0){TimeSet();}
}
}
void Timer1_Routine() interrupt 3
{
static unsigned int T1Count;
TL1 = 0x18; //设置定时初值
TH1 = 0xFC; //设置定时初值
T1Count++;
if(T1Count>=300)
{
T1Count=0;
Flag=!Flag;
}
}
3,加入闹钟部分:
这一部分不需要再加入其他模块。
这一部分还是挺简单的,只需要定义一个存放闹钟时间的数组,再添加一个闹钟设置的模式就行了。而闹钟设置函数本质上与时钟设置函数一样,只需改写一下数组就行了。记得在进入正式的循环前执行一次DS1302_SetTime()函数,这样可以保证上电的第一时间显示的就是内置的时间,不然可能会出现数码管乱显数的情况,主文件如下:
#include <REGX52.H>
#include "DELAY.H"
#include "Nixie.H"
#include "MatrixKey.H"
#include "DS1302.H"
#include "Timer1.H"
unsigned char MODE;//有三种模式,正常显示,时钟设置,闹钟设置
unsigned char KeyNum;//瞬间键码值
char Select;//数码管的位选(当前选择的数码管),Select>=0 && Select<=2
unsigned char Flag;//数码管闪烁标志
char ClockNum[3]={0,0,0};//存储闹钟时间的数组
void TimeShow(void)
{
DS1302_ReadTime();
Nixie(1,DS1302_Time[3]/10);//显示时
Nixie(2,DS1302_Time[3]%10);
Nixie(3,17);
Nixie(4,DS1302_Time[4]/10);//显示分
Nixie(5,DS1302_Time[4]%10);
Nixie(6,17);
Nixie(7,DS1302_Time[5]/10);//显示秒
Nixie(8,DS1302_Time[5]%10);
}
void TimeSet(void)
{
//选择设置1
if(KeyNum==1)
{
Select++;
if(Select>2){Select=0;}
}
//选择设置2
if(KeyNum==2)
{
Select--;
if(Select<0){Select=2;}
}
//按键时间加
if(KeyNum==3)
{
if(Select+3==3)
{
DS1302_Time[Select+3]++;
if(DS1302_Time[Select+3]>24)
{
DS1302_Time[Select+3]=0;
}
}
if(Select+3==4)
{
DS1302_Time[Select+3]++;
if(DS1302_Time[Select+3]>60)
{
DS1302_Time[Select+3]=0;
}
}
if(Select+3==5)
{
DS1302_Time[Select+3]++;
if(DS1302_Time[Select+3]>60)
{
DS1302_Time[Select+3]=0;
}
}
}
//按键时间减
if(KeyNum==4)
{
if(Select+3==3)
{
DS1302_Time[Select+3]--;
if(DS1302_Time[Select+3]<0)
{
DS1302_Time[Select+3]=24;
}
}
if(Select+3==4)
{
DS1302_Time[Select+3]--;
if(DS1302_Time[Select+3]<0)
{
DS1302_Time[Select+3]=60;
}
}
if(Select+3==5)
{
DS1302_Time[Select+3]--;
if(DS1302_Time[Select+3]<0)
{
DS1302_Time[Select+3]=60;
}
}
}
//闪烁判断
if(Select==0 && Flag==1){Nixie(1,16);Nixie(2,16);}
else {Nixie(1,DS1302_Time[3]/10);Nixie(2,DS1302_Time[3]%10);}
if(Select==1 && Flag==1){Nixie(4,16);Nixie(5,16);}
else {Nixie(4,DS1302_Time[4]/10);Nixie(5,DS1302_Time[4]%10);}
if(Select==2 && Flag==1){Nixie(7,16);Nixie(8,16);}
else {Nixie(7,DS1302_Time[5]/10);Nixie(8,DS1302_Time[5]%10);}
Nixie(3,17);
Nixie(6,17);
}
//闹钟设置函数
void ClockSet()
{
//选择设置1
if(KeyNum==1)
{
Select++;
if(Select>2){Select=0;}
}
//选择设置2
if(KeyNum==2)
{
Select--;
if(Select<0){Select=2;}
}
//按键时间加
if(KeyNum==3)
{
if(Select==0)
{
ClockNum[Select]++;
if(ClockNum[Select]>24)
{
ClockNum[Select]=0;
}
}
if(Select==1)
{
ClockNum[Select]++;
if(ClockNum[Select]>60)
{
ClockNum[Select]=0;
}
}
if(Select==2)
{
ClockNum[Select]++;
if(ClockNum[Select]>60)
{
ClockNum[Select]=0;
}
}
}
//按键时间减
if(KeyNum==4)
{
if(Select==0)
{
ClockNum[Select]--;
if(ClockNum[Select]<0)
{
ClockNum[Select]=24;
}
}
if(Select==1)
{
ClockNum[Select]--;
if(ClockNum[Select]<0)
{
ClockNum[Select]=60;
}
}
if(Select==2)
{
ClockNum[Select]--;
if(ClockNum[Select]<0)
{
ClockNum[Select]=60;
}
}
}
//闪烁判断
if(Select==0 && Flag==1){Nixie(1,16);Nixie(2,16);}
else {Nixie(1,ClockNum[0]/10);Nixie(2,ClockNum[0]%10);}
if(Select==1 && Flag==1){Nixie(4,16);Nixie(5,16);}
else {Nixie(4,ClockNum[1]/10);Nixie(5,ClockNum[1]%10);}
if(Select==2 && Flag==1){Nixie(7,16);Nixie(8,16);}
else {Nixie(7,ClockNum[2]/10);Nixie(8,ClockNum[2]%10);}
Nixie(3,17);
Nixie(6,17);
}
void main()
{
DS1302_Init();
Timer1_Init();
P2_5=0;
DS1302_SetTime();
while(1)
{
KeyNum=MatrixKey();
if(KeyNum==5)
{
if(MODE==0){MODE=1;Select=0;}//显示模式
else if(MODE==1){MODE=2;DS1302_SetTime();}//时钟设置模式
else if(MODE==2){MODE=0;}//闹钟设置模式
}
switch(MODE)//根据不同的功能执行不同的函数
{
case 0:TimeShow();break;
case 1:TimeSet();break;
case 2:ClockSet();break;
}
}
}
void Timer1_Routine() interrupt 3
{
static unsigned int T1Count;
TL1 = 0x18; //设置定时初值
TH1 = 0xFC; //设置定时初值
T1Count++;
if(T1Count>=300)
{
T1Count=0;
Flag=!Flag;
}
}
4,加入音乐部分:
终于来到了灵魂部分了(喜),这次我选的曲目是卡农,是一首很经典的钢琴曲,希望你能喜欢。这一部分我们除了上述模块外,还需要加入
Timer0定时器0模块,此模块用于控制蜂鸣器,按照不同的重装值驱使蜂鸣器发出不同频率的声音,再配合音符数组与乐谱数组就可以演奏任何你想听的音乐了。此模块会控制P2^5蜂鸣器驱动引脚。
music音乐模块,此模块up主给的源代码中没有,是我为了避免主文件出现头重脚轻而影响可读性创建的,在里面存放与音乐相关的变量和数组以及宏定义等。
首先,让我们在主文件里定义两个变量,音符数组选择位,乐谱数组选择位。将控制蜂鸣器引脚的地址赋给指针Buzzer。然后我们在定义一个音乐播放函数,具体这个函数怎么写出来的,介于篇幅,这里就不多赘述,想了解请移步至江协科技视频。需要注意的是想要循环播放音乐只要在乐谱数组里给一个停止标志位,然后在读到这个标志时将乐谱数组选择位置0就行了。我给这个闹钟设置的音乐播放时间是1分钟,通过一个if判断就行了,这个也是你自己设置,想让它响多久就多久。然后记得在时钟显示函数里加上几个if判断让单片机在不播放音乐的情况下关闭定时器0和蜂鸣器Buzzer(P2^5),免得浪费单片机的资源和蜂鸣器发出嗡嗡声,主文件如下:
#include <REGX52.H>
#include "DELAY.H"
#include "Nixie.H"
#include "MatrixKey.H"
#include "DS1302.H"
#include "Timer1.H"
#include "Timer0.H"
#include "music.H"
sbit Buzzer=P2^5;
unsigned char MODE;//有三种模式,正常显示,时钟设置,闹钟设置
unsigned char KeyNum;//瞬间键码值
char Select;//数码管的位选(当前选择的数码管),Select>=0 && Select<=2
unsigned char Flag;//数码管闪烁标志
char ClockNum[3]={0,0,0};
unsigned char FreqSelect,MusicSelect;
//音乐播放函数
void Musicplay()
{
if(Music[MusicSelect]!=0xFF) //如果不是停止标志位
{
FreqSelect=Music[MusicSelect]; //选择音符对应的频率
MusicSelect++;
Delay(SPEED/4*Music[MusicSelect]); //选择音符对应的时值
MusicSelect++;
TR0=0;
Delay(5); //音符间短暂停顿
TR0=1;
}
else //如果是停止标志位
{
MusicSelect=0;
}
if(DS1302_Time[4]==ClockNum[1]+1)
{
TR0=0;Buzzer=0;
}
}
//时间显示函数(含音乐播放函数)
void TimeShow(void)
{
DS1302_ReadTime();
Nixie(1,DS1302_Time[3]/10);//显示时
Nixie(2,DS1302_Time[3]%10);
Nixie(3,17);
Nixie(4,DS1302_Time[4]/10);//显示分
Nixie(5,DS1302_Time[4]%10);
Nixie(6,17);
Nixie(7,DS1302_Time[5]/10);//显示秒
Nixie(8,DS1302_Time[5]%10);
if(DS1302_Time[3]==ClockNum[0] && DS1302_Time[4]==ClockNum[1] && DS1302_Time[5]==ClockNum[2])
{
TR0=1;
}
if(TR0==1)
{
Musicplay();
}
}
//时间设置函数
void TimeSet(void)
{
//选择设置1
if(KeyNum==1)
{
Select++;
if(Select>2){Select=0;}
}
//选择设置2
if(KeyNum==2)
{
Select--;
if(Select<0){Select=2;}
}
//按键时间加
if(KeyNum==3)
{
if(Select+3==3)
{
DS1302_Time[Select+3]++;
if(DS1302_Time[Select+3]>24)
{
DS1302_Time[Select+3]=0;
}
}
if(Select+3==4)
{
DS1302_Time[Select+3]++;
if(DS1302_Time[Select+3]>60)
{
DS1302_Time[Select+3]=0;
}
}
if(Select+3==5)
{
DS1302_Time[Select+3]++;
if(DS1302_Time[Select+3]>60)
{
DS1302_Time[Select+3]=0;
}
}
}
//按键时间减
if(KeyNum==4)
{
if(Select+3==3)
{
DS1302_Time[Select+3]--;
if(DS1302_Time[Select+3]<0)
{
DS1302_Time[Select+3]=24;
}
}
if(Select+3==4)
{
DS1302_Time[Select+3]--;
if(DS1302_Time[Select+3]<0)
{
DS1302_Time[Select+3]=60;
}
}
if(Select+3==5)
{
DS1302_Time[Select+3]--;
if(DS1302_Time[Select+3]<0)
{
DS1302_Time[Select+3]=60;
}
}
}
//闪烁判断
if(Select==0 && Flag==1){Nixie(1,16);Nixie(2,16);}
else {Nixie(1,DS1302_Time[3]/10);Nixie(2,DS1302_Time[3]%10);}
if(Select==1 && Flag==1){Nixie(4,16);Nixie(5,16);}
else {Nixie(4,DS1302_Time[4]/10);Nixie(5,DS1302_Time[4]%10);}
if(Select==2 && Flag==1){Nixie(7,16);Nixie(8,16);}
else {Nixie(7,DS1302_Time[5]/10);Nixie(8,DS1302_Time[5]%10);}
Nixie(3,17);
Nixie(6,17);
}
//闹钟设置函数
void ClockSet()
{
//选择设置1
if(KeyNum==1)
{
Select++;
if(Select>2){Select=0;}
}
//选择设置2
if(KeyNum==2)
{
Select--;
if(Select<0){Select=2;}
}
//按键时间加
if(KeyNum==3)
{
if(Select==0)
{
ClockNum[Select]++;
if(ClockNum[Select]>24)
{
ClockNum[Select]=0;
}
}
if(Select==1)
{
ClockNum[Select]++;
if(ClockNum[Select]>60)
{
ClockNum[Select]=0;
}
}
if(Select==2)
{
ClockNum[Select]++;
if(ClockNum[Select]>60)
{
ClockNum[Select]=0;
}
}
}
//按键时间减
if(KeyNum==4)
{
if(Select==0)
{
ClockNum[Select]--;
if(ClockNum[Select]<0)
{
ClockNum[Select]=24;
}
}
if(Select==1)
{
ClockNum[Select]--;
if(ClockNum[Select]<0)
{
ClockNum[Select]=60;
}
}
if(Select==2)
{
ClockNum[Select]--;
if(ClockNum[Select]<0)
{
ClockNum[Select]=60;
}
}
}
//闪烁判断
if(Select==0 && Flag==1){Nixie(1,16);Nixie(2,16);}
else {Nixie(1,ClockNum[0]/10);Nixie(2,ClockNum[0]%10);}
if(Select==1 && Flag==1){Nixie(4,16);Nixie(5,16);}
else {Nixie(4,ClockNum[1]/10);Nixie(5,ClockNum[1]%10);}
if(Select==2 && Flag==1){Nixie(7,16);Nixie(8,16);}
else {Nixie(7,ClockNum[2]/10);Nixie(8,ClockNum[2]%10);}
Nixie(3,17);
Nixie(6,17);
}
//主函数
void main()
{
DS1302_Init();
Timer1_Init();
Timer0_Init();
Buzzer=0;
DS1302_SetTime();
while(1)
{
KeyNum=MatrixKey();
if(KeyNum==5)
{
if(MODE==0){MODE=1;Select=0;}//显示模式
else if(MODE==1){MODE=2;DS1302_SetTime();}//时钟设置模式
else if(MODE==2){MODE=0;}//闹钟设置模式
}
switch(MODE)//根据不同的功能执行不同的函数
{
case 0:TimeShow();break;
case 1:TimeSet();break;
case 2:ClockSet();break;
}
}
}
//定时器0中断负责音乐的播放
void Timer0_Routine() interrupt 1
{
if(FreqTable[FreqSelect]) //如果不是休止符
{
/*取对应频率值的重装载值到定时器*/
TL0 = FreqTable[FreqSelect]%256; //设置定时初值
TH0 = FreqTable[FreqSelect]/256; //设置定时初值
Buzzer=!Buzzer; //翻转蜂鸣器IO口
}
}
//定时器1负责设置位的闪烁
void Timer1_Routine() interrupt 3
{
static unsigned int T1Count;
TL1 = 0x18; //设置定时初值
TH1 = 0xFC; //设置定时初值
T1Count++;
if(T1Count>=300)
{
T1Count=0;
Flag=!Flag;
}
}
对这一部分的一些心得:
1.搞懂乐理部分真花了我不少时间,去网上找乐谱,去翻译成单片机能读懂的乐谱还要码进去,老实说,枯燥地码乐谱还是挺痛苦的,这个花了我一个晚上和一个早上的时间。
2.中途不断调试了很多次,一开始发现的问题是单片机播到某些片段的时候会“打嗝”,一开始我以为是走调的原因,因为我一开始拿的谱是包含了五种音阶,我用的up主给的模块内只有三种音阶,所以我对个别音符进行降升调处理了,后面我就去给它扩大音阶,又给它加上了两种音阶。而在我给音符数组加上元素时,我发现它之前缺失了一个元素,也就是少了一个音符,所以单片机才会“打嗝”,因为它要演奏的音符在数组里找不到对应的元素。最后还是把音阶给扩大了,毕竟做都做了。
3.因为music的那个音乐模块也是我第一次做,之前只是照着视频敲代码而已,我碰到了一些麻烦,然后我便去深入了解预编译,文件包含,条件编译,内部函数和外部函数。花了一晚上去深度学习了预编译与文件的边界与规则,这一切都是为了简化和便利我写代码做准备,因为乐谱和音符数组的代码很长,我想着用别的文件来储存,不放在主函数的.c文件里,这迫使我不得不去学习预编译,头文件和外部函数等。
4.本来快准备齐全了,后面才发现把乐谱搞错了,把双手谱搞成单手谱去了,没办法,浪费了一晚加一早,又花了快一个下午的时间重新找谱,译谱和码谱。(只能说学习中碰到困难和挫折是很常见的事咯)
5.硬件方面有点问题,这个不可通过软件避免,就是蜂鸣器在播放音乐时数码管会随音乐闪烁,音乐播完时就不闪了,虽然好像也挺酷的。
四,结语
因为我也是边做项目边学,所以做一个项目的周期比我想的久(这个就花了3,4天),写这篇文章又快花了我6,7个小时的时间,因为我还得去逐步拆解代码以及琢磨如何去讲解代码和文章的排版,没上次直接上传代码那么轻松。忠告:用知识做项目和学知识完全是两码事,实践和理论的差距很大,纸上得来终觉浅,绝知此事要躬行,只有做项目(实践)才能将学到的知识融汇贯通,虽然遇到了一些困难,也想过要不差不多就行了吧,但是我还是很讨厌我做不到这四个字,我也不甘心放弃,对我人生的第一个项目,我想给我自己一份满意的答卷。也感谢你看到这里,希望这篇文章对你有所帮助。