drivers: led: add led_dac
Add LED driver support for DAC based LED drivers. Signed-off-by: Jeppe Odgaard <jeppe.odgaard@prevas.dk>
This commit is contained in:
parent
4bd1de02bb
commit
634ba6955c
@ -10,6 +10,7 @@ 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_LED_AXP192_AXP2101 led_axp192.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_LED_DAC led_dac.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_LED_GPIO led_gpio.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_LED_NPM1300 led_npm1300.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_LED_PWM led_pwm.c)
|
||||
|
||||
@ -28,6 +28,7 @@ config LED_SHELL
|
||||
|
||||
# zephyr-keep-sorted-start
|
||||
source "drivers/led/Kconfig.axp192"
|
||||
source "drivers/led/Kconfig.dac"
|
||||
source "drivers/led/Kconfig.gpio"
|
||||
source "drivers/led/Kconfig.ht16k33"
|
||||
source "drivers/led/Kconfig.is31fl3194"
|
||||
|
||||
18
drivers/led/Kconfig.dac
Normal file
18
drivers/led/Kconfig.dac
Normal file
@ -0,0 +1,18 @@
|
||||
# Copyright (c) 2025 Prevas A/S
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config LED_DAC
|
||||
bool "DAC LED driver"
|
||||
default y
|
||||
depends on DT_HAS_DAC_LEDS_ENABLED
|
||||
select DAC
|
||||
help
|
||||
Enable DAC LED driver.
|
||||
|
||||
The driver is normally used with a VCCS (Voltage Controlled Current
|
||||
Source) between the DAC and the LED.
|
||||
|
||||
It might be possible to use this driver to supply an LED directly
|
||||
from a DAC, but LED_PWM is most likely more suited for this purpose.
|
||||
If the DAC is used without a VCCS, its capabilities should be checked
|
||||
carefully, and the output buffer should be enabled.
|
||||
146
drivers/led/led_dac.c
Normal file
146
drivers/led/led_dac.c
Normal file
@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Prevas A/S
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT dac_leds
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/drivers/dac.h>
|
||||
#include <zephyr/drivers/led.h>
|
||||
#include <zephyr/sys/math_extras.h>
|
||||
|
||||
struct led_dac_leds {
|
||||
const struct device *dac;
|
||||
struct dac_channel_cfg chan_cfg;
|
||||
uint32_t dac_max_brightness;
|
||||
uint32_t dac_min_brightness;
|
||||
};
|
||||
|
||||
struct led_dac_config {
|
||||
const struct led_dac_leds *leds;
|
||||
uint8_t num_leds;
|
||||
};
|
||||
|
||||
static int led_dac_set_raw(const struct device *dev, uint32_t led, uint32_t value)
|
||||
{
|
||||
const struct led_dac_config *config = dev->config;
|
||||
|
||||
return dac_write_value(config->leds[led].dac, config->leds[led].chan_cfg.channel_id, value);
|
||||
}
|
||||
|
||||
static int led_dac_set_brightness(const struct device *dev, uint32_t led, uint8_t pct)
|
||||
{
|
||||
const struct led_dac_config *config = dev->config;
|
||||
uint32_t value;
|
||||
|
||||
if (led >= config->num_leds) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
value = pct > 0 ? config->leds[led].dac_min_brightness +
|
||||
(uint64_t)(config->leds[led].dac_max_brightness -
|
||||
config->leds[led].dac_min_brightness) *
|
||||
pct / 100
|
||||
: 0;
|
||||
|
||||
return led_dac_set_raw(dev, led, value);
|
||||
}
|
||||
|
||||
static inline int led_dac_on(const struct device *dev, uint32_t led)
|
||||
{
|
||||
const struct led_dac_config *config = dev->config;
|
||||
|
||||
if (led >= config->num_leds) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return led_dac_set_raw(dev, led, config->leds[led].dac_max_brightness);
|
||||
}
|
||||
|
||||
static inline int led_dac_off(const struct device *dev, uint32_t led)
|
||||
{
|
||||
const struct led_dac_config *config = dev->config;
|
||||
|
||||
if (led >= config->num_leds) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return led_dac_set_raw(dev, led, 0);
|
||||
}
|
||||
|
||||
static DEVICE_API(led, led_dac_api) = {
|
||||
.on = led_dac_on,
|
||||
.off = led_dac_off,
|
||||
.set_brightness = led_dac_set_brightness,
|
||||
};
|
||||
|
||||
static int led_dac_init(const struct device *dev)
|
||||
{
|
||||
const struct led_dac_config *config = dev->config;
|
||||
int ret;
|
||||
|
||||
for (uint8_t i = 0; i < config->num_leds; ++i) {
|
||||
const struct led_dac_leds *led = &config->leds[i];
|
||||
|
||||
if (!device_is_ready(led->dac)) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = dac_channel_setup(led->dac, &led->chan_cfg);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define LED_DAC_MAX_MV(n) DT_PROP(n, voltage_max_dac_mv)
|
||||
#define LED_DAC_MAX_VAL(n) (BIT(DT_PROP(n, resolution)) - 1)
|
||||
|
||||
#define LED_DAC_MAX_BRIGHTNESS(n) \
|
||||
COND_CODE_1(DT_NODE_HAS_PROP(n, voltage_max_brightness_mv), \
|
||||
(DT_PROP(n, voltage_max_brightness_mv) * LED_DAC_MAX_VAL(n) / LED_DAC_MAX_MV(n)), \
|
||||
(LED_DAC_MAX_VAL(n)))
|
||||
|
||||
#define LED_DAC_MIN_BRIGHTNESS(n) \
|
||||
COND_CODE_1(DT_NODE_HAS_PROP(n, voltage_min_brightness_mv), \
|
||||
(DT_PROP(n, voltage_min_brightness_mv) * LED_DAC_MAX_VAL(n) / LED_DAC_MAX_MV(n)), \
|
||||
(0))
|
||||
|
||||
#define LED_DAC_DT_GET(n) \
|
||||
{ \
|
||||
.dac = DEVICE_DT_GET(DT_PHANDLE(n, dac_dev)), \
|
||||
.chan_cfg = \
|
||||
{ \
|
||||
.channel_id = DT_PROP(n, channel), \
|
||||
.resolution = DT_PROP(n, resolution), \
|
||||
.buffered = DT_PROP(n, output_buffer), \
|
||||
.internal = false, \
|
||||
}, \
|
||||
.dac_max_brightness = LED_DAC_MAX_BRIGHTNESS(n), \
|
||||
.dac_min_brightness = LED_DAC_MIN_BRIGHTNESS(n), \
|
||||
}
|
||||
|
||||
#define LED_DAC_DEFINE(n) \
|
||||
BUILD_ASSERT((DT_NODE_HAS_PROP(n, voltage_max_brightness_mv) || \
|
||||
DT_NODE_HAS_PROP(n, voltage_min_brightness_mv)) == \
|
||||
DT_NODE_HAS_PROP(n, voltage_max_dac_mv), \
|
||||
"'voltage-max-dac-mv' must be set when 'voltage-max-brightness-mv' or " \
|
||||
"'voltage-max-brightness-mv' is set"); \
|
||||
\
|
||||
static const struct led_dac_leds led_dac_##n[] = { \
|
||||
DT_INST_FOREACH_CHILD_SEP(n, LED_DAC_DT_GET, (,))}; \
|
||||
\
|
||||
static const struct led_dac_config led_config_##n = { \
|
||||
.leds = led_dac_##n, \
|
||||
.num_leds = ARRAY_SIZE(led_dac_##n), \
|
||||
}; \
|
||||
\
|
||||
DEVICE_DT_INST_DEFINE(n, &led_dac_init, NULL, NULL, &led_config_##n, POST_KERNEL, \
|
||||
CONFIG_LED_INIT_PRIORITY, &led_dac_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(LED_DAC_DEFINE)
|
||||
82
dts/bindings/led/dac-leds.yaml
Normal file
82
dts/bindings/led/dac-leds.yaml
Normal file
@ -0,0 +1,82 @@
|
||||
# Copyright (c) 2018, Linaro Limited
|
||||
# Copyright (c) 2025, Prevas A/S
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
title: Group of DAC-controlled LEDs.
|
||||
|
||||
description: |
|
||||
Each LED is defined in a child node of the dac-leds node.
|
||||
|
||||
Here is an example which defines an LED in the node /leds:
|
||||
|
||||
/ {
|
||||
leds {
|
||||
compatible = "dac-leds";
|
||||
led_0 {
|
||||
dac-dev = <&dac1>;
|
||||
channel = <0>;
|
||||
resolution = <12>;
|
||||
};
|
||||
led_1 {
|
||||
dac-dev = <&dac1>;
|
||||
channel = <1>;
|
||||
resolution = <12>;
|
||||
voltage-min-brightness-mv = <1400>;
|
||||
voltage-max-brightness-mv = <2700>;
|
||||
voltage-max-dac-mv = <3300>;
|
||||
output-buffer;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
Above:
|
||||
|
||||
- led_0 uses dac1 channel 0 with 12 bit resolution.
|
||||
- led_1 uses dac1 channel 1 with 12 bit resolution and setup to supply an LED directly.
|
||||
|
||||
compatible: "dac-leds"
|
||||
|
||||
child-binding:
|
||||
description: DAC LED child node
|
||||
properties:
|
||||
dac-dev:
|
||||
type: phandle
|
||||
required: true
|
||||
description: |
|
||||
Property containing phandle to DAC e.g. &dac.
|
||||
|
||||
channel:
|
||||
type: int
|
||||
required: true
|
||||
description: |
|
||||
The DAC channel.
|
||||
|
||||
resolution:
|
||||
type: int
|
||||
required: true
|
||||
description: |
|
||||
The DAC resolution to use.
|
||||
|
||||
voltage-min-brightness-mv:
|
||||
type: int
|
||||
description: |
|
||||
Voltage at brightness 0%.
|
||||
If not specified the minimum DAC output voltage is used.
|
||||
|
||||
voltage-max-brightness-mv:
|
||||
type: int
|
||||
description: |
|
||||
Voltage at brightness 100%.
|
||||
If not specified the maximum DAC output voltage is used.
|
||||
|
||||
voltage-max-dac-mv:
|
||||
type: int
|
||||
description: |
|
||||
The DAC maximum output voltage.
|
||||
Required if voltage-min-brightness-mv or voltage-max-brightness-mv is set.
|
||||
|
||||
output-buffer:
|
||||
type: boolean
|
||||
description: |
|
||||
Enable the output buffer of the DAC.
|
||||
This is required if it is used to drive an LED directly.
|
||||
@ -15,6 +15,26 @@
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
test_dac: dac@dac0dac0 {
|
||||
compatible = "vnd,dac";
|
||||
reg = <0xdac0dac0 0x1000>;
|
||||
status = "okay";
|
||||
#io-channel-cells = <1>;
|
||||
};
|
||||
|
||||
test_dac_leds {
|
||||
compatible = "dac-leds";
|
||||
test_dac_led0: test_dac_led_0 {
|
||||
dac-dev = <&test_dac>;
|
||||
channel = <0>;
|
||||
resolution = <16>;
|
||||
voltage-min-brightness-mv = <123>;
|
||||
voltage-max-brightness-mv = <1234>;
|
||||
voltage-max-dac-mv = <3456>;
|
||||
output-buffer;
|
||||
};
|
||||
};
|
||||
|
||||
test_gpio: gpio@deadbeef {
|
||||
compatible = "vnd,gpio";
|
||||
gpio-controller;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user