点一个RGB灯没想到这么多学问 - 基于MicroPython

发布时间 2024-01-12 11:29:52作者: 不才狸子

买来了源地设计的rp2040开发板,上面带一颗WS2812彩灯。那今天上手这块板就不点LED了,直接点RGB。
想起大学第一次上单片机实验课,主要是检验软件是否安装到位,需要编译例程让野火开发板上的LED闪烁。我为了卖弄自己的聪明才智,选择了编译RGB灯的例程给老师检查。老师也没多说什么,无功无过。当时可能还有点生气,后来学Android,后几次实验我交的Kotlin代码那个老师给我不及格的时候,我才念着这个老师的好。

先尝试点亮

from neopixel import NeoPixel
from machine import Pin

pin = Pin(23, Pin.OUT)
np = NeoPixel(pin, 1)
np[0] = (255,0,0)
np.write()

thonny点击直接运行,不亮……没反应。用万用表量了一下,WS2812数据输入引脚根本没连接到GPIO23,用万用表电流挡分别怼到GPIO23和输入引脚倒是亮了。打着台灯研究了一下,再用万用表量到数据输入引脚是从这个短接引脚连接到GPIO23的,一坨锡短接后正常工作。

循环

简单用for循环让红灯呼吸起来。

from neopixel import NeoPixel
from machine import Pin
import time


pin = Pin(23, Pin.OUT)
np = NeoPixel(pin, 1)
np[0] = (0,0,0)
np.write()


tick = 0.01

while True:
    for i in range(0, 255):
        np[0] = (i,0,0)
        np.write()
        time.sleep(tick)
    for i in range(255, 0, -1):
        np[0] = (i,0,0)
        np.write()
        time.sleep(tick)  

首先这代码就很长,两个for循环从0到255又到0,加上其他两个颜色至少还要再嵌套一个循环。混合颜色也是个问题。还有颜色的变化太“线性”,匀速变化直来直去不符合自然事物运动的直觉。

三角函数

循环的效果差而且的代码又臭又长,多控制两个颜色只会更长。如果找一个函数,有增长率快慢变化,上下跳动循环迭代,这不就是三角函数吗?

from neopixel import NeoPixel
from machine import Pin
import time
from math import sin, pi, floor

_5pi = 0.5 * pi

pin = Pin(23, Pin.OUT)
np = NeoPixel(pin, 1)
np[0] = (0,0,0)
np.write()


tick = 0.02


def sin_scaling(x, scal = 255):
    return floor(scal * (sin(x) + 1) / 2)


t = 0
while True:
    t += tick
    time.sleep(tick)
    np[0] = (sin_scaling(t), sin_scaling(t+_5pi), sin_scaling(t+2*_5pi))
    np.write()

终于有幻彩了,可惜我手机拍的效果不好,gif画质也太差,具体效果就自己买个开发板来试试吧。
但是再仔细看,灯的总亮度时亮时暗,这是由于3个三角函数叠加还是三角函数,RGB灯的总功率一直在变化,如图

三相电和gamma校正

这时我想到工业用的三相电正好是三位,而且输出功率稳定,他们之间相位相差120度。输出功率如图

不出意外,做出来的幻彩灯效果已比较理想,发到电子群里给群友看。有个哥们指出还需要进行gamma校正,参考知乎 调色名词浅析——Gamma(伽玛)校正 进行了校正,完善的代码如下。

from neopixel import NeoPixel
from machine import Pin
import time
from math import sin, pi, floor, pow

_66pi = 2 / 3 * pi

pin = Pin(23, Pin.OUT)
np = NeoPixel(pin, 1)
np[0] = (0,0,0)
np.write()


tick = 0.02


def gamma_correction(x):
    return pow(x, 2.2)


def sin_scaling(x, scal = 255):
    return floor(scal*gamma_correction((sin(x) + 1) /2))


t = 0
while True:
    t += tick
    time.sleep(tick)
    np[0] = (sin_scaling(t), sin_scaling(t+_66pi), sin_scaling(t+2*_66pi))
    np.write()

网上对gamma校正的做法是查表,不过rp2040性能强悍,我们这里只谈算法原理,不谈优化。

不足

  1. 没有循环走过所有的颜色,也没有提供合适的方法控制颜色
  2. 二看颜色变换还有些跳跃。网上搜到的方案是在hsv色域变换,然后映射到RGB的值显示,会更加自然。

也是电子群那哥们跟我说,他一个做软件的同事做氛围灯给干跑了。这里面还有更深奥的学问,hsv的变换原理太复杂,我现在学了也记不住,用起来一样抄代码,就不研究了?