更新时间:2022-03-02 02:05:58下载pdf
涂鸦 AVS SDK LED 灯效驱动框架基于 Linux 的 LED Class 扩展而来。涂鸦在标准的 Linux 的 LED 驱动之上新增了一个 AVSUX Class,其实现了大部分的 AVS 灯效处理逻辑,大大的降低了灯效驱动开发的复杂度,您只需实现简单的 LED 亮度设置接口即可实现 AVS 的灯效控制。
在进行驱动移植之前,您需要了解驱动框架中两个重要的数据结构:
struct led_avsux_animation
:对应一个完整的 LED 灯效动画,每个动画由若干个帧组成。struct led_avsux_pattern
:对应 LED 灯效动画中的一帧,驱动以帧为单位进行灯效更新。在拿到 LED 芯片原厂的驱动后,您可以按如下步骤进行移植:
将附件中的linux/drivers/leds/led-class-avsux.c
和 include/linux/led-class-avsux.h
拷贝到对应的内核目录下。
修改 include/linux/leds.h
头文件,添加 LED_DEV_CAP_AVS_UX
。
+++ b/include/linux/leds.h
@@ -48,6 +48,7 @@ struct led_classdev {
#define SET_BRIGHTNESS_ASYNC (1 << 21)
#define SET_BRIGHTNESS_SYNC (1 << 22)
#define LED_DEV_CAP_FLASH (1 << 23)
+ #define LED_DEV_CAP_AVS_UX (1 << 24)
/* Set LED brightness level */
/* Must not sleep, use a workqueue if needed */
在 Makefile 和 Kconfig 中添加 AVSUX Class 驱动框架支持。
+++ b/drivers/leds/Makefile
@@ -3,6 +3,7 @@
obj-$(CONFIG_NEW_LEDS) += led-core.o
obj-$(CONFIG_LEDS_CLASS) += led-class.o
obj-$(CONFIG_LEDS_CLASS_FLASH) += led-class-flash.o
+obj-$(CONFIG_LEDS_CLASS_AVS_UX) += led-class-avsux.o
obj-$(CONFIG_LEDS_TRIGGERS) += led-triggers.o
+++ b/drivers/leds/Kconfig
@@ -29,6 +29,16 @@ config LEDS_CLASS_FLASH
for the flash related features of a LED device. It can be built
as a module.
+config LEDS_CLASS_AVS_UX
+ tristate "Amazon Alexa Voice Service LED UX Class Support"
+ depends on LEDS_CLASS
+ help
+ This option enables the amazon alexa voice service led sysfs class in /sys/class/leds.
+ It wrapps LED Class and adds AVS ux LEDs specific sysfs attributes
+ and kernel internal API to it. You'll need this to provide support
+ for the AVS related features of a LED device. It can be built
+ as a module.
+
comment "LED drivers"
完成上面的步骤后,就可以使用涂鸦 AVS LED 驱动框架开发灯效驱动了。
LED 驱动开发非常简单,您只需要实现 brightness_set 接口,并调用 led_classdev_avsux_register 函数注册到驱动框架即可。
一个典型的 LED 亮度设置接口如下:
static void led_avs_brightness_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
struct led_classdev_avsux *auled_cdev = lcdev_to_aucdev(led_cdev);
struct led_avsux_animation *animation = auled_cdev->cur_anime;
struct led_avsux_pattern *pattern = animation->cur_pattern;
int max = led_cdev->max_brightness;
u8 red, green, blue;
int i;
if (value > max)
value = max;
/* 设置每个灯的 RGB 颜色 */
for (i = 0; i < auled_cdev->num_leds; i++) {
red = pattern->colors[i].red * value / max;
green = pattern->colors[i].green * value / max;
blue = pattern->colors[i].blue * value / max;
led_avs_set_rgb(i, red, green, blue);
}
}
然后注册驱动到涂鸦 AVS LED 驱动框架,这里以一个 I2C 接口的芯片为例。
static int led_avs_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id) {
struct led_avs_priv *avs = NULL;
struct device_node *np = i2c->dev.of_node;
struct led_classdev *led_cdev;
int ret = -1;
avs = devm_kzalloc(&i2c->dev, sizeof(*avs), GFP_KERNEL);
if (avs == NULL)
return -ENOMEM;
avs->i2c = i2c;
avs->dev = &i2c->dev;
if (np) {
ret = avs_parse_dt(&i2c->dev, aw20036, np);
if (ret) {
dev_err(&i2c->dev,
"%s: failed to parse device tree node\n",
__func__);
return -EIO;
}
} else {
avs->reset_gpio = -1;
}
if (gpio_is_valid(avs->reset_gpio)) {
ret = devm_gpio_request_one(&i2c->dev, avs->reset_gpio,
GPIOF_OUT_INIT_LOW, "avs_rst");
if (ret) {
dev_err(&i2c->dev, "%s: rst request failed\n",
__func__);
return -EIO;
}
}
avs->regmap = devm_regmap_init_i2c(i2c, &avs_regmap);
if (IS_ERR(avs->regmap)) {
ret = PTR_ERR(avs->regmap);
dev_err(&i2c->dev,
"Failed to allocate register map: %d\n", ret);
return ret;
}
avs_hw_reset(avs);
ret = avs_check_chip_id(avs);
if (ret < 0) {
dev_err(&i2c->dev, "%s: check chip id failed ret=%d\n",
__func__, ret);
}
avs_led_init(avs);
/* 设置灯的类型和个数 */
avs->aucdev.led_type = AVS_UX_TYPE_RADIAL,
avs->aucdev.num_leds = 12,
led_cdev = &aw20036->aucdev.led_cdev;
led_cdev->name = AVS_I2C_NAME;
led_cdev->max_brightness = MAX_BRIGHTNESS; /* 通过max brightnees来调整整体的亮度 */
led_cdev->brightness_set = led_avs_brightness_set;
led_cdev->flags |= LED_DEV_CAP_AVS_UX; /* 指定 AVS 灯效的支持 */
ret = led_classdev_avsux_register(&i2c->dev, &avs->aucdev);
if (ret < 0) {
dev_err(&i2c->dev, "Register avsux led failed:%d\n", ret);
return ret;
}
i2c_set_clientdata(i2c, avs);
return 0;
}
这里有几个地方需要注意:
LED 类型目前共有三种
说明 |
---|
S_UX_TYPE_SINGLE |
S_UX_TYPE_RADIAL |
S_UX_TYPE_LINEAR |
驱动框架默认最多支持12个LED,如需支持更多的LED,请修改头文件 led-class-avsux.h 中的宏定义
#define MAX_NUM_LEDS 12
对于使用PWM或GPIO驱动的单灯灯阵,涂鸦提供了一个通用驱动。
将附件中的 linux/drivers/leds/leds-pwm-avsux.c
拷贝到内核的对应目录,并修改 Makefile
。
然后在设备树中使能pwm,并添加如下节点,pwm根据实际情况修改:
pwmleds {
compatible = "avs-pwm-leds";
status = "okay";
avs-red {
label = "red";
pwms = <&pwm0 0 10000000 0>;
max-brightness = <255>;
};
avs-green {
label = "green";
pwms = <&pwm1 0 10000000 0>;
max-brightness = <255>;
};
avs-blue {
label = "blue";
pwms = <&pwm2 0 10000000 0>;
max-brightness = <255>;
};
};
如果是使用gpio来模拟pwm,将附件的 linux/drivers/pwm/pwm-gpio.c
拷贝到内核的对应目录,并修改 Makefile。然后在设备树中添加如下的内容,pwm根据实际情况修改:
pwm0: pwm-gpio-r {
compatible = "pwm-gpio";
#pwm-cells = <3>;
pwm-gpios = <&gpio1 RK_PA4 GPIO_ACTIVE_HIGH>;
};
pwm1: pwm-gpio-g {
compatible = "pwm-gpio";
#pwm-cells = <3>;
pwm-gpios = <&gpio1 RK_PA4 GPIO_ACTIVE_HIGH>;
};
pwm2: pwm-gpio-b {
compatible = "pwm-gpio";
#pwm-cells = <3>;
pwm-gpios = <&gpio1 RK_PA4 GPIO_ACTIVE_HIGH>;
};
另外, 在附件中涂鸦提供了 aw20036 的驱动,您可以参考并进行移植。
驱动编译加载后,会在 sysfs 下生成如下两个属性节点:
/sys/class/leds/avs-pwm-led/avsux_animation
/sys/class/leds/avs-pwm-led/avsux_select
先准备一个灯效文件 test.aniamtaion ,内容如下:
20:100000,100000,100000,100000,100000,100000,100000,100000,100000,100000,100000,100000
20:200000,200000,200000,200000,200000,200000,200000,200000,200000,200000,200000,200000
20:300000,300000,300000,300000,300000,300000,300000,300000,300000,300000,300000,300000
20:400000,400000,400000,400000,400000,400000,400000,400000,400000,400000,400000,400000
20:500000,500000,500000,500000,500000,500000,500000,500000,500000,500000,500000,500000
20:600000,600000,600000,600000,600000,600000,600000,600000,600000,600000,600000,600000
20:700000,700000,700000,700000,700000,700000,700000,700000,700000,700000,700000,700000
20:800000,800000,800000,800000,800000,800000,800000,800000,800000,800000,800000,800000
20:900000,900000,900000,900000,900000,900000,900000,900000,900000,900000,900000,900000
20:A00000,A00000,A00000,A00000,A00000,A00000,A00000,A00000,A00000,A00000,A00000,A00000
20:B00000,B00000,B00000,B00000,B00000,B00000,B00000,B00000,B00000,B00000,B00000,B00000
20:C00000,C00000,C00000,C00000,C00000,C00000,C00000,C00000,C00000,C00000,C00000,C00000
将文件拷贝到目标板的 /lib/firmware/
目录下。
加载灯效到驱动中:
/ # echo "test test.animation" > /sys/class/leds/avs-pwm-led/avsux_animation
选择并使能灯效:
/ # echo "test" > /sys/class/leds/avs-pwm-led/avsux_select
此时就能看到红色灯从暗到亮的闪烁。
.animation
文件描述了一个完整的灯效动画,其中每一行为一帧,对应一个 pattern。具体的格式如下:
其中 duration 表示这一帧的显示时长(单位为毫秒)。
HEX_XX 为对应灯的十六进制 RGB 值,每个灯之间以 , 分隔。需要注意的是,大于LED个数的值都会被驱动忽略。
附件的 led_animation 目录中已经定义了AVS用到的所有灯效,目前支持单灯、环灯和线灯,客户可以根据需要修改或添加新的灯效。
除了 AVSUX Class 驱动框架外,涂鸦还提供了 libLedAnimation 库用以实现更复杂的 LED 逻辑和状态机状态管理。
提供的 API 如下:
/**
* LedAnimationInit() - 灯效初始化函数
* @conf: 灯效配置文件路径,为NULL时默认从/etc/led_config.json加载
*/
ledHandle_t LedAnimationInit(const char *conf);
/**
* LedAnimationUpdate() - 灯效更新函数
* @handle: LedAnimationInit()返回的句柄
* @animation: 配置文件中定义的动画名称
*/
int LedAnimationUpdate(ledHandle_t* handle, const char* animation);
int LedAnimationRelease(ledHandle_t* handle);
灯效配置文件 /etc/led_config.json
的语法如下:
device | 说明 |
---|---|
device | LED驱动的sysfs路径 |
type | LED的类型 |
animations | 支持的灯效 |
而每个灯效又包含如下几个属性:
属性 | 说明 |
---|---|
fw_path | 灯效动画文件路径(/lib/firnware下的相对路径) |
priority | 灯效优先级,高优先级的灯效可以打断覆盖低优先级的灯效 |
loop | 灯效动画的循环次数,0 表示无限循环 |
pause | 两次循环间的暂停时间 |
{
"device": "/sys/class/leds/avs-pwm-led",
"type": "single"
"animations": {
"idle": {
"fw_path": "led_animation/single/idle.animation",
"priority": "1",
"loop": "0",
"pause":"0"
},
"bootup": {
"fw_path": "led_animation/single/bootup.animation",
"priority": "0",
"loop": "0",
"pause":"0"
},
"listening_start": {
"fw_path": "led_animation/single/active-waking.animation",
"priority": "0",
"loop": "0",
"pause":"0"
},
......
该内容对您有帮助吗?
是意见反馈该内容对您有帮助吗?
是意见反馈