准备做一款遥控音乐盒,可以显示时间,星期,定时,温度,湿度等功能,同时使用串口接个蓝牙就可以和手机通讯了。
时间相关的使用: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
- #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(); // 检测按键
- }
- }
复制代码
|