Compare commits

..

3 Commits

Author SHA1 Message Date
a4e3122d42 samples: drivers: led: add ktd202x driver sample
Adds sample application for Kinetic KTD202X 3/4 channel LED driver

Signed-off-by: Michael Estes <michael.estes@byteserv.io>
2026-01-23 18:59:41 -05:00
a422d4b6da dts: bindings: add ktd202x binding
Adds a DTS binding for the Kinetic KTD202X 3/4 channel LED driver

Signed-off-by: Michael Estes <michael.estes@byteserv.io>
2026-01-23 18:59:40 -05:00
41ee87a8cc drivers: led: add ktd202x driver
Adds driver for Kinetic KTD202X 3/4 channel LED driver

Signed-off-by: Michael Estes <michael.estes@byteserv.io>
2026-01-23 18:59:38 -05:00
11 changed files with 330 additions and 0 deletions

View File

@@ -12,6 +12,7 @@ zephyr_library_sources_ifdef(CONFIG_HT16K33 ht16k33.c)
zephyr_library_sources_ifdef(CONFIG_IS31FL3194 is31fl3194.c)
zephyr_library_sources_ifdef(CONFIG_IS31FL3216A is31fl3216a.c)
zephyr_library_sources_ifdef(CONFIG_IS31FL3733 is31fl3733.c)
zephyr_library_sources_ifdef(CONFIG_KTD202X ktd202x.c)
zephyr_library_sources_ifdef(CONFIG_LEDS_GROUP_MULTICOLOR leds_group_multicolor.c)
zephyr_library_sources_ifdef(CONFIG_LED_AXP192_AXP2101 led_axp192.c)
zephyr_library_sources_ifdef(CONFIG_LED_DAC led_dac.c)

View File

@@ -34,6 +34,7 @@ source "drivers/led/Kconfig.ht16k33"
source "drivers/led/Kconfig.is31fl3194"
source "drivers/led/Kconfig.is31fl3216a"
source "drivers/led/Kconfig.is31fl3733"
source "drivers/led/Kconfig.ktd202x"
source "drivers/led/Kconfig.leds-group-multicolor"
source "drivers/led/Kconfig.lp3943"
source "drivers/led/Kconfig.lp50xx"

View File

@@ -0,0 +1,11 @@
# Copyright (c) 2025 Michael Estes
# SPDX-License-Identifier: Apache-2.0
config KTD202X
bool "KTD202X LED driver"
default y
depends on DT_HAS_KINETIC_KTD202X_ENABLED
select I2C
help
Enable driver for the Kinetic KTD2026/2027 family of LED
controllers.

157
drivers/led/ktd202x.c Normal file
View File

