找回密碼
 立即注冊(cè)

QQ登錄

只需一步,快速開(kāi)始

搜索
查看: 2047|回復(fù): 0
打印 上一主題 下一主題
收起左側(cè)

遙控音樂(lè)盒制作日志(二) 簡(jiǎn)單定時(shí)器管理 附單片機(jī)程序

[復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:695961 發(fā)表于 2022-5-15 22:49 | 只看該作者 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
準(zhǔn)備做一款遙控音樂(lè)盒,可以顯示時(shí)間,星期,定時(shí),溫度,濕度等功能,同時(shí)使用串口接個(gè)藍(lán)牙就可以和手機(jī)通訊了。

時(shí)間相關(guān)的使用:DS1302

溫度和濕度使用:DHT11

顯示使用:12232

遙控使用了一款某寶上8塊錢(qián)包郵買的:2262/2272四路無(wú)線遙控套

上期實(shí)現(xiàn)了基本的演奏和遙控功能(上一曲,下一曲),詳細(xì)見(jiàn)論壇的帖子:
http://www.torrancerestoration.com/bbs/dpj-220369-1.html
上期的主要功能是實(shí)現(xiàn)了曲譜的壓縮(曲譜是從論壇中搜得的,感謝作者提供),歌曲的演奏和遙控的控制。我使用的單片機(jī)有三個(gè)定時(shí)器,其中拿出來(lái)T2定時(shí)器用于演奏音樂(lè),T0定時(shí)器用于控制每個(gè)節(jié)拍的演奏時(shí)間,由于要去抖,每次按鍵還需要軟件延時(shí)50ms,但是我發(fā)現(xiàn),延時(shí)的過(guò)程中,會(huì)出現(xiàn)音樂(lè)變調(diào),后面解釋變調(diào)的原因。T1留作波特率發(fā)生器。
我們大部分和外圍器件的交互都需要延時(shí),少的像12232,只需要延時(shí)1ms就可以。多的如DS1302和DHT11那就得幾十毫秒。后期,要每秒鐘刷新一次時(shí)間,以及溫度和濕度,如果用定時(shí)器的話,肯定不夠用。如果延時(shí)的話,這些外圍設(shè)備的延時(shí)累積起來(lái),這個(gè)時(shí)候要是演奏音樂(lè),肯定會(huì)讓音樂(lè)跑調(diào)(因?yàn)檠訒r(shí)的過(guò)程中,音樂(lè)的演示時(shí)間會(huì)被延時(shí)拉長(zhǎng))

51單片機(jī)不像STM32那樣可以使用官方提供的軟件定時(shí)器代碼,所以就自己動(dòng)手寫(xiě)了一個(gè)簡(jiǎn)單的軟件定時(shí)器,配備上附加的tag字段,讓音樂(lè)演奏和按鈕去抖都使用軟件延時(shí),軟件延時(shí)使用T0定義一個(gè)時(shí)間基時(shí)1.25ms(由于我將來(lái)要用串口通訊,所以使用11.0592的晶振,定為1.25ms的話誤差最,如果使用12M的晶振就可以把時(shí)間基時(shí)定為無(wú)誤差的1ms了),這樣以來(lái),單片機(jī)全程沒(méi)有無(wú)意義的空轉(zhuǎn)延時(shí)函數(shù),所有的延時(shí)全部使用軟件定時(shí)。單片機(jī)全部功能不卡頓。
這樣單片機(jī)先初始化軟件定時(shí)器,設(shè)定定時(shí)時(shí)間,開(kāi)始定時(shí)器,定時(shí)器中斷到來(lái)時(shí),所有定時(shí)器的value加1,判斷哪些定時(shí)器到時(shí)間了,到時(shí)間的執(zhí)行回調(diào)。

定時(shí)器代碼如下:


