有段时间没更新了,最近季度总结报告又连着一个国庆,比较忙还望见谅。
前面讲到了如何写代码点亮一个LED灯,在实际使用过程中,除了静态的显示一个LED灯的亮灭,更多的是动态的显示。下面就从动态显示上来说说如何控制LED。
流水灯
如果把LED比作人,那么流水灯可以认为是一群舞蹈演员的表演,而你就像是后台的导演,指挥着各个舞者的动作,通过每个人的表演组合形成如千手观音般的震撼效果。
![2ebbcecb931aa5a43cd9331d9cfa7864.png](https://i-blog.csdnimg.cn/blog_migrate/f2b44fae743f30d6d9f48ba6044962f1.jpeg)
那你既然是导演,就要想好如何显示:是1秒全亮,1秒全暗的闪烁;或是依次点亮的流水;亦或是亮暗交替的跳跃。(头脑中想象着舞台上的舞者在跳舞的情形)
在这个时候,我们就要有点分层的概念:你作为设计这个流水灯方案的人,只要想好每个灯在某个时间点是如何的状态,不需要关心这个状态是如何显示到真正的负载灯上的,也不需要关心这几个灯是在哪几个端口上。这样好处是一旦硬件修改,只要动硬件部分驱动,而不用涉及到应用层的修改,同时逻辑部分会更加清晰明确。
![dcb1755ea4a16f65e81362f563f6a9ba.png](https://i-blog.csdnimg.cn/blog_migrate/caa956620860d965501c0f5a4f57691f.jpeg)
下面进行具体分析:
应用层:
1)首先,假设我们要同时控制8组成的一排个LED灯。在应用层的思维中,我们可以理想化的进行操作,认为这8个LED是在同一个端口,我们可以这样定义一个联合体,这样对8个LED控制的变量会比较省,同时也可以单独控制每个独立的LED状态。
typedef union
{
unsigned char allLEDS;
struct
{
unsigned char led_1 :1;
unsigned char led_2 :1;
unsigned char led_3 :1;
unsigned char led_4 :1;
unsigned char led_5 :1;
unsigned char led_6 :1;
unsigned char led_7 :1;
unsigned char led_8 :1;
}LED;
}LED_TYPE;
LED_TYPE showLED; //应用层的LED变量
2)这个时候我们就可以开始我们的表演了:
情景1:每隔1秒,从左到右,每次点亮一颗灯,如此循环:
从表面分析,我们可以很简单的1秒,操作一个灯点亮,其他灯熄灭。但是从上面说明分析,我们可以用C语言变量的移位操作来进行,会简洁很多。
#define MAX_LED_NUMB 8//定义当前LED个数
unsigned char curShowIndex; //当前要显示LED的序号
......
if (bTime_1S) //1S时基
{
bTime_1S = false;
/*
第1S:*-------
第2S:-*------
第3S:—-*-----
第4S:---*----
第5S:----*---
第6S:-----*--
第7S:------*-
第8S:-------*
第9S:--------
*/
if (curShowIndex < MAX_LED_NUMB)
{
showLED.allLEDS = 0x01<<curShowIndex; //关键语句:根据当前要点亮的序号,每次往前点亮
curShowIndex++;
}
else
{
curShowIndex = 0;
showLED.allLEDS = 0;
}
SetLEDS(showLED); //往中间层设置LED状态
}
....
情景2:每隔1秒,从左到右,依次点亮一颗,全部点亮后熄灭,如此循环(像俄罗斯方块,叠加满8个消掉):
#define MAX_LED_NUMB 8//定义当前LED个数
unsigned char curShowIndex; //当前要显示LED的序号
......
if (bTime_1S) //1S时基
{
bTime_1S = false;
/*
第1S:*-------
第2S:**------
第3S:***-----
第4S:****----
第5S:*****---
第6S:******--
第7S:*******-
第8S:********
第9S:--------
*/
if (curShowIndex < MAX_LED_NUMB)
{
showLED.allLEDS |= 0x01<<curShowIndex; //关键语句:根据当前要点亮的序号,每次往前叠加点亮
curShowIndex++;
}
else
{
curShowIndex = 0;
showLED.allLEDS = 0; //全部熄灭
}
SetLEDS(showLED); //往中间层设置LED状态
}
......
情景3:每隔1秒,间隔亮4颗:
//初始化
showLED.allLEDS = 0x55; //0x55的二进制为0b01010101,既间隔1个亮灭
......
if (bTime_1S) //1S时基
{
bTime_1S = false;
/*
第1S:*-*-*-*-
第2S:-*-*-*-*
*/
showLED.allLEDS = ~showLED.allLEDS;
SetLEDS(showLED); //往中间层设置LED状态
}
......
中间层
因为硬件层没有硬件层想的那么理想,于是需要中间层将应用层理想的状态进行拆分映射。就像电话刚兴起时的接线员,打电话的人只想着我要打给谁,不在乎中间是怎么实现的,于是接线员牵线搭桥,最终实现通话。
![f9356e6abcad1b8c15ea169de35900b2.png](https://i-blog.csdnimg.cn/blog_migrate/2e8a2d4a382dec240e7c2f269f7d8be6.jpeg)
简单粗暴方法如下:
void SetLEDS(LED_TYPE state)
{
SetLED_1_State((STATE_INFO)state.LED.led_1);
SetLED_2_State((STATE_INFO)state.LED.led_2);
SetLED_3_State((STATE_INFO)state.LED.led_3);
SetLED_4_State((STATE_INFO)state.LED.led_4);
SetLED_5_State((STATE_INFO)state.LED.led_5);
SetLED_6_State((STATE_INFO)state.LED.led_6);
SetLED_7_State((STATE_INFO)state.LED.led_7);
SetLED_8_State((STATE_INFO)state.LED.led_8);
}
从上分析,所有的硬件层接口格式都一样,有很多重复性的,所有我们可以建立函数指针数组,用循环设置:
#define MAX_LED_NUMB 8//定义当前LED个数
......
typedef void (*LED_IO_SET)(void); //对硬件层的接口进行类型定义
LED_IO_SET ledIOFUncs[MAX_LED_NUMB] =
{
SetLED_1_State,
SetLED_2_State,
SetLED_3_State,
SetLED_4_State,
SetLED_5_State,
SetLED_6_State,
SetLED_7_State,
SetLED_8_State
};
void SetLEDS(LED_TYPE state)
{
for (unsigned char i = 0; i < MAX_LED_NUMB; i++)
{
ledIOFUncs[i]((STATE_INFO)(state.allLEDS>>i)&0x01); //根据序号,取出赌赢的函数指针,同时取出对应的状态,和上面的粗暴方式对比看
}
}
.......
硬件层
按上一篇文章,我们已经知道如何点亮一颗灯:
void SetLEDState(STATE_INFO State)
{
IO_LED(STATE_ON);
LedState = State;
}
对于可能8个灯的控制需求不一样(如有几个是扫描的,有几个是IO直接驱动的)可以分别写函数设置,如果对于都是相同类型控制的灯,可以将中间层简化,在硬件层做循环。
中间层:
#define MAX_LED_NUMB 8//定义当前LED个数
......
void SetLEDS(LED_TYPE state)
{
for (unsigned char i = 0; i < MAX_LED_NUMB; i++)
{
SetLEDState(i, (STATE_INFO)(state.allLEDS>>i)&0x01); //传入序号和对应的状态
}
}
......
硬件层
//IO口列
IO IO_LEDS[MAX_LED_NUMB] =
{
IO_LED_1,
IO_LED_2,
IO_LED_3,
IO_LED_4,
IO_LED_5,
IO_LED_6,
IO_LED_7,
IO_LED_8
};
void SetLEDState(unsigned char index, STATE_INFO State)
{
IO_LEDS[i](STATE_ON);
LedState[i] = State;
}
流水灯讲的差不多了,想想先发布,后面一节会讲下呼吸灯和矩阵LED扫描显示。