Skip to content

Commit b69ff55

Browse files
committed
drivers: led: is31fl3197 Arduino GIGA
Start of support for the is31fl3197 LED controller used by Arduino in the Arduino Giga Display shield. So far there is just enough implemented that allows me to control the LEDS, to the same extent as our version of the library we implemented that runs on ArduinoCore-zephyr Signed-off-by: Kurt Eckhardt <[email protected]>
1 parent e868e01 commit b69ff55

File tree

12 files changed

+461
-0
lines changed

12 files changed

+461
-0
lines changed

boards/arduino/giga_r1/arduino_giga_r1_stm32h747xx_m7.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ supported:
1212
- gpio
1313
- arduino_spi
1414
- spi
15+
- i2c
1516
- memc
1617
- usb_device
1718
vendor: arduino

drivers/led/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ zephyr_library()
77
# zephyr-keep-sorted-start
88
zephyr_library_sources_ifdef(CONFIG_HT16K33 ht16k33.c)
99
zephyr_library_sources_ifdef(CONFIG_IS31FL3194 is31fl3194.c)
10+
zephyr_library_sources_ifdef(CONFIG_IS31FL3197 is31fl3197.c)
1011
zephyr_library_sources_ifdef(CONFIG_IS31FL3216A is31fl3216a.c)
1112
zephyr_library_sources_ifdef(CONFIG_IS31FL3733 is31fl3733.c)
1213
zephyr_library_sources_ifdef(CONFIG_LEDS_GROUP_MULTICOLOR leds_group_multicolor.c)

drivers/led/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ source "drivers/led/Kconfig.dac"
3232
source "drivers/led/Kconfig.gpio"
3333
source "drivers/led/Kconfig.ht16k33"
3434
source "drivers/led/Kconfig.is31fl3194"
35+
source "drivers/led/Kconfig.is31fl3197"
3536
source "drivers/led/Kconfig.is31fl3216a"
3637
source "drivers/led/Kconfig.is31fl3733"
3738
source "drivers/led/Kconfig.leds-group-multicolor"

drivers/led/Kconfig.is31fl3197

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Copyright (c) 2024 Arduino SA
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
config IS31FL3197
5+
bool "IS31FL3197 LED driver"
6+
default y
7+
depends on DT_HAS_ISSI_IS31FL3197_ENABLED
8+
select I2C
9+
help
10+
Enable LED driver for Lumissil Microsystems (a division of ISSI)
11+
IS31FL3197. This chip supports one RGB LED or 4 independent LEDs.

