前言
点灯方法千千万,自加自减占一半。此博文分享了一种使用数学函数来平滑控制单个LED呈呼吸效果的方法,让点灯也变得高大上。
根据LED发光原理可知:在允许的的电压范围内,施加给LED两端的电压越高,LED的亮度越高,属正比例关系。但按照实际观察来看,效果不尽人意。如给LED两端施加的电压如下图红线所示,在人眼中的效果大致是如下图绿线所示。
(因为是GIF的原因,效果不是这么的明显,可以与文章最下面的GIF对比观察)
实际观察效果:从最暗到最亮,开始施加电压后的一段时间内,亮度变化较大,在电压到达顶峰前的很长一段时间内,LED亮度变化不大。
原因:人眼对不同亮度的敏感程度是不同的,对暗部的敏感要高于亮部。
代码设计
反推函数曲线
根据观察实际效果可知,要使观察亮度均匀变化,只需将亮度变化快时间段内的电压变化率降低,亮度变化慢时间段内的电压变化率增大。
根据电压和观察亮度变化图形可知,周期内,观察亮度呈幂函数曲线,那么只需要将电压的直线修改成符合上边需求的指数函数即可。
设计前提
此方法是基于单片机实时操作系统中,单任务控多LED思想所设计。即:只能在一个task中,操控一条灯带(多个LED)不同小节呈现不同效果,如闪烁、流水等,这个效果变化不能有延迟和不同步。这就要求了这个任务要有自己的时基—— 一个周期自增的计数值。LED的所有变化都依赖这个计数值进行。
注意:此博文只写其中呼吸灯平滑控制的方法,故下文的代码实现只是LED呼吸效果矫正的调用函数。
设计简述
将周期性自加的计数值转化成周期三角波(红色),再将三角波经过矫正(蓝色)。
函数及曲线图
在图形计算器Desmos中复制粘贴下列代码或者依照图片输入函数,可以直接观察到曲线。
z=\frac{A}{\pi}\arcsin\left(\sin\left(\frac{2\pi x}{T}-\frac{\pi}{2}\right)\right)+\frac{A}{2}\left|\left{x\ge0\right}\right|
y=\frac{Ab^{\frac{z}{A}}}{b}
注意:灌电流式控制电路的三角波函数相位需要加或减 π。
- 拉电流式LED控制电路
- 灌电流式LED控制电路
代码实现
#include <math.h>
typedef unsigned int BLN_TYPE;
/// @brief 基于周期自增的计数值进行LED呼吸效果的矫正函数
/// @param count 周期自增的计数值
/// @param A 函数曲线中的的振幅,可以控制LED的最高亮度。
/// @param T 函数曲线中的的周期。注意:这里填写的周期需要乘算计算值自增周期才是呼吸灯实际的周期,
/// 如计算值自增周期是10ms,这里填写100,那么表现的呼吸灯周期为1000ms
/// @param gamma_b 矫正值,注意:最好的值应该是振幅的10倍,用户可以依照效果进行调整
/// @return 经过矫正后的值。注意:返回值的取值范围与振幅的取值范围是一样的
BLN_TYPE LED_BLN(unsigned int count, BLN_TYPE A, unsigned int T, unsigned int gamma_b)
{
float z;
// z=(A/PI) * asin(sin( 2.f*PI*count/ (float)T - PI/2.f )) + A/2.f ;//产生周期三角波
// return (BLN_TYPE)((A*( pow(gamma_b,z/(float)A)) / gamma_b);//矫正
//简化
z= asin(sin( 2.f*PI*count/ (float)T - PI*0.5f )) / PI + 0.5f ;//产生周期三角波,拉电流
// z= asin(sin( 2.f*PI*count/ (float)T - PI*1.5f )) / PI + 0.5f ;//产生周期三角波,灌电流
return (BLN_TYPE)(A* pow(gamma_b,z)/ gamma_b);//矫正
}
效果展示
总结
正如前言,点灯方法千千万,此博文的函数式只是博主去参考一些文章后,根据项目需求所写的,它可以调整呼吸效果周期和呼吸效果的最高亮度,极大方便了项目的迭代。而在写出这个简单算法的几个月后,博主还发现了一个更为简单的调光方法,搜DALI调光曲线(嗯!我重复造了个轮子)。
参考文章1;参考文章2;
如果对你有用,请点个赞吧!