@@ -0,0 +1,157 @@
/*
* Copyright (c) 2025 Michael Estes
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT kinetic_ktd202x
#include <zephyr/drivers/i2c.h>
#include <zephyr/drivers/led.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/util_macro.h>
#include <zephyr/sys/util.h>
#define KTD202X_REG_RESET 0x00
#define KTD202X_REG_PERIOD 0x01
#define KTD202X_REG_DUTY 0x02
#define KTD202X_REG_LED_EN 0x04
#define KTD202X_REG_CURRENT 0x06
#define KTD202X_MAX_LEDS 4
#define KTD202X_RESET_MASK GENMASK(2, 0)
#define KTD202X_MAX_PERIOD 16380
#define KTD202X_FLASH_PERIOD_MASK GENMASK(6, 0)
LOG_MODULE_REGISTER(ktd202x, CONFIG_LED_LOG_LEVEL);
struct ktd202x_cfg {
struct i2c_dt_spec i2c;
};
struct ktd202x_data {
uint8_t enable;
};
static int ktd202x_write_buffer(const struct i2c_dt_spec *i2c, const uint8_t *buffer,
uint32_t num_bytes)
{
int status;
status = i2c_write_dt(i2c, buffer, num_bytes);
if (status < 0) {
LOG_ERR("Could not write buffer: %i", status);
return status;
}
return 0;
}
static int ktd202x_write_reg(const struct i2c_dt_spec *i2c, uint8_t reg, uint8_t val)
{
uint8_t buffer[2] = {reg, val};
return ktd202x_write_buffer(i2c, buffer, sizeof(buffer));
}
static uint8_t ktd202x_brightness_to_current(uint8_t brightness)
{
return (0xBFU * brightness) / LED_BRIGHTNESS_MAX;
}
static int ktd202x_led_set_brightness(const struct device *dev, uint32_t led, uint8_t value)
{
const struct ktd202x_cfg *config = dev->config;
struct ktd202x_data *data = dev->data;
uint8_t current;
int status;
if (led >= KTD202X_MAX_LEDS) {
return -EINVAL;
}
if (value == 0) {
data->enable &= ~(GENMASK(1, 0) << (led * 2));
} else if ((data->enable & (GENMASK(1, 0) << (led * 2))) == 0) {
WRITE_BIT(data->enable, led * 2, 1);
}
status = ktd202x_write_reg(&config->i2c, KTD202X_REG_LED_EN, data->enable);
if (status != 0) {
return status;
}
current = ktd202x_brightness_to_current(value);
status = ktd202x_write_reg(&config->i2c, KTD202X_REG_CURRENT + led, current);
return status;
}
static int ktd202x_led_blink(const struct device *dev, uint32_t led,
uint32_t delay_on, uint32_t delay_off)
{
const struct ktd202x_cfg *cfg = dev->config;
struct ktd202x_data *data = dev->data;
uint8_t period = 0;
uint8_t duty = 0;
int status = 0;
if (delay_on + delay_off > KTD202X_MAX_PERIOD) {
return -EINVAL;
}
if (delay_on + delay_off >= 256) {
period = ((delay_on + delay_off - 256) / 0x80) & KTD202X_FLASH_PERIOD_MASK;
}
duty = delay_on * UINT8_MAX / (delay_on + delay_off);
LOG_DBG("Setting period: %d, duty cycle: %d", period, duty);
status = ktd202x_write_reg(&cfg->i2c, KTD202X_REG_PERIOD, period);
if (status != 0) {
return status;
}
status = ktd202x_write_reg(&cfg->i2c, KTD202X_REG_DUTY, duty);
if (status != 0) {
return status;
}
/* Set enable for led to PWM */
data->enable |= 1 << (led * 2 + 1);
status = ktd202x_write_reg(&cfg->i2c, KTD202X_REG_LED_EN, data->enable);
return 0;
}
static int ktd202x_init(const struct device *dev)
{
const struct ktd202x_cfg *config = dev->config;
LOG_DBG("Initializing @0x%x...", config->i2c.addr);
if (!i2c_is_ready_dt(&config->i2c)) {
LOG_ERR("I2C device not ready");
return -ENODEV;
}
/* NACK expected - ignore return value */
ktd202x_write_reg(&config->i2c, KTD202X_REG_RESET, KTD202X_RESET_MASK);
k_sleep(K_USEC(200));
return 0;
}
static DEVICE_API(led, ktd202x_led_api) = {
.set_brightness = ktd202x_led_set_brightness,
.blink = ktd202x_led_blink,
};
#define KTD202X_INIT(id) \
static const struct ktd202x_cfg ktd202x_##id##_cfg = { \
.i2c = I2C_DT_SPEC_INST_GET(id), \
}; \
static struct ktd202x_data ktd202x_##id##_data = {0}; \
DEVICE_DT_INST_DEFINE(id, &ktd202x_init, NULL, &ktd202x_##id##_data, &ktd202x_##id##_cfg, \
POST_KERNEL, CONFIG_LED_INIT_PRIORITY, &ktd202x_led_api);
DT_INST_FOREACH_STATUS_OKAY(KTD202X_INIT)

View File

@@ -0,0 +1,9 @@
# Copyright (c) 2025 Michael Estes
# SPDX-License-Identifier: Apache-2.0
description: |
KTD202x 3 or 4 channel LED driver
compatible: "kinetic,ktd202x"
include: i2c-device.yaml

