yikuang 发表于 2015-8-22 11:35:06

发一个实用的LED或蜂鸣器、振动马达等的控制模块

最近有个项目用到多个LED,并以不同的闪烁方式提示多种工作状态。和一个小振动马达, 以不同的振动方式,提示不同的工作状态。
这个模块同样支持控制蜂鸣器。
这个模块支持的功能:(假设控制LED)可设置闪烁周期,周期内亮灭的时间,闪烁次数, 常亮和熄灭。
像LED这类共享资源,要注意多个任务对它访问的问题。

代码如下:
Indicator.h
#ifndef __INDICATER_H_
#define __INDICATER_H_

#include "MacroAndConst.h"


#define LED_ON(__LED)do { \
                                             __LED.chWidth = 0; \
                                             __LED.chPeriod = 0; \
                                             __LED.chBlinkCnt = 0; \
                                           } while (false)
                                          
#define LED_OFF(__LED) do { \
                                                   __LED.chWidth = 0xFF; \
                                             __LED.chPeriod = 0; \
                                             __LED.chBlinkCnt = 0; \
                     } while (false)

#define LED_BLINKING(__LED, __WIDTH, __PERIOD)do { \
                                                                                                  __LED.chWidth = __WIDTH; \
                                                                                                __LED.chPeriod = __PERIOD; \
                                                                                                __LED.chBlinkCnt = 0; \
                                                                            } while (false)

#define LED_BLINK_TIMES(__LED, __WIDTH, __PERIOD, __TIMES)do { \
                                                                                                                        __LED.chWidth = __WIDTH; \
                                                                                                                    __LED.chPeriod = __PERIOD; \
                                                                                                                    __LED.chBlinkCnt = __TIMES; \
                                                                                                } while (false)

#define MOTOR_SHAKE_TIMES(__MOTOR, __WIDTH, __PERIOD, __TIMES)do { \
                                                                                                                        __MOTOR.chWidth = __WIDTH; \
                                                                                                                      __MOTOR.chPeriod = __PERIOD; \
                                                                                                                      __MOTOR.chBlinkCnt = __TIMES; \
                                                                                                  } while (false)

typedef void (*pfn_indic_t)(void);

typedef struct {
        uint8_t chCounter;
        pfn_indic_t indic_on;
        pfn_indic_t indic_off;
} indic_ctrl_t;

typedef struct {
        uint8_t chWidth;
        uint8_t chPeriod;
        uint8_t chBlinkCnt;

        indic_ctrl_t tIndic;
} indic_set_t;

//extern void indic_io_init();

void indic_init(indic_set_t *ptIndic, pfn_indic_t pfn_indic_on, pfn_indic_t pfn_indic_off);
bool indic_task(indic_set_t *ptIndic);

#endif

Indicator.c
#include "indicator.h"



void indic_init(indic_set_t *ptIndic, pfn_indic_t pfn_indic_on, pfn_indic_t pfn_indic_off)
{
        if (NULL == ptIndic) {
                return ;
        }

        ptIndic->chWidth = 0xFF;
        ptIndic->chPeriod = 0;
        ptIndic->chBlinkCnt = 0;
       
        ptIndic->tIndic.chCounter = 0;
        ptIndic->tIndic.indic_on = pfn_indic_on;
        ptIndic->tIndic.indic_off = pfn_indic_off;
       
        //indic_io_init();
        (*ptIndic->tIndic.indic_off)();
}



