杂记:AI优化博客——LED呼吸流水灯发波控制

背景介绍

应用场景LED呼吸灯和流水灯效果广泛应用于智能家居、智能设备、汽车仪表盘等领域,用于提升用户体验和设备的美观性。例如,智能家居设备可以通过呼吸灯效果显示设备状态,汽车仪表盘可以通过流水灯效果显示车辆信息。技术背景LED灯控制的基本原理是通过调整LED的亮度来实现不同的视觉效果。呼吸灯效果通过模拟正弦波来实现亮度的渐变,而流水灯效果则通过控制多个LED灯的亮灭顺序来实现动态效果。

算法原理

正弦波生成呼吸灯效果的核心是通过正弦波来控制LED的亮度。正弦波的值范围为[-1, 1],通过将其映射到[0, 100]的范围,可以得到适合控制LED亮度的值。以下是正弦波生成的数学公式:
brightness = basisDuty + (100 - basisDuty) * sin(θ)
其中:
• brightness:当前亮度值。
• basisDuty:基础占空比,表示最小亮度。
• θ:当前相位,范围为[0, 2π]。

流水效果实现
流水灯效果通过控制多个LED灯的亮灭顺序实现。每个LED灯的亮度值由正弦波计算得出,通过调整相位偏移(phaseDiff)和周期(blinkCycle),可以实现不同的流水效果。
关键参数说明
• basisDuty:基础占空比,表示最小亮度。
• phaseDiff:相位偏移,用于控制流水灯的流动速度。
• blinkCycle:刷新周期,用于控制呼吸灯的频率。
• seg_num:段数,表示流水灯的分段数量。

代码实现

算法QT模拟

算法QT模拟,输出正弦波曲线。曲线有基准占空比,设置曲线条数,偏移相位,周期。20ms计算一次曲线当前值,在一定刷新周期(0.1ms)和输出值判断当前是否电量LED灯。根据占空比实时控制LED灯的亮度。

#define VAL_100xSIN_NUM		300
uint8_t VAL_100xSIN[] = {
    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
    20, 21, 22, 23, 24, 25, 26, 27, 29, 30, 31, 31, 32, 33, 34, 35, 36, 37, 38, 39,
    40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 51, 52, 53, 54, 55, 56, 57, 58,
    58, 59, 60, 61, 62, 63, 63, 64, 65, 66, 67, 67, 68, 69, 70, 70, 71, 72, 72, 73,
    74, 75, 75, 76, 77, 77, 78, 79, 79, 80, 81, 81, 82, 82, 83, 83, 84, 85, 85, 86,
    86, 87, 87, 88, 88, 89, 89, 90, 90, 91, 91, 91, 92, 92, 93, 93, 93, 94, 94, 94,
    95, 95, 95, 96, 96, 96, 96, 97, 97, 97, 97, 98, 98, 98, 98, 98, 99, 99, 99, 99,
    99, 99, 99, 99, 99, 99, 100, 100, 100, 100, 100, 100, 100, 100, 100, 99, 99, 99, 99, 99,
    99, 99, 99, 99, 99, 98, 98, 98, 98, 98, 97, 97, 97, 97, 96, 96, 96, 96, 95, 95,
    95, 94, 94, 94, 93, 93, 93, 92, 92, 91, 91, 91, 90, 90, 89, 89, 88, 88, 87, 87,
    86, 86, 85, 85, 84, 83, 83, 82, 82, 81, 81, 80, 79, 79, 78, 77, 77, 76, 75, 75,
    74, 73, 72, 72, 71, 70, 70, 69, 68, 67, 67, 66, 65, 64, 63, 63, 62, 61, 60, 59,
    58, 58, 57, 56, 55, 54, 53, 52, 51, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41,
    40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 31, 30, 29, 27, 26, 25, 24, 23, 22, 21,
    20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10,  9,  8,  7,  6,  5,  4,  3,  2,  1,
};

int basisDuty = 20, phaseDiff = 100; /* 30° / 180 ° 设置范围45-180 太小没必要 45 是看段数最大值确定的 phaseDiff*seg>=180最佳 */
int soc[5]={basisDuty,basisDuty,basisDuty,basisDuty,basisDuty};
static uint16_t cnt = 0;
cnt++;
uint8_t blinkCycle = 1, seg_num = 3;
uint16_t blinkCycleMaxCnts = VAL_100xSIN_NUM*blinkCycle/6;              /* 一个单独周期计数数量 不同周期跳不同的index间隔 */
uint16_t total_cnts=blinkCycleMaxCnts*seg_num*phaseDiff/180; /* 一个总的循环周期计数数量 */
if((1 == seg_num)||(total_cnts<blinkCycleMaxCnts))
{
    total_cnts = blinkCycleMaxCnts;
}
if(cnt>=total_cnts)
{
    cnt = 0;
}
uint16_t index = 0;

