【51单片机学习笔记】彩色呼吸灯

来源:互联网 发布:阿里云服务器购买 编辑:程序博客网 时间:2024/05/16 19:27

鼠标改造计划

               ——基于STC15F104E的彩色呼吸灯

青岛科技大学 信息科学技术学院 集成162 Listen C

一.任务简介

近来本人的鼠标终于光荣下岗了,出于“主子”对“伙计”的“怜悯之情”,加上近来又恰好发现STC15F104E这么个神奇的单片机,居然只有8个脚,于是主决定恩赐它新的生命活力……好吧不瞎说了,只是单纯的想搞个事情而已。不过,从现实的角度来看,好像确实不错,用尽量小的体积做尽量多的事情吧。也是出于上次那个播放器被这芯片坑了一次不服气,打算换个项目“征服”它。好吧其实结局是又发现了新的问题,后文详述,这里就先留个伏笔,哈哈。

二.任务要求

(一)       改造对象

原希望改造的鼠标是下图这位“功臣”,因为其体积小外围透明,又光荣下岗,故获此“殊誉”。

         但是,在后期改造的过程中,遇上点小麻烦……具体什么情况下文解释,这里为了文体整洁直接说明结果。结果就是,这个鼠标暂时搁置了,换了学长一个暂时不用的鼠标,下图:

         关于这两个鼠标技术上的区别,经实验发现,当按下鼠标按键时,前者小黑(第一个鼠标,下同)产生的是高电平,后者小白(第二个鼠标,下同)产生的是低电平,具体为什么考虑这个问题下文讨论。

(二)       改造思路

首先,先让我们看下STC15F104E的原理图吧!

         由其原理图外加查询数据手册,不难发现,除去VccGnd,还剩6IO,其中除了P3.1,都可以做外部中断使用。关于定时器中断,对不同批次的芯片存在争议,需按实际情况做决定,因为本次改造计划没有用到定时器中断,所以不去深究。

         而关于外部中断,数据手册上说,P3.2INT0)和P3.3INT1)既可做上升沿触发,也可下降沿触发,而剩下的P3.4INT2)、P3.5INT3)、P3.6INT4)只能下降沿触发。

         而关于彩灯,我选用了三灯芯共阳灯泡,下图所示:

这样,根据三基色的原理,理论上我们可以组合出各种颜色。请允许我盗个图。

         由此,我们的目的也就明确了。

(1)      首先,我们需要3IO去组合灯效

(2)      其次,我们需要三个按键来触发中断(鼠标左键、右键、中键)

(3)      然后,我们在中断未触发的情况下,让彩灯不断变换灯效

(4)      最后,产生中断时,灯效改变

当然,为了不那么死板,通过产生中断,我们可以改变灯效变换的频率,相当于是做了一个调节机制。

这样,细心的朋友一定会发现,哎,所有的IO刚好用完,三个下降沿也够用。这也就是为啥我要低电平的原因了。如果要高电平的话,只有2个中断可触发,所以是有限制的。不过不急,实际上我们可以用非门电路改变电平,比如三极管,反相器。反相器用74HC00的话有4路,多一路,但是要两个输入控制一个输出,详情请自行搜索下数据手册,也可用74HC04,六反相器,一输入对应一输出。当然本人手头只有印了74HC0474HC00芯片(不要问我为啥。。。贪便宜的我飘过),而且都是直插的,感觉特别大,所以只好先暂时搁浅下,有空再给小黑手术吧。反正,有小白呢。

顺便把原理图也附上吧!

(三)       改造效果简述

关于具体的灯效,上文有提,这里详述一下。

首先是未触发中断的情况。

这里,我采用了呼吸灯的原理,通过软件PWM,实现让灯效从A色缓缓变为B色,B色缓缓变为C色……G色缓缓变为A色,如此循环。关于颜色的组合,根据上图三基色原理,3种颜色任意组合:

单色:红,绿,蓝

双色:黄,青,紫

三色:百

