标题:
基于STC12C5A60S2的带有掉电记忆功能的音乐盒
[打印本页]
作者:
小码农
时间:
2019-2-12 18:11
标题:
基于STC12C5A60S2的带有掉电记忆功能的音乐盒
晶振值33.1776MHz
功能简介:K16控制暂停与继续
K4 上一曲
K12下一曲
数码管显示当前歌曲序号
具有lcd1602显示功能
应用EEPROM具有掉电记忆
默认演奏上次断电所演奏的歌曲
主函数
#include <stc12c5a60s2.h>
#define Xtal 33177600 //赋晶振值
#define All 4 //歌曲总数
sbit BUZZ = P1^3; //蜂鸣器控制引脚
sbit KEY = P2^4; //按键定义
sbit KEY4 = P2^7;
sbit KEY12 = P2^5;
/*sbit ADDR0 = P2^0; //数码管显示控制
sbit ADDR1 = P2^1;
sbit ADDR2 = P2^2;
sbit ENLED = P1^1; //总线收发器*/
bit KeySta = 1; //当前按键状态
bit KeySta4 = 1;
bit KeySta12 = 1;
bit backup = 1; //默认弹起时时一
bit backup4 = 1;
bit backup12 = 1;
bit cntkey = 0; //判断暂停还是播放
bit breakflage = 0;//用按键改变歌曲标志
unsigned char keybuf = 0xFF;
unsigned char keybuf4 = 0xFF;
unsigned char keybuf12 = 0xFF;
unsigned char Num; //第几首歌
unsigned char sizeofNote;//节拍的多少
void E2WriteByte(unsigned char addr, unsigned char dat);
unsigned char E2ReadByte(unsigned char addr);
void Delay_ms(unsigned int ms);//延时函数
extern void I2CStart();
extern void I2CStop();
extern unsigned char I2CReadNAK();
extern bit I2CWrite(unsigned char dat);
extern void LcdWriteCmd(unsigned char cmd);
extern void LcdWaitReady();
extern void InitLcd1602();
extern void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str);
void lcd1602updata();
void PlayControl(unsigned char Num);//歌曲选择函数
void Play();//演奏函数
//unsigned char code LedChar[] = {0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E};//数码管显示字符转换表
unsigned int code NoteFrequ[] = { //中音1-7低音1-7和高音1-7对应频率列表
523, 587, 659, 698, 784, 880, 988, //中音1-7
1047, 1175, 1319, 1397, 1568, 1760, 1976, //高音1-7
262, 294, 330, 349, 392, 440, 494,300//低音1-7 最后一个纯属凑数
};
unsigned int code NoteReload[] = { //中音1-7和高音1-7对应的定时器重载值
65536 - (Xtal/12) / (523*2), //中音1
65536 - (Xtal/12) / (587*2), //2
65536 - (Xtal/12) / (659*2), //3
65536 - (Xtal/12) / (698*2), //4
65536 - (Xtal/12) / (784*2), //5
65536 - (Xtal/12) / (880*2), //6
65536 - (Xtal/12) / (988*2), //7
65536 - (Xtal/12) / (1047*2), //高音1
65536 - (Xtal/12) / (1175*2), //2
65536 - (Xtal/12) / (1319*2), //3
65536 - (Xtal/12) / (1397*2), //4
65536 - (Xtal/12) / (1568*2), //5
65536 - (Xtal/12) / (1760*2), //6
65536 - (Xtal/12) / (1976*2), //7
65536 - (Xtal/12) / (262*2), //低音1
65536 - (Xtal/12) / (294*2), //2
65536 - (Xtal/12) / (330*2), //3
65536 - (Xtal/12) / (349*2), //4
65536 - (Xtal/12) / (392*2), //5
65536 - (Xtal/12) / (440*2), //6
65536 - (Xtal/12) / (494*2), //7
65536 - (Xtal/12) / (300*2), //纯属凑数
};
unsigned char *SongNote, *SongBeat;//指针
unsigned char code XiaoXingXingNote[] = { //小星星
1, 1, 5, 5, 6, 6, 5, 4, 4, 3, 3, 2, 2, 1,
5, 5, 4, 4, 3, 3, 2, 5, 5, 4, 4, 3, 3, 2 ,
1, 1, 5, 5, 6, 6, 5, 4, 4, 3, 3, 2, 2, 1, 0xff };
unsigned char code XiaoXingXingBeat[] = {
4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 8,
4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 8,
4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 8,};
unsigned char code TwoTigerNote[] = { //两只老虎音符表
1, 2, 3, 1, 1, 2, 3, 1, 3, 4, 5, 3, 4, 5,
5,6, 5,4, 3, 1, 5,6, 5,4, 3, 1, 1, 5, 1, 1, 5, 1,0xff};
unsigned char code TwoTigerBeat[] = { //两只老虎节拍表,4表示一拍,1就是1/4拍,8就是2拍
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 4, 4, 8,
3,1, 3,1, 4, 4, 3,1, 3,1, 4, 4, 4, 4, 8, 4, 4, 8,
};
unsigned char code HappybirthdayNote[] = { //生日快乐歌
5, 5, 6, 5, 8, 7, 22, 5, 5, 6, 5, 9, 8, 5, 5, 12, 10, 8, 7, 6, 11, 11, 10, 8, 9, 8,0xff };
unsigned char code HappybirthdayBeat[] = {
2, 2, 4, 4, 4, 4, 4, 2, 2, 4, 4, 4, 8, 2, 2, 4, 4, 4, 4, 8, 2, 2, 4, 4, 4, 8};
unsigned char code ZhiduanqingchangNote[] = { //纸短情长
//8, 9, 10, 9, 8, 6, 22, 11, 10, 9, 22, 7, 8, 9, 7, 7, 5, 22, 7, 9, 8, 8, 3, 4, 2, 3, 1, 1, 5, 2, //前奏
19, 3, 3, 3, 2, 3, 1, 2, 2, 2, 1, 2, 5, 1, 1, 1, 20, 1, 20, 19, 2, 2, 2, 3, 20, 19, 4, 4, 4, 3, 4, 1, 2, 2, 2, 1, 2, 5,
1, 1, 1, 20, 1, 20, 3, 2, 2, 1, 1, 22, 22, 22, 1, 1, 2, 3, 3, 3, 22, 3, 2, 1, 21, 6, 5, 22, 5, 6, 7, 8, 3, 3, 22, 8, 7, 8,
7, 3, 5, 5, 6, 8, 5, 6, 22, 6, 5, 4,5, 3, 2, 1, 20, 1, 3, 2, 2, 1, 2, 5, 1, 2, 5, 22, 22, 22, 1, 1, 2,
3, 3, 2, 3, 22, 3, 2, 1, 5, 5, 3, 5, 22, 5, 6, 7, 8, 8, 8, 8, 8, 7, 6, 7, 6, 3, 5, 5, 5, 6, 8, 5, 6, 22, 6, 5, 4,
5, 3, 2, 1, 6, 1, 3, 2, 2, 1, 2, 1, 20, 1, 22, 22, 20, 1, 3, 2, 2, 22, 2, 1, 2, 1, 1, 22, 20, 1, 1, 0xff};
unsigned char code ZhiduanqingchangBeat[] = {
//2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 8, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 8, 8, 12, 4, 5, 4, 8, 8, 4, //前奏
8, 2, 2, 2, 2, 2, 5, 2, 2, 2, 2, 2, 5, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 4, 5, 2, 2, 2, 2, 2, 5,
2, 2, 2, 2, 2, 5, 2, 2, 2, 2, 8, 4, 4, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2,
4, 2, 5, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 4, 2, 5, 1, 1, 2, 2, 2, 2, 2, 4, 2, 16, 16,4, 4, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 2, 4, 2, 2, 2, 4, 2, 2, 2, 2, 2,
2, 4, 2, 5, 1, 1, 2, 2, 2, 2, 2, 4, 1, 12, 4, 2, 1, 1, 2, 2, 8, 2, 1, 1, 2, 2, 8, 2, 2, 12, 16};
bit enable = 1; //蜂鸣器发声使能标志
bit tmrflag = 0; //定时器中断完成标志
unsigned char T0RH = 0xFF; //T0重载值的高字节
unsigned char T0RL = 0x00; //T0重载值的低字节
void main()
{
unsigned char dat;
EA = 1; //使能全局中断
TMOD = 0x11; //配置T0,T1工作在模式1
TH0 = T0RH;
TL0 = T0RL;
ET0 = 1; //使能T0中断
ET1 = 1; //使能T1中断
TR1 = 1; //启动T1
// ENLED = 0x00; //使能总线收发器
// ADDR2=1; ADDR1=0; ADDR0=1;//选择最右边的数码管
P23 = 0; //启用KEY
InitLcd1602();
dat = E2ReadByte(0x01); //读取指定地址上的一个字节
if(dat>All||dat<=0)
Num = 1;
else
Num = dat;
while (1)
{
//P0 = LedChar[Num]; //数码管显示当前歌曲序号
LcdWriteCmd(0x01); //清屏
lcd1602updata();
PlayControl(Num);
if(breakflage == 1)
breakflage = 0;
else
Delay_ms(500);
}
}
void PlayControl(unsigned char Num)
{
E2WriteByte(0x01, Num); //再写回到对应的地址上
switch(Num)
{
case 4:SongNote = XiaoXingXingNote; SongBeat = XiaoXingXingBeat; sizeofNote = sizeof(XiaoXingXingNote); Play();break;
case 2:SongNote = TwoTigerNote; SongBeat =TwoTigerBeat; sizeofNote = sizeof(TwoTigerNote); Play();break;
case 3:SongNote = HappybirthdayNote; SongBeat =HappybirthdayBeat; sizeofNote = sizeof(HappybirthdayNote); Play();break;
case 1:SongNote = ZhiduanqingchangNote;SongBeat =ZhiduanqingchangBeat;sizeofNote = sizeof(ZhiduanqingchangNote);Play();break;
default:break;
}
}
void lcd1602updata()
{
switch(Num)
{
case 1:LcdShowStr(4,0,"1.zhiduanqingchang");break;
case 2:LcdShowStr(4,0,"2.liangzhilaohu");break;
case 3:LcdShowStr(4,0,"3.zhunishengrikuaile");break;
case 4:LcdShowStr(4,0,"4.xiaoxingxing");break;
default:break;
}
}
/* 演奏函数 */
void Play()//演奏函数
{
unsigned char beat; //当前节拍索引
unsigned char note; //当前节拍对应的音符
unsigned int time = 0; //当前节拍计时
unsigned int beatTime = 0; //当前节拍总时间
unsigned int soundTime = 0; //当前节拍需发声时间
for (beat=0; beat<sizeofNote; ) //用节拍索引作为循环变量
{
if(breakflage == 1)
break;
while (!tmrflag); //每次定时器中断完成后,检测并处理节拍
tmrflag = 0;
if (time == 0) //当前节拍播完则启动一个新节拍
{
if(breakflage == 1)
break;
note = SongNote[beat] - 1;
if(note == 0xfe)
{
if(Num == All)
Num = 1;
else
Num++;
break;
}
T0RH = NoteReload[note] >> 8;
T0RL = NoteReload[note];
beatTime = (SongBeat[beat] * NoteFrequ[note]) >> 2;//计算节拍总时间,右移2位相当于除4,移位代替除法可以加快执行速度
soundTime = beatTime - (beatTime >> 2);//计算发声时间,
enable = 1; //指示蜂鸣器开始发声
if (note == 21)//简谱的0 等待
enable =0;
time++;
}
else //当前节拍未播完则处理当前节拍
{
if (time >= beatTime) //当前持续时间到达节拍总时间时归零,
{ //并递增节拍索引,以准备启动新节拍
time = 0;
beat++;
}
else //当前持续时间未达到总时间时,
{
time++; //累加时间计数
if (time == soundTime) //到达发声时间后,指示关闭蜂鸣器,
{ //插入0.25*总时间的静音间隔,
enable = 0; //用以区分连续的两个节拍
}
}
}
}
}
void Delay_ms(unsigned int ms)
{
for(;ms > 0;ms--)
{
unsigned char i, j;
i = 33;
j = 66;
do
{
while (--j);
} while (--i);
}
}
unsigned char E2ReadByte(unsigned char addr)
{
unsigned char dat;
I2CStart();
I2CWrite(0x51<<1);//发送E2PROM地址
I2CWrite(addr);//要读取的地址
I2CStart(); //重新发送起始信号
I2CWrite((0x51<<1) |0x01);
dat = I2CReadNAK();
I2CStop();
return dat;
}
void E2WriteByte(unsigned char addr, unsigned char dat)
{
I2CStart();
I2CWrite(0x51<<1);//发送E2PROM地址
I2CWrite(addr);//要写入的地址
I2CWrite(dat);//要写入的数据
I2CStop();
}
/*控制蜂鸣器发声 */
void InterruptTimer0() interrupt 1
{
TH0 = T0RH; //重新加载重载值
TL0 = T0RL;
tmrflag = 1;
if (enable) //使能时反转蜂鸣器控制电平
BUZZ = ~BUZZ;
else //未使能时关闭蜂鸣器
BUZZ = 1;
}
/*按键扫描*/
void InterruptTimer1() interrupt 3 //按键部分参看之前的帖子 带有计次功能的秒表
{
TH1 = 0xC9;
TL1 = 0xEA;
keybuf = (keybuf<<1) | KEY;
if (keybuf == 0x00)
KeySta = 0;
else if (keybuf == 0xFF)
KeySta = 1;
else{}
keybuf4 = (keybuf4<<1) | KEY4;
if (keybuf4 == 0x00)
KeySta4 = 0;
else if (keybuf4 == 0xFF)
KeySta4 = 1;
else{}
keybuf12= (keybuf12<<1) | KEY12;
if (keybuf12 == 0x00)
KeySta12 = 0;
else if (keybuf12 == 0xFF)
KeySta12 = 1;
else{}
if (KeySta != backup)
{
if (backup == 0)
{
cntkey=~cntkey;
TR0 = cntkey;
}
backup = KeySta;
}
if (KeySta4 != backup4)
{
if(backup4 ==0)
{
breakflage = 1;
if(Num <All)
Num++;
else if(Num == All)
Num =1;
LcdWriteCmd(0x01); //清屏
lcd1602updata();
//P0 = LedChar[Num];
}
backup4 = KeySta4;
}
if (KeySta12 != backup12)
{
if(backup12 ==0)
{
breakflage = 1;
if(Num >1)
Num--;
else if(Num == 1)
Num =All;
LcdWriteCmd(0x01); //清屏
lcd1602updata();
//P0 = LedChar[Num];
}
backup12 = KeySta12;
}
}
复制代码
LCD1602函数
#include <STC12C5A60S2.h>
#include <intrins.h>
#define LCD1602_DB P0
sbit LCD1602_RS = P2^0;
sbit LCD1602_RW = P2^1;
sbit LCD1602_E = P1^2;
# pragma optimize(4, SPEED)
void delayus(unsigned int n)
{
extern void _nop_(void);
register unsigned char i = n, j = (n>>8);
_nop_(); _nop_(); _nop_();
if ((--i) | j)
{
do
{
_nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_();
if (0xFF == (i--)) j--; else {_nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_();};
} while (i | j);
}
}
void delayms(unsigned int n) {while (n--) delayus(1000);}
/* 等待液晶准备好 */
void LcdWaitReady()
{
unsigned char sta;
LCD1602_DB = 0xFF;
LCD1602_RS = 0;
LCD1602_RW = 1;
do {
LCD1602_E = 1;
sta = LCD1602_DB; //读取状态字
LCD1602_E = 0;
} while (sta & 0x80); //bit7等于1表示液晶正忙,重复检测直到其等于0为止
}
/* 向LCD1602液晶写入一字节命令,cmd-待写入命令值 */
void LcdWriteCmd(unsigned char cmd)
{
LcdWaitReady();
LCD1602_RS = 0;
LCD1602_RW = 0;
LCD1602_DB = cmd;
delayus(1);
LCD1602_E = 1; delayus(1);
LCD1602_E = 0; delayms(2);
}
void LcdWriteDat(unsigned char dat)
{
LcdWaitReady();
LCD1602_RS = 1;
LCD1602_RW = 0;
LCD1602_DB = dat; delayus(1);
LCD1602_E = 1; delayus(1);
LCD1602_E = 0; delayms(2);
}
/* 设置显示RAM起始地址,亦即光标位置,(x,y)-对应屏幕上的字符坐标 */
void LcdSetCursor(unsigned char x, unsigned char y)
{
unsigned char addr;
if (y == 0) //由输入的屏幕坐标计算显示RAM的地址
addr = 0x00 + x; //第一行字符地址从0x00起始
else
addr = 0x40 + x; //第二行字符地址从0x40起始
LcdWriteCmd(addr | 0x80); //设置RAM地址
}
/* 在液晶上显示字符串,(x,y)-对应屏幕上的起始坐标,str-字符串指针 */
void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str)
{
bit flag = 0;
unsigned char Num = 0;
LcdSetCursor(x, y); //设置起始地址
while (*str != '\0') //连续写入字符串数据,直到检测到结束符
{
LcdWriteDat(*str++);
Num ++;
if(Num >=12)
{
flag = 1;
break;
}
}
if(flag == 1)
{
LcdSetCursor(0, 1);
while (*str != '\0') //连续写入字符串数据,直到检测到结束符
{
LcdWriteDat(*str++);
}
}
}
/* 初始化1602液晶 */
void InitLcd1602()
{
LcdWriteCmd(0x38); //16*2显示,5*7点阵,8位数据接口
LcdWriteCmd(0x0C); //显示器开,光标关闭
LcdWriteCmd(0x06); //文字不动,地址自动+1
LcdWriteCmd(0x01); //清屏
}
复制代码
作者:
admin
时间:
2019-2-13 00:43
本帖需要重新编辑补全电路原理图,源码,详细说明与图片即可获得100+黑币(帖子下方有编辑按钮)
作者:
saya0769
时间:
2019-3-29 20:11
下面还有吗??
作者:
快乐眼泪
时间:
2019-11-22 21:53
saya0769 发表于 2019-3-29 20:11
下面还有吗??
掉电记忆程序怎么写?
欢迎光临 (http://www.51hei.com/bbs/)
Powered by Discuz! X3.1