/******************************************
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
#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
/*************************************
延时函数库 By LZK
频率:12MHz
*************************************/
#include "USER_Config.h"
/* 延时x*9us */
void delay_8us(uint x)
{
uint i,j;
for(i=0;i
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); //默认蓝 {MOD}的对立 {MOD}为红+绿=黄
Open_LED(BLUE); //松开复位颜 {MOD}
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); //默认绿 {MOD}的对立为蓝+红=紫
CYCLE = INITCYCLE;
}
Open_LED(GREEN); //松开颜 {MOD}复位
}
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); //默认红 {MOD}的对立为蓝+绿=青
Open_LED(RED); //松开颜 {MOD}复位
if(CYCLE > 200)
CYCLE -= STEPCYCLE;
}
}
INT_CLKO |= 0x70;
}
其中要注意的是,我们的思路是这样的。首先,先触发中断,触发中断后,先暂时关掉中断,注意3个中断标志位不同,注释中是实际位,儿为了避免混乱,我全部都置0了,然后亮相应等效提醒进入中断了。然后继续判断,上文提到的非中键的话判断相反键是否按下,这里用了按键去抖,松开有效,随即对应增加或减少PWM周期。中键的判断只需要按下其他任意键并进行复位,故不需要去抖。
当完成这些工作时,我们的操作是松开最初按下的按键,此时我们需要重新打开中断。
LED.c
我们的核心部分到了,话不多说先上代码。
/*--------------------------------------------------------------------
彩灯控制文件
By Listen C 2017/6/26
Open_LED函数说明:
为方便调节灯 {MOD},本人将灯 {MOD}选择存在了一个unsigned char变量中
通过位运算
低1位为蓝 {MOD}开关
低2位为绿 {MOD}开关
低3位为红 {MOD}开关
具体三 {MOD}混合效果参见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,这里可能就很明白了,不知道的话可以自行查一下,网络的介绍比我这清楚多了。总之就是,在一个固定的周期内,高电平占一定时间,低电平占剩下的时间,这样等效地调节了灯的亮度。然后,如果低电平慢慢增加,那么就等效认为亮度在增加。我们只考虑一种颜 {MOD}的情况,那么它在缓慢变亮,反过来就缓慢变暗,如此就是呼吸灯的原理。注意全亮之后给一段时间固定,这样更加逼真。而我们的任务是,亮完这个颜 {MOD},在它灭掉的时候去亮下一种颜 {MOD},故将点灯部分改为了两种灯效。关于这两个灯效,在主函数中来计算,因为涉及到要让这个函数更加通用嘛。如果不去变换灯效,也一样可以调用它,大不了y取CLOSE_ALL。
main.c
既然核心部分都做好了,剩下的调用就好了。
/*---------------------------------------------------------------
基于STC15F104E的鼠标改造方案
目的:通过外部中断控制三灯芯小灯
通过PWM信号来变换灯效
按下鼠标左键时按下鼠标右键,PWM周期增加
按下鼠标右键时按下鼠标左键,PWM周期减少
按下鼠标中键时按下任意键,PWM周期复位
灯效参见it.c的注释
为方便调节灯 {MOD},本人为灯 {MOD}选择函数做了优化,详见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 {MOD}循环闪烁既可。
五.反思与总结
说句良心话,这个作品的Bug还是很大的。我遇到最大的问题是,当按键频率过高时,中断会莫名其妙挂掉了。虽然出了问题应该先考虑自身程序问题,但是我觉得我的中断没有进入这种状态的步骤,况且我在主函数中while里随时开启外部中断都无济于事,于是就上网查了下,果然有网友遇到了相同的问题。有人的解答是初期的该芯片存在Bug,但是后期的修复了。好吧我就暂且认为这锅芯片背吧!
其实,表面上,任务以改造鼠标做依托进行的,但这个灯效的小玩意可以做其他的装饰,比如USB彩灯。恰好又一位挚友过生日,于是亲手又焊了一个给他,礼轻情义重嘛~。不过说句良心话,做USB彩灯的效果,是优于放进鼠标里的。下图为送他的彩灯。

最后,附上共享链接:
链接:http://pan.baidu.com/s/1gf5n0vL 密码:yfpz
STC15F104E数据手册:
链接:http://pan.baidu.com/s/1qYuLNas 密码:a3au