|
- /************************
- DIY全彩渐变灯随机颜色渐变程序 蜂鸣器放歌 2合1 可同时工作
- 蜂鸣器放歌程序用到两个定时器,分别用于精准控制频率和时间。
- 贺卡音乐芯片唯一的制作方法,可加入其他功能,音乐数据可随意修改。
- 可直接移植到其他芯片中,在中断外的程序会严重影响到软件延时。
- 单片机型号:STC15F104E或STC15L104E或其他8脚STC芯片。
- 晶振频率:12Mhz
- 硬件连接:
- 红色到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>
- unsigned char rb=0;
- unsigned char rc=0;
- unsigned char rd=0;
- unsigned char re=0;
- unsigned char rf=0;
- unsigned char rg=0;
- unsigned char rh=0;
- unsigned char ri=0;
- #define FOSC 12000000L //晶振频率
- #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 key=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;
- bit j=0;
- unsigned char t=0;
- 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
- };
- unsigned char rand(){ //计算随机数 算法
- rb=(rc+3)*3;rc=(rd-3)*3;rd=(re+3)*3;re=(rf-3)*3;
- rf=(rg+3)*3;rg=(rh-3)*3;rh=(ri+3)*3;ri=(rb-3)*3;
- if(rb > 127) rc = rc^127;
- if(rc < 127) rd = rd^127;
- if(rd > 127) re = re^127;
- if(re < 127) rf = rf^127;
- if(rf > 127) rg = rg^127;
- if(rg < 127) rh = rh^127;
- if(rh > 127) ri = ri^127;
- if(ri < 127) rb = rb^127;
- return rb^rc^rd^re^rf^rg^rh^ri;
- }
- void srand(unsigned char i){
- rb=rand()^i;
- rc=rand()^i;
- rd=rand()^i;
- re=rand()^i;
- rf=rand()^i;
- rg=rand()^i;
- rh=rand()^i;
- ri=rand()^i;
- }
- 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=32768; //设置频率为32768Hz
- }
- TMOD = 0x00; //设置定时器为模式1(16位自动重装载)
- TMOD &= ~0x40; //C/T1=0, 对内部时钟进行时钟输出
- TL1 = (65536-FOSC/2/hz); //初始化计时值
- TH1 = (65536-FOSC/2/hz) >> 8;
- 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;
- INT_CLKO |= 0x02;
- INT_CLKO |= 0x10;
- 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--){ //计数循环替减
- g:
- set_led_color(red_size,green_size,blue_size); //设置输出LED颜色
-
- if(j == 1){
- new_red_size=rand();
- new_green_size=rand();
- new_blue_size=rand();
- red_size=rand();
- green_size=rand();
- blue_size=rand();
- j=0;
- }
- }
- }
-
- delay_led=rand()&0x7F+0x7F; //持续执行0~127个下面的指令。
- while(delay_led--){ //渐变完毕有1段时间保持颜色。
- set_led_color(red_size,green_size,blue_size); //设置输出LED颜色
-
- if(j == 1)goto g;
- }
-
-
-
-
-
- }
- }
- void Timer0Interrupt(void) interrupt 1{ //1ms 定时器中断程序
- TL0 = T1MS; //设置计数器低8位
- TH0 = T1MS >> 8; //设置计数器高8位
- if(t>0){
- t--;
- INT_CLKO &= 0xEF;
- } else {
- INT_CLKO |= 0x10;
- }
- if(p==0){ //延迟时间减到0
- p=md[m+1]; //设置延迟时间
- set_hz(md[m]); //设置鸣叫频率
- m+=2; //指针加2
- if(m>=212) m=0; //指针超过212则清零避免取出其他胡乱的数据
- } else {
- p--; //延迟时间减1
-
- }
- }
- void exint2() interrupt 10 //INT2中断入口 P3.2
- {
- if(key == 0){
- if(j==0 && t == 0){
- j=1;
- t=0xFF;
- }
- }
- }
复制代码
|
|