timer.h
  1. #ifndef __TIMER_H__
  2. #define __TIMER_H__

  3. #include "fyexing.h"

  4. #define TIMER_COUNT         6                        // 定時(shí)器數(shù)量(0-3:btns  4:music  5:lcd)

  5. // 定時(shí)器初值(1.25ms, 110592)
  6. #define VAL_L                    0x80                // 設(shè)置定時(shí)初值(低)
  7. #define VAL_H                    0xFB                // 設(shè)置定時(shí)初值(高)

  8. typedef void(*TIMERS_CALLBACK)(uint8);        // 定時(shí)器回調(diào)函數(shù)

  9. typedef struct _TIMER
  10. {
  11.         BYTE      id;                // 定時(shí)器ID
  12.         BYTE      enabled;        // 是否啟動(dòng)
  13.         uint16    count;        // 定時(shí)時(shí)間 = count * 1.25ms
  14.         uint16    value;        // 當(dāng)前時(shí)間 = value * 1.25ms
  15.         uint8          tag;                // 定時(shí)器附加數(shù)據(jù)
  16.         //TIMERS_CALLBACK callback;                // 單個(gè)定時(shí)器回調(diào)
  17.         
  18. } TIMER, *PTIMER;

  19. extern TIMERS_CALLBACK timersCallback;        // 回調(diào)函數(shù)
  20. extern TIMER timers[TIMER_COUNT];                // 定時(shí)器數(shù)組

  21. PTIMER TimerInit(void *callback);            // 初始化定時(shí)器
  22. void TimerRun();                                                // 定時(shí)器運(yùn)行

  23. #endif
復(fù)制代碼

timer.c
  1. #include "timer.h"

  2. // 定義定時(shí)器數(shù)組
  3. TIMER timers[TIMER_COUNT];
  4. uint8 timerEnabled = FALSE;                        // 定時(shí)器+1的開(kāi)關(guān)
  5. TIMERS_CALLBACK timersCallback;                // 定時(shí)器回調(diào)函數(shù)

  6. // 初始化定時(shí)器
  7. PTIMER TimerInit(void *callback)
  8. {
  9.         int i;
  10.         
  11.         // 定時(shí)器回調(diào)函數(shù)
  12.         timersCallback = callback;
  13.         
  14.         // 初始化定時(shí)器組
  15.         for(i = 0; i < TIMER_COUNT; i++)
  16.         {
  17.                 timers[i].id = i;
  18.         }
  19.         
  20.         // 初始化T0(11.0592, 1.25ms)
  21.         EA = 0;                        // 關(guān)閉總中斷
  22.         
  23.         TMOD &= 0xF0;        // 設(shè)置定時(shí)器模式
  24.         TMOD |= 0x01;        // 設(shè)置定時(shí)器模式
  25.         TL0 = VAL_L;        // 設(shè)置定時(shí)初值
  26.         TH0 = VAL_H;        // 設(shè)置定時(shí)初值
  27.         ET0 = 1;                // 允許定時(shí)器0中斷
  28.         
  29.         EA = 1;                        // 允許總中斷
  30.         TR0 = 1;                // 啟動(dòng)定時(shí)器0
  31.         
  32.         return &timers;
  33. }

  34. // 運(yùn)行定時(shí)器
  35. void TimerRun()
  36. {
  37.         uint8 i;
  38.         if(!timerEnabled)
  39.                 return;
  40.         
  41.         timerEnabled = FALSE;
  42.         for(i = 0; i < TIMER_COUNT; i++)
  43.         {
  44.                 if(!timers[i].enabled)
  45.                         continue;
  46.                
  47.                 timers[i].value++;
  48.                 if(timers[i].enabled && timers[i].value >= timers[i].count)
  49.                 {
  50.                         timers[i].value = 0;
  51.                         
  52.                         // 單獨(dú)的回調(diào)
  53. //                        if(timers[i].callback)
  54. //                                timers[i].callback(i);
  55.                         
  56.                         // 全局回調(diào)
  57.                         if(timersCallback)
  58.                                 timersCallback(i);
  59.                 }
  60.         }
  61. }

  62. // T0中斷處理函數(shù)
  63. void tm0_isr() interrupt 1
  64. {
  65.         TL0 = VAL_L;                // 設(shè)置定時(shí)初值
  66.         TH0 = VAL_H;                // 設(shè)置定時(shí)初值
  67.         timerEnabled = TRUE;
  68. }
