分享一款嵌入式开源LED指示灯控制代码框架cotLed

一、工程简介

        cotLed是一款轻量级的LED控制软件框架,可以十分方便地控制及自定义LED的各种状态,移植方便,无需修改,只需要在初始化时实现单片机硬件GPIO初始化,同时为框架接口提供GPIO写函数即可。

        框架代码工程地址:https://gitee.com/cot_package/cot_led

        工程代码结构也很精简,只用两个文件cot_led.c和cot_led.h实现具体功能,对外提供API接口函数,适用于裸机和带操作系统的应用代码。

图片

        通过该软件框架API,可以实现的LED控制接口功能有:

        (1)、单个LED的亮灭、翻转、闪烁、呼吸灯、自定义(如多少秒快闪几次等)等多种功能;

        (2)、多个LED组合的跑马灯、流水灯等功能;

        (3)、上述各个功能模式功能实现的次数设置。


二、工程代码分析

        在头文件cot_led.h中包括:

        (1)定义了LED亮灭的枚举cotLedState_e;

        (2)定义了写入IO状态的函数指针cotLedCtrl_f;

        (3)定义了LED核心控制功能成员变量的结构体cotLedProc_t;

        (4)外部声明的LED控制接口函数。

/* Define to prevent recursive inclusion -----------------------------------------------------------------------------*/
#ifndef _COT_LED_H_
#define _COT_LED_H_

/* Includes ----------------------------------------------------------------------------------------------------------*/
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>

/* Exported types ----------------------------------------------------------------------------------------------------*/

// 状态bit位所使用的byte数,bit之间的控制时间为时间颗粒度
#define LED_STATE_BYTE_NUM       4

/**
  * @brief  LED亮灭枚举定义
  */
typedef enum{
    COT_LED_OFF = 0,                     /*!< (0)灯灭 */
    COT_LED_ON = !COT_LED_OFF            /*!< (1)灯亮 */
} cotLedState_e;

typedef void (*cotLedCtrl_f)(cotLedState_e state);
typedef uint16_t led_t;

typedef struct
{
    uint8_t validBits;                 /*!< 状态有效bit位 */
    uint8_t offset;                    /*!< 状态bit位偏移 */
    uint8_t count;                     /*!< 控制次数 */
    uint8_t isSetCount : 1;            /*!< 是否设置了控制次数 */
    uint8_t defState : 1;              /*!< 默认状态 */
    uint8_t curState : 1;              /*!< 当前状态 */
    uint8_t pwmDir : 1;                /*!< PWM增减方向 */
    uint8_t isPwm : 1;                 /*!< PWM模式 */
    uint16_t tic;                      /*!< 时间计数器 */
    uint16_t interval;                 /*!< 每次控制的时间颗粒度,单位为1ms */
    union
    {
        struct
        {
            uint16_t onTime;               /*!< 亮的时长 */
            uint16_t tic;                  /*!< PWM计时 */
        } pwm;
        uint8_t state[LED_STATE_BYTE_NUM];  /*!< 状态bit位 */
    } data;
} cotLedProc_t;

typedef struct
{
    cotLedProc_t proc;
    cotLedCtrl_f pfnLedCtrl;
} cotLedCfg_t;

/* Exported constants ------------------------------------------------------------------------------------------------*/
/* Exported macro ----------------------------------------------------------------------------------------------------*/
/* Exported functions ------------------------------------------------------------------------------------------------*/

extern int cotLed_Init(cotLedCfg_t pCfgTable[], size_t num);

extern int cotLed_SetState(led_t led, cotLedState_e state);
extern int cotLed_SetStateWithTime(led_t led, cotLedState_e state, uint16_t time);

extern int cotLed_ON(led_t led);
extern int cotLed_OFF(led_t led);
extern int cotLed_Toggle(led_t led);

extern int cotLed_Twinkle(led_t led, uint16_t time);
extern int cotLed_TwinkleWithCount(led_t led, uint16_t time, uint8_t count, cotLedState_e defState);

extern int cotLed_Breathe(led_t led, uint16_t period);
extern int cotLed_BreatheWithCount(led_t led, uint16_t period, uint8_t count, cotLedState_e defState);

extern int cotLed_Custom(led_t led, ...);
extern int cotLed_CustomWithCount(led_t led, uint8_t count, cotLedState_e defState, ...);

extern int cotLed_Marquee(led_t led[], uint8_t ledNum, int32_t time);
extern int cotLed_MarqueeWithCount(led_t led[], uint8_t ledNum, int32_t time, uint8_t count, cotLedState_e defState);
extern int cotLed_Waterfall(led_t led[], uint8_t ledNum, int32_t time);
extern int cotLed_WaterfallWithCount(led_t led[], uint8_t ledNum, int32_t time, uint8_t count, cotLedState_e defState);