总共7种颜色。

         当按下鼠标按键时,我让它对应显示单色,目前我的对应原则如下,大家可以随意调换:

         左键:红

         中键:绿

         右键:蓝

当按下右键时再按下左键,我让它亮出自身颜色的其他颜色即蓝的对立黄,此时增加彩灯周期,同理反之减少彩灯周期。为了防止二货的我调乱了,当按下中键时按下任意其他键,我的操作是让周期复位。这样,一个简单的灯效思路就有了。

三.改造与制作

(一)       鼠标

首先将鼠标“大卸八块”,探索内部结构,顺便测试是哪种情况(小黑or小白)

(二)焊接电路

根据原理图准备材料

 

彩灯 x1

STC15F104E x1

220Ω电阻 x3

单片机底座 x1

洞洞板 x0.5(整个太大了就掰开了)

导线若干

 

         然后,根据原理图将它们焊好

         最后,将它与鼠标焊在一起。装好就OK啦。

四.程序设计

最后就是我们的重头戏——程序了。老规矩,先让我们看看文件结构。

 

头文件:

 

STC15F104E.h          芯片配置

USER_Config.h                用户配置

interrupt.h                中断配置

delay.h                       延时配置

LED.h                         彩灯配置

 

程序文件

 

main.c                        主函数文件

it.c                              中断服务文件

delay.c                       延时函数文件

LED.c                          彩灯控制文件

 

         下面分别介绍下各部分的实现

 

USER_Config.h

 

/******************************************USER HEARD2017/6/25*******************************************/#ifndef _USERCONFIG_H_#define _USERCONFIG_H_/*    定义数据类型    */#define uchar unsigned char#define uint unsigned int#define NOP() _nop_()/*    灯效    *//*Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0    -    -    -    -    -   红   绿   蓝低电平0为亮,高电平1为暗*/#define WHITE0x00//白 ----_-000#define YELLOW      0x01//黄 ----_-001#define PURPLE      0x02//紫 ----_-010#define RED         0x03//红 ----_-011#define CYAN        0x04//青 ----_-100#defineGREEN0x05//绿 ----_-101#define BLUE      0x06//蓝 ----_-110#define CLOSE_ALL0x07//无 ----_-111/*    PWM周期调节    */#define INITCYCLE1500//初值#define STEPCYCLE100//步进值/*    包含头文件    */#include "STC15F104E.h"#include <intrins.h>#include "interrupt.h"#include "delay.h"#include "LED.h"/*    彩灯    */sbit LED_Blue  =  P3^3;sbit LED_Green =  P3^2;sbit LED_Red   =  P3^1;/*    按键    */sbit Key_Left  =  P3^4;sbit Key_Mid   =  P3^5;sbit Key_Right =  P3^0;#endif

这里包含了用户所有需要的东西,包括定义数据类型、引用头文件、宏定义、定义引脚功能等。这里仅介绍主要部分

         先说说PWM调节周期吧。对于PWM调节,我仅仅做了周期调节处理。这里我定义了一个频率初始值和一个调节步进值,便于更改。具体这两个值的作用,在LED.c中详细解释。

         对于七种灯效值的由来,大概可以这么理解。首先我将3中颜色点亮与否的值分别存在了一个unsigned char数据中的低3位,二进制中的0表示低电平,而这个LED是共阳的,所以是灌电流模式,这样就产生了一个电势差,故点亮LED。而置1时,两端都为高电平,电势差为0,故灭掉LED。这样,再根据三基色原理,对着颜色取值,就取出了7种标准颜色的值。

         那么问题来了,三灯芯的7色灯只能显示7种颜色吗?我认为,其实不是的。别忘了,我们只是取了标准色,如果说,混合颜色的“亮度”不同,即颜色的深度不同,就可以混合出其他颜色了。如果要这么实现的话,我个人认为有两种方案。一是PWM分别调节三色,使得等效为灯的亮度不同。二我们可以采用DAC,直接给模拟量,调节更加方便。这里我们只要等效循环变换,不需要精确取色,故不做深入处理了。

 