bool indic_task(indic_set_t *ptIndic)
{
        if (NULL == ptIndic) {
                return false;
        }
       
        if (ptIndic->chPeriod >= ptIndic->chWidth) {
                if (ptIndic->tIndic.chCounter <= ptIndic->chWidth) {
                        (*ptIndic->tIndic.indic_on)();
                } else {
                        (*ptIndic->tIndic.indic_off)();
                }
               
                if (++ ptIndic->tIndic.chCounter >= ptIndic->chPeriod) {
                        ptIndic->tIndic.chCounter = 0;
                        if (ptIndic->chBlinkCnt > 0) {
                                if (0 == (-- ptIndic->chBlinkCnt)) {
                                        ptIndic->chWidth = 0xFF;
                                        ptIndic->chPeriod = 0;
                                        ptIndic->chBlinkCnt = 0;

                                        return true;
                                }
                        }
                }
        } else {
                ptIndic->chBlinkCnt = 0;
                ptIndic->tIndic.chCounter = 0;
                (*ptIndic->tIndic.indic_off)();
        }

        return false;
}

程序外部,需用户提供最基本的IO初始化,高低电平输出函数等,如:
void led_io_init(void)
{
        LED_RED_OUT();
        LED_RED_SET();
}

void led_red_on(void)
{
        LED_RED_CLR();
}

void led_red_off(void)
{
        LED_RED_SET();
}

......

一个简单的例子:
indic_set_t g_tRedLed, g_tGreenLed, g_tBlueLed;

void main(void)
{
        ... ...
       
        indic_init(&g_tRedLed, led_red_on, led_red_off);
        indic_init(&g_tGreenLed, led_green_on, led_green_off);
        indic_init(&g_tBlueLed, led_blue_on, led_blue_off);

        LED_ON(g_tRedLed);
        LED_BLINKING(g_tGreenLed, 50, 100);
        LED_BLINK_TIMES(g_tBlueLed, 30, 70, 10);

        indic_init(&g_tMotor, motor_on, motor_off);
        MOTOR_SHAKE_TIMES(g_tMotor, 25, 50, 4);
       
        while(1) {
                if (event_get(&g_tEvent, EVENT_10MS_OVERFLOW)) {

                        ... ...
                       
                        indic_task(&g_tRedLed);
                        indic_task(&g_tGreenLed);
                        indic_task(&g_tBlueLed);
                       
                        indic_task(&g_tMotor);
                       
                        event_delete(&g_tEvent, EVENT_10MS_OVERFLOW);
                }
        }
}

发现不足之处,欢迎提出,谢谢!


four_zhg 发表于 2015-8-22 15:43:37

不错,谢楼主共享,先看看

蓝蓝的恋 发表于 2015-8-22 16:02:41

好像有一个跟这样类似的帖子{:smile:}

ponder2077 发表于 2015-8-22 18:09:25

如果波形是不规则的呢,例如:LED 高100ms,低30ms,高80ms,低,20ms,高20ms,低70ms,......没有规则, 很多LED控制类和马达控制类的都有这种波形,你的程序还适用吗?

yikuang 发表于 2015-8-22 20:34:47

ponder2077 发表于 2015-8-22 18:09
如果波形是不规则的呢,例如:LED 高100ms,低30ms,高80ms,低,20ms,高20ms,低70ms,......没有规则, 很多LED控 ...

正是为这个功能而设计的,如调用宏:LED_BLINKING(__LED, __WIDTH, __PERIOD) 或 LED_BLINK_TIMES(__LED, __WIDTH, __PERIOD, __TIMES) ,其中__WIDTH为LED点亮时间,__PERIOD为闪烁周期,因此,熄灭的时间为__PERIOD - __WIDTH。 __TIMES为设定闪烁的次数。

yikuang 发表于 2015-8-22 20:43:47

蓝蓝的恋 发表于 2015-8-22 16:02
好像有一个跟这样类似的帖子

你说的是这个吧?
[代码分享]一个LED状态模块,支持亮、灭时间和闪烁次数设定
http://www.amobbs.com/thread-5628956-1-1.html
(出处: amoBBS 阿莫电子论坛)

这个好像只是可设置一颗LED的,即他的函数不能重入,增加一颗LED要需要重写相同的函数。

机器人天空 发表于 2015-8-22 20:59:25

比我写的好多了,收下了

ponder2077 发表于 2015-8-24 10:31:06

