搜索
bottom↓
回复: 44
打印 上一主题 下一主题

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

  [复制链接]

出0入0汤圆

跳转到指定楼层
1
发表于 2015-8-22 11:35:06 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
最近有个项目用到多个LED,并以不同的闪烁方式提示多种工作状态。和一个小振动马达, 以不同的振动方式,提示不同的工作状态。
这个模块同样支持控制蜂鸣器。
这个模块支持的功能:(假设控制LED)可设置闪烁周期,周期内亮灭的时间,闪烁次数, 常亮和熄灭。
像LED这类共享资源,要注意多个任务对它访问的问题。

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

  3. #include "MacroAndConst.h"


  4. #define LED_ON(__LED)  do { \
  5.                                                __LED.chWidth = 0; \
  6.                                                __LED.chPeriod = 0; \
  7.                                                __LED.chBlinkCnt = 0; \
  8.                                            } while (false)
  9.                                           
  10. #define LED_OFF(__LED) do { \
  11.                                                    __LED.chWidth = 0xFF; \
  12.                                                __LED.chPeriod = 0; \
  13.                                                __LED.chBlinkCnt = 0; \
  14.                        } while (false)

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

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

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

  30. typedef void (*pfn_indic_t)(void);

  31. typedef struct {
  32.         uint8_t chCounter;
  33.         pfn_indic_t indic_on;
  34.         pfn_indic_t indic_off;
  35. } indic_ctrl_t;

  36. typedef struct {
  37.         uint8_t chWidth;
  38.         uint8_t chPeriod;
  39.         uint8_t chBlinkCnt;

  40.         indic_ctrl_t tIndic;
  41. } indic_set_t;

  42. //extern void indic_io_init();

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

  45. #endif
复制代码


Indicator.c
  1. #include "indicator.h"



  2. void indic_init(indic_set_t *ptIndic, pfn_indic_t pfn_indic_on, pfn_indic_t pfn_indic_off)
  3. {
  4.         if (NULL == ptIndic) {
  5.                 return ;
  6.         }

  7.         ptIndic->chWidth = 0xFF;
  8.         ptIndic->chPeriod = 0;
  9.         ptIndic->chBlinkCnt = 0;
  10.        
  11.         ptIndic->tIndic.chCounter = 0;
  12.         ptIndic->tIndic.indic_on = pfn_indic_on;
  13.         ptIndic->tIndic.indic_off = pfn_indic_off;
  14.        
  15.         //indic_io_init();
  16.         (*ptIndic->tIndic.indic_off)();
  17. }



  18. bool indic_task(indic_set_t *ptIndic)
  19. {
  20.         if (NULL == ptIndic) {
  21.                 return false;
  22.         }
  23.        
  24.         if (ptIndic->chPeriod >= ptIndic->chWidth) {
  25.                 if (ptIndic->tIndic.chCounter <= ptIndic->chWidth) {
  26.                         (*ptIndic->tIndic.indic_on)();
  27.                 } else {
  28.                         (*ptIndic->tIndic.indic_off)();
  29.                 }
  30.                
  31.                 if (++ ptIndic->tIndic.chCounter >= ptIndic->chPeriod) {
  32.                         ptIndic->tIndic.chCounter = 0;
  33.                         if (ptIndic->chBlinkCnt > 0) {
  34.                                 if (0 == (-- ptIndic->chBlinkCnt)) {
  35.                                         ptIndic->chWidth = 0xFF;
  36.                                         ptIndic->chPeriod = 0;
  37.                                         ptIndic->chBlinkCnt = 0;

  38.                                         return true;
  39.                                 }
  40.                         }
  41.                 }
  42.         } else {
  43.                 ptIndic->chBlinkCnt = 0;
  44.                 ptIndic->tIndic.chCounter = 0;
  45.                 (*ptIndic->tIndic.indic_off)();
  46.         }

  47.         return false;
  48. }
复制代码


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

  6. void led_red_on(void)
  7. {
  8.         LED_RED_CLR();
  9. }

  10. void led_red_off(void)
  11. {
  12.         LED_RED_SET();
  13. }

  14. ......
复制代码


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

  2. void main(void)
  3. {
  4.         ... ...
  5.        
  6.         indic_init(&g_tRedLed, led_red_on, led_red_off);
  7.         indic_init(&g_tGreenLed, led_green_on, led_green_off);
  8.         indic_init(&g_tBlueLed, led_blue_on, led_blue_off);

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

  12.         indic_init(&g_tMotor, motor_on, motor_off);
  13.         MOTOR_SHAKE_TIMES(g_tMotor, 25, 50, 4);
  14.        
  15.         while(1) {
  16.                 if (event_get(&g_tEvent, EVENT_10MS_OVERFLOW)) {

  17.                         ... ...
  18.                        
  19.                         indic_task(&g_tRedLed);
  20.                         indic_task(&g_tGreenLed);
  21.                         indic_task(&g_tBlueLed);
  22.                        
  23.                         indic_task(&g_tMotor);
  24.                        
  25.                         event_delete(&g_tEvent, EVENT_10MS_OVERFLOW);
  26.                 }
  27.         }
  28. }
