问题描述
在网上看了很多文章,发现大部分都是用单片机芯片方式控制的LED灯带,即使有相关GPIO控制的,也是建议用PWM的方式实现。
先来看JE2815灯带介绍:
JE2815是一个集控制电路与发光电路于一体的智能外控LED光源,外型与5050LED灯珠相同,每个灯珠都是一个像素点。像素点内包含数字接口、数据锁存、信号整形放大驱动电路,还包含高精度的内部振荡器和12V高压可编程定电流控制部分。
数据协议采用单线归零码的通讯方式,像素点在上电复位以后,DIN端接受从控制器传输过来的数据,首先送过来的24bit数据被第一个像素点提取后,送到像素点内部的数据锁存器,剩余的数据经过内部整形处理电路整形放大后通过DO端口开始转发输出给下一个级联的像素点,每经过一个像素点的传输,信号减少24bit。像素点采用自动整形转发技术,使得该像素点的级联个数不受信号传送的限制,仅仅受限信号传输速度要求。
从原理来看使用GPIO口拉高拉低在µs的时间内实现模拟0码和1码实现数据通信应该是可行的
解决方案:
于是在dts和驱动中增加je2815灯带的相关配置和驱动代码.
// LED灯带
je2815_gpio: je2815-gpio {
compatible = "rgb-je2815";
je2815_gpio = <&gpio0 RK_PC3 GPIO_ACTIVE_LOW>;
status = "okay";
};
但是在测试时发现,按照灯带资料的时间发送0码和1码,并没有达到通信要求,无法点亮灯珠。
使用示波器测量后发现,µs的持续时间实际比写入值要长,这应该是RK-GPIO口控制时,控制函数
本身有一定延迟导致的。于是在多次尝试下测的如下数值可以正常点亮je2815,其他型号灯带不知能否适用。
// Wi:低速传输时间
// JE2815 timings low speed
#define T0H_TIME 300 //0.3us = 300ns
#define T0L_TIME 900 //0.9us = 900ns
#define T1H_TIME 900 //0.6us = 900ns
#define T1L_TIME 300 //0.6us = 300ns
// Wi:复位JE2815
static void je2815_rst(void)
{
gpio_set_value(je2815_gpio, 0);
udelay(80);
}
// Wi:发送0码数据
static void je2815_sendbit_0(void)
{
gpio_set_value(je2815_gpio, 1);
//ndelay(T0H_TIME);
gpio_set_value(je2815_gpio, 0);
ndelay(T0L_TIME);
}
// Wi:发送1码数据
static void je2815_sendbit_1(void)
{
gpio_set_value(je2815_gpio, 1);
ndelay(T1H_TIME);
gpio_set_value(je2815_gpio, 0);
//ndelay(T1L_TIME);
}
直接取消2个ndelay的传输时间,再加上GPIO本身操控延迟,可以正好达到通信时间要求。
驱动由于要求,无法附上,有上面的0码和1码的传输时间控制,一个简单的gpio控制逻辑,相信对大家来说并不是很难。如是在不知如何编写驱动,可自行去github上找一份相关的灯带尝试修改试试。
通过驱动,创建节点,可以实现LED灯带任意数量灯珠的RGB 255色的随意变化和0-100的亮度控制,通过线程实现呼吸灯效果等。
附上关键函数:
// Wi:写三个字节的数据,表示LED灯的颜色
static void je2815_Write_24Bits(uint8_t red, uint8_t green, uint8_t blue, int brightness)
{
unsigned long flags;
unsigned int n = 255;
// 根据亮度值计算新的n值,0~100
n = (n * brightness) / 100;
// 将范围从0~255缩小到0~n
red = (red * n) / 255;
green = (green * n) / 255;
blue = (blue * n) / 255;
spin_lock_irqsave(&lock, flags);
je2815_Write_Byte(red);
je2815_Write_Byte(green);
je2815_Write_Byte(blue);
spin_unlock_irqrestore(&lock, flags);
}