跳到主要内容

LEDC - 发光二极管控制器

LEDC(Light Emitting Diode Controller),发光二极管控制器。支持驱动目前市场上已有成熟的智能外控 LED,例如 WS2812 系列,每个 LED 的三基色均可实现 256 级亮度显示,因此整个 LED 可完成 256ˆ3(即 16777216)种颜色的全真色彩显示。模块支持最大1024颗外控 LED级联。

模块介绍​

LED 典型电路如图所示,其中 DI 表示控制数据输入脚,DO 表示控制数据输出脚。DI 端接收从控制器传过来的数据,每个 LED 内部的数据锁存器会存储 24bit 数据,剩余的数据经过内部整形处理电路整形放大后通过 DO 端口开始转发输出给下一个级联的 LED。因此,每经过一个LED,数据减少 24bit。

image-20230324173621026

LED典型电路如上图所示,其中 DI 表示控制数据输入脚,DO 表示控制数据输出脚。DI 端接受从控制器传输过来的数据,每个 LED 内部的数据锁存器会存储 24bit (分别对应 R,G,B 三种颜色)数据,剩余的数据经过内部整形处理电路整形放大后通过 DO 端口开始转发输出给下一个级联的 LED 。因此,每经过一个 LED ,数据减少 24bit。

image-20240329003829327

image-20230324173635697

模块配置​

驱动配置​

驱动位于

LED Drivers ->
<*> LEDC Support for Allwinner SoCs

image-20240329004108251

设备树配置​

在设备树中配置如下:

在 SoC 的 dtsi 文件中提炼了内存基地址、中断控制、时钟等共性信息,是该类芯片所有平台的模块配置

ledc: ledc@2008000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "allwinner,sunxi-leds";
reg = <0x0 0x02008000 0x0 0x400>;
interrupts = <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&ccu CLK_LEDC>, <&ccu CLK_BUS_LEDC>;
clock-names = "clk_ledc", "clk_cpuapb";
resets = <&ccu RST_BUS_LEDC>;
reset-names = "ledc_reset";
dmas = <&dma 42>, <&dma 42>;
dma-names = "rx", "tx";
status = "disabled";
};

在设备端,配置了 LEDC 的引脚,配置位于 pio 节点下

&pio {
ledc_pins_a: ledc@0 {
pins = "PG0";
function = "ledc";
drive-strength = <10>;
};

ledc_pins_b: ledc@1 {
pins = "PG0";
function = "gpio_in";
};
}

配置灯珠数量,时序等

&ledc  {
pinctrl-names = "default", "sleep";
pinctrl-0 = <&ledc_pins_a>;
pinctrl-1 = <&ledc_pins_b>;
led_count = <34>;
output_mode = "GRB";
reset_ns = <84>;
t1h_ns = <800>;
t1l_ns = <320>;
t0h_ns = <300>;
t0l_ns = <800>;
wait_time0_ns = <84>;
wait_time1_ns = <84>;
wait_data_time_ns = <600000>;
status = "disabled";
};
  • pinctrl-names: 用于表示 0 和 1 的 pinctrl 哪个是默认和休眠状态。
  • pinctrl-0: 引脚配置,这里是默认使用的时候配置。
  • pinctrl-1: 同上,这里是休眠时的配置。
  • led_count: LED 灯的数目,根据硬件配置。
  • output_mode: LED 灯输出模式,根据 LED 灯的 datasheet 进行配置。
  • reset_ns: LED 灯 reset 时间控制。
  • t1h_ns: 1 码高电平时间,根据 LED 灯的 datasheet 进行配置。
  • t1l_ns: 1 码低电平时间,根据 LED 灯的 datasheet 进行配置。
  • t0h_ns: 0 码高电平时间,根据 LED 灯的 datasheet 进行配置。
  • t0l_ns: 0 码低电平时间,根据 LED 灯的 datasheet 进行配置。
  • wait_time0_ns: 两个 LED 数据之间的等待时间,根据 LED 灯的 datasheet 进行配置。
  • wait_time1_ns: 帧数据之间的等待时间,根据 LED 灯的 datasheet 进行配置。
  • wait_data_time_ns: 内部 FIFO 等待数据时间,超过时间触发异常中断。
  • status: 设备状态。

通常,如果想要使用一款新的 LEDC 灯,需要确认上述全部配置项都配置正确,比如说引脚配置以及 LED 灯的参数配置(包括 01 码高低电平时间、reset 时间以及 wait 时间),全部配置正确才能成功点亮。