复制代码


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


阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!

出0入0汤圆

2
发表于 2015-8-22 15:43:37 | 只看该作者
不错,谢楼主共享,先看看

出0入8汤圆

3
发表于 2015-8-22 16:02:41 | 只看该作者
好像有一个跟这样类似的帖子

出0入0汤圆

4
发表于 2015-8-22 18:09:25 | 只看该作者
如果波形是不规则的呢,例如:LED 高100ms,低30ms,高80ms,低,20ms,高20ms,低70ms,......没有规则, 很多LED控制类和马达控制类的都有这种波形,你的程序还适用吗?

出0入0汤圆

5
 楼主| 发表于 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为设定闪烁的次数。

出0入0汤圆

6
 楼主| 发表于 2015-8-22 20:43:47 | 只看该作者
蓝蓝的恋 发表于 2015-8-22 16:02
好像有一个跟这样类似的帖子

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

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

出0入0汤圆

7
发表于 2015-8-22 20:59:25 来自手机 | 只看该作者
比我写的好多了,收下了

出0入0汤圆

8
发表于 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, ...


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

出0入0汤圆

9
 楼主| 发表于 2015-8-24 12:03:51 | 只看该作者
ponder2077 发表于 2015-8-24 10:31
连续的波形,  难道每次输出一个波形后就要跟据下一个波形调用一次相应的宏? 这样是不是太复杂了? 用查表 ...

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

出0入0汤圆

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

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

出100入85汤圆

11
发表于 2015-8-24 22:55:00 来自手机 | 只看该作者
event_get,event_delete的实现也说说

出0入8汤圆

12
发表于 2015-8-25 08:55:41 | 只看该作者
要不少ram啊,一个LED好像用了8字节RAM

出0入0汤圆

13
发表于 2015-8-25 09:48:41 | 只看该作者
学习

出0入0汤圆

14
 楼主| 发表于 2015-8-25 09:54:25 | 只看该作者
本帖最后由 yikuang 于 2015-8-25 10:01 编辑
whatcanitbe 发表于 2015-8-24 22:55
event_get,event_delete的实现也说说


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

  3. #include "MacroAndConst.h"

  4. #define EVENT_NULL  0x00

  5. typedef uint8_t event_t;

  6. void event_init(event_t *ptEvent);
  7. void event_set(event_t *ptEvent, event_t tEvent);
  8. bool event_get(event_t *ptEvent, event_t tEvent);
  9. void event_delete(event_t *ptEvent, event_t tEvent);

  10. #endif
复制代码


event.c
  1. #include "event.h"


  2. void event_init(event_t *ptEvent)
  3. {
  4.         if (NULL == ptEvent) {
  5.                 return ;
  6.         }
  7.        
  8.         *ptEvent = EVENT_NULL;
  9. }

  10. void event_set(event_t *ptEvent, event_t tEvent)
  11. {
  12.         if (NULL == ptEvent) {
  13.                 return ;
  14.         }
  15.        
  16.         *ptEvent |= tEvent;
  17. }

  18. bool event_get(event_t *ptEvent, event_t tEvent)
  19. {
  20.         if (NULL == ptEvent) {
  21.                 return false;
  22.         }
  23.        
  24.         if(*ptEvent & tEvent) {
  25.                 return true;
  26.         } else {
  27.                 return false;
  28.         }
  29. }

  30. void event_delete(event_t *ptEvent, event_t tEvent)
  31. {
  32.         if (NULL == ptEvent) {
  33.                 return ;
  34.         }
  35.        
  36.         *ptEvent &= ~tEvent;
  37. }
复制代码


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

  2. enum {
  3.         EVENT_10MS_OVERFLOW = 0x01,
  4.        
  5.         EVENT_MASTER_CALIB_INDICATE = 0x02,
  6.         EVENT_MASTER_CALIB_COMPLETE = 0x04,
  7.        
  8.         EVENT_SLAVE_NORTH = 0x08,
  9.         EVENT_SLAVE_WEST = 0x10,
  10.         EVENT_SLAVE_SOUTH = 0x20,
  11.         EVENT_SLAVE_EAST = 0x40
  12. };

  13. enum {
  14.         EVENT1_10MS_OVERFLOW = 0x01,
  15.         ... ...
  16. };



  17. void main(void)
  18. {
  19.         ... ...
  20.        
  21.         event_init(&g_tEvent);
  22.         event_init(&g_tEvent1);

  23.         event_set(&g_tEvent, EVENT_10MS_OVERFLOW);

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

  27.                         event_delete(&g_tEvent, EVENT_10MS_OVERFLOW);
  28.                 }
  29.         }
  30. }
