点亮led灯的个数_我的单片机学习之路(4)-玩转LED(二)

有段时间没更新了,最近季度总结报告又连着一个国庆,比较忙还望见谅。

前面讲到了如何写代码点亮一个LED灯,在实际使用过程中,除了静态的显示一个LED灯的亮灭,更多的是动态的显示。下面就从动态显示上来说说如何控制LED。

流水灯

如果把LED比作人,那么流水灯可以认为是一群舞蹈演员的表演,而你就像是后台的导演,指挥着各个舞者的动作,通过每个人的表演组合形成如千手观音般的震撼效果。

2ebbcecb931aa5a43cd9331d9cfa7864.png

那你既然是导演,就要想好如何显示:是1秒全亮,1秒全暗的闪烁;或是依次点亮的流水;亦或是亮暗交替的跳跃。(头脑中想象着舞台上的舞者在跳舞的情形)

在这个时候,我们就要有点分层的概念:你作为设计这个流水灯方案的人,只要想好每个灯在某个时间点是如何的状态,不需要关心这个状态是如何显示到真正的负载灯上的,也不需要关心这几个灯是在哪几个端口上。这样好处是一旦硬件修改,只要动硬件部分驱动,而不用涉及到应用层的修改,同时逻辑部分会更加清晰明确。

dcb1755ea4a16f65e81362f563f6a9ba.png

下面进行具体分析:

应用层:

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

简单粗暴方法如下:

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扫描显示。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值