找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 7865|回复: 42
收起左侧

DIY全彩灯随机颜色渐变程序 1677万色输出 蜂鸣器放歌 2合1 可同时工作

  [复制链接]
ID:57657 发表于 2016-7-7 11:44 | 显示全部楼层 |阅读模式
蜂鸣器放歌程序用到两个定时器,分别用于精准控制频率和时间。
贺卡音乐芯片唯一的制作方法,可加入其他功能,音乐数据可随意修改。
可直接移植到其他芯片中,在中断外的程序会严重影响到软件延时。
单片机型号: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
               
        }
}





评分

参与人数 1黑币 +100 收起 理由
admin + 100 共享资料的黑币奖励!

查看全部评分

回复

使用道具 举报

ID:85607 发表于 2016-7-7 14:31 | 显示全部楼层
好东西~
回复

使用道具 举报

ID:74143 发表于 2016-7-7 15:23 | 显示全部楼层
不错,学习了。
回复

使用道具 举报

ID:74143 发表于 2016-7-7 15:24 | 显示全部楼层
不错,好东西。
回复

使用道具 举报

ID:129577 发表于 2016-7-7 17:19 来自手机 | 显示全部楼层
挺好的
回复

使用道具 举报

ID:64089 发表于 2016-7-8 13:27 | 显示全部楼层
有点意思,不错!
回复

使用道具 举报

ID:91165 发表于 2016-7-8 13:34 | 显示全部楼层
想看看这个
回复

使用道具 举报

ID:100826 发表于 2016-7-8 14:48 | 显示全部楼层
学习  谢谢分享............
回复

使用道具 举报

ID:129742 发表于 2016-7-8 20:43 | 显示全部楼层
看看,好东西
回复

使用道具 举报

ID:102293 发表于 2016-7-8 23:33 | 显示全部楼层
这个不错,学习学习!谢谢分享
回复

使用道具 举报

ID:93151 发表于 2016-7-13 20:41 | 显示全部楼层
正在学习,看下
回复

使用道具 举报

ID:75461 发表于 2016-7-14 23:39 | 显示全部楼层
好东西,看看
回复

使用道具 举报

ID:87738 发表于 2016-7-16 22:49 | 显示全部楼层
不错!
回复

使用道具 举报

ID:57149 发表于 2016-7-17 08:55 | 显示全部楼层
这个应该不错。
回复

使用道具 举报

ID:130771 发表于 2016-7-17 10:45 来自手机 | 显示全部楼层
好好好
回复

使用道具 举报

ID:79544 发表于 2016-7-17 11:45 | 显示全部楼层
看看学习一下
回复

使用道具 举报

ID:91780 发表于 2016-7-20 14:47 | 显示全部楼层
好,非常棒,人才哈
回复

使用道具 举报

ID:75002 发表于 2016-7-20 15:57 | 显示全部楼层
好东西~
回复

使用道具 举报

ID:62929 发表于 2016-7-20 21:37 来自手机 | 显示全部楼层
好东西!学习啦!
回复

使用道具 举报

ID:73463 发表于 2016-7-21 08:20 | 显示全部楼层
好东西
回复

使用道具 举报

ID:134545 发表于 2016-7-22 05:33 | 显示全部楼层
蜂鸣器放歌, 有意思
回复

使用道具 举报

ID:99527 发表于 2016-7-25 00:03 | 显示全部楼层
好东西吧
回复

使用道具 举报

ID:130816 发表于 2016-7-25 06:25 | 显示全部楼层
天天在学 就是没长进,还是年轻好啊!
回复

使用道具 举报

ID:134860 发表于 2016-7-26 22:20 | 显示全部楼层
#在这里快速回复# 这个不错,学习学习!谢谢分享
回复

使用道具 举报

ID:72399 发表于 2016-7-27 22:06 | 显示全部楼层
什么东西
回复

使用道具 举报

ID:64913 发表于 2016-7-28 23:10 | 显示全部楼层
这个要看看,学习学习
回复

使用道具 举报

ID:64913 发表于 2016-7-28 23:22 | 显示全部楼层
代码好规范啊
回复

使用道具 举报

ID:137251 发表于 2016-8-21 18:12 | 显示全部楼层
我也要
回复

使用道具 举报

ID:137220 发表于 2016-8-21 22:25 | 显示全部楼层
向楼主学习
回复

使用道具 举报

ID:3212 发表于 2016-8-22 06:16 | 显示全部楼层
学习了
回复

使用道具 举报

ID:137531 发表于 2016-8-24 23:06 来自手机 | 显示全部楼层
好强大,我是菜鸟,正在发奋中
回复

使用道具 举报

ID:123732 发表于 2016-8-25 08:41 | 显示全部楼层
学习一下
回复

使用道具 举报

ID:92231 发表于 2016-8-31 00:39 来自手机 | 显示全部楼层
音乐与光色同时运行,学习了
回复

使用道具 举报

ID:22995 发表于 2016-8-31 08:43 | 显示全部楼层
学习了,支持楼主的精神
回复

使用道具 举报

ID:138037 发表于 2016-8-31 12:25 来自手机 | 显示全部楼层
看看,应该是好东西
回复

使用道具 举报

ID:138379 发表于 2016-9-3 22:47 来自手机 | 显示全部楼层
学习学习 嘿嘿
回复

使用道具 举报

ID:127965 发表于 2016-9-4 10:13 | 显示全部楼层
谢谢你的分享
回复

使用道具 举报

ID:138421 发表于 2016-9-4 15:21 | 显示全部楼层
自己尝试写过,效果不好。学习下高手怎么写的
回复

使用道具 举报

ID:136460 发表于 2016-9-12 21:56 | 显示全部楼层
楼主好人,谢谢分享
回复

使用道具 举报

ID:140489 发表于 2016-9-28 09:15 | 显示全部楼层
很好的资料,谢谢分享
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表