$regfile = "m8def.dat" ' 設置使用的AVR型號為ATmega8
$crystal = 8000000 ' 設置AVR系統時鐘頻率為8MHz
' -------------------- 硬件引腳定義 --------------------
Su_port Alias Pinb.0 ' 將PB0引腳命名為聲音輸出端口
' -------------------- 端口初始化 --------------------
Config Su_port = Output ' 將聲音輸出端口設置為輸出模式
Portb = &B11111110 ' 端口B初始化: PB0為輸出(0),PB1-PB7啟用內部上拉電阻(1)
' ==================== 變量定義 ====================
' 音樂控制變量
Dim Mu_tempo As Word ' 速度值,決定每個音符的基礎時長(毫秒)
Dim Mu_octave As Byte ' 當前八度值(范圍1-7)
Dim Mu_octdiv As Word ' 八度分頻值,用于計算不同八度的頻率
Dim Mu_pldata As String * 40 ' 音樂數據行緩沖區,每行最多40個字符
Dim Mu_dtlen As Byte ' 當前數據行的剩余字符長度
Dim Mu_dtposi As Byte ' 當前數據行的讀取位置
Dim Mu_str As String * 1 ' 從數據行讀取的單個字符
Dim Mu_freq As Word ' 計算出的音符頻率值
' 通用臨時變量
Dim Mu_temp1 As Byte ' 臨時變量Byte型 No.1 - 常用于存儲ASCII碼或中間結果
Dim Mu_temp2 As Byte ' 臨時變量Byte型 No.2 - 常用于存儲音符索引
Dim Mu_temp3 As Byte ' 臨時變量Byte型 No.3 - 常用于存儲音符時值等級
Dim Mu_temp4 As Byte ' 臨時變量Byte型 No.4 - 常用于存儲音符代碼
Dim mu_h As Byte ' 高八度標志
Dim mu_L As Byte ' 低八度標志
Dim mu_Z As Byte ' 當前八度標志
Dim Mu_tempw1 As Word ' 臨時變量Word型 No.1 - 常用于存儲音符時長
Dim Mu_tempw2 As Word ' 臨時變量Word型 No.2 - 常用于存儲頻率計算值
Dim Mu_templ1 As Long ' 臨時變量Long型 No.1 - 用于高精度計算
' ==================== I2C及OLED顯示屏初始化 ====================
Config Scl = Portc.5 ' 設置I2C時鐘引腳(SCL)為PC5
Config Sda = Portc.4 ' 設置I2C數據引腳(SDA)為PC4
Config Twi = 400000 ' 設置I2C總線速度為400kHz
I2cinit ' 初始化I2C硬件
$lib "i2c_twi.lbx" ' 使用TWI硬件I2C庫(非軟件模擬)
$lib "glcdSSD1306-I2C.lib" ' 使用SSD1306 OLED屏幕專用庫
' 圖形LCD配置: 128列 x 64行,使用SSD1306控制器
Config Graphlcd = Custom , Cols = 128 , Rows = 32 , Lcdname = "SSD1306"
Cls ' 清屏
Setfont font8x8TT ' 選擇8x8字體
' ==================== 主程序循環 ====================
Do ' 無限循環開始
' 播放第一首樂曲:莫扎特 G大調弦樂小夜曲 第一樂章
Restore Mu_data ' 將數據指針指向音樂數據表1的起始位置
Gosub Mu_play ' 調用音樂播放子程序
cls
Wait 2 ' 播放完成后等待2秒鐘
' 播放第二首樂曲:蘇聯國歌《牢不可破的聯盟》
Restore Mu_data1 ' 將數據指針指向音樂數據表2的起始位置
Gosub Mu_play ' 調用音樂播放子程序
cls
Wait 2 ' 播放完成后等待2秒鐘
Loop ' 重復循環,實現音樂連續播放
End ' 程序結束(實際不會執行到這里,因為循環無限)
' ==================== 音樂播放子程序 ====================
' 功能: 主音樂播放流程控制
' 調用關系: 主程序 → Mu_play → Mu_playini → Mu_play1 → Mu_playsub → (Mu_datcnv/Mu_noterd)
Mu_play:
Gosub Mu_playini ' ① 初始化:讀取速度、設置初始八度
Mu_play1:
Gosub Mu_playsub ' ② 解析并執行一個音樂代碼(音符/八度/休止符)
If Mu_temp1 = 0 Then Goto Mu_play1 ' ③ 如果未結束(Mu_temp1=0),繼續處理下一個音符
Return ' ④ 當前曲目播放完畢,返回主程序
' -------------------- 初始化子程序 --------------------
' 功能: 初始化音樂播放參數,讀取速度值
Mu_playini:
Mu_octave = 4 ' 將八度初始值設為4 (中央C所在的八度)
Mu_octdiv = 80 ' 設置默認八度分頻值(O4對應的值)
Read Mu_temp1 ' 讀取第一個數據(速度值,例如120或85)
Mu_tempo = 60000 / Mu_temp1 ' 將速度轉換為毫秒:60000ms/速度值 = 每四分音符毫秒數
Mu_tempo = Mu_tempo * 4 ' 計算全音符的時長:四分音符時長 × 4
Mu_dtlen = 0 ' 初始化數據行長度為0,表示需要讀取新行
Return
' -------------------- 音樂代碼處理子程序 --------------------
' 功能: 解析并執行一個音樂指令(音符、八度變化、休止符等)
Mu_playsub:
' === 第1步:獲取下一個字符 ===
If Mu_dtlen = 0 Then
Read Mu_pldata ' 從DATA語句讀取一行音樂數據
Mu_dtlen = Len(mu_pldata) ' 獲取這行數據的字符長度
Mu_dtposi = 1 ' 設置讀取位置為第1個字符
End If
Gosub Mu_datcnv ' 從數據行讀取一個字符到Mu_temp1
Mu_temp1 = Mu_temp1 And &HDF ' 將ASCII碼轉換為大寫(清除第6位,即小寫轉大寫)
' === 第2步:OLED顯示處理(調試用)===
If Mu_temp1 = &H41 Then Lcdat 3 , 60 , "6" ' A -> 6
If Mu_temp1 = &H42 Then Lcdat 3 , 60 , "7" ' B -> 7
If Mu_temp1 = &H43 Then Lcdat 3 , 60 , "1" ' C -> 1
If Mu_temp1 = &H44 Then Lcdat 3 , 60 , "2" ' D -> 2
If Mu_temp1 = &H45 Then Lcdat 3 , 60 , "3" ' E -> 3
If Mu_temp1 = &H46 Then Lcdat 3 , 60 , "4" ' F -> 4
If Mu_temp1 = &H47 Then Lcdat 3 , 60 , "5" ' G -> 5
' === 第3步:判斷指令類型 ===
If Mu_temp1 = &H4F Then Goto Mu_plays2 ' ASCII 4F = "O" - 八度改變指令
If Mu_temp1 = &H52 Then Goto Mu_plays3 ' ASCII 52 = "R" - 休止符指令
If Mu_temp1 > &H47 Then Return ' ASCII 47 = "G"以上,可能是"Z"結束代碼
' === 第4步:處理音符指令(A-G) ===
Mu_temp1 = Mu_temp1 - &H40 ' 將A~G (ASCII 41-47) 轉換為數字1~7
Mu_temp2 = Mu_temp1 + Mu_temp1 ' 乘以2,為查表做準備(表中每項占2字節)
Gosub Mu_datcnv ' 讀取下一個字符(檢查是否有升降音)
' 檢查是否為升音(#)或降音($)
If Mu_temp1 < &H31 Then ' ASCII小于"1"的字符可能是#或$
If Mu_temp1 = &H23 Then ' ASCII 23 = "#" 升半音
Mu_temp2 = Mu_temp2 + 1 ' 索引加1,對應升半音的頻率
Else ' 否則是降音"$"
Mu_temp2 = Mu_temp2 - 1 ' 索引減1,對應降半音的頻率
End If
Gosub Mu_datcnv ' 再讀取下一個字符(音符時值)
End If
Gosub Mu_noterd1 ' 解析音符時值(全分、二分、四分等)
' === 第5步:計算音符頻率 ===
Mu_temp2 = Mu_temp2 - 1 ' 將1-7轉換為0-6的索引
Mu_freq = Lookup(mu_temp2 , Mu_scale) ' 查表獲取基礎頻率
Mu_freq = Mu_freq / Mu_octdiv ' 根據當前八度調整頻率
Mu_tempw2 = Mu_freq * 12 ' 計算SOUND指令需要的頻率參數
Mu_templ1 = _xtal / Mu_tempw2 ' 根據晶振頻率計算分頻值
Mu_tempw2 = Mu_templ1 ' 保存分頻值
' === 第6步:計算音符時長 ===
Mu_templ1 = Mu_tempw1 * Mu_freq ' 時長 × 頻率
Mu_templ1 = Mu_templ1 / 1000 ' 轉換為毫秒
Mu_tempw1 = Mu_templ1 ' 保存時長參數
' === 第7步:OLED顯示高低音符號 ===
IF Mu_octave = mu_h THEN Lcdat 1 , 60 , chr(46) ' 高音顯示點在上方
IF Mu_octave = mu_L THEN Lcdat 4 , 60 , chr(46) ' 低音顯示點在下方
IF Mu_octave = mu_Z THEN
Lcdat 1 , 60 , chr(32) ' 中音清除上方點
Lcdat 4 , 60 , chr(32) ' 中音清除下方點
ENDIF
' === 第8步:輸出聲音 ===
Sound Su_port , Mu_tempw1 , Mu_tempw2 ' 在PB0產生指定頻率和時長的方波
Mu_plays4:
Mu_temp1 = 0 ' 設置標志,表示指令處理完成
Return
' -------------------- 八度處理子程序 --------------------
' 功能: 處理"O"指令,改變當前八度
Mu_plays2:
Gosub Mu_datcnv ' 讀取八度參數
If Mu_temp1 < &H31 Then ' 如果參數是+或-(ASCII小于"1")
If Mu_temp1 = &H2B Then ' ASCII 2B = "+"
Mu_octave = Mu_octave + 1 ' 八度增加
Else ' 否則是"-"
Mu_octave = Mu_octave - 1 ' 八度降低
End If
Else ' 否則是數字1-7
Mu_octave = Mu_temp1 - &H30 ' 將ASCII數字轉換為數值
' 指定中、低、高音頻段分頻值
mu_h = Mu_octave + 1 ' 高八度閾值(當前八度+1)
mu_L = Mu_octave - 1 ' 低八度閾值(當前八度-1)
mu_Z = Mu_octave ' 當前八度
End If
Mu_temp1 = Mu_octave - 1 ' 轉換為0-6的索引
Mu_octdiv = Lookup(mu_temp1 , Mu_ocdiv) ' 查表獲取對應的分頻值
Goto Mu_plays4
' -------------------- 休止符處理子程序 --------------------
' 功能: 處理"R"指令,靜音指定時長
Mu_plays3:
Gosub Mu_noterd ' 解析休止符時長
Waitms Mu_tempw1 ' 等待指定毫秒(靜音)
Goto Mu_plays4
' -------------------- 字符讀取子程序 --------------------
' 功能: 從當前數據行讀取一個字符并轉換為ASCII碼
Mu_datcnv:
Mu_str = Mid(mu_pldata , Mu_dtposi , 1) ' 從緩沖區提取一個字符
Mu_dtposi = Mu_dtposi + 1 ' 讀取位置后移
Mu_dtlen = Mu_dtlen - 1 ' 剩余長度減1
Mu_temp1 = Asc(mu_str) ' 將字符轉換為ASCII碼
Return
' -------------------- 音符/休止符時長解析子程序 --------------------
' 功能: 從數據中提取音符時值并計算實際時長
Mu_noterd:
Gosub Mu_datcnv ' 讀取時值參數
Mu_noterd1:
Mu_temp4 = Mu_temp1 ' 保存時值代碼
Select Case Mu_temp4
Case &H36 : ' ASCII "6" - 64分音符
Mu_temp3 = 6 ' 右移6位(除以64)
Mu_dtposi = Mu_dtposi + 1 ' 跳過下一個字符
Mu_dtlen = Mu_dtlen - 1
Case &H33 : ' ASCII "3" - 32分音符
Mu_temp3 = 5 ' 右移5位(除以32)
Mu_dtposi = Mu_dtposi + 1
Mu_dtlen = Mu_dtlen - 1
Case &H31 : ' ASCII "1" - 可能是全音符或16分音符
Mu_temp3 = 0 ' 先假設是全音符
If Mu_dtlen <> 0 Then ' 如果還有字符
Gosub Mu_datcnv ' 再讀一個字符
If Mu_temp1 = &H36 Then ' 如果是"6"(16分音符)
Mu_temp3 = 4 ' 右移4位(除以16)
Else ' 否則是真正的全音符
Mu_dtposi = Mu_dtposi - 1 ' 回退指針
Mu_dtlen = Mu_dtlen + 1
End If
End If
Case &H38 : ' ASCII "8" - 8分音符
Mu_temp3 = 3 ' 右移3位(除以8)
Case &H32 : ' ASCII "2" - 2分音符
Mu_temp3 = 1 ' 右移1位(除以2)
Case Else : ' 其他情況(默認) - 4分音符
Mu_temp3 = 2 ' 右移2位(除以4)
End Select
Mu_tempw1 = Mu_tempo ' 取全音符時長
If Mu_temp3 <> 0 Then ' 如果不是全音符
Shift Mu_tempw1 , Right , Mu_temp3 ' 右移計算實際時長
End If
Return
' ==================== 數據表 ====================
' 音階頻率數據表
' 格式:每個音階對應的頻率值(以字WORD類型存儲)
' 排列順序:Ab, A, A#=Bb, B, (占位), C, C#=Db, D, D#=Eb, E, (占位), F, F#=Gb, G, G#
Mu_scale:
Data 33228% , 35200% , 37296% , 39512% , 39512% , 20930% , 22176% , 23496%
Data 24892% , 26372% , 26372% , 27936% , 29600% , 31360% , 33228%
' 八度分頻數據表
' 對應八度1-7的分頻系數,用于將基礎頻率調整到正確的八度
' 值越小,頻率越高(高八度)
Mu_ocdiv:
Data 640% , 320% , 160% , 80% , 40% , 20% , 10%
' ==================== 音樂數據表 ====================
' 音樂代碼語法說明:
' Ox : 改變八度 [1~7] (O4A對應440Hz標準音)
' + / - : 基于當前八度值增減(例如O+表示升一個八度)
' C D E F G A B : 分別對應Do Re Mi Fa Sol La Si
' # : 升高半音
' $ : 降低半音
' 1 2 4 8 16 32 64 : 音符時值(全音符、二分、四分、八分等)
' Rx : 休止符(長度與音符相同,例如R4表示四分休止)
' Z : 結束代碼,標記音樂數據結束
' 曲目1:莫扎特 G大調弦樂小夜曲 第一樂章
Mu_data:
Data 120 ' TEMPO 速度值(每分鐘120拍)
Data "o7"
Data "d4r8o-a8o+d4r8o-a8o+d8o-a8o+d8f#8a4r4"
Data "g4r8e8g4r8e8g8e8c#8e8o-a4o+r4"
Data "d4d4d8f#8e8d8d8c#8c#4c#8e8g8c#8"
Data "e8d8d4d8f#8e8d8d8c#8c#4c#8e8g8c#8"
Data "d8d8d16c#16o-b16o+c#16d8d8f#16e16d16e16"
Data "f#8f#8a16g16f#16g16a4r4z"
' 曲目2:蘇聯國歌《牢不可破的聯盟》- 帶歌詞注釋
Mu_data1:
Data 85 ' TEMPO 莊嚴的進行曲速度(每分鐘85拍)
Data "o4"
Data "r8g8o+c4o-g8a8b4e8e8a4g8f8g4c8c8"
Data "d4d8e8f4f8g8a4b8o+c8d4o-g4"
Data "o+e4d8c8d4o-g8g8o+c4o-b8a8b4e8e8"
Data "a4g8f8g4c8c8o+c4o-b8a8g4g4"
Data "o+e2d8c8o-b8o+c8d4o-g4g2"
Data "o+c4c4o-b8a8g8a8b4e4e4r4"
Data "o+c4o-a8b8o+c4o-a8b8"
Data "o+c4o-a4o+c8f4r8"
Data "f2e8d8c8d8"
Data "e4c8c2"
Data "d2c8o-b8a8b8"
Data "o+c4o-a8a2"
Data "o+c4o-b8a8g4c8c8"
Data "o+c4o-b8a8g4r8g8"
Data "g2a4b4"
Data "o+c1z" ' Z標記音樂結束
' ==================== 程序結束 ====================
$include "../font8x8TT.font" ' 包含8x8字體文件
屏幕錄制 2.zip
(1.01 MB, 下載次數: 0)
2026-4-11 12:49 上傳
點擊文件名下載附件
|