drivers/led/is31fl3197.c

Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
/*
2+
* Copyright (c) 2024 Arduino SA
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#define DT_DRV_COMPAT issi_is31fl3197
8+
9+
/**
10+
* @file
11+
* @brief IS31FL3197 LED driver
12+
*
13+
* The IS31FL3197 is a 4-channel LED driver that communicates over I2C.
14+
*/
15+
16+
#include <zephyr/device.h>
17+
#include <zephyr/drivers/i2c.h>
18+
#include <zephyr/drivers/led.h>
19+
#include <zephyr/kernel.h>
20+
#include <zephyr/logging/log.h>
21+
22+
#include <zephyr/dt-bindings/led/led.h>
23+
24+
LOG_MODULE_REGISTER(is31fl3197, CONFIG_LED_LOG_LEVEL);
25+
26+
#define IS31FL3197_PROD_ID_REG 0x00
27+
#define IS31FL3197_SHUTDOWN_REG 0x01
28+
#define IS31FL3197_OPER_CONFIG_REG 0x02
29+
#define IS31FL3197_OUT1_REG 0x10
30+
#define IS31FL3197_OUT2_REG 0x11
31+
#define IS31FL3197_OUT3_REG 0x12
32+
#define IS31FL3197_OUT4_REG 0x13
33+
#define IS31FL3197_UPDATE_REG 0x2b
34+
35+
#define IS31FL3197_SHUTDOWN_REG_VAL 0xf1 /* enable all channels */
36+
#define IS31FL3197_OPER_CONFIG_REG_VAL 0xff /* set all to current level */
37+
#define IS31FL3197_UPDATE_VAL 0xc5
38+
39+
#define IS31FL3197_CHANNEL_COUNT 4
40+
41+
static const uint8_t led_channels[] = {
42+
IS31FL3197_OUT1_REG,
43+
IS31FL3197_OUT2_REG,
44+
IS31FL3197_OUT3_REG,
45+
IS31FL3197_OUT4_REG,
46+
};
47+
48+
struct is31fl3197_config {
49+
struct i2c_dt_spec bus;
50+
uint8_t num_leds;
51+
const struct led_info *led_infos;
52+
};
53+
54+
static const struct led_info *is31fl3197_led_to_info(const struct is31fl3197_config *config,
55+
uint32_t led)
56+
{
57+
if (led < config->num_leds) {
58+
return &config->led_infos[led];
59+
}
60+
61+
return NULL;
62+
}
63+
64+
static int is31fl3197_get_info(const struct device *dev,
65+
uint32_t led,
66+
const struct led_info **info_out)
67+
{
68+
const struct is31fl3197_config *config = dev->config;
69+
const struct led_info *info = is31fl3197_led_to_info(config, led);
70+
71+
if (info == NULL) {
72+
return -EINVAL;
73+
}
74+
75+
*info_out = info;
76+
return 0;
77+
}
78+
79+
static int is31fl3197_set_color(const struct device *dev, uint32_t led, uint8_t num_colors,
80+
const uint8_t *color)
81+
{
82+
const struct is31fl3197_config *config = dev->config;
83+
const struct led_info *info = is31fl3197_led_to_info(config, led);
84+
int ret = 0;
85+
86+
if (info == NULL) {
87+
return -ENODEV;
88+
}
89+
90+
if (info->num_colors > 4) {
91+
return -ENOTSUP;
92+
}
93+
94+
if (num_colors != info->num_colors) {
95+
return -EINVAL;
96+
}
97+
98+
for (int i = 0; i < num_colors; i++) {
99+
uint8_t value;
100+
101+
switch (info->color_mapping[i]) {
102+
case LED_COLOR_ID_RED:
103+
value = color[0];
104+
break;
105+
case LED_COLOR_ID_GREEN:
106+
value = color[1];
107+
break;
108+
case LED_COLOR_ID_BLUE:
109+
value = color[2];
110+
break;
111+
case LED_COLOR_ID_WHITE:
112+
value = color[3];
113+
break;
114+
default:
115+
/* unreachable: mapping already tested in is31fl3197_check_config */
116+
return -EINVAL;
117+
}
118+
119+
ret = i2c_reg_write_byte_dt(&config->bus, led_channels[i], value);
120+
if (ret != 0) {
121+
break;
122+
}
123+
}
124+
125+
if (ret == 0) {
126+
ret = i2c_reg_write_byte_dt(&config->bus,
127+
IS31FL3197_UPDATE_REG,
128+
IS31FL3197_UPDATE_VAL);
129+
}
130+
131+
if (ret != 0) {
132+
LOG_ERR("%s: LED write failed: %d", dev->name, ret);
133+
}
134+
135+
return ret;
136+
}
137+
138+
static int is31fl3197_set_brightness(const struct device *dev, uint32_t led, uint8_t value)
139+
{
140+
return -ENOTSUP;
141+
}
142+
143+
/*
144+
* Counts red, green, blue channels; returns true if color_id is valid
145+
* and no more than one channel maps to the same color
146+
*/
147+
static bool is31fl3197_count_colors(const struct device *dev,
148+
uint8_t color_id, uint8_t *rgb_counts)
149+
{
150+
bool ret = false;
151+
152+
switch (color_id) {
153+
case LED_COLOR_ID_RED:
154+
ret = (++rgb_counts[0] == 1);
155+
break;
156+
case LED_COLOR_ID_GREEN:
157+
ret = (++rgb_counts[1] == 1);
158+
break;
159+
case LED_COLOR_ID_BLUE:
160+
ret = (++rgb_counts[2] == 1);
161+
break;
162+
default:
163+
ret = false;
164+
}
165+
166+
if (!ret) {
167+
LOG_ERR("%s: invalid color %d (duplicate or not RGB)",
168+
dev->name, color_id);
169+
}
170+
171+
return ret;
172+
}
173+
174+
static int is31fl3197_check_config(const struct device *dev)
175+
{
176+
const struct is31fl3197_config *config = dev->config;
177+
const struct led_info *info;
178+
uint8_t rgb_counts[4] = { 0 };
179+
180+
181+
/* Currntly only supporting individual LEDS up to 4 */
182+
/* check that each LED is single-color */
183+
for (int i = 0; i < config->num_leds; i++) {
184+
info = &config->led_infos[i];
185+
186+
if (info->num_colors != 1) {
187+
LOG_WRN("%s: invalid number of colors %d "
188+
"(must be 1 when defining multiple LEDs)",
189+
dev->name, info->num_colors);
190+
/* for now not erroring on this */
191+
}
192+
193+
if (!is31fl3197_count_colors(dev, info->color_mapping[0], rgb_counts)) {
194+
return -EINVAL;
195+
}
196+
}
197+
return 0;
198+
}
199+
200+
static int is31fl3197_init(const struct device *dev)
201+
{
202+
const struct is31fl3197_config *config = dev->config;
203+
int ret;
204+
uint8_t prod_id;
205+
206+
ret = is31fl3197_check_config(dev);
207+
if (ret != 0) {
208+
return ret;
209+
}
210+
211+
if (!i2c_is_ready_dt(&config->bus)) {
212+
LOG_ERR("%s: I2C device not ready", dev->name);
213+
return -ENODEV;
214+
}
215+
216+
ret = i2c_reg_read_byte_dt(&config->bus, IS31FL3197_PROD_ID_REG, &prod_id);
217+
if (ret != 0) {
218+
LOG_ERR("%s: failed to read product ID", dev->name);
219+
return ret;
220+
}
221+
222+
LOG_DBG("IS31FL319 ID: %u(%x) bus addr: %x\n", prod_id, prod_id, config->bus.addr);
223+
/* The product ID (8 bit) should be the I2C address(7 bit) */
224+
if (prod_id != (config->bus.addr << 1)) {
225+
LOG_ERR("%s: invalid product ID 0x%02x (expected 0x%02x)", dev->name, prod_id,
226+
config->bus.addr << 1);
227+
return -ENODEV;
228+
}
229+
230+
ret = i2c_reg_write_byte_dt(&config->bus, IS31FL3197_SHUTDOWN_REG,
231+
IS31FL3197_SHUTDOWN_REG_VAL);
232+
if (ret != 0) {
233+
LOG_ERR("%s: failed to set current limit", dev->name);
234+
return ret;
235+
}
236+
237+
return i2c_reg_write_byte_dt(&config->bus, IS31FL3197_OPER_CONFIG_REG,
238+
IS31FL3197_OPER_CONFIG_REG_VAL);
239+
}
240+
241+
static DEVICE_API(led, is31fl3197_led_api) = {
242+
.set_brightness = is31fl3197_set_brightness,
243+
.get_info = is31fl3197_get_info,
244+
.set_color = is31fl3197_set_color,
245+
};
246+
247+
#define COLOR_MAPPING(led_node_id) \
248+
static const uint8_t color_mapping_##led_node_id[] = \
249+
DT_PROP(led_node_id, color_mapping);
250+
251+
#define LED_INFO(led_node_id) \
252+
{ \
253+
.label = DT_PROP(led_node_id, label), \
254+
.num_colors = DT_PROP_LEN(led_node_id, color_mapping), \
255+
.color_mapping = color_mapping_##led_node_id, \
256+
},
257+
258+
#define LED_CURRENT(led_node_id) \
259+
DT_PROP(led_node_id, current_limit),
260+
261+
#define IS31FL3197_DEFINE(id) \
262+
\
263+
DT_INST_FOREACH_CHILD(id, COLOR_MAPPING) \
264+
\
265+
static const struct led_info is31fl3197_leds_##id[] = \
266+
{ DT_INST_FOREACH_CHILD(id, LED_INFO) }; \
267+
BUILD_ASSERT(ARRAY_SIZE(is31fl3197_leds_##id) > 0, \
268+
"No LEDs defined for " #id); \
269+
\
270+
static const struct is31fl3197_config is31fl3197_config_##id = { \
271+
.bus = I2C_DT_SPEC_INST_GET(id), \
272+
.num_leds = ARRAY_SIZE(is31fl3197_leds_##id), \
273+
.led_infos = is31fl3197_leds_##id, \
274+
}; \
275+
DEVICE_DT_INST_DEFINE(id, &is31fl3197_init, NULL, NULL, \
276+
&is31fl3197_config_##id, POST_KERNEL, \
277+
CONFIG_LED_INIT_PRIORITY, &is31fl3197_led_api);
278+
279+
DT_INST_FOREACH_STATUS_OKAY(IS31FL3197_DEFINE)
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Copyright (c) 2024 Arduino SA
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
description: |
5+
IS31FL3197 4-channel LED driver with programmable pattern sequencing
6+
7+
This driver supports single-channel and RGB LEDs. For single channel LEDs,
8+
the led_set_brightness() API can be used to set the brightness of each LED.
9+
For RGB LEDs, the led_set_color() API can be used to set the red, green and
10+
blue components; the driver takes care of routing to the outputs described
11+
by the color-mapping property.
12+
13+
The LED_SHELL application can be used for testing.
14+
15+
The following shows configuration for Arduino Giga Display shield
16+
17+
is31fl3197@50 {
18+
status = "okay";
19+
compatible = "issi,is31fl3197";
20+
reg = <0x50>;
21+
22+
led_rgb {
23+
label = "RGB LED";
24+
color-mapping = <LED_COLOR_ID_RED>,
25+
<LED_COLOR_ID_GREEN>,
26+
<LED_COLOR_ID_BLUE>;
27+
};
28+
};
29+
30+
compatible: "issi,is31fl3197"
31+
32+
include: ["i2c-device.yaml", "led-controller.yaml"]
33+
34+
child-binding:
35+
properties:
36+
label:
37+
required: true
38+
39+
color-mapping:
40+
required: true
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
cmake_minimum_required(VERSION 3.20.0)
4+
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
5+
project(led_is31fl3197)
6+
7+
target_sources(app PRIVATE src/main.c)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
.. zephyr:code-sample:: is31fl3197
2+
:name: IS31FL3197 RGB LED
3+
:relevant-api: led_interface
4+
5+
Cycle colors on an RGB LED connected to the IS31FL3197 using the LED API.
6+
7+
Overview
8+
********
9+
10+
This sample cycles several colors on an RGB LED forever using the LED API.
11+
12+
Building and Running
13+
********************
14+
15+
This sample can be built and executed on an Arduino Giga R1 with a Giga
16+
display shield installed, or on any board where the devicetree has an
17+
I2C device node with compatible
18+
:dtcompatible:`issi,is31fl3197` enabled, along with the relevant bus
19+
controller node also being enabled.
20+
21+
.. zephyr-app-commands::
22+
:zephyr-app: samples/drivers/led/is31fl3197
23+
:board: arduino_giga_r1/stm32h747xx/m7
24+
:goals: build flash
25+
:compact:
26+
27+
After flashing, the LED starts to switch colors and messages with the current
28+
LED color are printed on the console. If a runtime error occurs, the sample
29+
exits without printing to the console.

0 commit comments

Comments
 (0)