for(int i = 1; i<=5; i++)
{
    index = (VAL_100xSIN_NUM*seg_num + 6*cnt/blinkCycle - VAL_100xSIN_NUM*(i-1)*phaseDiff/180)%VAL_100xSIN_NUM;     /* CAL_blinkCycle_MS计算占空比index + 相位4*phaseDiff/180*VAL_100xSIN_NUM + 取余数 */
        if((seg_num>=i)&&(cnt>blinkCycleMaxCnts*(i-1)*phaseDiff/180 && cnt<blinkCycleMaxCnts*((i-1)*phaseDiff+180)/180))
{
    soc[i-1] = VAL_100xSIN[index]*(100-basisDuty)/100 + basisDuty;
}
else if((seg_num>=i)&&(cnt%total_cnts)<(blinkCycleMaxCnts*(180-(seg_num-(i-1))*phaseDiff)/180))
{
    index = (index + seg_num*VAL_100xSIN_NUM*phaseDiff/180)%VAL_100xSIN_NUM;
            soc[i-1] = VAL_100xSIN[index]*(100-basisDuty)/100 + basisDuty;
}
}
    ui->widgetPlot2D->addData("soc[0]", soc[0]);
    ui->widgetPlot2D->addData("soc[1]", soc[1]);
    ui->widgetPlot2D->addData("soc[2]", soc[2]);
    ui->widgetPlot2D->addData("soc[3]", soc[3]);
    ui->widgetPlot2D->addData("soc[4]", soc[4]);

输出效果

不同颜色的输出波形代表的时LED1-5输出占空比的变化。
在这里插入图片描述

STM32控制程序


#ifndef __LED_CTRL_C
#define __LED_CTRL_C

/******************************************************************************
* Include Files
******************************************************************************/
#include "ledctrl.h"

/******************************************************************************
* Static Variable Definitions
******************************************************************************/
uint8_t TIM100US;
LedCtrlSt gLedCtrl;
 
 
#define VAL_100xSIN_NUM		300
uint8_t VAL_100xSIN[VAL_100xSIN_NUM] = {
 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 
40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50, 51, 52, 53, 54, 55, 56, 57, 57, 
58, 59, 60, 61, 62, 62, 63, 64, 65, 66, 66, 67, 68, 69, 70, 70, 71, 72, 72, 73, 
74, 75, 75, 76, 77, 77, 78, 79, 79, 80, 80, 81, 82, 82, 83, 83, 84, 85, 85, 86, 
86, 87, 87, 88, 88, 89, 89, 90, 90, 90, 91, 91, 92, 92, 93, 93, 93, 94, 94, 94, 
95, 95, 95, 96, 96, 96, 96, 97, 97, 97, 97, 98, 98, 98, 98, 98, 98, 99, 99, 99, 
99, 99, 99, 99, 99, 99, 99, 100, 100, 100, 100, 100, 100, 100, 99, 99, 99, 99, 99, 99, 
99, 99, 99, 99, 98, 98, 98, 98, 98, 98, 97, 97, 97, 97, 96, 96, 96, 96, 95, 95, 
95, 94, 94, 94, 93, 93, 93, 92, 92, 91, 91, 90, 90, 90, 89, 89, 88, 88, 87, 87, 
86, 86, 85, 85, 84, 83, 83, 82, 82, 81, 80, 80, 79, 79, 78, 77, 77, 76, 75, 75, 
74, 73, 72, 72, 71, 70, 70, 69, 68, 67, 66, 66, 65, 64, 63, 62, 62, 61, 60, 59, 
58, 57, 57, 56, 55, 54, 53, 52, 51, 50, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 
40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 
20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10,  9,  8,  7,  6,  5,  4,  3,  2,  1,
};


/******************************************************************************
* Static Function Prototype Definitions
******************************************************************************/
/**
 * @brief  调试灯控制接口
 * @param  定时器周期Tus 默认100us
 * @retval 
 */
void debug_led_ctrl(uint8_t Tus)
{	
	static uint16_t Count = 0;
	Count++;					
	if(((Count*Tus) % 100000) < 50000)
	{
		SYSY_RUN_LED_ON;
	}
	else
	{
		SYSY_RUN_LED_OFF;
	}
}
/**
 * @brief  告警指示灯接口
 * @param  
 * @retval 
 */
static void alarm_led_ctrl(uint8_t onoff)
{
	if(ON == onoff)
	{
		ALARM_LED_ON;
	}
	else
	{
		ALARM_LED_OFF;	
	}
}

/**
 * @brief  SOC显示灯接口
 * @param  
 * @retval 
 */