extern int cotLed_Ctrl(uint32_t sysTime);

#endif

        由于cot_led.c中代码比较多,这里暂时就不贴出,可以参考具体文件中的函数定义和注释内容。

        在头文件cot_led.c中包括:

        (1)呼吸灯软件模拟PWM频率;

        (2)在LED控制接口中需要用到的一些宏定义;

        (3)各类型的LED控制接口。

图片


三、工程代码应用

        工程代码的使用可以参考工程代码包中的examples和readme文件。这里以在stm32平台上使用带有freertos的应用代码中进行各个LED接口函数的测试,测试时使用宏开关TEST_LED_MODE控制分别进行接口功能测试。在应用代码中进行了接口使用的注释。

        应用的流程顺序为:

        (1)初始化各个LED对应GPIO口驱动。

        (2)定义各个LED的IO状态控制函数,调用LED控制初始化函数cotLed_Init,入参为各个LED的IO状态控制函数和LED数量。

        (3)创建周期执行逻辑代码,在带操作系统的代码中创建周期任务;而在裸机系统代码中则创建周期计数逻辑或定时器逻辑。

        (4)在周期执行逻辑代码中周期调用LED控制函数cotLed_Ctrl,入参为当前时间戳ms级的系统时长。

        (5)条件触发调用执行对应的各个LED控制接口函数。

        应用时的一些注意点如下:

        (1)调用接口函数时,不要重复调用,重复调用设置函数相关信息会复位,使用触发式调用即达到某一触发条件时调用接口一次。在实际测试时LED翻转接口cotLed_Toggle需要在循环中重复调用。

        (2)在调用的带时间设置入参的LED控制接口时,该时间需要设置为任务调度周期的倍数;裸机调用时设置为定时器或计数器周期的倍数。比如调度周期或计数器周期为500ms,为该接口设置的时间参数就为500*N。

        (3)如果设置某个LED为呼吸灯模式,则需要保证cotLed_Ctrl调用周期为1ms(优先级需要最高,或者定时器调度效果最好)。

        (4)在操作系统下使用时非线程安全,最好可以使用读写锁,如果没有读写锁则至少使用互斥锁。

#include "sys.h"
#include "delay.h"
#include "FreeRTOS.h"
#include "task.h"
#include "cot_led.h"

//任务优先级
#define START_TASK_PRIO     1
//任务堆栈大小    
#define START_STK_SIZE      128  
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);

//任务优先级
#define LED0_TASK_PRIO      2
//任务堆栈大小    
#define LED0_STK_SIZE       50  
//任务句柄
TaskHandle_t LED0Task_Handler;
//任务函数
void led0_task(void *pvParameters);

//任务优先级
#define LED1_TASK_PRIO      3
//任务堆栈大小    
#define LED1_STK_SIZE       50  
//任务句柄
TaskHandle_t LED1Task_Handler;
//任务函数
void led1_task(void *pvParameters);

typedef enum
{
    LED_0 = 0,
    LED_1,
    /* 勿删除,用来统计LED的数目 */
    LED_MAX_NUM
} LedType_e;

//LED的IO驱动初始化
void LED_Init(void)
{       
  GPIO_InitTypeDef  GPIO_InitStructure;
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);  //使能GPIOF时钟
  //GPIOF9,F10初始化设置
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;  //LED0和LED1对应IO口
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;      //普通输出模式
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;      //推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;    //100MHz
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;      //上拉
  GPIO_Init(GPIOF, &GPIO_InitStructure);          //初始化GPIO
  GPIO_SetBits(GPIOF,GPIO_Pin_9 | GPIO_Pin_10);      //GPIOF9,F10设置高,灯灭

}

void CtrlLed0(cotLedState_e state) //LED0的IO状态控制函数
{
    state == COT_LED_ON ? GPIO_ResetBits(GPIOF, GPIO_Pin_9) :  GPIO_SetBits(GPIOF, GPIO_Pin_9);
}

void CtrlLed1(cotLedState_e state) //LED1的IO状态控制函数
{
    state == COT_LED_ON ? GPIO_ResetBits(GPIOF, GPIO_Pin_10) :  GPIO_SetBits(GPIOF, GPIO_Pin_10);
}

void FML_LED_Init(void)
{
    static cotLedCfg_t s_ledTable[LED_MAX_NUM] = {
        {.pfnLedCtrl = CtrlLed0},
        {.pfnLedCtrl = CtrlLed1},
    };

    LED_Init();             //初始化LED驱动IO端口
    cotLed_Init(s_ledTable, LED_MAX_NUM); //LED控制初始化
}

