天下乌鸦一般黑 发表于 2022-11-9 00:20:10

分享,自己用的led闪烁程序。

使用方式:
主要有三个函数:
vLedInit,用来初始化灯的模式,亮度调节,呼吸灯,闪烁,显示1字节数据,四种模式。支持pwm输出控制led,或者用io模拟pwm实现呼吸灯效果。
ucLedTriggerMode,比如当前处于呼吸灯模式,由于响应一些按键,需要灯做出响应,可以用这个实现,用来触发或者永久修改灯的模式
lLedRun,周期运行,暂时只支持1毫秒一次运行。

举例1,IO模式:
LedCtrl_T tLed ;
main()
{
        vLedInit(&tLed,0,1,BREATHING,2);//输出0对应最大灯亮度,输出1代表熄灭灯,呼吸灯模式,2秒周期。
        while(1)
        {
                LED_GPIO = lLedRun(&tLed);
                vTaskDelay(1);//1ms
        }
}
举例2,PWM模式:
LedCtrl_T tLed ;
main()
{
        vLedInit(&tLed,255,0,BREATHING,2);//输出255代表最大亮度,0代表熄灭灯,呼吸灯模式,2秒周期。
        while(1)
        {
                set_pwm_val(lLedRun(&tLed));
                vTaskDelay(1);//1ms
        }
}



/******************************************************************
* 文件名: led_ctrl.c
* 内容简述:
* 控制led灯的亮灭,可实现4种状态,亮度调整,闪烁,呼吸灯,字节显示
* 文件历史:
* 版本号 日期 作者 说明
******************************************************************/

#include "led_ctrl.h"

#define MIN_BRIGHTNESS_HZ       40      //亮度控制频率,测试电路板上led灯在40hz看起来不闪烁
#define MAX_TWINKLING_HZ      50      //闪烁频率,赫兹
#define MIN_TWINKLING_HZ      1       //闪烁频率,赫兹
#define MAX_BREATHING_S         10      //呼吸灯时间,秒
#define MIN_BREATHING_S         1       //呼吸灯时间,秒
#define START_BIT_DELAY_MS      1000    //启动位延时时间,毫秒
#define ONE_BIT_DELAY_MS      1000    //1位显示时间
#define STOP_BIT_DELAY_MS       1000    //停止位延时时间,毫秒
#define BIT_1_TWINKLING_HZ      20      //1的显示频率
#define BIT_0_TWINKLING_HZ      5       //0的显示频率

static int32_t lLedBrightnessCtrl(LedCtrl_T *tpLed);
static int32_t lLedTwinklingCtrl(LedCtrl_T *tpLed);
static int32_t lLedBreathingCtrl(LedCtrl_T *tpLed);
static int32_t lLedDisplayByteCtrl(LedCtrl_T *tpLed);

/******************************************************************
* 函数名: ledInit()
* 功 能: 初始化配置
* 输 入:
* tpLed:led指针
* lOnVal:设置点亮电平值,如果是pwm,这两个值的为pwm定时器重装数
* lOffVal:设置熄灭电平值,如果是pwm,这两个值的为pwm定时器重装数
* eMode:
    BRIGHTNESS亮度
    TWINKLING   闪烁
    BREATHING   呼吸灯
    DISPLAYBYTE 字节显示
* ucValue:
    BRIGHTNESS亮度范围:0-100,代表0%-100%
    TWINKLING   闪烁范围:Min_TWINKLING - MAX_TWINKLING,频率,单位Hz
    BREATHING   呼吸灯范围:Min_BREATHING - MAX_BREATHING,周期,单位秒
    DISPLAYBYTE 字节显示范围:0x00~0xFF,起始位1秒常亮,低位在前显示,1快闪,0慢闪,停止位2秒常灭
* 输 出: 无
*/
void vLedInit(LedCtrl_T *tpLed,int32_t lOnVal,int32_t lOffVal,LED_MODE_E eMode,uint8_t ucValue)
{
    tpLed->tConfig.lOnVal = lOnVal;
    tpLed->tConfig.lOffVal = lOffVal;
    tpLed->eMode = eMode;
    tpLed->ucValue = ucValue;
    tpLed->tPriData.lTimeCount_ms = 0;
    tpLed->tPriData.lCurDispBitNum = 0;
    tpLed->tPriData.lTimeTrgger_ms = -1;
    tpLed->tPriData.eModeExp = tpLed->eMode;
    tpLed->tPriData.ucValueExp = tpLed->ucValue;
    tpLed->tPriData.eModeBackup = tpLed->eMode;
    tpLed->tPriData.ucValueBackup = tpLed->ucValue;
}

