蜂鸣器放歌程序用到两个定时器,分别用于精准控制频率和时间。
贺卡音乐芯片唯一的制作方法,可加入其他功能,音乐数据可随意修改。
可直接移植到其他芯片中,在中断外的程序会严重影响到软件延时。
单片机型号:STC15F104E或STC15L104E或其他8脚STC芯片。
晶振频率:24Mhz
硬件连接:
红色到P3.3
公共极直接到P3.2
绿色到P3.1
蓝色到P3.0
蜂鸣器连接P3.5(不同型号芯片可能会有不同,根据寄存器决定)
版本更新历史:
1.修正为逐渐导通扫描LED,防止3v芯片不接电阻,多个LED同时导通而导致颜色显示不正常。
2.添加了颜色渐变功能。
3.P3.0~P3.3口可直接连接4脚全彩LED,可设置共阴共阳。
注意:
片内必须包含控制IO口输出频率的特殊功能寄存器,否则将无法输出频率。
晶振频率过高会导致低频信号不能鸣叫,同时耗电量增加,看门狗更容易跑飞,
过低则会导致时间频率误差加大。
源码:
#include <reg51.h>
#include<intrins.h>
#include <stdlib.h>
#include <stdio.h>
//#include <eeprom.h>
#define FOSC 24000000L //晶振频率
#define public 1 //共阳为1,共阴为0
unsigned int T1MS=65536-FOSC/1000; //计算定时器计时1毫秒需要的时间
sbit io=P3^2; //公共极
sbit io_blue=P3^0; //蓝色LED通道
sbit io_green=P3^1;//绿色LED通道
sbit io_red=P3^3;//红色LED通道
sbit led=P3^4;
//***********************
sfr P3M0 = 0xB1; // 定义P3M1寄存器
sfr P3M1 = 0xB2; // 定义P3M0寄存器
sfr eeprom_data = 0xC2; //EEPROM数据地址寄存器
sfr eeprom_addrh = 0xC3; //EEPROM高8位地址寄存器
sfr eeprom_addrl = 0xC4; //EEPROM低8位地址寄存器
sfr eeprom_cmd = 0xC5; //EEPROM模式操作寄存器
sfr eeprom_trig = 0xC6; //EEPROM命令有效寄存器
sfr eeprom_contr = 0xC7; //EEPROM命令有效寄存器
sfr CLK_DIV = 0x97;
sfr AUXR = 0x8e; //辅助特殊功能寄存器
sfr INT_CLKO = 0x8f; //唤醒和时钟输出功能寄存器
sbit T1CLKO = P3^5; //蜂鸣器连接VCC和P3.5
bit bit_blue=1;
bit bit_green=1;
bit bit_red=1;
unsigned int p=0; //延迟毫秒替减,为0则切换频率
unsigned char m=0;//当前音乐数据指向的指针
unsigned int code md[212]={ //音乐数据,包含频率和持续时间的数据。
0,580,600,300,600,100,900,200,1200,350,900,170,1200,200,1500,170,1600,260,1500,260,1180,180,1350,350,0,360,600,250,600,100,900,170,1200,360,900,170,1200,180,1500,170,1600,260,1500,260,1200,170,1330,200,0,150,1500,240,1330,100,1200,350,1200,270,1330,80,1500,170,2000,180,2000,175,1800,170,1600,170,1500,180,1330,170,1600,180,1500,180,1200,170,900,350,1000,350,1000,250,1100,100,1200,180,1500,350,1200,180,1340,170,1500,180,1600,170,1670,170,1800,190,1340,150,900,150,750,200,600,300,600,100,900,200,1200,350,900,170,1200,200,1500,170,1600,260,1500,260,1180,180,1350,350,0,360,600,250,600,100,900,170,1200,360,900,170,1200,180,1500,170,1600,260,1500,260,1200,170,1330,200,0,150,1500,260,1330,90,1200,350,1200,260,1330,90,1500,170,2000,350,1800,170,1600,170,1500,180,1330,170,1600,170,1500,180,1200,170,900,350,1000,350,1000,260,1100,90,1200,170,1500,340,1200,180,1330,170,1200,170,1120,180,1330,170,1200,350,0,250
};
void InitTimer0(void){ //初始化1ms中断定时器
TMOD = 0x01;
TL0 = T1MS; //设置计数器低8位
TH0 = T1MS >> 8; //设置计数器高8位
EA = 1;
ET0 = 1;
TR0 = 1;
}
void set_hz(unsigned int hz){ //设置蜂鸣器频率
if(0 < (int)(65536-FOSC/2/hz)) { //频率计算溢出
hz=65535; //设置频率为65535Hz
}
TMOD = 0x00; //设置定时器为模式1(16位自动重装载)
TMOD &= ~0x40; //C/T1=0, 对内部时钟进行时钟输出
TL1 = (65536-FOSC/2/hz); //初始化计时值
TH1 = (65536-FOSC/2/hz) >> 8;
INT_CLKO = 0x02;
TR1 = 1; //开始计数
}
void eeprom_init(){ //EEPROM初始化程序
eeprom_contr = 0x83; //把第7位置1允许EEPROM可以读写
eeprom_cmd = 0x00; //待机模式无ISP操作
eeprom_addrh = 0x00;
eeprom_addrl = 0x00;
}
unsigned char eeprom_read_dat(unsigned char addr_h,unsigned char addr_l){ //读取EEPROM数据
unsigned char e_dat = 0x00;
eeprom_cmd = 0x01; //模式选择为读EEPROM
eeprom_addrh = addr_h;
eeprom_addrl = addr_l;
eeprom_trig = 0x5A; //使命令有效
eeprom_trig = 0xA5; //使命令有效
e_dat = eeprom_data;
return e_dat;
}
void eeprom_write_dat(unsigned char addr_h,unsigned char addr_l,unsigned char w_dat){ //写入EEPROM数据
eeprom_cmd = 0x02; //模式选择为写EEPROM
eeprom_addrh = addr_h;
eeprom_addrl = addr_l;
eeprom_data = w_dat;
eeprom_trig = 0x5A; //使命令有效
eeprom_trig = 0xA5; //使命令有效
}
void eeprom_erase_dat(unsigned char addr_h,unsigned char addr_l){ //擦除EEPROM数据
eeprom_cmd = 0x03; //模式选择为擦除EEPROM
eeprom_addrh = addr_h;
eeprom_addrl = addr_l;
eeprom_trig = 0x5A; //使命令有效
eeprom_trig = 0xA5; //使命令有效
}
unsigned char delay_led=0; //LED延时
unsigned char srand_add=0; //随机数种子(累加)
unsigned char red_size=0; //当前红色LED亮度
unsigned char green_size=0;//当前绿色LED亮度
unsigned char blue_size=0;//当前蓝色LED亮度
unsigned char new_red_size=0; //到达的红色LED亮度
unsigned char new_green_size=0;//到达的绿色LED亮度
unsigned char new_blue_size=0;//到达的蓝色LED亮度
unsigned int delay_rand=0; //随机延时
void set_led_color(unsigned char red,unsigned char green,unsigned char blue){//设置LED颜色(3路不同占空比方波)
unsigned char input_red,input_green,input_blue;
unsigned char rgb_switch=1; //RGB切换开关,1是红色,2是绿色,3是蓝色。
unsigned char while_delay=0xFF; //该变量循环替减,变量等于0后退出循环。
unsigned char red_delay=input_red; //红色LED占空比
unsigned char green_delay=input_green; //绿色LED占空比
unsigned char blue_delay=input_blue; //蓝色LED占空比
unsigned char not_red_delay=0xFF-input_red; //红色LED占空比【位取反】
unsigned char not_green_delay=0xFF-input_green; //绿色LED占空比【位取反】
unsigned char not_blue_delay=0xFF-input_blue; //蓝色LED占空比【位取反】
bit red_on=1;bit green_on=1;bit blue_on=1; //LED亮灭状态,1灭0亮。
bit not_red=1;bit not_green=1; bit not_blue=1; //是否处理某颜色通道 1处理0不处理
if(public == 1){ //共阳极
input_red=0xFF-red; //红色位取反
input_green=0xFF-green; //绿色位取反
input_blue=0xFF-blue; //蓝色位取反
} else { //共阴级
input_red=red; //红色位赋值
input_green=green; //绿色位赋值
input_blue=blue; //蓝色位赋值
}
if(red==0x00){ //红色亮度等于0
not_red=0; //不处理红色通道
bit_red=public; //红色亮灭等于公共极
}
if(red==0xFF){ //红色亮度等于255
not_red=0; //不处理红色通道
bit_red=!public; //红色亮灭等于取反公共极
}
if(green==0x00){ //绿色亮度等于0
not_green=0; //不处理绿色通道
bit_green=public; //绿色亮灭等于公共极
}
if(green==0xFF){ //绿色亮度等于255
not_green=0; //不处理绿色通道
bit_green=!public; //绿色亮灭等于取反公共极
}
if(blue==0x00){ //蓝色亮度等于0
not_blue=0; //不处理蓝色通道
bit_blue=public; //蓝色亮灭等于公共极
}
if(blue==0xFF){ //蓝色亮度等于255
not_blue=0; //不处理蓝色通道
bit_blue=!public; //蓝色亮灭等于取反公共极
}
while( while_delay != 0x00 && not_red_delay + red_delay + not_green_delay + green_delay + not_blue_delay + blue_delay != 0x00){ //所有替减变量大于或等于0
while_delay--; //循环变量替减
if(bit_red==!public){ //红色LED缓存区与公共极相反
io_red=!public; //红色LED的I/O接口等于取反公共极,可以构成回路,点亮LED。
io_green=public;//绿色LED的I/O接口等于公共极,无法构成回路,熄灭LED。
io_blue=public; //蓝色LED的I/O接口等于公共极,无法构成回路,熄灭LED。
//delay10us(); //延时10微秒
}
if(bit_green==!public){ //绿色LED缓存区与公共极相反
io_red=public; //红色LED的I/O接口等于公共极,无法构成回路,熄灭LED。
io_green=!public; //绿色LED的I/O接口等于取反公共极,可以构成回路,点亮LED。
io_blue=public; //蓝色LED的I/O接口等于公共极,无法构成回路,熄灭LED。
//delay10us(); //延时10微秒
}
if(bit_blue==!public){ //蓝色LED缓存区与公共极相反
io_red=public; //红色LED的I/O接口等于公共极,无法构成回路,熄灭LED。
io_green=public; //绿色LED的I/O接口等于公共极,无法构成回路,熄灭LED。
io_blue=!public; //蓝色LED的I/O接口等于取反公共极,可以构成回路,点亮LED。
//delay10us(); //延时10微秒
}
if(rgb_switch == 1 && not_red == 1) { //处理红色通道
if(red_on == 0){
bit_red=0; //将LED亮灭状态放入缓存区。
not_red_delay--; //红色LED反占空比减1
if(not_red_delay == 0) red_on=1; //红色LED亮灭状态放入缓存区
}
if(red_on == 1){
bit_red=1; //将LED亮灭状态放入缓存区。
red_delay--; //红色LED占空比减1
if(red_delay == 0) red_on=0; //红色LED亮灭状态放入缓存区
}
rgb_switch++; //切换到绿色通道
} else {
rgb_switch++; //切换到绿色通道
not_red_delay--;
red_delay--;
}
if(rgb_switch == 2 && not_green == 1){ //处理绿色通道
if(green_on == 0){
bit_green=0; //将LED亮灭状态放入缓存区。
not_green_delay--; //绿色LED反占空比减1
if(not_green_delay == 0) green_on=1; //绿色LED亮灭状态放入缓存区
}
if(green_on == 1){
bit_green=1; //将LED亮灭状态放入缓存区。
green_delay--; //绿色LED占空比减1
if(green_delay == 0) green_on=0; //绿色LED亮灭状态放入缓存区
}
rgb_switch++; //切换到蓝色通道
} else {
rgb_switch++; //切换到蓝色通道
not_green_delay--;
green_delay--;
}
if(rgb_switch == 3 && not_blue == 1){ //处理蓝色通道
if(blue_on == 0){
bit_blue=0; //将LED亮灭状态放入缓存区。
not_blue_delay--; //蓝色LED反占空比减1
if(not_blue_delay == 0) blue_on=1; //蓝色LED亮灭状态放入缓存区
}
if(blue_on == 1){
bit_blue=1; //将LED亮灭状态放入缓存区。
blue_delay--; //蓝色LED占空比减1
if(blue_delay == 0) blue_on=0; //蓝色LED亮灭状态放入缓存区
}
rgb_switch=1; //切换红色通道
} else {
rgb_switch=1; //切换红色通道
not_blue_delay--;
blue_delay--;
}
}
}
void main(){ //主函数
AUXR |= 0xC0;
InitTimer0();
eeprom_init(); //EEPROM初始化
srand_add=eeprom_read_dat(0x00,0x01);//从EEPROM中读取随机随种子
eeprom_erase_dat(0x00,0x01);//从EEPROM中擦除数据
srand_add++; //随机数种子累加1个
eeprom_write_dat(0x00,0x01,srand_add); //将新种子写入EEPROM中
srand(srand_add); //初始化随机数种子,用于每次芯片上电都会产生不重复的颜色。
delay_led=0; //LED延时计数器清零
red_size=0x00; //当前红色值为0
green_size=0x00; //当前绿色值为0
blue_size=0x00; //当前蓝色值为0
if(public==1){ //如果LED为共阳则 (注:修改IO口位置需要修改这里。)
P3M1 = 0x04; //定义P3.2口为强上拉输出,提供更多电流以驱动LED彩灯。
} else { //共阴则
P3M1 = 0x0B; //定义P3.0,P3,1,P3,3口为强上拉输出,提供更多电流以驱动LED彩灯。
}
io=public; //公共极赋值,否则LED就亮不了。
while(1) { //无限循环
new_red_size=rand();//红色亮度取随机数
new_green_size=rand();//蓝色亮度取随机数
new_blue_size=rand();//绿色亮度取随机数
while(new_red_size != red_size && new_green_size != green_size && new_blue_size != blue_size){ //当前颜色不等于随机到达的颜色则循环
if(red_size<new_red_size) { //当前红色亮度小于到达的红色亮度
red_size++; //红色亮度相加
} else { //否则
red_size--; //红色亮度相减
}
if(green_size<new_green_size) { //当前绿色亮度小于到达的绿色亮度
green_size++; //绿色亮度相加
} else { //否则
green_size--; //绿色亮度相减
}
if(blue_size<new_blue_size) { //当前蓝色亮度小于到达的蓝色色亮度
blue_size++; //蓝色亮度相加
} else { //否则
blue_size--; //蓝色亮度相减
}
delay_led=rand()&0xF+0xF; //设置替减次数
while(delay_led--){ //计数循环替减
set_led_color(red_size,green_size,blue_size); //设置输出LED颜色
}
}
delay_led=rand()&0x7F+0x7F; //持续执行0~127个下面的指令。
while(delay_led--){ //渐变完毕有1段时间保持颜色。
set_led_color(red_size,green_size,blue_size); //设置输出LED颜色
}
}
}
void Timer0Interrupt(void) interrupt 1{ //1ms 定时器中断程序
TH0 = 0x0A2;
TL0 = 0x40;
if(p==0){ //延迟时间减到0
p=md[m+1]; //设置延迟时间
set_hz(md[m]); //设置鸣叫频率
m+=2; //指针加2
if(m>=212) m=0; //指针超过212则清零避免取出其他胡乱的数据
} else {
p--; //延迟时间减1
}
}
|