驱动接口​

驱动会在 /sys/class/leds 注册每个LED对应的设备目录

/sys/class/leds/sunxi_led0r // 红色
/sys/class/leds/sunxi_led0g // 绿色
/sys/class/leds/sunxi_led0b // 蓝色

其中 0 表示LED的编号,如果是 1 号 LED 则是

/sys/class/leds/sunxi_led1r
/sys/class/leds/sunxi_led1g
/sys/class/leds/sunxi_led1b

需要调节第 0 个LED的颜色为白光且最亮,操作如下:

echo 255 > /sys/class/leds/sunxi_led0r/brightness
echo 255 > /sys/class/leds/sunxi_led0g/brightness
echo 255 > /sys/class/leds/sunxi_led0b/brightness

使用 DEMO​

这段代码的功能是打开一个 LED 设备文件,向该设备写入数据并计算帧率,最后关闭设备文件。

#include <linux/input.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>

int main(int argc, char *argv[])
{
int fd;
int led_counts = atoi(argv[1]); /* 将参数转换为整数 */
unsigned char led_raw_buf[led_counts * 3];
struct timeval start, end;
long s, us;
long elapsed;
int time_count = 1000;
float fps;

if ((fd = open("/sys/class/led/light", O_RDWR)) < 0) {
printf("can't open %s(%s)\n", "/sys/class/led/light", strerror(errno));
return -1;
}

gettimeofday(&start, NULL);

for (int i = 0; i < time_count; i++) {
memset(led_raw_buf, ((i * 2) % 255), led_counts * 3); // 根据循环变量 i 设置 LED 数据
write(fd, led_raw_buf, sizeof(led_raw_buf)); // 写入 LED 数据
}

gettimeofday(&end, NULL);

s = end.tv_sec - start.tv_sec;
us = end.tv_usec - start.tv_usec;
elapsed = s * 1000000 + us;
fps = time_count / ((float)elapsed / 1000000); // 计算帧率

printf("test count %d, elapsed %ld us, fps %f\n", time_count, elapsed, fps);

sleep(1);
close(fd);
printf("%s", "test finished\n");

return 0;
}

调试接口​

LEDC 相关的 debugfs 文件节点所在目录为 /sys/kernel/debug/sunxi_leds,节点说明如下:

节点名称功能描述可设置范围
reset_ns设置和读取LED的reset时间80ns-327us
t1h_ns设置和读取1码高电平时间80ns-2560ns
t1l_ns设置和读取1码低电平时间80ns-1280ns
t0h_ns设置和读取0码高电平时间80ns-1280ns
t0l_ns设置和读取0码低电平时间80ns-2560ns
wait_time0_ns设置和读取LED数据之间等待时间80ns-10us
wait_time1_ns设置和读取帧数据之间等待时间80ns-85s
wait_data_time_ns设置和读取LEDC内部FIFO等待数据的时间容忍度80ns-655us
data读取data buffer中的数据-
output_mode设置和读取当前输出模式GRB、GBR、RGB、RBG、BGR、BRG
trans_mode设置和读取数据传输模式CPU或DMA
hwversion查看当前LEDC的硬件版本-

请注意

  1. 设置的时间必须在所说明的时间范围内,否则不会做任何操作。
  2. 最终设置寄存器之后得到的时间均为42ns的整数倍,若通过节点设置的时间不遵循42ns的整数倍,则实际所设置的时间为小于该值的最大能够被42ns整除的数。例如通过reset_ns设置90ns,则设置成功之后的LED reset时间为84ns。

实例:

  • echo 84 > /sys/kernel/debug/sunxi_leds/reset_ns:将LED的reset时间设置为84ns。这个操作用于调整LED的复位时间,以确保LED在正常工作时能够正确地接收数据信号。

  • cat /sys/kernel/debug/sunxi_leds/data:读取data buffer中的数据。该操作用于查看当前LED的数据状态,以便进行调试或监视LED的输出情况。

  • echo RGB > /sys/kernel/debug/sunxi_leds/output_mode:将LED的输出模式设置为RGB。这个操作用于指定LED输出的颜色顺序,以确保LED正确显示所需的颜色。

  • echo DMA > /sys/kernel/debug/sunxi_leds/trans_mode:将LED的数据传输模式设置为DMA。这个操作用于指定LED数据传输的方式,可以选择CPU或DMA,DMA通常能够提供更高的数据传输效率。

  • cat /sys/kernel/debug/sunxi_leds/hwversion:查看当前LEDC的硬件版本。