/******************************************************************
* 函数名: vLedTriggerMode()
* 功 能: 触发led模式,永久修改模式,或者触发一定时间再恢复之前模式
* 输 入:
* tpLed:led指针
* eMode:
    BRIGHTNESS亮度
    TWINKLING   闪烁
    BREATHING   呼吸灯
    DISPLAYBYTE 字节显示
* ucValue:
    BRIGHTNESS亮度范围:0-100,代表0%-100%
    TWINKLING   闪烁范围:Min_TWINKLING - MAX_TWINKLING,频率,单位Hz
    BREATHING   呼吸灯范围:Min_BREATHING - MAX_BREATHING,周期,单位秒
    DISPLAYBYTE 字节显示范围:0x00~0xFF,起始位1秒常亮,低位在前显示,1快闪,0慢闪,停止位2秒常灭
* lTime:触发新的模式持续时间,单位,毫秒。注意,如果为0,则永久修改模式
* 输 出: 成功设置,返回0,失败,返回1
*/
uint8_t ucLedTriggerMode(LedCtrl_T *tpLed,LED_MODE_E eMode,uint8_t ucValue,int32_t lTime_ms)
{
    if(tpLed->tPriData.lTimeTrgger_ms == -1)
    {
      tpLed->tPriData.eModeExp = eMode;
      tpLed->tPriData.ucValueExp = ucValue;
      if(lTime_ms == 0)
      {
            tpLed->tPriData.eModeBackup = tpLed->eMode;
            tpLed->tPriData.ucValueBackup = tpLed->ucValue;
      }
      else
      {
            tpLed->tPriData.eModeBackup = tpLed->tPriData.eModeExp;
            tpLed->tPriData.ucValueBackup = tpLed->tPriData.ucValueExp;
      }
      tpLed->tPriData.lTimeTrgger_ms = lTime_ms;
      return 0;
    }
    else
    {
      return 1;
    }
}

/******************************************************************
* 函数名: lLedSoftPWMGenerator()
* 功 能: 模拟PWM发生器
* 输 入:
* lTimeCount_ms:定时器时间,可以大于lPeriodSet_ms,会自动计算余数用于调整占空比,毫秒
* lPeriodSet_ms:周期时间,毫秒
* lDutyRatio_PCT:百分比,0-100
* 输 出: 当前输出电平,1高,0低
*/
static int32_t lLedSoftPWMGenerator(int32_t lTimeCount_ms,int32_t lPeriodSet_ms,uint8_t ucDutyRatioSet_PCT)
{
    int32_t lHighLevelTime_ms = lPeriodSet_ms * ucDutyRatioSet_PCT / 100;
    if((lTimeCount_ms % lPeriodSet_ms) < lHighLevelTime_ms)
    {
      return 1;
    }
    else
    {
      return 0;
    }
}

/******************************************************************
* 函数名: lLedBrightnessCtrl()
* 功 能: 亮度控制
* 输 入:
* tpLed:led指针
* 输 出: IO控制量
*/
static int32_t lLedBrightnessCtrl(LedCtrl_T *tpLed)
{
    int32_t lReturnVal = 0;
    int32_t lTempCheck = tpLed->tConfig.lOnVal - tpLed->tConfig.lOffVal;
    if(lTempCheck >= -1 && lTempCheck <= 1)//io模式
    {
      int32_t lPeriod_ms = (int32_t)(1000 / MIN_BRIGHTNESS_HZ);
      int32_t lPWMLevel = lLedSoftPWMGenerator(tpLed->tPriData.lTimeCount_ms,lPeriod_ms,tpLed->ucValue);
      lReturnVal = lPWMLevel == 1 ? tpLed->tConfig.lOnVal : tpLed->tConfig.lOffVal;
      tpLed->tPriData.lTimeCount_ms++;
      if(tpLed->tPriData.lTimeCount_ms > lPeriod_ms)
      {
            tpLed->tPriData.lTimeCount_ms = 0;
      }
    }
    else//pwm模式
    {
      if(lTempCheck > 1)
      {
            lReturnVal = tpLed->ucValue / 100 * lTempCheck + tpLed->tConfig.lOffVal;
      }
      else if(lTempCheck < -1)
      {
            lReturnVal = tpLed->ucValue / 100 * -lTempCheck - tpLed->tConfig.lOffVal;
      }
    }
    return lReturnVal;
}