int main(void)
{ 
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
    delay_init(168);        //初始化延时函数
    FML_LED_Init();
    
    //创建开始任务
    xTaskCreate((TaskFunction_t )start_task,            //任务函数
                (const char*    )"start_task",          //任务名称
                (uint16_t       )START_STK_SIZE,        //任务堆栈大小
                (void*          )NULL,                  //传递给任务函数的参数
                (UBaseType_t    )START_TASK_PRIO,       //任务优先级
                (TaskHandle_t*  )&StartTask_Handler);   //任务句柄              
    vTaskStartScheduler();          //开启任务调度
}

#define TEST_LED_MODE  1   //用于测试各个LED控制接口的宏开关

//开始任务任务函数
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();           //进入临界区
#if ((TEST_LED_MODE >= 1) && (TEST_LED_MODE <= 6))
    //创建LED0任务
    xTaskCreate((TaskFunction_t )led0_task,         
                (const char*    )"led0_task",       
                (uint16_t       )LED0_STK_SIZE, 
                (void*          )NULL,              
                (UBaseType_t    )LED0_TASK_PRIO,    
                (TaskHandle_t*  )&LED0Task_Handler);                        
    vTaskDelete(StartTask_Handler); //删除开始任务
#else                               
    //创建LED1任务
    xTaskCreate((TaskFunction_t )led1_task,     
                (const char*    )"led1_task",   
                (uint16_t       )LED1_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )LED1_TASK_PRIO,
                (TaskHandle_t*  )&LED1Task_Handler);    
#endif                              
    taskEXIT_CRITICAL();            //退出临界区
}

//LED任务函数 
void led0_task(void *pvParameters)
{       
#if (TEST_LED_MODE == 1)
    cotLed_Twinkle(LED_0, 2000); //设置LED0每隔2s进行闪烁(亮灭间隔时间)
    cotLed_Twinkle(LED_1, 5000); //设置LED1每隔5s进行闪烁(亮灭间隔时间
#elif (TEST_LED_MODE == 2)
  //设置LED0每隔2s秒进行闪烁,共5次(一亮一灭为一次),次数完成后灯灭
    cotLed_TwinkleWithCount(LED_0, 2000, 5, COT_LED_ON);
  //设置LED1每隔1s秒进行闪烁,共4次(一亮一灭为一次),次数完成后点亮
    cotLed_TwinkleWithCount(LED_1, 1000, 4, COT_LED_OFF);
#elif (TEST_LED_MODE == 3)
    cotLed_SetStateWithTime(LED_0, COT_LED_ON, 5000);//设置LED0亮持续5秒后熄灭
    cotLed_SetStateWithTime(LED_1, COT_LED_OFF, 3000);//设置LED1灭持续5秒后点亮
#endif      
    while(1)
    {
#if (TEST_LED_MODE == 4)
        cotLed_Toggle(LED_0);//周期翻转LED0
        cotLed_Toggle(LED_1);//周期翻转LED1   
#elif (TEST_LED_MODE == 5)          
    if (xTaskGetTickCount() == 0) {
      cotLed_ON(LED_0);//点亮LED0
      cotLed_ON(LED_1);//点亮LED1
    } else if (xTaskGetTickCount() == 5000) {
      cotLed_OFF(LED_0);//熄灭LED0
      cotLed_OFF(LED_1);//熄灭LED1
    } else if(xTaskGetTickCount() == 10000) {
      cotLed_ON(LED_0);
      cotLed_ON(LED_1);
    }
#elif (TEST_LED_MODE == 6)              
    if (xTaskGetTickCount() == 0) {
        cotLed_SetState(LED_0, COT_LED_ON);//点亮LED0
      cotLed_SetState(LED_1, COT_LED_ON);//点亮LED1
    } else if(xTaskGetTickCount() == 5000) {
      cotLed_SetState(LED_0, COT_LED_OFF);//熄灭LED0
      cotLed_SetState(LED_1, COT_LED_OFF);//熄灭LED1
    } else if(xTaskGetTickCount() == 10000) {
      cotLed_SetState(LED_0, COT_LED_ON);
      cotLed_SetState(LED_1, COT_LED_ON);
    }
#endif
        //周期调用LED控制函数,入参为当前时间戳ms级的系统时长
        cotLed_Ctrl(xTaskGetTickCount());
        vTaskDelay(100);
    }
} 

