找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 1257|回复: 0
打印 上一主题 下一主题
收起左侧

遥控音乐盒制作日志(二) 简单定时器管理 附单片机程序

[复制链接]
跳转到指定楼层
楼主
ID:695961 发表于 2022-5-15 22:49 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
准备做一款遥控音乐盒,可以显示时间,星期,定时,温度,湿度等功能,同时使用串口接个蓝牙就可以和手机通讯了。

时间相关的使用:DS1302

温度和湿度使用:DHT11

显示使用:12232

遥控使用了一款某宝上8块钱包邮买的:2262/2272四路无线遥控套

上期实现了基本的演奏和遥控功能(上一曲,下一曲),详细见论坛的帖子:
http://www.51hei.com/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
  1. #ifndef __TIMER_H__
  2. #define __TIMER_H__

  3. #include "fyexing.h"

  4. #define TIMER_COUNT         6                        // 定时器数量(0-3:btns  4:music  5:lcd)

  5. // 定时器初值(1.25ms, 110592)
  6. #define VAL_L                    0x80                // 设置定时初值(低)
  7. #define VAL_H                    0xFB                // 设置定时初值(高)

  8. typedef void(*TIMERS_CALLBACK)(uint8);        // 定时器回调函数

  9. typedef struct _TIMER
  10. {
  11.         BYTE      id;                // 定时器ID
  12.         BYTE      enabled;        // 是否启动
  13.         uint16    count;        // 定时时间 = count * 1.25ms
  14.         uint16    value;        // 当前时间 = value * 1.25ms
  15.         uint8          tag;                // 定时器附加数据
  16.         //TIMERS_CALLBACK callback;                // 单个定时器回调
  17.         
  18. } TIMER, *PTIMER;

  19. extern TIMERS_CALLBACK timersCallback;        // 回调函数
  20. extern TIMER timers[TIMER_COUNT];                // 定时器数组

  21. PTIMER TimerInit(void *callback);            // 初始化定时器
  22. void TimerRun();                                                // 定时器运行

  23. #endif
复制代码

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

  2. // 定义定时器数组
  3. TIMER timers[TIMER_COUNT];
  4. uint8 timerEnabled = FALSE;                        // 定时器+1的开关
  5. TIMERS_CALLBACK timersCallback;                // 定时器回调函数

  6. // 初始化定时器
  7. PTIMER TimerInit(void *callback)
  8. {
  9.         int i;
  10.         
  11.         // 定时器回调函数
  12.         timersCallback = callback;
  13.         
  14.         // 初始化定时器组
  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;                        // 关闭总中断
  22.         
  23.         TMOD &= 0xF0;        // 设置定时器模式
  24.         TMOD |= 0x01;        // 设置定时器模式
  25.         TL0 = VAL_L;        // 设置定时初值
  26.         TH0 = VAL_H;        // 设置定时初值
  27.         ET0 = 1;                // 允许定时器0中断
  28.         
  29.         EA = 1;                        // 允许总中断
  30.         TR0 = 1;                // 启动定时器0
  31.         
  32.         return &timers;
  33. }

  34. // 运行定时器
  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.                         // 单独的回调
  53. //                        if(timers[i].callback)
  54. //                                timers[i].callback(i);
  55.                         
  56.                         // 全局回调
  57.                         if(timersCallback)
  58.                                 timersCallback(i);
  59.                 }
  60.         }
  61. }

  62. // T0中断处理函数
  63. void tm0_isr() interrupt 1
  64. {
  65.         TL0 = VAL_L;                // 设置定时初值
  66.         TH0 = VAL_H;                // 设置定时初值
  67.         timerEnabled = TRUE;
  68. }
复制代码

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. // 定时器回调
  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;                        // 按下状态
  24.                         else
  25.                                 timers[id].tag = 0;                        // 还原弹起状态
  26.                         
  27.                         timers[id].enabled = FALSE;                // 关闭定时器
  28.                         break;
  29.                         
  30.                 // 音符演奏结束
  31.                 case 4:
  32.                         MusicHandle();                                        // 音乐软中断处理程序
  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.                 // 检测按钮按是否下
  52.                 if(!timers[i].enabled && btnIndex == 0 && timers[i].tag == 0)
  53.                 {
  54.                         timers[i].count = 40;                // 定时50ms
  55.                         timers[i].value = 0;
  56.                         timers[i].tag = 1;                        // 按下状态
  57.                         timers[i].enabled = TRUE;        // 启动定时器
  58.                 }
  59.                
  60.                 // 松开按钮,执行相应的命令
  61.                 if(btnIndex == 1 && timers[i].tag == 2)
  62.                 {
  63.                         switch(i)
  64.                         {
  65.                                 // A按钮
  66.                                 case 0:
  67.                                         PreviousMusic();                // 上一首音乐;
  68.                                         break;
  69.                                 
  70.                                 // B按钮
  71.                                 case 1:
  72.                                         NextMusic();                        // 下一首音乐;
  73.                                         break;
  74.                         }
  75.                         
  76.                         timers[i].tag = 0;                        // 弹起状态
  77.                 }
  78.         }        
  79. }

  80. void main()
  81. {
  82.         TimerInit(timerCallback);        // 初始化定时器0,并初始化软定时器
  83.         Lcd1602Init();                                // 初始化LCD1602
  84.         MusicInit();                                // 初始化音乐
  85.         
  86.         while(1)
  87.         {
  88.                 TimerRun();                // 运行定时器
  89.                 key();                        // 检测按键
  90.         }
  91. }
复制代码


评分

参与人数 1黑币 +50 收起 理由
admin + 50

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏2 分享淘帖 顶1 踩
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表