标题:
遥控音乐盒制作日志(二) 简单定时器管理 附单片机程序
[打印本页]
作者:
chinayanhui
时间:
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
#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(); // 检测按键
}
}
复制代码
欢迎光临 (http://www.51hei.com/bbs/)
Powered by Discuz! X3.1