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

鼠标改造计划

               ——基于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 HEARD		2017/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 WHITE				0x00		//白 ----_-000
#define YELLOW      0x01		//黄 ----_-001
#define PURPLE      0x02		//紫 ----_-010
#define RED         0x03		//红 ----_-011
#define CYAN        0x04		//青 ----_-100
#define	GREEN				0x05		//绿 ----_-101
#define BLUE	      0x06		//蓝 ----_-110
#define CLOSE_ALL		0x07		//无 ----_-111

/*    PWM周期调节    */
#define INITCYCLE		1500		//初值
#define STEPCYCLE		100			//步进值

/*    包含头文件    */	
#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_0000
	EA   =   1;
}
/*    外部中断2,3,4 对应 10,11,16  */
void Int2_Left() interrupt 10
{
	INT_CLKO   = 0x8F;			//1110_1111
	Open_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_1111
	Open_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_1111
	Open_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/26
		
		Open_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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值