本帖最后由 ponder2077 于 2015-8-24 10:32 编辑

yikuang 发表于 2015-8-22 20:34
正是为这个功能而设计的,如调用宏:LED_BLINKING(__LED, __WIDTH, __PERIOD) 或 LED_BLINK_TIMES(__LED, ...

连续的波形,难道每次输出一个波形后就要跟据下一个波形调用一次相应的宏? 这样是不是太复杂了? 用查表的方法就很简单

yikuang 发表于 2015-8-24 12:03:51

ponder2077 发表于 2015-8-24 10:31
连续的波形,难道每次输出一个波形后就要跟据下一个波形调用一次相应的宏? 这样是不是太复杂了? 用查表 ...

请问需要连续不规则的波形的LED, 用在什么场合?这样不规则,肉眼怎么分辨? 当然,如果你要用作红外编码或其他编码,这个模块是不适合的,不能混为一谈的。

ponder2077 发表于 2015-8-24 14:22:35

yikuang 发表于 2015-8-24 12:03
请问需要连续不规则的波形的LED, 用在什么场合?这样不规则,肉眼怎么分辨? 当然,如果你要用作红外编 ...

有的, LED灯条各种效果, 还有按摩器,振动棒都会用到, 音乐的输出 也是这种方式!

whatcanitbe 发表于 2015-8-24 22:55:00

event_get,event_delete的实现也说说

lindabell 发表于 2015-8-25 08:55:41

要不少ram啊,一个LED好像用了8字节RAM

zuokong2006 发表于 2015-8-25 09:48:41

学习{:smile:}

yikuang 发表于 2015-8-25 09:54:25

本帖最后由 yikuang 于 2015-8-25 10:01 编辑

whatcanitbe 发表于 2015-8-24 22:55
event_get,event_delete的实现也说说

其实说白了就是简单的位操作,掩人耳目而已。这样方便那些不能定义位变量的单片机,可以节省不少RAM空间。
event.#ifndef __EVENT_H
#define __EVENT_H

#include "MacroAndConst.h"

#define EVENT_NULL0x00

typedef uint8_t event_t;

void event_init(event_t *ptEvent);
void event_set(event_t *ptEvent, event_t tEvent);
bool event_get(event_t *ptEvent, event_t tEvent);
void event_delete(event_t *ptEvent, event_t tEvent);

#endif

event.c
#include "event.h"


void event_init(event_t *ptEvent)
{
        if (NULL == ptEvent) {
                return ;
        }
       
        *ptEvent = EVENT_NULL;
}

void event_set(event_t *ptEvent, event_t tEvent)
{
        if (NULL == ptEvent) {
                return ;
        }
       
        *ptEvent |= tEvent;
}

bool event_get(event_t *ptEvent, event_t tEvent)
{
        if (NULL == ptEvent) {
                return false;
        }
       
        if(*ptEvent & tEvent) {
                return true;
        } else {
                return false;
        }
}

void event_delete(event_t *ptEvent, event_t tEvent)
{
        if (NULL == ptEvent) {
                return ;
        }
       
        *ptEvent &= ~tEvent;
}

一个简单地例子:
event_t g_tEvent = EVENT_NULL, g_tEvent1 = EVENT_NULL;

enum {
        EVENT_10MS_OVERFLOW = 0x01,
       
        EVENT_MASTER_CALIB_INDICATE = 0x02,
        EVENT_MASTER_CALIB_COMPLETE = 0x04,
       
        EVENT_SLAVE_NORTH = 0x08,
        EVENT_SLAVE_WEST = 0x10,
        EVENT_SLAVE_SOUTH = 0x20,
        EVENT_SLAVE_EAST = 0x40
};

enum {
        EVENT1_10MS_OVERFLOW = 0x01,
        ... ...
};



void main(void)
{
        ... ...
       
        event_init(&g_tEvent);
        event_init(&g_tEvent1);

        event_set(&g_tEvent, EVENT_10MS_OVERFLOW);

        while (1) {
                if (event_get(&g_tEvent, EVENT_10MS_OVERFLOW)) {
                        ... ...

                        event_delete(&g_tEvent, EVENT_10MS_OVERFLOW);
                }
        }
}

yikuang 发表于 2015-8-25 09:56:28

lindabell 发表于 2015-8-25 08:55
要不少ram啊,一个LED好像用了8字节RAM

是的,对Ram有要求的慎用。

Excellence 发表于 2015-8-25 10:09:43

不错。看看。

whatcanitbe 发表于 2015-8-25 12:15:25

yikuang 发表于 2015-8-25 09:54
其实说白了就是简单的位操作,掩人耳目而已。这样方便那些不能定义位变量的单片机,可以节省不少RAM空间 ...

谢谢共享代码

10xjzheng 发表于 2015-9-8 20:26:34

看懂了,多谢楼主

10xjzheng 发表于 2015-9-8 20:28:22

看懂了,多谢楼主

talkingbeast 发表于 2015-9-8 20:35:08

谢谢分享。

cd4000 发表于 2015-9-8 21:34:57

学习一下!

XTXB 发表于 2015-10-2 14:40:12

学习了,谢谢分享!

tanek 发表于 2015-10-3 19:35:43

这缩进太难受了,不想看代码。。。

yikuang 发表于 2015-10-4 01:40:45

tanek 发表于 2015-10-3 19:35
这缩进太难受了,不想看代码。。。

缩进原本还好好的,贴到这里来就变成这样了。。。

YS126 发表于 2015-10-5 13:39:19

回去电脑再慢慢看…谢!

tanek 发表于 2015-10-7 14:55:52

yikuang 发表于 2015-10-4 01:40
缩进原本还好好的,贴到这里来就变成这样了。。。

那说明原本就没搞好。搞好了到哪里都一样

renxupeng 发表于 2015-10-7 21:21:38

学习了,谢谢分享!

liaihua1997 发表于 2015-10-7 22:27:41

楼主,发个压缩包,这样太难看了,

GZZXB 发表于 2015-10-13 14:10:23

看代码风格应该是傻孩子的高徒吧,没仔细看。想请问下楼主如果真实项目中就实现楼主的这几个模块的功能。那么楼主会选什么芯片?无他 好奇而已{:lol:}

didadida 发表于 2015-10-13 16:41:30

赞赞赞!!模块封装

yikuang 发表于 2015-10-13 18:09:56

GZZXB 发表于 2015-10-13 14:10
看代码风格应该是傻孩子的高徒吧,没仔细看。想请问下楼主如果真实项目中就实现楼主的这几个模块的功能。那 ...

a. 我不是傻孩子老师的高徒,只是他的一个小徒儿,我自认比较懒,没认真跟他学,只学到些皮毛。。。你这样说会被我丢了他的颜面。
b. 我发的这个模块,完全没有经傻孩子老师的批阅,不代表傻孩子工作室的作品。
c. 我知道你想说,这个模块很吃RAM。这我也很清楚,我本意想表达的是,这个模块方便管理多个LED而已。
如果我要在实际项目中使用这个模块的话,并且如果我选用的芯片在RAM允许的情况下,我都会使用。就比如,我现在有
个项目用ATmega88, 其中,用这个模块管理4个LED和1个振动马达,整个工程还剩200字节的RAM,我觉得用的非常合理。
ofc,如果明知道RAM很紧张还用这个模块,那是作死的节奏。
d. 本模块仅供参考或讨论,我更期待您或者其他人能够提供更好的这类模块。

感谢你的回复!

yiming988 发表于 2015-10-13 20:33:26

yikuang 发表于 2015-10-13 18:09
a. 我不是傻孩子老师的高徒,只是他的一个小徒儿,我自认比较懒,没认真跟他学,只学到些皮毛。。。你这 ...

赞一下~

这点ram消耗没什么啦带来的可维护性、可扩展性、可移植复用性的好处多得多,我觉得后者更重要一些。
也不可能一个模块什么好处都占了~

GZZXB 发表于 2015-10-16 10:04:16

yikuang 发表于 2015-10-13 18:09
a. 我不是傻孩子老师的高徒,只是他的一个小徒儿,我自认比较懒,没认真跟他学,只学到些皮毛。。。你这 ...

   首先我是傻孩子的忠实粉丝^-^,其次代码风格很好。之所以这样问,我是觉得当前有两条分支,一种是暴利型的产品另一种是大批量生产的产品。
对于批量型的产品来说,能省1毛是1毛能用4k的片子就不会用8k,能用2k的片子肯定不会用4k的。有时候为了省成本甚至插入大量的汇编来把4k的c代码
压缩到2k的片子里去。对于老板来说一年省个一辆宝马来可能都不是问题了。对于暴利型的产品(这种产品一般量少,对成本不敏感吧)你可以上个ARM
老板也不会反对^-^。我一直很苦恼在代码架构好及代码压缩率之间到底该如何取舍。现在的情况是有些代码你可以写的很通用很好移植从一个平台到一个
平台毫不费劲,可是老板就不乐意了,我不知道坛子里有多少是从事成本不敏感产品开发的,浏览了大部分帖子发现大多还是以代码架构及后期维护为优先
原则,或者这才是以后开发的大势所趋吧。不知道楼主工作了没?楼主学了点傻孩子的皮毛就能写出这样的代码,估计傻孩子看了会会心一笑的。看好你哦。记得在
网上看到过一句话: 傻孩子当老师是浪费,不当老师也是浪费。呵呵

我爱大秦 发表于 2015-10-18 17:42:10

谢谢楼主

trm99999 发表于 2015-10-18 18:18:11

好厉害,佩服

FireHe 发表于 2015-10-18 20:39:03

感谢楼主分享

能否上传一个完整范例压缩包?现在这个没有对齐缩进,实在太费劲了。

无心星矢 发表于 2015-10-18 20:43:41

感谢楼主分享。

maimaige 发表于 2016-4-9 18:50:45

mark一下,这个帖子比较好
发一个实用的LED或蜂鸣器、振动马达等的控制模块

Yvan 发表于 2016-4-16 16:25:36

还没看正文。最近一直在思考用状态机写一些控制LED、蜂鸣器和按键的模块,现在看到这个不知是否适合
,感谢分享。

小小菜 发表于 2016-4-16 16:39:53

LED,振动,是我想太多了吗

闲鱼翻身 发表于 2016-4-16 17:06:41

好像还不错,感谢分享

jianbo513 发表于 2016-4-16 20:38:28

感谢楼主分享!

Yvan 发表于 2016-4-16 22:13:59

看懂了。不过有几个建议,不知能否实现,回头我会试着改一下:
1.定义一个结构体数组,存放每个LED结构体数据,假如我有15个LED,这样写:indic_set_t    g_tRedLed, g_tGreenLed, g_tBlueLed;要写好多个。
2.在indic_task()里面,逐个取结构体数组的数据出来处理,这样只需要一个写indic_task()就够了。
3.为了节省空间,可以考虑去掉打开和关闭函数指针。写两个函数来替代。分别是Open(LED号)和Close(LED号)函数,函数里面调用相应的开关LED宏。
目前想到的改进就这些。但是使用函数指针更容易建立库,感觉更有模块化的感觉。

shuimubai 发表于 2016-7-29 13:38:37

以前记得傻孩子好像做过一个呼吸灯的程序现在好像找不到了?谁知道啊

cjqfeng 发表于 2017-7-9 23:57:07

LED_BLINKING(g_tGreenLed, 1, 2);
LED_BLINKING(g_tGreenLed, 2, 3);
这样子会不会常亮
页: [1]
查看完整版本: 发一个实用的LED或蜂鸣器、振动马达等的控制模块