摘要:本設計基於我們所學習的單片機課程講座、實例以及自學,掌握單片機技術是一門不可或缺的技術,對我們將來的工作以及生活都有密切的聯繫。單片機是一種集成電路芯片,是採用超大規模集成電路技術把具有數據處理能力的中央處理器CPU隨機存儲器RAM、只讀存儲器ROM、多種I/O口和中斷系統、定時器/計時器等功能(可能還包括顯示驅動電路、脈寬調製電路、模擬多路轉換器、A/D轉換器等電路)集成到一塊硅片上構成的一個小而完善的微型計算機系統,在工業控制領域的廣泛應用。從上世紀80年代,由當時的4位、8位單片機,發展到現在的32位300M的高速單片機。
關鍵詞:IAP15F2K61S2芯片 STC89C51單片機 蜂鳴器 數碼管 led燈 中斷 定時器 嵌入式系統
前言:近年來,隨着電子技術和微機計算機的迅速發展,單片機的檔次不斷提高,其運用領域也在不斷的擴大,已在工業控制,尖端科學,智能儀器儀表,日用家電,汽車電子系統,辦公自動化,個人信息終端及通信產品中得到了廣泛的運用,成爲現代電子系統中最重要的智能化的核心部件,在嵌入式系統中充當着人體頭腦的最重要的作用。
一、課程設計目的,任務要求
設計概述:該設計的目的是設計出一個可以顯示電子音樂的樂譜的工程。我們經常可以在酷狗,蝦米音樂等等音樂app裏使用聽歌識曲的功能,哼着一個沒有歌詞的調子就可以搜出這個歌曲,對於喜歡音樂的人來說極其便捷。顯然,通過一個曲調就識別然後匹配出相應的歌曲,這裏必可以通過識別這首歌的一段樂譜,然後在樂曲庫裏尋找和這段曲調相似的歌曲。另一方面,有些人的確在音樂方面比較懵懂,播放一個樂曲直接學,很多調都聽不準所以學不準,如果只有音樂沒有樂譜,對於學習音樂就比較困難。所有如果能識別出音樂的樂譜並輸出給人們看,會大大減小學習音樂的困難,是音樂感不強的人學音樂的福利。
任務要求:播放一段音樂,通過不同按鍵控制不同顯示樂譜的方式,在數碼管上顯示簡譜,或者用led燈顯示樂譜。
二、程序的設計流程圖
播放音樂子程序設計流程圖如下:
整個程序設計流程圖如下:
三、程序相關電路及工作原理說明
1.IAP15F2K61S2芯片40個引腳如下:
sbit beep=P3^4; //蜂鳴器引腳
sbit sel_led=P2^3; //控制數碼管和led燈轉換的引腳
sbit sel_0=P2^0;
sbit sel_1=P2^1;
sbit sel_2=P2^2; //控制三個進位的引腳
sbit key1=P3^2;
sbit key2=P3^3;
sbit key3=P1^7; //3個按鍵引腳
2.共陰極數碼管電路圖如下: P0口輸入高電壓led燈亮
3.按鍵電路示意圖:按下是低電平, 高電平時常態
4.無源蜂鳴器電路示意圖如下:輸入方波,發出聲音
四、程序設計框架
1.宏定義
#defineuint unsigned int
#defineuchar unsigned char
2.變量定義
bit k1=0;
bit k2=0;
bit k3=0; //控制三種樂譜顯示方式的標誌位
uchar qudiao,jp,weizhi; //曲調 音符 位置
uchar duanxuan[] //定義段選 數碼管從0-7
uchar led[] //定義0-8led燈亮的情況
uchar timeh,timel; //定義定時器的重裝值 表示不同的頻率
3.程序函數說明
voiddelay(unsigned int xms); //延時程序
ucharquyin(uchar tem); //找到tem音符在quzi數組中的位置值,識別輸入的音符,並根據不同的按鍵按下來控制不同的樂譜輸出方式
voidplaymusic(); //播放音樂函數
voidinit(); //定時器初始化配置io口(系統初始化)
void tim1()interrupt 1; //定時器0中斷服務程序,重新裝值,產生方波,對應蜂鳴器發聲
五、下載與調試(實驗日誌)
1. 2016.8.22 問題:程序下載到板子上沒有反映,沒有顯示
解決思路:顯示電路主要有usb口 、晶振片和iap15f2k61s2芯片組成,檢測到usb口電路沒有焊錯,應該就是晶振片壞了。
解決過程:換了晶振,果然可以了。
已解決。
2. 2016.8.30 霍爾實例下載驗證不了
解決思路:磁鐵靠近與遠離,霍爾元器件沒有任何反應,檢查霍爾元器件,發現正負極焊反了。
解決過程:把霍爾元器件再重新焊對一遍。
已解決。
3. 2016.9.2 設計樂譜顯示工程:
2016.9.2:剛開始只設計出一種在數碼管上顯示樂譜的顯示方式,也沒有按鍵控制功能。
多次修改程序,下載驗證成功。
2016.9.3:加一個用led燈來輸出樂譜的方式,沒有按鍵功能。
多次修改程序,下載驗證成功。
2016.9.4:在原來的基礎上,用按鍵來控制選擇哪種方式來輸出樂譜。
多次修改程序,下載驗證成功。
六、總結與心得體會
學習c51單片機,我覺得最重要也最基礎應該要搞懂中斷和定時器這兩個問題。中斷就是cpu在處理A事件,B事件發生(中斷產生)了並要求cpu馬上處理(發出中斷請求),cpu轉去處理B事件(中斷響應),並執行中斷處理程序(中斷服務),處理完後返回處理A事件(中斷返回)。畫個流程圖,就是:中斷產生-->中斷響應-->中斷服務-->中斷返回。c51單片機裏有五種中斷源,分別是外部中斷0,定時器中斷0,外部中斷1,定時器中斷1與串行口。搞明白了中斷源,還要明白中斷響應是有條件的,要符合三個條件:一個是cpu要開中斷開關(EA=1),一個是中斷源有請求,一個是中斷允許位爲1。定時器就要明白一個機械週期等於12個振盪週期,如果是12MHZ的晶振片,那麼一個計數週期T=1/(12*10^6) *12=10^(-6)=1us。16位計數器總的就是65536us,最多了可以定時65ms。軟件程序的調試一般可以把重點放在分模塊調試上,統調是最後一環,通過各模塊的單步或跟蹤調試,使程序逐步趨於正確,最後統調程序。
七、參考文獻
1.《51單片機c語言應用程序設計實例精講》 戴佳 戴衛恆編著 電子工業出版社
2.《計算機硬件技術基礎實驗教程》 方愷晴主編 清華大學出版社
3.《單片機原理與運用及c51程序設計》 楊家國 清華大學出版社
4.《51單片機課程設計》 周向紅 華中科技大學出版社
5.10天學會51單片機視頻 郭天祥主講
6.C15單片機及c語言知識點必備祕籍
7.51單片機學習論壇
8.百度百科
附錄:源程序
/**********************
說明:直接播放音樂按key1鍵就顯示在led燈上顯示樂譜
按key2鍵就在數碼管7上顯示樂譜
按key3鍵就在數碼管0上顯示音調
***********************/
#include <STC15F2K60S2.h>
#define uint unsigned int
#define uchar unsigned char
sbit beep=P3^4; //蜂鳴器引腳
sbit sel_led=P2^3; //數碼管和led轉換引腳
sbit sel_0=P2^0;
sbit sel_1=P2^1;
sbit sel_2=P2^2; //控制3個進位的管腳
sbit key1=P3^2;
sbit key2=P3^3;
sbit key3=P1^7; //控制三個按鍵k1 k2 k3 的引腳
bit k1=0;
bit k2=0;
bit k3=0; //控制3個樂譜顯示方式的標誌位
uchar qudiao,jp,weizhi; //定義曲調,音符和位置
ucharduanxuan[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07}; //數碼管顯示0-7
ucharled[]={0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80}; //led燈從左到右分別亮
uchar timeh,timel; //定義定時器的重裝值
uchar code music[] ={
0x33,0x10,0x32,0x10,0x33,0x10,0x32,0x10,0x33,0x10, //音樂代碼:《致愛麗絲》
0x27,0x10,0x32,0x10,0x31,0x10,0x26,0x20,
0x23,0x10,0x26,0x10,0x31,0x10,0x27,0x20,
0x23,0x10,0x31,0x10,0x27,0x10,0x26,0x20,
0x31,0x10,0x31,0x10,0x32,0x10,0x33,0x20,
0x25,0x10,0x34,0x10,0x33,0x10,0x32,0x20,
0x25,0x10,0x33,0x10,0x32,0x10,0x31,0x20,
0x25,0x10,0x32,0x10,0x31,0x10,0x27,0x20,
0x23,0x10,0x31,0x10,0x27,0x10,0x26,0x20,
//音樂代碼,歌曲爲《同一首歌》,格式爲: 音符, 節拍, 音符, 節拍,
0x15,0x20,0x21,0x10, //音符的十位代表是低八度,中八度還是高八度,1代表高八度,2代表中八度,3代表高八度
0x22,0x10,0x23,0x15, //個位代表簡譜的音符,例如0x15代表低八度的S0,0x21代表中八度的DO。
0x24,0x05,0x23,0x10, //節拍則是代表音長,例如:0x10代表一拍,0x20代表兩拍,0x05代表1/2拍
0x21,0x10,0x22,0x20,
0x21,0x10,0x16,0x10,
0x21,0x40,0x15,0x20,
0x21,0x10,0x22,0x10,
0x23,0x10,0x23,0x05,
0x24,0x05,0x25,0x10,
0x21,0x10,0x24,0x15,
0x23,0x05,0x25,0x10,
0x22,0x05,0x23,0x05,
0x23,0x05,0x22,0x05,
0x22,0x30,0x23,0x20,
0x25,0x10,0x31,0x10,
0x27,0x15,0x26,0x05,
0x26,0x20,0x25,0x10,
0x25,0x05,0x26,0x05,
0x27,0x10,0x26,0x05,
0x25,0x05,0x23,0x40,
0x24,0x15,0x24,0x05,
0x25,0x10,0x26,0x10,
0x25,0x10,0x24,0x05,
0x23,0x05,0x22,0x20,
0x17,0x10,0x17,0x05,
0x16,0x05,0x15,0x10,
0x16,0x10,0x21,0x40,
0x00,0x00
};
uchar code quzi[] ={ //此數組數據爲各個音符在定時器中的重裝值,第一列是高位,第二列是低位
0xf8,0x8c, //低八度,低1
0xf9,0x5b,
0xfa,0x15, //低3
0xfa,0x67,
0xfb,0x04, //低5
0xfb,0x90,
0xfc,0x0c, //低7
0xfc,0x44, //中央C調
0xfc,0xac, //中2
0xfd,0x09,
0xfd,0x34, //中4
0xfd,0x82,
0xfd,0xc8, //中6
0xfe,0x06,
0xfe,0x22, //高八度,高1
0xfe,0x56,
0xfe,0x6e, //高3
0xfe,0x9a,
0xfe,0xc1, //高5
0xfe,0xe4,
0xff,0x03 //高7
};
/**********************
函數名稱:voiddelay(unsigned int xms)
功能描述:延時
入口參數:xms:輸入需要延時的毫秒值
出口參數:無
備註:
***********************/
void delay(unsigned int xms)
{
uinti,j;
for(i=xms;i>0;i--)
for(j=124;j>0;j--);
}
/**********************
函數名稱:voidinit()
功能描述:系統初始化,功能是配置IO口 定時器的初始化
入口參數:無
出口參數:無
備註:
//P2^0,P2^1,P2^2,P2^3都設爲推輓輸出
***********************/
void init()
{
P0M0=0x00;
P0M1=0x00;
P2M0=0x0f;
P2M1=0x00;
P3M0=0x10;
P3M1=0x00;
P4M0=0x00;
P4M1=0x00;
P5M0=0x00;
P5M1=0x00;
TMOD=0x01;
TH0=0xD8;
TL0=0xEF;
EA=1;
ET0=1;
TR0=0;
}
/**********************
函數名稱:ucharquyin(uchar tem)
功能描述:識別輸入的音符並且控制3個不同key鍵的按下來控制不同的樂譜輸出方式
在quzi數組中,找到music數組定義的簡譜音符的重裝值,並返回其在quzi數組中的位置
入口參數:tem:music數組中定義的簡譜音符
出口參數:返回的是tem音符在quzi數組中的位置值
備註:
***********************/
uchar quyin(uchar tem)
{
qudiao=tem/16; //高4位是曲調值
jp=tem%16; //低4位是音符
if(key1==0)
{k3=0;
k2=0;
k1=1;
}
if(k1==1)
{
sel_led=1; //選擇led燈亮來顯示樂譜
P0=led[jp];
}
if(key2==0)
{
k2=1;
k1=0;
k3=0;
}
if(k2==1)
{
sel_led=0;//先選數碼管亮
sel_0=1;
sel_1=1;
sel_2=1;
P0=duanxuan[jp]; //選數碼管輸出音符樂譜
delay(1);
}
if(key3==0)
{
k1=0;
k2=0;
k3=1;
}
if(k3==1)
{
sel_led=0;
sel_0=0;
sel_1=0;
sel_2=0;
P0=duanxuan[qudiao];//用數碼管輸出曲調
delay(1);
}
if(qudiao==1) //當曲調值爲1時,即是低八度,低八度在quzi數組中基址爲0
qudiao=0;
elseif(qudiao==2) //當曲調值爲2時,即是中八度,中八度在quzi數組中基址爲14
qudiao=14;
elseif(qudiao==3) //當曲調值爲3時,即是高八度,高八度在quzi數組中,基址爲28
qudiao=28;
weizhi=qudiao+(jp-1)*2; //通過基址加上音符作爲偏移量,即可定位此音符在quzi數組中的位置
returnweizhi; //返回這一個位置值
}
/**********************
函數名稱:voidplaymusic()
功能描述:播放音樂
入口參數:無
出口參數:無
備註:
***********************/
void playmusic()
{
ucharp,m,tem; //m爲節拍
uchari=0;
while(1)
{
p=music[i]; //如果碰到結束符,延時1秒,回到開始再來一遍
if(p==0x00)
{
i=0;
delay(1000);
}
elseif(p==0xff) //若碰到休止符,延時100ms,繼續取下一音符
{
i=i+2;
delay(100);
TR0=0;
}
else //正常情況下取音符和節拍
{
tem=quyin(music[i]); //取出當前音符在quzi數組中的位置值
timeh=quzi[tem]; //把音符相應的計時器重裝載值賦予timeh和timel
timel=quzi[tem+1];
i++;
TH0=timeh; //把timeh和timel賦予計時器
TL0=timel;
m=music[i]; //取得節拍
i++;
}
TR0=1; //開定時器1
delay(m*180); //等待節拍完成, 通過P3^4口輸出音頻
TR0=0; //關定時器1
}
}
//主函數入口
void main()
{
init();
P0=0x00;
playmusic();
while(1);
}
/**********************
函數名稱:voidtim1() interrupt 1
功能描述:定時器0中斷處理,重新裝值,並把beep值取反,產生方波
入口參數:無
出口參數:無
備註:
***********************/
void tim1() interrupt 1 //計時器控制頻率
{
TH0=timeh;
TL0=timel;
beep=~beep;
}