1,简介
在产品开发中,经常会碰到一些LED不同闪烁效果的功能,在这里如果用作像是指示灯(WiFi或者蓝牙连接的指示灯)或者少量LED灯珠闪烁效果可以参考本节,彻底帮大家解决这个烦恼。
2,实现原理
我们思考一下,其实可以解决的方案有很多,就是先思考输出的波形,把这些波形进行转换为数据,因为不管是单片机还是任何处理器,最终处理的都是数据。
假设我们要实现一个LED灯闪烁的周期是1s。
我们用数组的形式表示出来{1,500,0,500,1,500,0,500,循环或者是单次的标志} 1就代表是高电平,0就代表低电平。循环或者单次的标志可以用最大的两个数表示,假设我们数组是两个字节,我们可用0xFFFF代表循环,0xFFFE代表单次。
最终我们配合定时器来进行处理
3实现代码
LedMsgInput(LED1,LED_LIGHT_100MS,1);
LedMsgInput(BUZ,LED_DARK,1);
从函数的调用作为入口,我们这个函数只需要调用一个函数,然后传入三个参数,分别是设备的类型type选择你要控制的设备,在这里我们可以举一反三其实有多种的设备都可以用这种方法进行控制LED灯或者是蜂鸣器,下一个参数是命令cmd,最后一个参数是是否要清除队列的参数,立即执行当前命令。
这些参数我们都可以用枚举来定义一个类型。
#define LED1_PORT GPIOA
#define LED1_PIN GPIO_Pin_1
#define BUZ_PORT GPIOB
#define BUZ_PIN GPIO_Pin_0
#define LED_EFFECT_END 0xFFFE//单次
#define LED_EFFECT_AGN 0xFFFF//重复
typedef enum
{
LED1,//0
BUZ,//1
LED_SUM /*这边一个技巧,因为枚举类型是从0开始数的,我们每次定义一个枚举类型都在最后加一个XX_SUM。*/
}LED_TYPEDEF;//设备类型
typedef enum
{
LED_DARK,
LED_LIGHT,
LED_LIGHT_100MS,
LED_BLINK1,
LED_BLINK2,
LED_BLINK3,
LED_BLINK4,
}LED_EFFECT_TEPEDEF;//命令
最终也是通过这些命令来查看赋值到哪个数组,为了后期维护,数组和枚举类型必须一一对应。
unsigned short LedTimer[LED_SUM];
unsigned short *pLed[LED_SUM];/*指针数组*/
unsigned char LedLoadFlag[LED_SUM];
unsigned short Led_Dark[] = {0,10,LED_EFFECT_END};
unsigned short Led_Light[] = {1,10,LED_EFFECT_END};
unsigned short Led_Light100ms[] = {1,10,0,10,LED_EFFECT_END};
unsigned short Led_Blink1[] = {1,10,0,10,LED_EFFECT_AGN,2};
unsigned short Led_Blink2[] = {1,10,0,10,1,10,0,10,1,10,0,200,LED_EFFECT_AGN,6};
unsigned short Led_Blink3[] = {1,30,0,30,LED_EFFECT_AGN,2};
unsigned short Led_Blink4[] = {1,50,0,50,LED_EFFECT_AGN,2};
定义一个四个字节的队列数组,数组刚好就是设备类型的大小
Queue4 LedCmdBuff[LED_SUM]; //LED信箱
void (*hal_LedDrive[LED_SUM])(unsigned char) = { hal_Led1Drive,
hal_BuzDrive,
};
void LedMsgInput(unsigned char type,LED_EFFECT_TEPEDEF cmd,unsigned char clr)
{
unsigned char bLedCMD;
if(type >= LED_SUM)//检查设备类型是否合法
{
return;
}
bLedCMD = cmd;
if(clr)/*第三个参数判断是否要马上进行打断*/
{
QueueEmpty(LedCmdBuff[type]);/*清空队列*/
LedLoadFlag[type] = 0;/*空闲*/
}
QueueDataIn(LedCmdBuff[type],&bLedCMD,1);/*将这个命令的地址直接丢进我们的信箱当中*/
}
将命令丢到队列当中后,我们开始处理这些数据。下面这个函数是一个任务,开启系统定时器,将这个任务没10ms进行一次执行。系统就会调度这个函数。
void hal_LedProc(void)
{
unsigned char i;
unsigned char cmd;
for(i=0; i<LED_SUM; i++)/*将所有设备遍历一遍*/
{
if((QueueDataLen(LedCmdBuff[i])>0) && (LedLoadFlag[i]==0))/*检测队列的长度,判读是否有数据,判断是否处于空闲状态*/
{
QueueDataOut(LedCmdBuff[i], &cmd);/*将数据进行出列,赋值给cmd*/
LedLoadFlag[i] = 1;/*这时候设备就变成忙碌状态*/
switch(cmd)
{
case LED_DARK:/*判断命令*/
//unsigned short Led_Dark[] = {0,10,LED_EFFECT_END};
/*我们可以分析一下
pled[i]=&Led_Dark[0]
LedTimer[i] = Led_Dark[1] */
pLed[i] = (unsigned short *)Led_Dark;/*如果成立会将对应命令的数组的首地址赋给这个指针数组*/
LedTimer[i] = *(pLed[i]+1);/*电平持续的时间就是这个指针数组中存放的地址+1然后就行取值*/
break;
case LED_LIGHT:
pLed[i] = (unsigned short *)Led_Light;
LedTimer[i] = *(pLed[i]+1);
break;
case LED_LIGHT_100MS:
pLed[i] = (unsigned short *)Led_Light100ms;
LedTimer[i] = *(pLed[i]+1);
break;
case LED_BLINK1:
pLed[i] = (unsigned short *)Led_Blink1;
LedTimer[i] = *(pLed[i]+1);
break;
case LED_BLINK2:
pLed[i] = (unsigned short *)Led_Blink2;
LedTimer[i] = *(pLed[i]+1);
break;
case LED_BLINK3:
pLed[i] = (unsigned short *)Led_Blink3;
LedTimer[i] = *(pLed[i]+1);
break;
case LED_BLINK4:
pLed[i] = (unsigned short *)Led_Blink4;
LedTimer[i] = *(pLed[i]+1);
break;
}
}
}
}
最终的处理函数我们放到定时器中断里面去处理,每10ms进行处理一次,所以说上面命令数组中的10代表100ms
static void hal_LedHandle(void)
{
unsigned char i;
for(i=0; i<LED_SUM; i++)
{
if(LedTimer[i])
{
LedTimer[i]--;
}
if(!LedTimer[i])
{
//定时时间到
//Led_Light[] = {1,10,LED_EFFECT_END};
if(*(pLed[i]+2) == LED_EFFECT_END)
{
LedLoadFlag[i] = 0;
}else
{
pLed[i] += 2;
if(*pLed[i] == LED_EFFECT_AGN)
{
//Led_Blink1[] = {1,10,0,10,LED_EFFECT_AGN,2};
pLed[i] = pLed[i] - (*(pLed[i]+1) * 2);
}
LedTimer[i] = *(pLed[i]+1);
}
}
hal_LedDrive[i](*pLed[i]);
}
hal_ResetTimer(T_LED,T_STA_START);
}
但最开始要先对所有状态位进行初始化,如何实现消息队列,后面会单独出一篇文章
void hal_LedInit(void)
{
unsigned char i;
hal_ledConfig();
hal_CreatTimer(T_LED,hal_LedHandle,200,T_STA_START);
for(i=0; i<LED_SUM; i++)
{
LedLoadFlag[i] = 0;
pLed[i] = (unsigned short *)Led_Dark;
LedTimer[i] = *(pLed[i]+1);
QueueEmpty(LedCmdBuff[i]);
}
LedMsgInput(LED1,LED_LIGHT_100MS,1);
LedMsgInput(BUZ,LED_DARK,1);
}