//LED1任务函数 
void led1_task(void *pvParameters)
{
#if (TEST_LED_MODE == 7)
  //LED0在2秒内1次快闪,亮100ms,灭1900ms,无限制次数
    cotLed_Custom(LED_0, 100, -1900, 0);
  //LED1在3秒内1次快闪,亮200ms,灭2800ms,无限制次数
    cotLed_Custom(LED_1, 200, -2800, 0);
#elif (TEST_LED_MODE == 8)
    //LED0在2秒内3次快闪,总共5次,次数完成后灯灭  
    cotLed_CustomWithCount(LED_0, 5, COT_LED_OFF, 100, -100, 100, -100, 100, -100, -1400, 0);
  //LED1在3秒内2次快闪,总共8次,次数完成后灯亮  
    cotLed_CustomWithCount(LED_1, 8, COT_LED_ON, 200, -200, 200, -200, -2200, 0);
#elif (TEST_LED_MODE == 9)      
    cotLed_Breathe(LED_0, 2000);//LED0在2秒内完成一次呼吸亮灭操作,一直执行不限次数
    cotLed_Breathe(LED_1, 3000);//LED1在3秒内完成一次呼吸亮灭操作,一直执行不限次数
#elif (TEST_LED_MODE == 10)
  //LED0在2秒内完成一次呼吸亮灭操作,总共3次,次数完成后灯灭
    cotLed_BreatheWithCount(LED_0, 2000, 3, COT_LED_OFF);
  //LED1在3秒内完成一次呼吸亮灭操作,总共5次,次数完成后灯亮
    cotLed_BreatheWithCount(LED_1, 3000, 5, COT_LED_ON);
#elif (TEST_LED_MODE == 11)
    led_t led[] = {LED_0, LED_1};
    cotLed_Waterfall(led, 2, 300);//流水灯一直执行,LED之间亮间隔300ms
#elif (TEST_LED_MODE == 12)
    led_t led[] = {LED_0, LED_1};
    //流水灯执行8次,LED之间亮间隔300ms,执行完后熄灭
    cotLed_WaterfallWithCount(led, 2, 300, 8, COT_LED_OFF);
#elif (TEST_LED_MODE == 13) 
     led_t led[] = {LED_0, LED_1};
    cotLed_Marquee(led, 2, 500);//跑马灯一直执行,LED之间亮间隔500ms
#elif (TEST_LED_MODE == 14) 
    led_t led[] = {LED_0, LED_1};
    //跑马灯执行5次,LED之间亮间隔500ms,执行完后熄灭
    cotLed_MarqueeWithCount(led, 2, 500, 5, COT_LED_OFF);
#endif
    while(1)
    {
    //周期调用LED控制函数,入参为当前时间戳ms级的系统时长
        cotLed_Ctrl(xTaskGetTickCount());
        vTaskDelay(1);
    }
}

↓↓↓更多技术内容和书籍资料获取,入群技术交流敬请关注“明解嵌入式”↓↓↓ 

相关推荐

  1. 嵌入—模块代码()

    2024-07-12 03:08:09       31 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-12 03:08:09       67 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-12 03:08:09       71 阅读
  3. 在Django里面运行非项目文件

    2024-07-12 03:08:09       58 阅读
  4. Python语言-面向对象

    2024-07-12 03:08:09       69 阅读

热门阅读

  1. 从零开始学习嵌入式----Makefile工具

    2024-07-12 03:08:09       17 阅读
  2. SpringBoot 定时任务之@Scheduled cron表达式

    2024-07-12 03:08:09       17 阅读
  3. C/C++开发,IniFile源码下载

    2024-07-12 03:08:09       19 阅读
  4. jQuery Mobile 安装指南

    2024-07-12 03:08:09       25 阅读
  5. centos ssh一键升级到9.8版本脚本

    2024-07-12 03:08:09       20 阅读
  6. Kotlin 中 is 关键字的作用与智能类型转换

    2024-07-12 03:08:09       21 阅读
  7. Promise

    2024-07-12 03:08:09       16 阅读
  8. asp .net core 避免请求body数据量过大

    2024-07-12 03:08:09       20 阅读
  9. 社交媒体数据分析:赋能企业营销策略的利器

    2024-07-12 03:08:09       19 阅读
  10. SpringSecurity中文文档(Servlet OAuth2)

    2024-07-12 03:08:09       24 阅读
  11. Docker核心知识手册

    2024-07-12 03:08:09       22 阅读
  12. Linux——测网速例子,在网站查看生成的图片

    2024-07-12 03:08:09       27 阅读
  13. 华为od-C卷200分题目6 - 5G 网络建设

    2024-07-12 03:08:09       22 阅读