復(fù)制代碼

main.c
  1. //#include <intrins.h>
  2. //#include "STC89C5xRC.H"
  3. #include "fyexing.h"
  4. #include "timer.h"
  5. #include "music.h"
  6. #include "lcd1602.h"

  7. // 定時(shí)器回調(diào)
  8. void timerCallback(int8 id)
  9. {
  10.         uint8 btnIndex, keys;
  11.         
  12.         switch(id)
  13.         {
  14.                
  15.                 // 如果50ms后B還按下,則切換紅色的LED
  16.                 case 0:
  17.                 case 1:
  18.                 case 2:
  19.                 case 3:
  20.                         keys = BTNS;
  21.                         btnIndex = (keys >> id) & 0x01;
  22.                         if(btnIndex == 0)
  23.                                 timers[id].tag = 2;                        // 按下?tīng)顟B(tài)
  24.                         else
  25.                                 timers[id].tag = 0;                        // 還原彈起狀態(tài)
  26.                         
  27.                         timers[id].enabled = FALSE;                // 關(guān)閉定時(shí)器
  28.                         break;
  29.                         
  30.                 // 音符演奏結(jié)束
  31.                 case 4:
  32.                         MusicHandle();                                        // 音樂(lè)軟中斷處理程序
  33.                         break;
  34.                
  35.                 // LCD1602
  36.                 case 5:
  37.                         Lcd1602Runting();
  38.                         break;
  39.         }
  40. }

  41. void key()
  42. {
  43.         uint8 i, btnIndex;
  44.         uint8 keys = BTNS;
  45.         
  46.         // 掃描按鍵
  47.         for(i = 0; i < 4; i++)
  48.         {
  49.                 btnIndex = (keys >> i) & 0x01;
  50.                
  51.                 // 檢測(cè)按鈕按是否下
  52.                 if(!timers[i].enabled && btnIndex == 0 && timers[i].tag == 0)
  53.                 {
  54.                         timers[i].count = 40;                // 定時(shí)50ms
  55.                         timers[i].value = 0;
  56.                         timers[i].tag = 1;                        // 按下?tīng)顟B(tài)
  57.                         timers[i].enabled = TRUE;        // 啟動(dòng)定時(shí)器
  58.                 }
  59.                
  60.                 // 松開(kāi)按鈕,執(zhí)行相應(yīng)的命令
  61.                 if(btnIndex == 1 && timers[i].tag == 2)
  62.                 {
  63.                         switch(i)
  64.                         {
  65.                                 // A按鈕
  66.                                 case 0:
  67.                                         PreviousMusic();                // 上一首音樂(lè);
  68.                                         break;
  69.                                 
  70.                                 // B按鈕
  71.                                 case 1:
  72.                                         NextMusic();                        // 下一首音樂(lè);
  73.                                         break;
  74.                         }
  75.                         
  76.                         timers[i].tag = 0;                        // 彈起狀態(tài)
  77.                 }
  78.         }        
  79. }

  80. void main()
  81. {
  82.         TimerInit(timerCallback);        // 初始化定時(shí)器0,并初始化軟定時(shí)器
  83.         Lcd1602Init();                                // 初始化LCD1602
  84.         MusicInit();                                // 初始化音樂(lè)
  85.         
  86.         while(1)
  87.         {
  88.                 TimerRun();                // 運(yùn)行定時(shí)器
  89.                 key();                        // 檢測(cè)按鍵
  90.         }
  91. }
復(fù)制代碼


評(píng)分

參與人數(shù) 1黑幣 +50 收起 理由
admin + 50

查看全部評(píng)分

分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏3 分享淘帖 頂1 踩
回復(fù)

使用道具 舉報(bào)

本版積分規(guī)則

手機(jī)版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表