static void soc_led_ctrl(uint8_t pin, uint8_t onoff)
{
	if(ON == onoff)
	{
		switch(pin)
		{
			case 0:SOC_LED1_ON;break;
			case 1:SOC_LED2_ON;break;
			case 2:SOC_LED3_ON;break;
			case 3:SOC_LED4_ON;break;		
			case 4:SOC_LED5_ON;break;
			default:break;
		}
	}
	else
	{
		switch(pin)
		{
			case 0:SOC_LED1_OFF;break;
			case 1:SOC_LED2_OFF;break;
			case 2:SOC_LED3_OFF;break;
			case 3:SOC_LED4_OFF;break;
			case 4:SOC_LED5_OFF;break;
			default:break;
		}
	}
}
/**
 * @brief  SOC灯根据SOC计算电量段数
 * @param  
 * @retval 0-(0%)、1-(1-20%)、2-(21-40%)、3-(41-60%)、4-(61-80%)、5-(81-100%)
 */
static uint8_t soc_led_seg_cal(uint8_t soc)
{
	uint8_t seg_num = (soc+19)*5/100;
	return seg_num;
}

/**
 * @brief  SOC显示灯控制状态
 * @param  
 * @retval 
 */
void soc_led_proc(uint8_t Tus, LedCtrlSt stLedCtrl)
{
	static uint16_t DUTY_CNT = 0, CYCLE_CNT = 0;	
	static uint16_t  DUTY_CMP[5] = {0, 0, 0, 0, 0};
	uint16_t seg_num = 0, total_cnts = VAL_100xSIN_NUM, index = 0;
	uint8_t i = 0;
	
	switch(stLedCtrl.state)
	{
		case STATE_IDLE:
		{
			for(i=0;i<5;i++)
			{
				soc_led_ctrl(i, OFF);
			}
			break;
		}
		case STATE_READY:
		{
			DUTY_CNT++;
			for(i=0;i<5;i++)
			{
				if(DUTY_CNT<=stLedCtrl.basisDuty)	/* 固定占空比 */
				{
					soc_led_ctrl(i, ON);
				}
				else if(DUTY_CNT<=100)
				{
					soc_led_ctrl(i, OFF);
				}
				else
				{
					DUTY_CNT = 0;
				}
			}
			break;
		}
		case STATE_CHARGING:
		{
			CYCLE_CNT++;		
			DUTY_CNT++;			
			if(DUTY_CNT>100)	//0.1ms*100 = 10ms - 100Hz刷新率
			{
				DUTY_CNT = 0;
			}
			seg_num = soc_led_seg_cal(stLedCtrl.soc);
			uint16_t blinkCycleMaxCnts = VAL_100xSIN_NUM*stLedCtrl.blinkCycle/6;     /* 一个单独周期计数数量 不同周期跳不同的index间隔 */
			total_cnts = blinkCycleMaxCnts*seg_num*stLedCtrl.phaseDiff/180;          /* 一个总的循环周期计数数量 */
			if((1 == seg_num)||(total_cnts<blinkCycleMaxCnts))
			{
				total_cnts = blinkCycleMaxCnts;
			}
			if(CYCLE_CNT>=total_cnts)
			{
				CYCLE_CNT = 0;
			}

			for(i=0;i<5;i++)
			{
				if(i<seg_num)
				{
					/* 根据时间实时计算的占空比 */
					if(0 == stLedCtrl.blinkMode)													/* 呼吸灯(一起亮灭) */
					{
						if(CYCLE_CNT>VAL_100xSIN_NUM)
						{
							CYCLE_CNT = 0;
						}
						index = (6*CYCLE_CNT/stLedCtrl.blinkCycle)%VAL_100xSIN_NUM;		/* CAL_CYCLE_MS计算占空比index */
						DUTY_CMP[i] = VAL_100xSIN[index]*(100-stLedCtrl.basisDuty)/100 + stLedCtrl.basisDuty;
					}
					else if(1 == stLedCtrl.blinkMode)												/* 流水灯呼吸灯,最亮部分流动呼吸 */
					{
						index = (VAL_100xSIN_NUM*seg_num + 6*DUTY_CNT/stLedCtrl.blinkCycle - VAL_100xSIN_NUM*(i-1)*stLedCtrl.phaseDiff/180)%VAL_100xSIN_NUM;     /* CAL_blinkCycle_MS计算占空比index + 相位4*phaseDiff/180*VAL_100xSIN_NUM + 取余数 */
						if((seg_num>=i)&&(DUTY_CNT>blinkCycleMaxCnts*(i-1)*stLedCtrl.phaseDiff/180 && DUTY_CNT<blinkCycleMaxCnts*((i-1)*stLedCtrl.phaseDiff+180)/180))
						{
							DUTY_CMP[i-1] = VAL_100xSIN[index]*(100-stLedCtrl.basisDuty)/100 + stLedCtrl.basisDuty;
						}
						else if((seg_num>=i)&&(DUTY_CNT%total_cnts)<(blinkCycleMaxCnts*(180-(seg_num-(i-1))*stLedCtrl.phaseDiff)/180))
						{
							index = (index + seg_num*VAL_100xSIN_NUM*stLedCtrl.phaseDiff/180)%VAL_100xSIN_NUM;
							DUTY_CMP[i-1] = VAL_100xSIN[index]*(100-stLedCtrl.basisDuty)/100 + stLedCtrl.basisDuty;
						}
					}
					if(DUTY_CNT<=DUTY_CMP[i])
					{
						soc_led_ctrl(i, ON);
					}
					else if(DUTY_CNT<=100)
					{
						soc_led_ctrl(i, OFF);
					}
				}
				else
				{
					soc_led_ctrl(i, OFF);
				}
			}
			break;
		}
		case STATE_FINISH:	/*< 根据SOC来判断显示几段 */
		{
			for(i=0;i<5;i++)
			{
				if(i<soc_led_seg_cal(stLedCtrl.soc))
				{
					soc_led_ctrl(i, ON);
				}
				else
				{
					soc_led_ctrl(i, OFF);
				}
			}
			break;
		}
		default:break;
	}
}
/**
 * @brief  SOC显示灯控制参数初始化
 * @param  
 * @retval 
 */
