feat(led_strip): Add driver for CKLED2001 LED strip controller
Signed-off-by: XiNGRZ <hi@xingrz.me>
This commit is contained in:
parent
1c862c5b94
commit
5e6342b1cb
8 changed files with 249 additions and 0 deletions
|
@ -5,3 +5,4 @@ add_subdirectory_ifdef(CONFIG_ZMK_DRIVERS_GPIO gpio)
|
|||
add_subdirectory(kscan)
|
||||
add_subdirectory(sensor)
|
||||
add_subdirectory(display)
|
||||
add_subdirectory(led_strip)
|
||||
|
|
|
@ -5,3 +5,4 @@ rsource "gpio/Kconfig"
|
|||
rsource "kscan/Kconfig"
|
||||
rsource "sensor/Kconfig"
|
||||
rsource "display/Kconfig"
|
||||
rsource "led_strip/Kconfig"
|
||||
|
|
4
app/drivers/led_strip/CMakeLists.txt
Normal file
4
app/drivers/led_strip/CMakeLists.txt
Normal file
|
@ -0,0 +1,4 @@
|
|||
# Copyright (c) 2022 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
zephyr_sources_ifdef(CONFIG_CKLED2001 ckled2001.c)
|
4
app/drivers/led_strip/Kconfig
Normal file
4
app/drivers/led_strip/Kconfig
Normal file
|
@ -0,0 +1,4 @@
|
|||
# Copyright (c) 2022 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
rsource "Kconfig.ckled2001"
|
12
app/drivers/led_strip/Kconfig.ckled2001
Normal file
12
app/drivers/led_strip/Kconfig.ckled2001
Normal file
|
@ -0,0 +1,12 @@
|
|||
# Copyright (c) 2022 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
DT_COMPAT_ZMK_CKLED2001 := zmk,ckled2001
|
||||
|
||||
config CKLED2001
|
||||
bool "CKLED2001 compatible LED strip controller driver"
|
||||
default $(dt_compat_enabled,$(DT_COMPAT_ZMK_CKLED2001))
|
||||
depends on I2C
|
||||
depends on LED_STRIP
|
||||
help
|
||||
Enable driver for CKLED2001 compatible LED strip controller.
|
181
app/drivers/led_strip/ckled2001.c
Normal file
181
app/drivers/led_strip/ckled2001.c
Normal file
|
@ -0,0 +1,181 @@
|
|||
/*
|
||||
* Copyright (c) 2022 The ZMK Contributors
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT zmk_ckled2001
|
||||
|
||||
#include <drivers/led_strip.h>
|
||||
|
||||
#define LOG_LEVEL CONFIG_LED_STRIP_LOG_LEVEL
|
||||
#include <logging/log.h>
|
||||
LOG_MODULE_REGISTER(ckled2001);
|
||||
|
||||
#include <zephyr.h>
|
||||
#include <device.h>
|
||||
#include <drivers/i2c.h>
|
||||
|
||||
#define REG_SET_CMD_PAGE 0xFD
|
||||
#define LED_CONTROL_PAGE 0x00
|
||||
#define LED_PWM_PAGE 0x01
|
||||
#define FUNCTION_PAGE 0x03
|
||||
#define CURRENT_TUNE_PAGE 0x04
|
||||
|
||||
#define REG_CONFIGRATION 0x00
|
||||
#define MSKSW_SHUTDOWN_MODE 0x0
|
||||
#define MSKSW_NORMAL_MODE 0x1
|
||||
|
||||
#define REG_PDU 0x13
|
||||
#define MSKSET_CA_CB_CHANNEL 0xAA
|
||||
#define MSKCLR_CA_CB_CHANNEL 0x00
|
||||
|
||||
#define REG_SCAN_PHASE 0x14
|
||||
#define MSKPHASE_CHANNELS(cnt) (12 - cnt)
|
||||
|
||||
#define REG_SLEW_RATE_CONTROL_MODE1 0x15
|
||||
#define MSKPWM_DELAY_PHASE_ENABLE 0x04
|
||||
#define MSKPWM_DELAY_PHASE_DISABLE 0x00
|
||||
|
||||
#define REG_SLEW_RATE_CONTROL_MODE2 0x16
|
||||
#define MSKDRIVING_SINKING_CHHANNEL_SLEWRATE_ENABLE 0xC0
|
||||
#define MSKDRIVING_SINKING_CHHANNEL_SLEWRATE_DISABLE 0x00
|
||||
|
||||
#define REG_SOFTWARE_SLEEP 0x1A
|
||||
#define MSKSLEEP_ENABLE 0x02
|
||||
#define MSKSLEEP_DISABLE 0x00
|
||||
|
||||
#define COUNT_BETWEEN(a, b) ((b - a) + 1)
|
||||
|
||||
#define LED_CONTROL_CNT 24
|
||||
#define LED_PWM_CNT 192
|
||||
#define CURRENT_TUNE_CNT 12
|
||||
|
||||
struct ckled2001_channel_map {
|
||||
uint8_t ch_r;
|
||||
uint8_t ch_g;
|
||||
uint8_t ch_b;
|
||||
};
|
||||
|
||||
struct ckled2001_config {
|
||||
struct i2c_dt_spec bus;
|
||||
uint8_t scan_phase_channels;
|
||||
struct ckled2001_channel_map *map;
|
||||
uint32_t map_cnt;
|
||||
uint8_t *pwm_buffer;
|
||||
};
|
||||
|
||||
static inline int ckled2001_write_reg(const struct device *dev, uint8_t reg, uint8_t value) {
|
||||
const struct ckled2001_config *config = dev->config;
|
||||
return i2c_burst_write_dt(&config->bus, reg, &value, 1);
|
||||
}
|
||||
|
||||
static inline int ckled2001_set_control(const struct device *dev, uint8_t value) {
|
||||
ckled2001_write_reg(dev, REG_SET_CMD_PAGE, LED_CONTROL_PAGE);
|
||||
for (int i = 0; i < LED_CONTROL_CNT; i++) {
|
||||
ckled2001_write_reg(dev, i, value);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int ckled2001_flush_pwm_buffer(const struct device *dev) {
|
||||
const struct ckled2001_config *config = dev->config;
|
||||
ckled2001_write_reg(dev, REG_SET_CMD_PAGE, LED_PWM_PAGE);
|
||||
return i2c_burst_write_dt(&config->bus, 0, (const uint8_t *)config->pwm_buffer, LED_PWM_CNT);
|
||||
}
|
||||
|
||||
static int ckled2001_update_rgb(const struct device *dev, struct led_rgb *pixels,
|
||||
size_t num_pixels) {
|
||||
const struct ckled2001_config *config = dev->config;
|
||||
|
||||
if (num_pixels > config->map_cnt) {
|
||||
num_pixels = config->map_cnt;
|
||||
}
|
||||
for (size_t i = 0; i < num_pixels; i++) {
|
||||
config->pwm_buffer[config->map[i].ch_r] = pixels[i].r;
|
||||
config->pwm_buffer[config->map[i].ch_g] = pixels[i].g;
|
||||
config->pwm_buffer[config->map[i].ch_b] = pixels[i].b;
|
||||
}
|
||||
|
||||
return ckled2001_flush_pwm_buffer(dev);
|
||||
}
|
||||
|
||||
static int ckled2001_update_channels(const struct device *dev, uint8_t *channels,
|
||||
size_t num_channels) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ckled2001_init(const struct device *dev) {
|
||||
const struct ckled2001_config *config = dev->config;
|
||||
|
||||
LOG_INF("Loaded %d channel mappings", config->map_cnt);
|
||||
|
||||
if (!device_is_ready(config->bus.bus)) {
|
||||
LOG_ERR("I2C bus not ready: %s", config->bus.bus->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
// Set functions
|
||||
ckled2001_write_reg(dev, REG_SET_CMD_PAGE, FUNCTION_PAGE);
|
||||
ckled2001_write_reg(dev, REG_CONFIGRATION, MSKSW_SHUTDOWN_MODE);
|
||||
ckled2001_write_reg(dev, REG_PDU, MSKSET_CA_CB_CHANNEL);
|
||||
ckled2001_write_reg(dev, REG_SCAN_PHASE, MSKPHASE_CHANNELS(config->scan_phase_channels));
|
||||
ckled2001_write_reg(dev, REG_SLEW_RATE_CONTROL_MODE1, MSKPWM_DELAY_PHASE_ENABLE);
|
||||
ckled2001_write_reg(dev, REG_SLEW_RATE_CONTROL_MODE2,
|
||||
MSKDRIVING_SINKING_CHHANNEL_SLEWRATE_ENABLE);
|
||||
ckled2001_write_reg(dev, REG_SOFTWARE_SLEEP, MSKSLEEP_DISABLE);
|
||||
|
||||
// Turn off all LEDs
|
||||
ckled2001_set_control(dev, 0x00);
|
||||
|
||||
// Init PWM page
|
||||
memset(config->pwm_buffer, 0x00, LED_PWM_CNT);
|
||||
ckled2001_flush_pwm_buffer(dev);
|
||||
|
||||
// Init current page
|
||||
ckled2001_write_reg(dev, REG_SET_CMD_PAGE, CURRENT_TUNE_PAGE);
|
||||
for (int i = 0; i < CURRENT_TUNE_CNT; i++) {
|
||||
switch (i) {
|
||||
case 2:
|
||||
case 5:
|
||||
case 8:
|
||||
case 11:
|
||||
ckled2001_write_reg(dev, i, 0xA0);
|
||||
break;
|
||||
default:
|
||||
ckled2001_write_reg(dev, i, 0xFF);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Turn on all LEDs
|
||||
ckled2001_set_control(dev, 0xFF);
|
||||
|
||||
// Set to normal mode
|
||||
ckled2001_write_reg(dev, REG_SET_CMD_PAGE, FUNCTION_PAGE);
|
||||
ckled2001_write_reg(dev, REG_CONFIGRATION, MSKSW_NORMAL_MODE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct led_strip_driver_api ckled2001_api = {
|
||||
.update_rgb = ckled2001_update_rgb,
|
||||
.update_channels = ckled2001_update_channels,
|
||||
};
|
||||
|
||||
#define CKLED2001_INIT(n) \
|
||||
static uint8_t ckled2001_channel_map##n[] = DT_INST_PROP(n, map); \
|
||||
\
|
||||
static uint8_t ckled2001_pwm_buffer_##n[LED_PWM_CNT]; \
|
||||
\
|
||||
static const struct ckled2001_config ckled2001_config_##n = { \
|
||||
.bus = I2C_DT_SPEC_INST_GET(n), \
|
||||
.scan_phase_channels = DT_INST_PROP_OR(n, scan_phase_channels, 12), \
|
||||
.map = (struct ckled2001_channel_map *)ckled2001_channel_map##n, \
|
||||
.map_cnt = DT_INST_PROP_LEN(n, map) / 3, \
|
||||
.pwm_buffer = ckled2001_pwm_buffer_##n, \
|
||||
}; \
|
||||
\
|
||||
DEVICE_DT_INST_DEFINE(n, &ckled2001_init, NULL, NULL, &ckled2001_config_##n, POST_KERNEL, \
|
||||
CONFIG_LED_STRIP_INIT_PRIORITY, &ckled2001_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(CKLED2001_INIT);
|
38
app/drivers/zephyr/dts/bindings/led_strip/zmk,ckled2001.yaml
Normal file
38
app/drivers/zephyr/dts/bindings/led_strip/zmk,ckled2001.yaml
Normal file
|
@ -0,0 +1,38 @@
|
|||
# Copyright (c) 2022 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
description: CKLED2001 LED strip controller
|
||||
|
||||
compatible: "zmk,ckled2001"
|
||||
|
||||
include: [i2c-device.yaml]
|
||||
|
||||
properties:
|
||||
label:
|
||||
type: string
|
||||
required: true
|
||||
|
||||
chain-length:
|
||||
type: int
|
||||
required: true
|
||||
|
||||
scan-phase-channels:
|
||||
type: int
|
||||
default: 12
|
||||
enum:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
- 4
|
||||
- 5
|
||||
- 6
|
||||
- 7
|
||||
- 8
|
||||
- 9
|
||||
- 10
|
||||
- 11
|
||||
- 12
|
||||
|
||||
map:
|
||||
type: array
|
||||
required: true
|
8
app/include/dt-bindings/zmk/ckled2001.h
Normal file
8
app/include/dt-bindings/zmk/ckled2001.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* Copyright (c) 2022 The ZMK Contributors
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define CK(ch, idx) ((ch << 4) | idx)
|
Loading…
Add table
Reference in a new issue