/******************************************************************
* 函数名: lLedTwinklingCtrl()
* 功 能: 闪烁控制
* 输 入:
* tpLed:led指针
* 输 出: IO控制量
*/
static int32_t lLedTwinklingCtrl(LedCtrl_T *tpLed)
{   
    int32_t lTemp_Hz = 0;
    int32_t lReturnVal = 0;
    int32_t lPeriod_ms = 0;
    int32_t lPWMLevel = 0;
    lTemp_Hz = tpLed->ucValue < MIN_TWINKLING_HZ ? MIN_TWINKLING_HZ : tpLed->ucValue;
    lTemp_Hz = tpLed->ucValue > MAX_TWINKLING_HZ ? MAX_TWINKLING_HZ : tpLed->ucValue;
    lPeriod_ms = 1000 / lTemp_Hz;
    lPWMLevel = lLedSoftPWMGenerator(tpLed->tPriData.lTimeCount_ms,lPeriod_ms,50);
    lReturnVal = lPWMLevel == 1 ? tpLed->tConfig.lOnVal : tpLed->tConfig.lOffVal;
    tpLed->tPriData.lTimeCount_ms++;
    if(tpLed->tPriData.lTimeCount_ms > lPeriod_ms)
    {
      tpLed->tPriData.lTimeCount_ms = 0;
    }
    return lReturnVal;
}

/******************************************************************
* 函数名: lLedBreathingCtrl()
* 功 能: 呼吸灯控制
* 输 入:
* tpLed:led指针
* 输 出: IO控制量
*/
static int32_t lLedBreathingCtrl(LedCtrl_T *tpLed)
{   
    int32_t lPeriod_ms = 0;
    int32_t lRiseUpTime_ms = 0;
    int32_t lReturnVal = 0;   
    float fBrightnessPCT = 0.0f;
    lPeriod_ms = tpLed->ucValue < MIN_BREATHING_S ? MIN_BREATHING_S * 1000 : tpLed->ucValue * 1000 ;
    lPeriod_ms = tpLed->ucValue > MAX_BREATHING_S ? MAX_BREATHING_S * 1000 : tpLed->ucValue * 1000 ;
    lRiseUpTime_ms = lPeriod_ms / 2;
    //亮度切换
    if(tpLed->tPriData.lTimeCount_ms <= lRiseUpTime_ms)//由暗变亮
    {
      fBrightnessPCT = (float)(tpLed->tPriData.lTimeCount_ms) / (float)lRiseUpTime_ms;
    }
    else if(tpLed->tPriData.lTimeCount_ms > lRiseUpTime_ms && tpLed->tPriData.lTimeCount_ms <= lPeriod_ms)//由亮变暗
    {
      fBrightnessPCT = (float)(lPeriod_ms - tpLed->tPriData.lTimeCount_ms) / (float)lRiseUpTime_ms;
    }
    else
    {
      fBrightnessPCT = 0.0f;
      tpLed->tPriData.lTimeCount_ms = 0;
    }
    //调光曲线
    fBrightnessPCT = fBrightnessPCT * fBrightnessPCT;
    //亮度控制
    {
      int32_t lTempCheck = tpLed->tConfig.lOnVal - tpLed->tConfig.lOffVal;
      if(lTempCheck >= -1 && lTempCheck <= 1)//io模式
      {
            int32_t lPeriod_ms = (int32_t)(1000 / MIN_BRIGHTNESS_HZ);
            int32_t lPWMLevel = lLedSoftPWMGenerator(tpLed->tPriData.lTimeCount_ms,lPeriod_ms,(uint8_t)(fBrightnessPCT * 100.0f));
            lReturnVal = lPWMLevel == 1 ? tpLed->tConfig.lOnVal : tpLed->tConfig.lOffVal;
      }
      else//pwm模式
      {
            if(lTempCheck > 1)
            {
                lReturnVal = (int32_t)(fBrightnessPCT * (float)lTempCheck + (float)tpLed->tConfig.lOffVal);
            }
            else if(lTempCheck < -1)
            {
                lReturnVal = (int32_t)(fBrightnessPCT * -(float)lTempCheck - (float)tpLed->tConfig.lOffVal);
            }
      }
    }
    tpLed->tPriData.lTimeCount_ms++;
    return lReturnVal;
}