delay.c

普通的延时函数,不过多解释直接上代码

 

/*************************************延时函数库By LZK频率:12MHz*************************************/#include "USER_Config.h"/*    延时x*9us    */void delay_8us(uint x){uint i,j;for(i=0;i<x;i++)for(j=0;j<1;j++);}/*    延时xms    */void delay_ms(uint x){uint i,j;for(i=0;i<x;i++)for(j=0;j<120;j++);}/*    延时    */void delay(uint x){while(x--);}

it.c

中断服务。这里我们用的是三个外部中断,下降沿触发。关于怎么使用这三个中断,这里就需要查询我们的数据手册了。数据手册我一起扔进了压缩包里大家自取好了。

/*---------------------------------------------------中断服务文件By Listen C    2017/6/25参考自官方数据手册----------------------------------------------------*/#include "USER_Config.h"extern uint CYCLE;/*    中断初始化    */void IT_Config(){/*  开启外部中断  */Key_Left  = 1;Key_Mid   = 1;Key_Right = 1;INT_CLKO |= 0x70;// 0111_0000EA   =   1;}/*    外部中断2,3,4 对应 10,11,16  */void Int2_Left() interrupt 10{INT_CLKO   = 0x8F;//1110_1111Open_LED(BLUE);while(!Key_Left){if(!Key_Right)//如果按下鼠标右键{delay_ms(10);//去抖while(!Key_Right)Open_LED(YELLOW);//默认蓝色的对立色为红+绿=黄Open_LED(BLUE);//松开复位颜色if(CYCLE < 65400)CYCLE += STEPCYCLE;}}INT_CLKO  |= 0x70;}void Int3_Mid() interrupt 11{INT_CLKO   = 0x8F;//1101_1111Open_LED(GREEN);while(!Key_Mid){if((!Key_Left)||(!Key_Right))//在按住鼠标中键时判断是否按下鼠标左键或鼠标右键{//是的话,周期复位Open_LED(PURPLE);//默认绿色的对立为蓝+红=紫CYCLE  = INITCYCLE;}Open_LED(GREEN);//松开颜色复位}INT_CLKO  |= 0x70;}void Int2_Right() interrupt 16{INT_CLKO   = 0x8F;//1011_1111Open_LED(RED);while(!Key_Right){if(!Key_Left)//如果按下鼠标左键{delay_ms(10);//去抖while(!Key_Left)Open_LED(CYAN);//默认红色的对立为蓝+绿=青Open_LED(RED);//松开颜色复位if(CYCLE > 200)CYCLE -= STEPCYCLE;}}INT_CLKO  |= 0x70;}

其中要注意的是,我们的思路是这样的。首先,先触发中断,触发中断后,先暂时关掉中断,注意3个中断标志位不同,注释中是实际位,儿为了避免混乱,我全部都置0了,然后亮相应等效提醒进入中断了。然后继续判断,上文提到的非中键的话判断相反键是否按下,这里用了按键去抖,松开有效,随即对应增加或减少PWM周期。中键的判断只需要按下其他任意键并进行复位,故不需要去抖。

当完成这些工作时,我们的操作是松开最初按下的按键,此时我们需要重新打开中断。

 

LED.c

         我们的核心部分到了,话不多说先上代码。