复制代码

出0入0汤圆

15
 楼主| 发表于 2015-8-25 09:56:28 | 只看该作者
lindabell 发表于 2015-8-25 08:55
要不少ram啊,一个LED好像用了8字节RAM

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

出0入0汤圆

16
发表于 2015-8-25 10:09:43 | 只看该作者
不错。看看。

出100入85汤圆

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

谢谢共享代码

出0入10汤圆

18
发表于 2015-9-8 20:26:34 | 只看该作者
看懂了,多谢楼主

出0入10汤圆

19
发表于 2015-9-8 20:28:22 | 只看该作者
看懂了,多谢楼主

出20入0汤圆

20
发表于 2015-9-8 20:35:08 | 只看该作者
  谢谢分享。

出0入0汤圆

21
发表于 2015-9-8 21:34:57 | 只看该作者
学习一下!

出0入0汤圆

22
发表于 2015-10-2 14:40:12 | 只看该作者
学习了,谢谢分享!

出0入0汤圆

23
发表于 2015-10-3 19:35:43 | 只看该作者
这缩进太难受了,不想看代码。。。

出0入0汤圆

24
 楼主| 发表于 2015-10-4 01:40:45 来自手机 | 只看该作者
tanek 发表于 2015-10-3 19:35
这缩进太难受了,不想看代码。。。

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

出0入0汤圆

25
发表于 2015-10-5 13:39:19 来自手机 | 只看该作者
回去电脑再慢慢看…谢!

出0入0汤圆

26
发表于 2015-10-7 14:55:52 | 只看该作者
yikuang 发表于 2015-10-4 01:40
缩进原本还好好的,贴到这里来就变成这样了。。。

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

出0入0汤圆

27
发表于 2015-10-7 21:21:38 | 只看该作者
学习了,谢谢分享!

出0入8汤圆

28
发表于 2015-10-7 22:27:41 | 只看该作者
楼主,发个压缩包,这样太难看了,

出0入36汤圆

29
发表于 2015-10-13 14:10:23 | 只看该作者
看代码风格应该是傻孩子的高徒吧,没仔细看。想请问下楼主如果真实项目中就实现楼主的这几个模块的功能。那么楼主会选什么芯片?无他 好奇而已

出0入0汤圆

30
发表于 2015-10-13 16:41:30 | 只看该作者
赞赞赞!!模块封装

出0入0汤圆

31
 楼主| 发表于 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. 本模块仅供参考或讨论,我更期待您或者其他人能够提供更好的这类模块。

感谢你的回复!

出0入0汤圆

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

赞一下~

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

出0入36汤圆

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

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

出0入0汤圆

34
发表于 2015-10-18 17:42:10 来自手机 | 只看该作者
谢谢楼主

出0入0汤圆

35
发表于 2015-10-18 18:18:11 来自手机 | 只看该作者
好厉害,佩服

出0入0汤圆

36
发表于 2015-10-18 20:39:03 | 只看该作者
感谢楼主分享

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

出0入0汤圆

37
发表于 2015-10-18 20:43:41 | 只看该作者
感谢楼主分享。

出0入0汤圆

38
发表于 2016-4-9 18:50:45 | 只看该作者
mark一下,这个帖子比较好
发一个实用的LED或蜂鸣器、振动马达等的控制模块

出0入0汤圆

39
发表于 2016-4-16 16:25:36 | 只看该作者
还没看正文。最近一直在思考用状态机写一些控制LED、蜂鸣器和按键的模块,现在看到这个不知是否适合
,感谢分享。

出0入17汤圆

40
发表于 2016-4-16 16:39:53 | 只看该作者
LED,振动,是我想太多了吗

出0入0汤圆

41
发表于 2016-4-16 17:06:41 | 只看该作者
好像还不错,感谢分享

出0入0汤圆

42
发表于 2016-4-16 20:38:28 | 只看该作者
感谢楼主分享!

出0入0汤圆

43
发表于 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宏。
目前想到的改进就这些。但是使用函数指针更容易建立库,感觉更有模块化的感觉。

出0入0汤圆

44
发表于 2016-7-29 13:38:37 | 只看该作者
以前记得傻孩子好像做过一个呼吸灯的程序  现在好像找不到了?谁知道啊

出0入0汤圆

45
发表于 2017-7-9 23:57:07 | 只看该作者
LED_BLINKING(g_tGreenLed, 1, 2);
LED_BLINKING(g_tGreenLed, 2, 3);
这样子会不会常亮
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-27 08:16

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表