/******************************************************************
* 函数名: lLedDisplayByteCtrl()
* 功 能: 字节显示控制
* 输 入:
* tpLed:led指针
* 输 出: IO控制量
*/
static int32_t lLedDisplayByteCtrl(LedCtrl_T *tpLed)
{   
    int32_t lReturnVal = 0;
    if(tpLed->tPriData.lCurDispBitNum == 0)
    {
      tpLed->tPriData.lTimeCount_ms = 0;
      tpLed->tPriData.lCurDispBitNum++;
    }
    else if(tpLed->tPriData.lCurDispBitNum == 1)//起始位,常亮
    {         
      lReturnVal = tpLed->tConfig.lOnVal;
      if(tpLed->tPriData.lTimeCount_ms > START_BIT_DELAY_MS)
      {
            tpLed->tPriData.lTimeCount_ms = 0;
            tpLed->tPriData.lCurDispBitNum++;
      }
    }
    else if(tpLed->tPriData.lCurDispBitNum >= 2 && tpLed->tPriData.lCurDispBitNum <= 9)//数据位
    {
      uint8_t ucCurBitDisplayVal = (tpLed->ucValue >> (tpLed->tPriData.lCurDispBitNum - 2)) & 0x01;
      int32_t lPeriod_ms = ucCurBitDisplayVal == 1 ? 1000 / BIT_1_TWINKLING_HZ : 1000 / BIT_0_TWINKLING_HZ;
      int32_t lPWMLevel = lLedSoftPWMGenerator(tpLed->tPriData.lTimeCount_ms,lPeriod_ms,50);
      lReturnVal = lPWMLevel == 1 ? tpLed->tConfig.lOnVal : tpLed->tConfig.lOffVal;
      if(tpLed->tPriData.lTimeCount_ms > ONE_BIT_DELAY_MS)
      {
            tpLed->tPriData.lTimeCount_ms = 0;
            tpLed->tPriData.lCurDispBitNum++;
      }
    }
    else if(tpLed->tPriData.lCurDispBitNum == 10)//停止位
    {      
      lReturnVal = tpLed->tConfig.lOffVal;
      if(tpLed->tPriData.lTimeCount_ms > STOP_BIT_DELAY_MS)
      {
            tpLed->tPriData.lTimeCount_ms = 0;
            tpLed->tPriData.lCurDispBitNum++;
      }
    }
    else
    {
      tpLed->tPriData.lCurDispBitNum = 0;
    }
    tpLed->tPriData.lTimeCount_ms++;
    return lReturnVal;
}

/******************************************************************
* 函数名: lLedRun()
* 功 能: 运行led,并返回IO控制量
* 输 入:
* tpLed:led指针
* 输 出: IO控制量
*/
int32_t lLedRun(LedCtrl_T *tpLed)
{
    if(tpLed->tPriData.lTimeTrgger_ms == 0)
    {
      tpLed->eMode = tpLed->tPriData.eModeBackup;
      tpLed->ucValue = tpLed->tPriData.ucValueBackup;
    }
    else if(tpLed->tPriData.lTimeTrgger_ms > 0)
    {
      tpLed->eMode = tpLed->tPriData.eModeExp;
      tpLed->ucValue = tpLed->tPriData.ucValueExp;
    }
    else
    {
      tpLed->tPriData.lTimeTrgger_ms = -1;
    }
    tpLed->tPriData.lTimeTrgger_ms--;
    switch(tpLed->eMode)
    {
      case BRIGHTNESS:    //亮度范围:0-100,代表0%-100%
            return lLedBrightnessCtrl(tpLed);
      case TWINKLING:   //闪烁范围:Min_TWINKLING - MAX_TWINKLING,频率,单位Hz
            return lLedTwinklingCtrl(tpLed);
      case BREATHING:   //呼吸灯范围:Min_BREATHING - MAX_BREATHING,周期,单位秒
            return lLedBreathingCtrl(tpLed);
      case DISPLAYBYTE:   //字节显示
            return lLedDisplayByteCtrl(tpLed);
      default:
            return tpLed->tConfig.lOffVal;
    }
}