View File

@@ -0,0 +1,9 @@
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(led_pca9633)
FILE(GLOB app_sources src/*.c)
target_sources(app PRIVATE ${app_sources})

View File

@@ -0,0 +1,38 @@
.. zephyr:code-sample:: ktd202x
:name: KTD202X LED
:relevant-api: led_interface
Control 3 LEDs connected to a KTD2026 driver chip.
Overview
********
This sample controls 3 LEDs connected to a KTD2026 driver, using the
following pattern:
1. turn on LEDs
2. turn off LEDs
3. set the brightness to 50%
4. turn off LEDs
5. blink the LEDs
6. turn off LEDs
Building and Running
********************
Build the application for the :zephyr:board:`nucleo_g431rb` board, and connect
a KTD2026 LED driver on the bus I2C Arduino.
.. zephyr-app-commands::
:zephyr-app: samples/drivers/led/ktd2026
:board: nucleo_g431rb_board
:goals: build
:compact:
For flashing the application, refer to the Flashing section of the
:zephyr:board:`nucleo_g431rb` board documentation.
References
**********
- KTD202X: https://www.kinet-ic.com/uploads/web/KTD2026/KTD2026-7-04h.pdf

View File

@@ -0,0 +1,11 @@
/* SPDX-License-Identifier: Apache-2.0 */
&arduino_i2c {
status = "okay";
clock-frequency = <I2C_BITRATE_STANDARD>;
ktd202x@30 {
compatible = "kinetic,ktd202x";
reg = <0x30>;
};
};

View File

@@ -0,0 +1,3 @@
CONFIG_LOG=y
CONFIG_LED=y

View File

@@ -0,0 +1,8 @@
sample:
description: Demonstration of the KTD202X LED driver
name: KTD202X sample
tests:
sample.drivers.led.ktd2026:
depends_on: arduino_i2c
build_only: true
tags: LED

View File

@@ -0,0 +1,82 @@
/*
* Copyright (c) 2025 Michael Estes
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/device.h>
#include <errno.h>
#include <zephyr/drivers/led.h>
#include <zephyr/sys/util.h>
#include <zephyr/kernel.h>
#define LOG_LEVEL CONFIG_LOG_DEFAULT_LEVEL
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(main);
#define NUM_LEDS 3
#define HALF_BRIGHTNESS (LED_BRIGHTNESS_MAX / 2)
#define BLINK_DELAY_ON 500
#define BLINK_DELAY_OFF 500
#define DELAY_TIME_MS 1000
#define DELAY_TIME K_MSEC(DELAY_TIME_MS)
int main(void)
{
const struct device *const led_dev = DEVICE_DT_GET_ANY(kinetic_ktd202x);
int i, ret;
if (!led_dev) {
LOG_ERR("No devices with compatible kinetic,ktd202x found");
return 0;
} else if (!device_is_ready(led_dev)) {
LOG_ERR("LED device %s is not ready", led_dev->name);
return 0;
}
LOG_INF("Found LED device %s", led_dev->name);
LOG_INF("Testing leds");
while (1) {
/* Turn on LEDs one by one */
for (i = 0; i < NUM_LEDS; i++) {
ret = led_on(led_dev, i);
if (ret < 0) {
return 0;
}
k_sleep(DELAY_TIME);
}
/* Turn off LEDs one by one */
for (i = 0; i < NUM_LEDS; i++) {
ret = led_off(led_dev, i);
if (ret < 0) {
return 0;
}
k_sleep(DELAY_TIME);
}
/* Set the brightness to half max of LEDs one by one */
for (i = 0; i < NUM_LEDS; i++) {
ret = led_set_brightness(led_dev, i, HALF_BRIGHTNESS);
if (ret < 0) {
return 0;
}
k_sleep(DELAY_TIME);
}
/* Turn off LEDs one by one */
for (i = 0; i < NUM_LEDS; i++) {
ret = led_off(led_dev, i);
if (ret < 0) {
return 0;
}
k_sleep(DELAY_TIME);
}
}
return 0;
}