这篇文章大部分内容是参考一篇国外极客写的文章,然后加上我们的理解以及其他文献资料的补充来帮助读者理解。在这里不得不佩服原作者的分享精神以及理论结合实际的能力,因为他的文章确实帮助我们解决了在线性调LED亮度中遇到的问题。
lightness = LED 亮度 luminance = LED 的照度(单位:流明),即单位面积上通过的光量
其实用PWM控制LED的亮度并不是你想的那么简单。下图描绘出PWM是一种通过调整占空比的方式工作的方波:
通过调整PWM高电平的占空比,我们可以控制LED的亮度(lightness)。大部分单片机都带PWM外设,或者你用软件模拟一个PWM也行。
然而当我们用下面的代码线性控制PWM输出时,LED的亮度(lightness)在我们人眼视觉的上看却不是线性的变化:
void loop() { byte i = 0; while (1) { analogWrite(2, i); delay(10); i++; } }
可以看到,从暗到亮,线性效果并不是那么好,在前一段亮度增加的非常快,后期大部分时间都是在全亮的状态,如下图:
这背后的原因就是人眼对光的感知并不是线性的,而是呈log函数形式,那么解决问题的方法就来了!
为了解决问题,我们必须让PWM的输出符合人眼的视觉感知曲线。下面的CIE 1931 lightness公式即描绘了我们人眼是如何感知光的亮度:
将公式转换一下得到:
Y = (L* / 903.3) if L* ≤ 8
Y = ((L* + 16) / 119)^3 if L* > 8
分析一下上面的公式,L就是我们人眼感知的亮度,Y就是光线的照度流明,那么怎么将L、Y以及PWM联系起来呢?通过查阅相关文献,我知道了luminance和PWM是呈正比的关系,其实也很好解释,PWM为高电平时LED导通,导通后就有光通过,而luminance表征的正是光通过的量。参考文献见下图:
然后根据上面的公式,我们只需要将L作为输入,线性的给L赋值,就可以得到对应的Y(同PWM)。
如果在单片机中实现上面的公式会很慢,应为涉及到除法及开立方运算。所以需要我们生成一个保存运算结果的表格供单片机查询即可。下面的Python 脚本程序即提供了自动生成L->PWM表格的功能。Python 脚本如何使用请自行学习。
INPUT_SIZE = 255 # Input integer size OUTPUT_SIZE = 255 # Output integer size INT_TYPE = 'const unsigned char' TABLE_NAME = 'cie'; def cie1931(L): L = L*100.0 if L <= 8: return (L/902.3) else: return ((L+16.0)/116.0)**3 x = range(0,int(INPUT_SIZE+1)) y = [round(cie1931(float(L)/INPUT_SIZE)*OUTPUT_SIZE) for L in x] f = open('cie1931.h', 'w') f.write('// CIE1931 correction table\n') f.write('// Automatically generated\n\n') f.write('%s %s[%d] = {\n' % (INT_TYPE, TABLE_NAME, INPUT_SIZE+1)) f.write('\t') for i,L in enumerate(y): f.write('%d, ' % int(L)) if i % 10 == 9: f.write('\n\t') f.write('\n};\n\n') f.close()
在电脑上执行上面的Python脚本代码后,会在本地生成一个.h头文件,里面包含一个Lightness 和 PWM关系的一维数组表格。 脚本中 INPUT_SIZE = 255 表示将LED的亮度(lightness)等分成256阶,即256灰阶度,OUTPUT_SIZE = 255 表示你用的是一个8-bit 的PWM。下面举个栗子: 比如你的单片机用的是10-bit PWM外设,而LED的亮度你希望等分成32阶,那么你需要将INPUT_SIZE设置成31,OUTPUT_SIZE 设置成1023。最后运行脚本你会生成一个包含32个元素的一维数组,元素值域在0~1023,形如:
const unsigned char cie[32] = {0, 4, 7, … , 940, 1023};
将生成数组应用到单片机程序中,用如下代码测试:
#include "cie1931.h" void loop() { byte i = 0; while (1) { analogWrite(2, cie[i]); delay(10); i++;
最后,可以看到LED的亮度在我们人眼的观察下,最终呈现线性的变化!
简单、快捷、高效、强大的物联网开发板
© 2022. All Rights Reserved. 粤ICP备2021058065号