準備做一款遙控音樂盒,可以顯示時間,星期,定時,溫度,濕度等功能,同時使用串口接個藍牙就可以和手機通訊了。
時間相關的使用:DS1302
溫度和濕度使用:DHT11
顯示使用:12232
遙控使用了一款某寶上8塊錢包郵買的:2262/2272四路無線遙控套
上期實現了基本的演奏和遙控功能(上一曲,下一曲),詳細見論壇的帖子:
http://www.zg4o1577.cn/bbs/dpj-220369-1.html
上期的主要功能是實現了曲譜的壓縮(曲譜是從論壇中搜得的,感謝作者提供),歌曲的演奏和遙控的控制。我使用的單片機有三個定時器,其中拿出來T2定時器用于演奏音樂,T0定時器用于控制每個節拍的演奏時間,由于要去抖,每次按鍵還需要軟件延時50ms,但是我發現,延時的過程中,會出現音樂變調,后面解釋變調的原因。T1留作波特率發生器。
我們大部分和外圍器件的交互都需要延時,少的像12232,只需要延時1ms就可以。多的如DS1302和DHT11那就得幾十毫秒。后期,要每秒鐘刷新一次時間,以及溫度和濕度,如果用定時器的話,肯定不夠用。如果延時的話,這些外圍設備的延時累積起來,這個時候要是演奏音樂,肯定會讓音樂跑調(因為延時的過程中,音樂的演示時間會被延時拉長)
51單片機不像STM32那樣可以使用官方提供的軟件定時器代碼,所以就自己動手寫了一個簡單的軟件定時器,配備上附加的tag字段,讓音樂演奏和按鈕去抖都使用軟件延時,軟件延時使用T0定義一個時間基時1.25ms(由于我將來要用串口通訊,所以使用11.0592的晶振,定為1.25ms的話誤差最,如果使用12M的晶振就可以把時間基時定為無誤差的1ms了),這樣以來,單片機全程沒有無意義的空轉延時函數,所有的延時全部使用軟件定時。單片機全部功能不卡頓。
這樣單片機先初始化軟件定時器,設定定時時間,開始定時器,定時器中斷到來時,所有定時器的value加1,判斷哪些定時器到時間了,到時間的執行回調。
定時器代碼如下:
timer.h
- #ifndef __TIMER_H__
- #define __TIMER_H__
- #include "fyexing.h"
- #define TIMER_COUNT 6 // 定時器數量(0-3:btns 4:music 5:lcd)
- // 定時器初值(1.25ms, 110592)
- #define VAL_L 0x80 // 設置定時初值(低)
- #define VAL_H 0xFB // 設置定時初值(高)
- typedef void(*TIMERS_CALLBACK)(uint8); // 定時器回調函數
- typedef struct _TIMER
- {
- BYTE id; // 定時器ID
- BYTE enabled; // 是否啟動
- uint16 count; // 定時時間 = count * 1.25ms
- uint16 value; // 當前時間 = value * 1.25ms
- uint8 tag; // 定時器附加數據
- //TIMERS_CALLBACK callback; // 單個定時器回調
-
- } TIMER, *PTIMER;
- extern TIMERS_CALLBACK timersCallback; // 回調函數
- extern TIMER timers[TIMER_COUNT]; // 定時器數組
- PTIMER TimerInit(void *callback); // 初始化定時器
- void TimerRun(); // 定時器運行
- #endif
復制代碼
timer.c
- #include "timer.h"
- // 定義定時器數組
- TIMER timers[TIMER_COUNT];
- uint8 timerEnabled = FALSE; // 定時器+1的開關
- TIMERS_CALLBACK timersCallback; // 定時器回調函數
- // 初始化定時器
- PTIMER TimerInit(void *callback)
- {
- int i;
-
- // 定時器回調函數
- timersCallback = callback;
-
- // 初始化定時器組
- for(i = 0; i < TIMER_COUNT; i++)
- {
- timers[i].id = i;
- }
-
- // 初始化T0(11.0592, 1.25ms)
- EA = 0; // 關閉總中斷
-
- TMOD &= 0xF0; // 設置定時器模式
- TMOD |= 0x01; // 設置定時器模式
- TL0 = VAL_L; // 設置定時初值
- TH0 = VAL_H; // 設置定時初值
- ET0 = 1; // 允許定時器0中斷
-
- EA = 1; // 允許總中斷
- TR0 = 1; // 啟動定時器0
-
- return &timers;
- }
- // 運行定時器
- void TimerRun()
- {
- uint8 i;
- if(!timerEnabled)
- return;
-
- timerEnabled = FALSE;
- for(i = 0; i < TIMER_COUNT; i++)
- {
- if(!timers[i].enabled)
- continue;
-
- timers[i].value++;
- if(timers[i].enabled && timers[i].value >= timers[i].count)
- {
- timers[i].value = 0;
-
- // 單獨的回調
- // if(timers[i].callback)
- // timers[i].callback(i);
-
- // 全局回調
- if(timersCallback)
- timersCallback(i);
- }
- }
- }
- // T0中斷處理函數
- void tm0_isr() interrupt 1
- {
- TL0 = VAL_L; // 設置定時初值
- TH0 = VAL_H; // 設置定時初值
- timerEnabled = TRUE;
- }
復制代碼
main.c
- //#include <intrins.h>
- //#include "STC89C5xRC.H"
- #include "fyexing.h"
- #include "timer.h"
- #include "music.h"
- #include "lcd1602.h"
- // 定時器回調
- void timerCallback(int8 id)
- {
- uint8 btnIndex, keys;
-
- switch(id)
- {
-
- // 如果50ms后B還按下,則切換紅色的LED
- case 0:
- case 1:
- case 2:
- case 3:
- keys = BTNS;
- btnIndex = (keys >> id) & 0x01;
- if(btnIndex == 0)
- timers[id].tag = 2; // 按下狀態
- else
- timers[id].tag = 0; // 還原彈起狀態
-
- timers[id].enabled = FALSE; // 關閉定時器
- break;
-
- // 音符演奏結束
- case 4:
- MusicHandle(); // 音樂軟中斷處理程序
- break;
-
- // LCD1602
- case 5:
- Lcd1602Runting();
- break;
- }
- }
- void key()
- {
- uint8 i, btnIndex;
- uint8 keys = BTNS;
-
- // 掃描按鍵
- for(i = 0; i < 4; i++)
- {
- btnIndex = (keys >> i) & 0x01;
-
- // 檢測按鈕按是否下
- if(!timers[i].enabled && btnIndex == 0 && timers[i].tag == 0)
- {
- timers[i].count = 40; // 定時50ms
- timers[i].value = 0;
- timers[i].tag = 1; // 按下狀態
- timers[i].enabled = TRUE; // 啟動定時器
- }
-
- // 松開按鈕,執行相應的命令
- if(btnIndex == 1 && timers[i].tag == 2)
- {
- switch(i)
- {
- // A按鈕
- case 0:
- PreviousMusic(); // 上一首音樂;
- break;
-
- // B按鈕
- case 1:
- NextMusic(); // 下一首音樂;
- break;
- }
-
- timers[i].tag = 0; // 彈起狀態
- }
- }
- }
- void main()
- {
- TimerInit(timerCallback); // 初始化定時器0,并初始化軟定時器
- Lcd1602Init(); // 初始化LCD1602
- MusicInit(); // 初始化音樂
-
- while(1)
- {
- TimerRun(); // 運行定時器
- key(); // 檢測按鍵
- }
- }
復制代碼
|