108鍵全調性鋼琴
- 一 基本介紹
- 1 項目簡介
- 2 實現方式
- 3 項目構成
- 二 實現過程
- 0 前置基本外設驅動
- 1 聲音控制
- 2 樂譜錄入&基礎樂理
- 3 點陣屏譜點動態刷新
- 4 項目交互控制
- 5 錄入新曲子過程
- 三 展示,與鏈接視頻地址
- 1 主要功能函數一覽
- 2 下載鏈接
- 3 視頻效果
一 基本介紹
本章為項目整體介紹
1 項目簡介
本項目通過搖桿模塊
控制模式切換,TFTST7735S屏幕
LED與點陣屏Max7219
顯示交互效果,喇叭輸出聲音。搖桿可控制8種模式,搖桿Z軸
控制確定返回:
- 播放音樂1-2-3
- 控制聲音:增加,減小,串口直接設定具體值
- 搖桿音樂模式
- 點陣譜點開關
下為具體特點介紹:
(1)廣泛音調支持
支持從1=CC#DD#EFF#GG#AA#B
的所有音調,還可以設置大小調,或者用升降半調
作為起始音,還能使用不同的八度區作為起始音等級,完美適配數字簡譜
,幾乎可以演奏你熟悉的任何曲子。內部代碼會根據設置的初始參數,算出該調性下其余一百零七個鍵的頻率,不需要提前打表一堆數組,節省空間。
(2)精準還原聲音
因為支持修改每個音符的升降半調
和升降度
,并能精準控制音長節拍
,所以播放音樂分辨率很高,音準極好,類似一臺一百零八鍵全頻率的電子鋼琴。錄入了3首音樂:含克羅地亞狂想曲
與夢回還
。
(3)使用簡單
錄一首譜子只需:一個純數字
的一維
樂譜數組
,和一個一鍵設置調性的函數
,用來配置新曲子。前者只要按照順序和一定的規律對著數字簡譜抄數字
,后者更簡單,根據簡譜信息填兩三個字母數字。不用懂很多,只要會看數字簡譜。可播放任何
單手數字簡譜。
(4)點陣燈譜掃描
可以通過點陣屏
顯示不同音符
和節拍長度
。一條掃描線
從頂部掃描至底部,每個音符都有長度與不同位置,觸碰到掃描線
即消失
,類似音游
。有兩個注意點,一是如何讓掃描線和聲音同步變化,對時序必須嚴格控制。二是如何及時更新不同音符在點陣屏上的譜點,因為點陣屏長度有限,所以一定會遇到某個音符觸碰底部,部分被截斷的情況,需要及時在屏幕頂部恢復被截斷的信息。
詳細效果,見底部視頻
項目涉及到許多常用外設,如用像是
AD控制
搖桿模塊,
脈寬調制PWM控制聲音,
串口收發數據,
SPI,IIC`等基礎知識。
2 實現方式
- 音符 1-7:PWM控制喇叭發出不同聲音。
- 不同調性:了解樂理基礎,根據數字簡譜,提取參數,編寫算法。
- 樂譜錄入:一個音符含:
度數/本身頻率/時長/升降半調
4個綜合信息,通過4位十進制表示。(第二章最后詳細解釋) - 動態樂譜顯示:點陣屏驅動控制,精準到控制每一個點,從左到右x列對應以音符1-7,y軸表示音符長度,一行則表示最小節拍時長。
- 交互:搖桿控制功能切換,TFT與LED顯示內容變化。
3 項目構成
(1) 軟件環境
上傳程序:keil5
圖片轉c數組:img-lcd
字體編輯:PctoLcd
串口測試:串口助手
(2) 所需硬件
- stm32F103C8T6
- TFTst7735_RGB128*160——8引腳
- 小喇叭 (或蜂鳴器,大喇叭)
- 點陣屏MAX7219
- 搖桿模塊:XYZ3軸
- LED燈RGB3色(非必須)
(3)接線
串口 | stm32 |
---|---|
RX | A9 |
TX | A10 |
OLED | stm32 |
---|---|
SCL | B8 |
SDA | B9 |
ST7735 | stm32 |
---|---|
GND | 電源地 |
VCC | 3.3v電源 |
SCL | 接PA5 |
SDA | 接PA7 |
RES | 接PB0 |
DC | 接PB1 |
CS | 接PA4 |
BL | 空/3.3v |
MAX7219點陣屏 | stm32 |
---|---|
Din | A6 |
Cs | B13 |
Clk | B14 |
LED | stm32 |
---|---|
R | B10 |
G | B11 |
B | B13 |
搖桿模塊 | stm32 |
---|---|
X | A0 |
Y | A1 |
Z | C15 |
喇叭 | stm32 |
---|---|
紅 | A2 |
黑 | GND |
喇叭,TFT, 搖桿為必要外設。如不開燈譜,點陣屏亦可不接
二 實現過程
本章詳解每一步如何實現
0 前置基本外設驅動
基礎驅動實現,本文不在贅述。詳細解釋請跳轉以下文章:
MAX7219 & IIC過程
TFTST7735 & SPI過程
串口收發數據 & UART協議
PWM脈寬調制
AD模數轉換
1 聲音控制
本節解釋如何利用pwm發出不同頻率,響度的聲音
(1)聲音特性
音調:指聲音的高低,由物體振動的頻率決定,頻率越高,音調越高;頻率越低,音調越低
響度:表示聲音的大小或強弱,由振幅和人耳與聲源的距離決定。振幅越大,距離越近,響度越大;振幅越小,距離越遠,響度越小
聲音的物理量
頻率:單位時間內物體振動的次數,單位為赫茲(Hz),人類能聽到的音頻范圍是20Hz~20kHz。
周期:物體完成一次振動所需的時間,單位為秒(s),與頻率成反比。
(2)PWM控制聲音
控制發出不同聲音,就是是控制聲音不同的音調
與響度
,也就是頻率
與聲音大小
。回想使用PWM控制舵機時,舵機頻率是固定的50Hz,通過改變占空比控制舵機角度。而控制聲音完全不同:
- 第一:需要生成
多種PWM頻率
- 第二:修改占空比,僅能改變
聲音大小
,不改變音調頻率。
故而在使用PWM播放音樂是,一定會面臨需要經常變化PWM頻率的問題,而我們知道:
PWM頻率=(定時器頻率/PSC)/ARR = 定時器頻率/(PSC * ARR)
所以改變PWM頻率,本質是不斷修改PSC或ARR的值,為了簡便二者改一個就可以。如:把pwm頻率
修改為:fre
,則只要令
ARR=(定時器頻率/PSC)/fre
PSC不變,故分子為常數。以72Mh
時鐘頻率,PSC=72
為例,修改PWM頻率代碼為:
/*
功能:運行中,修改PWM的頻率
參數:修改后的頻率 fre
公式:PWM頻率=(定時器頻率/PSC)/ARR = 定時器頻率/(PSC * ARR)
*/
void PWM_SetFrequency(uint16_t fre) {int arr=72000000/72/fre;TIM2->ARR = arr;
}
正常人耳能聽到聲音的頻率范圍:大致在20Hz~20000Hz之間
108鍵鋼琴的頻率范圍:是從最低音A0的27.5Hz到最高音C8的4186.01Hz
(3)代碼核心部分
PWM.h
#ifndef __PWM_H
#define __PWM_H
/*
Tim2 4通道可控制4路PWM信號,使用通道3
PWM輸出引腳:A2
*/
void PWM_Init(void);
void PWM_SetCompare3(uint16_t Compare); //改聲音大小 1-999
void PWM_SetFrequency(uint16_t fre); //改聲音發聲#endif
PWM.c 初始設定
#include "stm32f10x.h" // Device header
#include "PWM.h"
/*
功能:PWM初始化
*/
void PWM_Init(void)
{//僅舉核心部分TIM_TimeBaseInitStructure.TIM_Period = 1000 - 1; //初始給個1000TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1; //分頻后為1MH
}
/*
功能:TIM2通道3-A2,占空比 (改變每個周期內高電平或低電平的持續時間)
參數:Compare要寫入的CCR的值,范圍:0~20000 ,即為ARR范圍
*/
void PWM_SetCompare3(uint16_t Compare)
{TIM_SetCompare3(TIM2, Compare); //設置CCR1的值,精確地設置高電平持續時間
}
/*
功能:運行中,修改PWM的頻率
參數:計數周期,需要的頻率
公式:PWM頻率=(定時器頻率/PSC)/ARR = 定時器頻率/PSC * ARR
*/
void PWM_SetFrequency(uint16_t fre) {int arr=1000000.0/fre;TIM2->ARR = arr;
}
2 樂譜錄入&基礎樂理
數字簡譜閱讀基礎必備:
基礎樂理
(1)音符規律
由于音符間的音符十二平均律
,相鄰鍵(含黑鍵)頻率乘以1.0594630943
(二的十二次方根),頻率大一度為2倍
關系,低一度除以2。故只要確定1=?
初始調性,即可計算出剩余108鍵的頻率值,存入一個二維數組即可,大小為[9][12]
,代表0-8 九個度數區間
的1-7音符頻率、以及中間的黑鍵頻率
,對應鋼琴9個區,每個區為12個鍵。
(2)大小調
大小調簡單理解為就是一種1-7不同位置
的按法
,大小調頻率表相同,不需要重新計算。區分
大小調只需在代碼種只需要加入2個映射數
組即可(20種彈法也沒有問題)
計算頻率參考全全局變量值:
//------------------------計算頻率參數-------------------
/*
音符數組-標準鋼琴108鍵
1 8個度區,升降度直接改一維下標
2 每個度區,含cdefgab以及降半調共12個全黑白鍵,設計一映射數組1-7到第二維度,降半調+10計算
3 樂譜4位十進制:半調(0-2) 音調:1-7 音度:0-8 (大1則高8度) 時長:1-2-4 拍數
*///計算頻率參考數組
uint16_t note[9][12]={//C C# D D# E F F# G G# A A# B{16,17,18,19,20,21,23,24,25,27,29,30}, //0 八度區 {32,34,36,38,41,43,46,48,51,55,58,61}, //1{65,69,73,77,82,87,92,97,103,110,116,123},//2{130,138,146,155,164,174,785,196,207,220,233,246}, //3{261,277,293,311,329,349,369,392,415,440,466,493}, //4{523,554,587,622,659,698,739,783,830,880,932,987}, //5{1046,1108,1174,1244,1318,1396,1480,1568,1661,1760,1864,1975},//6{2093,2217,2349,2489,2637,2793,2960,3136,3322,3520,3729,3951},//7{4186},//8 C8
};
//在note中找值的映射數組
uint8_t book_note[8]={0,0,2,4,5,7,9,11};//計算107個頻率生成數組,存入此處,一曲一更,覆蓋。
uint16_t note2[9][12]={0};//大調映射 0-0 1-0 2-2 3-4 4-5 5-7 6-9 7-11(下一個1略) 全全半全全全(半),+2 +2 +1 +2 +2 +2 +(1)
uint8_t major_scale[8]={0,0,2,4,5,7,9,11};//
//小調映射 0-0 1-0 2-2 3-3 4-5 5-7 6-8 7-10(下一個1略) 全半全全半全(全)+2 +1 +2 +2 +1 +2 +(2)
uint8_t minor_key[8]={0,0,2,3,5,7,8,10};
生成對應調性如下:另外107鍵的頻率過程:
/*
功能:制造專屬歌曲的頻率數組
參數Scale:音調 C——B,c-b 大小調
參數dp: 0-1-2 降半調,無,升半調
參數degree:度數0-8
解釋:適配 'do'=(C-B,c-b,#/b,0-8)全音調,108鍵全區每次播放歌曲前,調用此函數,計算出合適的頻率并保存例:make_scale(C,1,4) do=C4make_scale(D,2,5) do=D#5
原理:根據一個參考數組,算出當前調性下的初始'do'的頻率,往后排11個鍵,就是該度下所有鍵上下跨度,乘2或除2就可以得出。
*/
void make_scale(int Scale,int dp,int degree){//大小調do頻率固定int t;double ans=1.0594630943;double tem=0;if(Scale=='a'||Scale=='b') //定初始調位置1-7t=6+Scale-'a';else t=Scale-'c'+1;if(Scale=='A'||Scale=='B')t=6+Scale-'A';else t=Scale-'C'+1;t=book_note[t]+dp-1; //定do的位置,dp-1 -1 0 1,計算升降調tem=note[degree][t];//不管大小調,初始do頻率相同,變化都的是2-7位置,也就是2個映射數組//degree度,12鍵音note2[degree][0]=tem;for(int i=1;i<12;i++){tem*=ans; //保留進度note2[degree][i]=round(tem); //四舍五入}//degreee-1~0for(int i=degree-1;i>=0;i--)for(int j=0;j<12;j++)note2[i][j]=note2[i+1][j]/2;//degreee+1~8for(int i=degree+1;i<=8;i++)for(int j=0;j<12;j++)note2[i][j]=note2[i-1][j]*2;
}
(3)曲譜錄入
由于本項目對于音準的高要求,所以簡譜中每個音符包含4個信息:是否升降半調
、本身音調
、是否升降度
、節拍
。不需要引入字母,使用4位數字記錄,個十百千
位的數字記錄信息,做法類似于寄存器2進制寫入數據。
- 千:0 1 2
降/平/升
半調 (升降半調,即#/b,輸出數組后一位或前一位元素) - 百:頻率 1-7
- 十:度:0-8度 (升度頻率乘2)
- 個:記錄節拍 1 2 4 (根據曲速可計算
一拍時長t
,t除以4/(1、2、4)便是該音符發聲時長,為了方便記憶,否則設為421也可以,本質是得出有幾個最小一拍
) - 曲速:如曲速為112,則一拍時長為:60/112 s
如實際譜子:
/*
曲1:Dream of cherry tree
1=Db 大調 beat=68 4/4 68太慢了,提高到80
*/
double beat1=60.0/80*1000; //DC曲速 原曲:60.0/68
uint16_t song_DC[]={1351,1251,1352,1552,1351,1351,1051,1041,1251,1351,1251,1352,1552,1161,1161,1061,1162,1752,1652,1552,1351,1251,1352,1552,1351,1351,1051,1542,1642,1152,1252,1351,1251,1352,1552,1361,1361,1061,1262,1162,1752,1552,1651,1051,1551,1051,1451,1551,1652,1552,1351,1251,1051,1152,1742,1642,1152,1741,1041,1041,1041,1552,1652,1752,1162,1651,1652,1352,1451,1551,1652,1452,1351,1251,1042,1352,1742,1252,1151,1152,1644,1744,1152,1742,1152,1252,1352,1252,1251,1351,1251,1352,1552,1361,1042,1262,1162,1752,1652,1552,1551,1351,1251,1352,1552,1361,1042,1462,1362,1262,1162,1752,1751,1042,1752,1162,1262,1161,1162,1552,1651,1652,1452,1552,1452,1352,1252,1151,1051,1051,1051
};
(4)播放聲音
遍歷樂譜數組,依次播放每個音符即可。下為播放一個音符的函數:
/*
功能:按照時長演奏一個音符(不含點陣屏變化)
參數ans:復合音符
參數btspeed:一拍的曲速單位:ms
參數scale:大調1 小調0
解釋:根據音符信息,演奏音符,oled顯示參數無燈模式效果精準,音長更及時
*/
void unlight_Sound(int ans,int btspeed,int scale){int half=ans/1000; //0 1 2 降半音b 正常 升半音#int msc=ans/100%10; //1-7 ,不含0 8int degree=ans/10%10; //0-8 度int tm=ans%10; //節拍 1 2 4 int fre;//<1> fre為獲取二維數組下標,1-7與大小調映射if(scale) fre=major_scale[msc]; //大小調-判斷else fre=minor_key[msc];if(half-1==1) fre++; //<2> 判斷是否升降半調else if(half-1==-1) fre--; // -1 0 1 升/降半調,前一個或后一個元素if(msc==0) PWM_SetCompare3(0); //<3>修改聲音大小,空音設占空比0else PWM_SetCompare3(voice);PWM_SetFrequency(note[degree][fre]); //<4>設置聲音頻率發聲tm=btspeed/tm; //<5>控制發聲時長Delay_ms(tm); //OLDED參數顯示OLED_ShowString(3,7,"degree:"); OLED_ShowNum(3,14,degree,1);OLED_ShowString(4,1,"fre:");OLED_ShowNum(4,5,note[degree][fre],4);OLED_ShowString(4,10,"msc:");OLED_ShowNum(4,14,msc,1);//PWM_SetCompare3(0);//每個音符后額外停頓,曲譜精準時,無需設置//Delay_ms(5);
}
此函數僅用于播放聲音,不控制點陣屏,注意與下面函數區分
3 點陣屏譜點動態刷新
點陣屏8*8XY軸代表信息:
X:1-7列對應音符1-7
Y:每一行代表音符發聲時長
,一行為最小節拍,通過60/曲速計算得出,Delay進行控制
掃描線會從上至下掃描,碰到音符
,使其消失
,和樂譜中每個音符的時長對應同步。此算法設計包含以下部分:
- 掃描線
變化
與樂譜的聲音長度同步
(以Delay延時最小節拍為基礎) - 當掃描線達到
頂端
-立刻向后更新一屏的音符信息(非同步,而是某時刻
更新) 預處理
曲譜中每一個音符的顯示信息(代碼計算處理,人無需計算)- 恢復被截斷的底部音符保證正確性(準確性)
(1)預處理樂譜每個音符的點陣信息
預處理
每個音符在點陣屏上出現的起始行
與結束行
位置,存入標記數組msc_sort中。用于后續輸出某音符在點陣屏上的具體位置
,注意截斷:
/*
功能: 并預處理曲鋪每個音符燈點位置信息
參數: 1-3,播放歌曲前,先調用此函數
解釋:msc_sort數組覆蓋,每次播放前都需調用獲取每個音符在點陣屏上 出現的起始行與結束行位置,存入msc_sort*起始不可超過8,結尾可大于等于8,會在動態更新點陣屏時利用結尾特性,判斷觸底截斷音符
*/
void load_msc(int num){int tem,mucis_num; if(num==1){mucis_num=sizeof(song_DC)/sizeof(song_DC[0]); //音符數msc_sort[0][0]=1;msc_sort[0][1]=4/(song_DC[0]%10);for(int i=1;i<mucis_num;i++) //每一個的值,依據前一個結尾計算{tem=msc_sort[i-1][1];if(tem>=8) tem=tem%8+1;else tem+=1;int r=4/(song_DC[i]%10); //當前音符所占1/4節拍數,就是需要幾個點點亮msc_sort[i][0]=tem; //起始必須小于等于8msc_sort[i][1]=tem+r-1; //結尾可以大于等于8}}if(num==2){//同上}if(num==3){//同上}
}
(2)顯示單個音符譜點函數
欲顯示整個樂譜,需先從顯示一個音符開始:
/*
功能:顯示一個音符的位置
參數k:曲庫中下標,音符
參數ans:復合音符值
介 紹:在預處理數組中記錄了開始點與結束點,此處顯示時超過底部不予顯示在動態更新樂譜時,在對確實的另外半截手動設置填補,否則會覆蓋第一行
*/
void note_show(int k,int ans){int t=4/(ans%10); //音符長度int x=ans/100%10; //音符0-7int y=msc_sort[k][0]; //音符起始行,不會超過8//向下顯示t個音符,超過不顯示,所以不用msc_sort[1]for(int i=0;i<t;i++){Max7219_ShowGraph(y,x,x);y++;if(y>8) break; //先顯示一次,在結束}
}
需注意,由于音符觸底超長部分,需要截斷,留在下一次顯示,所以超過第8行不顯示。
補充截斷部分
已在對應處單獨編寫代碼。
(3)掃描線、聲音、音符點、同步方式(核心)
此處與上述unlight_Sound數組區別是,添加的while循環部分
。單純播放聲音,只需要PWM寫入頻率
然后延時
即可。但添加樂譜后,由于需要控制掃描線
與音符聲長
同步,以2個最小節拍長的音符為例,則實際過程為:
PWM
發聲
->掃描線1出現->Delay最小節拍1->掃描線1消失->掃描線2出現->Delay最小節拍2->掃描線2消失。
這樣可以做出聲音與點陣屏同步更新
的效果,以最小延時
為基礎,人眼是察覺不到先后順序的,形成聲畫同步效果。其次是,每個音符的譜點并非是:只有播放到該音符,才更新一個
,而是當掃描線出現在頂部
時,一次提前更新一屏幕
的內容,這樣才有提前讀譜的效果(類似全民k歌),而判段什么時候到達底部,以及如何恢復被截斷的音符部分:通過預處理的譜點數組msc_sort
實現。
/*
功能:按照時長演奏一個音符,掃描線對應同步變化
參數1:音符編號
參數3:復合數組 (需要預處理點陣,所以要傳遞數組)
參數3:beat節拍速度
參數4:大調1,小調0
解釋:掃描線于聲音同步,樂譜點是在每次掃描到第一行時更新滿屏。掃描線:與聲音同步,每個最小節拍間隔,點陣屏都要亮滅一次樂譜點陣顯示:每次到第一行時,刷新一整個點陣屏的音符點,向后遍歷遇到底部停止難點在于補全上一次末尾超長截斷的燈點
*/
void light_Sound(int k,uint16_t *ans,int btspeed,int scale){int half=ans[k]/1000; //0 1 2 降半音b 正常 升半音#int msc=ans[k]/100%10; //0-7int degree=ans[k]/10%10; //0-8 度int tm=4/(ans[k]%10); //幾個1/4節拍 1 2 4 int fre; //<1> fre為獲取二維數組下標,1-7與大小調映射if(scale) fre=major_scale[msc]; //大小調-判斷else fre=minor_key[msc];if(half-1==1) fre++; //<2> 是否升/降半調,-1 + 1前一個或后一個元素else if(half-1==-1) fre--; if(msc==0) PWM_SetCompare3(0); //<3> 音量設置,注意0不發聲else PWM_SetCompare3(voice);PWM_SetFrequency(note[degree][fre]); //<4>發聲int y=msc_sort[k][0]; //掃描線位置 1-8行while(tm--) //tm個最小節拍{if(y==1){ //<5> 掃描到第一行,立刻向后更新一屏的樂譜點陣for(int i=0;i<8;i++){if(i==0){ //當前字符是否為被截斷字符,并恢復截斷長度if(msc_sort[k][1]>=8){ //避免連續出現一個長度,無法到達下一個,從而斷屏for(int n=1;n<=msc_sort[i][1]-8;n++)Max7219_ShowGraph(n,msc,msc);}elsenote_show(k,ans[k]);}else{note_show(k+i,ans[k+i]); //顯示單個字符的點陣if(msc_sort[k+i][1]>=8) //到底,結束,超長部分不顯示break;}}}Max7219_ShowGraph(y,1,8); //<6>掃描線隨 最小節拍更新Delay_ms(btspeed/4.0); //發音,同時亮燈 Max7219_Clearline(y);y++; //掃描線位置更新,最多到9if(y>8) y=y%8; //超過8,則%8為1-7,不能在加1,會跳過空行}OLED_ShowString(3,7,"degree:");OLED_ShowNum(3,14,degree,1);OLED_ShowString(4,1,"fre:");OLED_ShowNum(4,5,note[degree][fre],4);OLED_ShowString(4,10,"msc:");OLED_ShowNum(4,14,msc,1);//PWM_SetCompare3(0);//每個音符間額外停頓//Delay_ms(5);
}
此處為 模擬過程,想要直接理解有難度,最好自己去編寫嘗試
(4)完整播放樂曲函數
最終封裝遍歷音樂的函數,可設置是否打開點陣
屏模式與大小調
選擇,由于基于最小延時節拍
,同步點陣與聲音,所以會占據部分cpu性能,若只需播放音樂的話,可設置關閉。
/*
功能:播放音樂
參數1:曲目1-3
參數2:1 燈光 0 非燈光
參數3:1 大調 0 小調
解釋:可選擇燈光模式
*/
void Play_msc(int num,int mode,int scale){//可調控曲速if(num==1){int r=sizeof(song_DC)/sizeof(song_DC[0]);if(mode){for(int i=0;i<r;i++) //有動態燈譜light_Sound(i,song_DC,beat1,scale); //<2>修改2,大小調,1為大PWM_SetCompare3(0);//播放完靜音}else{for(int i=0;i<r;i++)unlight_Sound(song_DC[i],beat1,scale);PWM_SetCompare3(0);}}if(num==2){//同上}if(num==3){//同上}
}
變速: 如一首曲子需要
多個曲速
,可以在對應for循環內部中判斷,當到達某個音符時 ,改變beat值接口,加幾個if語句即可解決。
4 項目交互控制
本質為利用搖桿8個模擬值區間(上下左右與斜對角),控制8個不同模式,利用不同flag狀態
控制界面
rocker_music
函數效果為,根據AD采集搖桿信息,判斷搖桿位置
0-8值,來播放不同的音調1-7
TFT_update
效果為:根據不同數字,顯示不同的文字內容
AD搖桿模塊的實現,過于基礎不再描述。包括設置全局變量聲音、串口通信獲取信息、TF圖片文字顯示。
/*接線
1 串口連接:RX-A9 TX-A10
2 OLED連接:SCL-B8 SDA-B9
3 TFTST7735:GND 電源地VCC 3.3v電源SCL 接PA5SDA 接PA7RES 接PB0DC 接PB1CS 接PA4 BL 空/3.3v
4 MAX7219點陣屏: Din-A6 Cs-B13 Clk-B14
5 LEd_RGB: B10 B11 A3
6 搖桿:XYZ: x-A0 y-A1 z-C15
7 喇叭:A2 (另一根線接GND)
*/
#include "stm32f10x.h" // USE_STDPERIPH_DRIVER
#include "delay.h" //--no-multibyte-chars
#include "OLED.h"
#include "Serial.h"#include "LED.h"
#include "KEY.h"
#include "AD.h"
#include "PWM.h"#include "St7735tft.h" //TFT
#include "Max7219.h" //點陣屏
#include "playmusic.h" //主要功能集成函數int main(void)
{AD_Init();PWM_Init();LED_Init();KEY_Init();TFT_Init();Max7219_InitDisplay();OLED_Init();Serial_Init();TFT_UI(); //顯示自定義界面OLED_ShowString(1,1,"SKY_Music");int z,m1,mode=0; //z軸狀態,m1與mode用來記錄搖桿位置int falg_rocker=0;//搖桿標記/*C-B調全升降調打表,以do=4度為例,最大支持0-8度區間for(int i='A';i<='G';i++){for(int j=0;j<=2;j++)make_scale(i,j,4);Serial_Printf("\r\n");}*/while (1){//可隨時串口更新音量if(Serial_RXString())set_voice();z=KEY_Getnum(); //z軸狀態OLED_ShowString(3,1,"Z:");OLED_ShowNum(3,3,z,1);//自由編曲獨占模式if(falg_rocker){if(z==1) falg_rocker=0;rocker_music();continue;}//記錄搖桿值m1=Get_rocker(); //搖桿一松手會歸0,所以需要2個值記錄,無法及時TFT_update(m1);if(m1!=0) mode=m1; //記錄變化的操作/*@@@@@串口測試,獲取值Serial_Printf("z=%d m1=%d mode=%d\r\n",z,m1,mode); *///調用功能if(z&&mode==1){ //上:DC Dream of cherry treeload_msc(1);//載入歌曲make_scale('D',DOWN,4); //調性D,b降半調,度數4 1=Db4Play_msc(1,light_Turn,MAJOR); //曲目編號,燈光模式,MAJOR大調}else if(z&&mode==2){ //左上:add聲音lower_voice();TFT_update(2);}else if(z&&mode==3){ //左:克羅迪亞load_msc(2);make_scale('E',DOWN,4); //1=Eb4,小調Play_msc(2,light_Turn,MAJOR); }else if(z&&mode==4){ //左下:low聲音upper_voice();TFT_update(4);}else if(z&&mode==5){ //下:夢回還load_msc(3);Delay_ms(400);//曲目長,更新下數組make_scale('A',NORMAL,4); //A4 不降調Play_msc(3,light_Turn,MAJOR);}else if(z&&mode==6){ //下右:空}else if(z&&mode==7){ //右: 自由搖滾-獨占,rocker音樂falg_rocker=1; }else if(z&&mode==8){ //右上:燈光模式light_Turn=!light_Turn;TFT_update(8);LED_On(1);Delay_ms(100);LED_On(2);Delay_ms(150);LED_On(3);Delay_ms(200);LED_Off(1);LED_Off(2);LED_Off(3);}}
}//搖桿的按鈕掛個中斷,及時反應
void EXTI15_10_IRQHandler(void){ //(5)創建中斷函數//startup_stm32f0x_md.s找——->Table Mapped,DCD欄以EXTI_IRQHandler結尾//判斷是不是15通道,exti.h————>EXTI_GetITStatus與EXTI_ClearITPendingBit函數if(EXTI_GetITStatus(EXTI15_10_IRQn)==RESET){EXTI_ClearITPendingBit(EXTI_Line15);//清除標志位Serial_Printf("Z按鈕進入中斷\r\n");}
}
5 錄入新曲子過程
(1)錄入樂譜
進入Song.h文件:
- 樂譜數組
覆蓋
3首中隨機一首的信息即可。 - 修改對應beat曲速,數字簡譜會有曲速說明
以一小節為例,4/4拍:
錄譜過程:
實際錄譜時,使用
語音識別輸入
,與excel
批量處理數字數據,很快捷,因為規律十分的簡單
(2)修改帶調性函數
進入main.c文件,修改對應調性
,大小調
參數即可:
//根據編號信息修改
else if(z&&mode==3){load_msc(2);make_scale('E',DOWN,4); //1=Eb4, 改A-G,DOWN/NORMAL/RISE 降平升半調,初始度數0-8(一般為4)Play_msc(2,light_Turn,MAJOR); //改大小調,MAJOR或MIRROR }
#為升:使用RISE
b為降:用DOWN
否則:NORMAL
A-G大小寫通用
大調:MAJOR
小調:MIRROR
三 展示,與鏈接視頻地址
本章內容為:主要功能函數展示與實現效果,附上下載鏈接
1 主要功能函數一覽
#ifndef __PLAYMUSIC_H
#define __PLAYMUSIC_H/*song中引用文件:
note[9][12] 頻率文件
book_note[8] 映射
song_DC[] 曲庫1
song_Croatia[] 曲庫2
song_DreamBack[] 曲庫3
beat1 曲1節拍
beat2
beat3
voice 聲音
*/#define RISE 2 //升半調
#define NORMAL 1
#define DOWN 0 //降半調
#define MAJOR 1 //大調
#define MINOR 0 //小調//playmusic定義
extern int light_Turn; //開關燈光模式
extern uint16_t msc_sort[800][2]; //預處理:每個音符在點陣屏初始位置信息//----------------功能函數-----------------------
//音樂播放
void set_voice(void); //串口準確更新聲音1-999
void lower_voice(void); //按一下,+200
void upper_voice(void);void load_msc(int num); //載入曲譜燈鋪信息
void make_scale(int Scale,int dp,int degree);//制作不同曲目調108鍵調性表
void rocker_music(void);//搖桿音樂
void Play_msc(int num,int mode,int scale); //曲目編號,燈光模式0-1//TFT
void TFT_UI(void);
void TFT_update(int k);//-------------------內部函數-----------------------
//燈
void unlight_Sound(int ans,int btspeed,int scale); //音符無燈
void light_Sound(int k,uint16_t *ans,int btspeed,int scale); //音符帶燈,動態更新樂譜
void note_show(int k,int ans); //顯示單個音符的點陣(超過底部部分不顯示)#endif
2 下載鏈接
點擊下載源文件:
與君共勉
3 視頻效果
stm32自制可視化樂譜108鍵鋼琴,小白輕松奏神曲