一个好玩的LED指示灯驱动,常规\编码形式闪烁

文摘   科技   2023-08-27 14:40   广东  

Keep Moving 

保持·热爱

LED控制

驱动

嵌入式C

常规闪烁、编码闪烁



介绍LED指示灯驱动,并举例

独立按键、组合键的驱动

项目开发规范的总结

关于ry_tick_task

后台回复开源获取全部资料



概述

概述

产品装机后,往往因各种情况需要调试,常规输出调试信息的方式很多,考虑到系统一般存在指示灯,那么能不能像摩尔斯电码,搞个闪烁的LED灯输出信息。


想法落地,设计一个LED驱动:



设计实现

结构链表             

              链表构想

如何闪烁

常规思路,延时:

显然:

  • 阻塞延时导致浪费CPU时间

  • 其它任务怎么办

  • 非阻塞延时,那别的LED无法刷新

没有什么东西是时间解决不了的,所以引入时基,给LED注入心跳:

这里采用占空比固定、变频的方案,只因代码简单好写。


如何编码

  • 人眼、测量设备能识别出0、1两种状态

  • 每个编码、每串编码做好时间间隔


自由调整编码

总的需求:

  • 能解析ASCII码的高低序列,进行闪烁

  • 运行时,编码位数自由调整

  • 编码位数、数量可扩展


编码模式的应用

常规的应用:

  • 三长两短周期性闪烁

  • 周期性快闪、慢闪N次

  • 解析一串字符,逐位闪烁,以灯光传递信息


设计数据结构

依据上述需求,设计LED对象:

  • 秉承最小内存占用的精神,使用位域,能省就省

  • 需要扩展编码时,就给它多分配一些

typedef struct{    led_event_t  *encode;               /* 指向编码数组 */#if RY_LED_ENCODE_EXPAND == 1    uint8_t       encode_num;           /* 总的编码数 */    uint8_t       encode_cnt;           /* 编码已处理的计数器 */    uint8_t       init_bits       : 4;  /* 编码的位数 */    uint8_t       remaining_bits  : 4;  /* 当前未处理的位数 */#else    uint8_t       encode_num      : 4;    uint8_t       encode_cnt      : 4;    uint8_t       init_bits       : 3;    uint8_t       remaining_bits  : 3;#endif    uint8_t       encode_circular : 1;  /* 编码是否循环处理 */    uint8_t       open            : 1;  /* 开关控制 */    uint8_t       tick;                 /* 时基 */    led_event_t   event;                /* LED的事件 */    void        (*ctrl_cbk)(uint8_t);   /* LED亮灭回调函数 */}ry_led_t;


所有API

写代码,实现上述功能:

/* 创建一个LED,并设置其默认状态 */extern void ry_led_create(E_user_led_sn_t led, led_event_t e, void (*ctrl_cbk)(uint8_t));/* 配置LED的一般事件 */extern void ry_led_event_cfg(E_user_led_sn_t led, led_event_t e);/* 配置LED的循环编码模式 */extern void ry_led_encode_circular_cfg(E_user_led_sn_t led, led_event_t *encode, uint8_t encode_num);/* 设置LED的编码位数 */extern void ry_set_led_encode_bits(E_user_led_sn_t led, uint8_t bits);/* LED扫描,定时调用该函数实现LED控制 */extern void ry_led_scan(void);



示例

创建一个LED

先在“ry_led.h”中定义一个LED枚举:

/* 自行注册LED,如下注册了一个“LED_USER” */typedef enum{    LED_USER,        RY_LED_SN_MAX,}E_user_led_sn_t;

开始注册LED、同时注册一个周期50ms的扫描任务:

static ry_task_t __taskLED;static uint8_t __ledEncode[] = "ry_led";void drv_led_create(void){    /* 创建一个LED */    ry_led_create(LED_USER, RY_LED_OFF, bsp_led_ctrl);    /* LED输出“ry_led”。慢闪为1,快闪为0,MSB方式 */    /* 字符间有短暂停顿,处理完一串字符后,有长停顿 */    ry_led_encode_circular_cfg(LED_USER, __ledEncode, 6);    /* 注册一个LED扫描任务 */    ry_task_reg(&__taskLED,          /* 任务控制块 */                ry_led_scan,         /* 任务主体 */                RY_TASK_MODE_NORMAL, /* 普通模式,周期性运行即可 */                RY_TASK_READY,       /* 就绪态 */                "led",               /* 任务名字 */                LED_SCAN_CYCLE);     /* 扫描周期,1个系统时基1ms */}

扫描任务启动后,就按照我们的意志运行下去。


实验现象

观察LED的闪烁情况,并抓取波形:

  • 低电平亮灯

  • 占空比固定,周期不同,实现快闪、慢闪

  • 上图6个16进制数,对应:ry_led


最后的

示例源码,已按照最新的程序设计思想编写,如下:





-END-


碎片聚合
求真务实
 最新文章