/*--------------------------------------------------------------------彩灯控制文件By Listen C    2017/6/26Open_LED函数说明:为方便调节灯色,本人将灯色选择存在了一个unsigned char变量中通过位运算低1位为蓝色开关低2位为绿色开关低3位为红色开关具体三色混合效果参见USER_Config.h-------------------------------------------------------------------*/#include "USER_Config.h"uint PWM_LOW  =  0;uint CYCLE    =  INITCYCLE;/*    点灯函数    */void Open_LED(uchar x){LED_Blue  =  x&0x01;LED_Green =  (x&0x02)>>1;LED_Red   =  (x&0x04)>>2;}/*    彩灯效果    */void PWM_LED(uchar x,uchar y){for(PWM_LOW = 1;PWM_LOW < CYCLE;PWM_LOW++){Open_LED(x);delay(PWM_LOW);Open_LED(y);delay(CYCLE-PWM_LOW);}delay(CYCLE);}

         首先先让我们详细探索下点灯函数。稍有基础的朋友大概一看就明白了,这里用的是位运算,取某位的值。即,先对对应位进行1位与,其他0位与,以只保留对应位,然后通过右移操作移到最低位,给引脚赋值。

         说句题外话,用位运算存储数据真的非常好用,曾经我比赛时对4方位的红外障碍进行了位运算的存储,将四个方位的状态存在一个数据里,就是跟我们这个任务恰好相反的过程,直接优势就是,在判断过程中变的异常容易,只需要判断数据的值就能知道哪里遇到障碍了,除去了冗杂的if判断,程序上反正是舒服多了,也因此那部分做的效果还是不错的。

         关于PWM调节的问题,就是我们灯效的控制了。如果知道PWM,这里可能就很明白了,不知道的话可以自行查一下,网络的介绍比我这清楚多了。总之就是,在一个固定的周期内,高电平占一定时间,低电平占剩下的时间,这样等效地调节了灯的亮度。然后,如果低电平慢慢增加,那么就等效认为亮度在增加。我们只考虑一种颜色的情况,那么它在缓慢变亮,反过来就缓慢变暗,如此就是呼吸灯的原理。注意全亮之后给一段时间固定,这样更加逼真。而我们的任务是,亮完这个颜色,在它灭掉的时候去亮下一种颜色,故将点灯部分改为了两种灯效。关于这两个灯效,在主函数中来计算,因为涉及到要让这个函数更加通用嘛。如果不去变换灯效,也一样可以调用它,大不了yCLOSE_ALL

 

main.c

         既然核心部分都做好了,剩下的调用就好了。

/*---------------------------------------------------------------基于STC15F104E的鼠标改造方案目的:通过外部中断控制三灯芯小灯通过PWM信号来变换灯效按下鼠标左键时按下鼠标右键,PWM周期增加按下鼠标右键时按下鼠标左键,PWM周期减少按下鼠标中键时按下任意键,PWM周期复位灯效参见it.c的注释为方便调节灯色,本人为灯色选择函数做了优化,详见LED.c  By Listen C    2017/6/25-----------------------------------------------------------------*/#include "STC15F104E.h"#include "USER_Config.h"uchar i = 0;void main(){uchar x,y;Open_LED(CLOSE_ALL);IT_Config();while(1){for(i=0;i<7;i++){x=(i+1)%7;y=i;PWM_LED(x,y);}}}

         注意先要中断初始化,然后7色循环闪烁既可。

 

五.反思与总结

说句良心话,这个作品的Bug还是很大的。我遇到最大的问题是,当按键频率过高时,中断会莫名其妙挂掉了。虽然出了问题应该先考虑自身程序问题,但是我觉得我的中断没有进入这种状态的步骤,况且我在主函数中while里随时开启外部中断都无济于事,于是就上网查了下,果然有网友遇到了相同的问题。有人的解答是初期的该芯片存在Bug,但是后期的修复了。好吧我就暂且认为这锅芯片背吧!

其实,表面上,任务以改造鼠标做依托进行的,但这个灯效的小玩意可以做其他的装饰,比如USB彩灯。恰好又一位挚友过生日,于是亲手又焊了一个给他,礼轻情义重嘛~。不过说句良心话,做USB彩灯的效果,是优于放进鼠标里的。下图为送他的彩灯。



最后,附上共享链接:

链接:http://pan.baidu.com/s/1gf5n0vL 密码:yfpz


STC15F104E数据手册:

链接:http://pan.baidu.com/s/1qYuLNas 密码:a3au

 

原创粉丝点击