void soc_led_ctrl_init(uint8_t Tus, LedCtrlSt *pstLedCtrl)
{
	pstLedCtrl->state 	   = STATE_IDLE;	
	pstLedCtrl->alarm 	   = 0;		
	pstLedCtrl->blinkMode  = 1;
	pstLedCtrl->blinkCycle = 1;
	pstLedCtrl->basisDuty  = 20;
	pstLedCtrl->phaseDiff  = 135;
}

/**
 * @brief  显示灯控制
 * @param  
 * @retval 
 */
void led_ctrl_proc(uint8_t Tus, LedCtrlSt stLedCtrl)
{
	debug_led_ctrl(Tus);
	
	if(0 == gLedCtrl.alarm)
	{
		alarm_led_ctrl(OFF);
		soc_led_proc(Tus, stLedCtrl);
	}
	else 
	{	
		alarm_led_ctrl(ON);
		for(uint8_t i=0;i<5;i++)	/*< 告警绿灯灭,只显示红灯 */
		{
			soc_led_ctrl(i, OFF);
		}
	}
}
#endif /*__LED_CTRL_C */

以下将根据你提供的代码,按模块分析并生成对应的 mermaid 流程图代码。我们可以将代码分为几个主要模块:初始化模块、调试灯控制模块、告警指示灯控制模块、SOC 显示灯控制模块和整体显示灯控制模块。

1. 初始化模块(soc_led_ctrl_init

这个模块主要对 SOC 显示灯控制参数进行初始化。

开始
设置状态为 STATE_IDLE
设置告警标志为 0
设置闪烁模式为 1
设置闪烁周期为 1
设置基础占空比为 20
设置相位差为 135
结束

2. 调试灯控制模块(debug_led_ctrl

该模块根据定时器周期控制调试灯的开关。

开始
计数器加 1
计数器乘以定时器周期对 100000 取余是否小于 50000
打开调试灯
关闭调试灯
结束

3. 告警指示灯控制模块(alarm_led_ctrl

此模块根据输入的开关状态控制告警指示灯。

开始
输入状态是否为 ON
打开告警指示灯
关闭告警指示灯
结束

4. SOC 显示灯控制模块(soc_led_proc

这个模块根据不同的状态控制 SOC 显示灯,状态包括 STATE_IDLESTATE_READYSTATE_CHARGINGSTATE_FINISH
在这里插入图片描述

5. 整体显示灯控制模块(led_ctrl_proc

该模块综合控制调试灯、告警指示灯和 SOC 显示灯。

开始
调用调试灯控制模块
告警标志是否为 0
关闭告警指示灯
调用 SOC 显示灯控制模块
打开告警指示灯
关闭所有 SOC 显示灯
结束

总结与扩展

这段时间Deepseek比较火,突发奇想通过AI工具来优化博客质量,提高可读性,让读者更容易理解。这也是提高我们博客记录质量的一种尝试。通过KIMI的优化,本文增加了一些原理性的介绍,让初次接触的同学能够更了解本文代码的主题思想。优化前的文章和优化后的文章同时发布出来,并且做了一篇分析文章,对比优化前后的效果。为以后博客撰写质量的提高提供了一种思路。后面还需要更多摸索。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Geek__1992

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值