/******************************************************************
* 文件名: led_ctrl.h
* 内容简述:
* 控制led灯的亮灭,可实现4种状态,亮度调整,闪烁,呼吸灯,字节显示
* 文件历史:
* 版本号 日期 作者 说明
******************************************************************/

#ifndef __LED_CTRL_H
#define __LED_CTRL_H

#ifdef __cplusplus
extern "C" {
#endif

#include <stdint.h>

/*枚举led驱动模式*/
typedef enum tagLED_MODE_E
{
    BRIGHTNESS = 0, //亮度
    TWINKLING,      //闪烁
    BREATHING,      //呼吸灯
    DISPLAYBYTE   //字节显示
}LED_MODE_E;

/*根据实际硬件,设置lOnVal和lOffVal,IO为1或者0,PWM控制为定时器重装值*/
typedef struct tagLedConfig_T
{
    int32_t lOnVal;
    int32_t lOffVal;
}LedConfig_T;

/*运行所需内部变量*/
typedef struct tagLedPrivateData_T
{
    int32_t lTimeCount_ms;//内部计时器
    int32_t lCurDispBitNum; //字节显示模式中,用于标记当前显示的是哪位
    int32_t lTimeTrgger_ms; //触发,计时器
    LED_MODE_E eModeExp;    //触发,期望模式
    uint8_t ucValueExp;   //触发,期望模式值
    LED_MODE_E eModeBackup; //触发,备份当前模式
    uint8_t ucValueBackup;//触发,备份当前值
}LedPrivateData_T;

/*描述一个led*/
typedef struct tagLedCtrl_T
{
      LED_MODE_E eMode;
    uint8_t ucValue;
    LedConfig_T tConfig;
    LedPrivateData_T tPriData;
}LedCtrl_T;

/*外部调用函数*/
void vLedInit(LedCtrl_T *tpLed,int32_t lOnVal,int32_t lOffVal,LED_MODE_E eMode,uint8_t ucValue);
uint8_t ucLedTriggerMode(LedCtrl_T *tpLed,LED_MODE_E eMode,uint8_t ucValue,int32_t lTime_ms);
int32_t lLedRun(LedCtrl_T *tpLed);

#ifdef __cplusplus
}
#endif

#endif

whatcanitbe 发表于 2022-11-9 09:04:19

LED都能被你玩出了花

我是一个大白菜 发表于 2022-11-9 15:49:21

感谢分享

liang16888 发表于 2022-11-9 16:06:25


感谢分享 Thank you

52HLX 发表于 2022-11-9 16:07:28


感谢分享

maxking 发表于 2022-11-11 08:16:41

感谢分享!

cloudxxcloud 发表于 2022-11-11 13:01:58

点个灯而已,何必这么复杂

hecat 发表于 2022-11-11 19:14:17

有用的自然有用

sweet_136 发表于 2022-11-11 21:35:39

卧槽。。这也太难了吧

colinzhao 发表于 2022-11-11 21:56:00

亮瞎眼了!

swdebug 发表于 2022-11-12 00:30:57

实际应用的话这样设计不合理, 应该采用定时器方式更新pwm值.

advarx21ic 发表于 2022-11-14 22:49:27

学习了,谢谢分享

tang_qianfeng 发表于 2022-11-14 22:55:31

pwm方式最省事

1a2b3c 发表于 2022-11-14 23:27:12

点个灯泡比我所有的程序都复杂:(
我们这些不懂程序的人该咋办。。。

kinoko 发表于 2022-11-15 01:10:26

太复杂,pwm+查表调亮度,省时省力。
页: [1]
查看完整版本: 分享,自己用的led闪烁程序。