drivers: ws2812_gpio: Make timing configurable and less hardware dependend
The current driver contains assembly code which is specific for the nRF51 SOC which makes it incompatible with other SOC's. This patch adds support for other nRF SOC's as well. The timing is calucated based on the CPU clock frequency, but can be configured manually as well if needed. Changes have been verified on a Adafruit Feather nRF52840 Express board, which contains a single NeoPixel RGB LED. Timings have been verified using a scope connected to the WS1812 data line. Signed-off-by: Chaim Zax <chaim.zax@zaxx.pro>
This commit is contained in:
parent
be523ad396
commit
f54a53b4b3
6 changed files with 169 additions and 41 deletions
3
doc/build/dts/api/api.rst
vendored
3
doc/build/dts/api/api.rst
vendored
|
@ -453,3 +453,6 @@ device.
|
|||
* - zephyr,usb-device
|
||||
- USB device node. If defined and has a ``vbus-gpios`` property, these
|
||||
will be used by the USB subsystem to enable/disable VBUS
|
||||
* - zephyr,led-strip
|
||||
- A LED-strip node which is used to determine the timings of the
|
||||
WS2812 GPIO driver
|
||||
|
|
|
@ -170,6 +170,8 @@ Drivers and Sensors
|
|||
|
||||
* LED Strip
|
||||
|
||||
* Updated ws2812 GPIO driver to support dynamic bus timings
|
||||
|
||||
* LoRa
|
||||
|
||||
* Mailbox
|
||||
|
|
|
@ -30,20 +30,68 @@ config WS2812_STRIP_I2S
|
|||
|
||||
config WS2812_STRIP_GPIO
|
||||
bool "WS2812 LED strip GPIO driver"
|
||||
# Only an Cortex-M0 inline assembly implementation for the nRF51
|
||||
# is supported currently.
|
||||
# Only an Cortex-M inline assembly implementation for the nRF91, nRF51,
|
||||
# nRF52 and nRF53 is supported currently.
|
||||
default y
|
||||
depends on DT_HAS_WORLDSEMI_WS2812_GPIO_ENABLED
|
||||
depends on SOC_SERIES_NRF51X
|
||||
depends on (SOC_SERIES_NRF91X || SOC_SERIES_NRF51X || SOC_SERIES_NRF52X || SOC_SERIES_NRF53X)
|
||||
select LED_STRIP_RGB_SCRATCH
|
||||
help
|
||||
Enable driver for WS2812 (and compatibles) LED strip directly controlling with GPIO.
|
||||
The GPIO driver does bit-banging with inline assembly,
|
||||
and is not available on all SoCs.
|
||||
Enable driver for WS2812 (and compatibles) LED strip directly
|
||||
controlling with GPIO. The GPIO driver does bit-banging with inline
|
||||
assembly, and is not available on all SoCs.
|
||||
|
||||
Note that this driver is not compatible with the Everlight B1414
|
||||
controller.
|
||||
|
||||
if WS2812_STRIP_GPIO
|
||||
|
||||
DT_CHOSEN_LED_STRIP := zephyr,led-strip
|
||||
DT_CHOSEN_LED_STRIP_PATH := $(dt_chosen_path,$(DT_CHOSEN_LED_STRIP))
|
||||
|
||||
config DELAY_T1H
|
||||
int "Delay 1 bit high pulse"
|
||||
default $(dt_node_int_prop_int,$(DT_CHOSEN_LED_STRIP_PATH),delay-t1h) \
|
||||
if $(dt_node_has_prop,$(DT_CHOSEN_LED_STRIP_PATH),delay-t1h)
|
||||
default $(div,$(mul,700,$(dt_node_int_prop_int,/cpus/cpu@0,clock-frequency)),1000000000) \
|
||||
if $(dt_node_has_prop,/cpus/cpu@0,clock-frequency)
|
||||
default 7
|
||||
help
|
||||
Number of NOP assembly operations to create a delay for a 1 bit, high voltage period (default 700 nsec)
|
||||
|
||||
config DELAY_T1L
|
||||
int "Delay 1 bit low pulse"
|
||||
default $(dt_node_int_prop_int,$(DT_CHOSEN_LED_STRIP_PATH),delay-t1l) \
|
||||
if $(dt_node_has_prop,$(DT_CHOSEN_LED_STRIP_PATH),delay-t1l)
|
||||
default $(div,$(mul,600,$(dt_node_int_prop_int,/cpus/cpu@0,clock-frequency)),1000000000) \
|
||||
if $(dt_node_has_prop,/cpus/cpu@0,clock-frequency)
|
||||
default 6
|
||||
help
|
||||
Number of NOP assembly operations to create a delay for a 1 bit, low voltage period (default 600 nsec)
|
||||
|
||||
config DELAY_T0H
|
||||
int "Delay 0 bit high pulse"
|
||||
default $(dt_node_int_prop_int,$(DT_CHOSEN_LED_STRIP_PATH),delay-t0h) \
|
||||
if $(dt_node_has_prop,$(DT_CHOSEN_LED_STRIP_PATH),delay-t0h)
|
||||
default $(div,$(mul,350,$(dt_node_int_prop_int,/cpus/cpu@0,clock-frequency)),1000000000) \
|
||||
if $(dt_node_has_prop,/cpus/cpu@0,clock-frequency)
|
||||
default 3
|
||||
help
|
||||
Number of NOP assembly operations to create a delay for a 0 bit, high voltage period (default 350 nsec)
|
||||
|
||||
config DELAY_T0L
|
||||
int "Delay 0 bit low pulse"
|
||||
default $(dt_node_int_prop_int,$(DT_CHOSEN_LED_STRIP_PATH),delay-t0l) \
|
||||
if $(dt_node_has_prop,$(DT_CHOSEN_LED_STRIP_PATH),delay-t0l)
|
||||
default $(div,$(mul,800,$(dt_node_int_prop_int,/cpus/cpu@0,clock-frequency)),1000000000) \
|
||||
if $(dt_node_has_prop,/cpus/cpu@0,clock-frequency)
|
||||
default 8
|
||||
help
|
||||
Number of NOP assembly operations to create a delay for a 0 bit, low voltage period (default 800 nsec)
|
||||
|
||||
endif
|
||||
|
||||
|
||||
config WS2812_STRIP_RPI_PICO_PIO
|
||||
bool "WS2812 LED strip Raspberry Pi Pico PIO driver"
|
||||
default y
|
||||
|
|
|
@ -23,6 +23,7 @@ LOG_MODULE_REGISTER(ws2812_gpio);
|
|||
#include <zephyr/drivers/clock_control.h>
|
||||
#include <zephyr/drivers/clock_control/nrf_clock_control.h>
|
||||
#include <zephyr/dt-bindings/led/led.h>
|
||||
#include <zephyr/sys/util_macro.h>
|
||||
|
||||
struct ws2812_gpio_cfg {
|
||||
struct gpio_dt_spec gpio;
|
||||
|
@ -31,25 +32,6 @@ struct ws2812_gpio_cfg {
|
|||
size_t length;
|
||||
};
|
||||
|
||||
/*
|
||||
* This is hard-coded to nRF51 in two ways:
|
||||
*
|
||||
* 1. The assembly delays T1H, T0H, TxL
|
||||
* 2. GPIO set/clear
|
||||
*/
|
||||
|
||||
/*
|
||||
* T1H: 1 bit high pulse delay: 12 cycles == .75 usec
|
||||
* T0H: 0 bit high pulse delay: 4 cycles == .25 usec
|
||||
* TxL: inter-bit low pulse delay: 8 cycles == .5 usec
|
||||
*
|
||||
* We can't use k_busy_wait() here: its argument is in microseconds,
|
||||
* and we need roughly .05 microsecond resolution.
|
||||
*/
|
||||
#define DELAY_T1H "nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n"
|
||||
#define DELAY_T0H "nop\nnop\nnop\nnop\n"
|
||||
#define DELAY_TxL "nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n"
|
||||
|
||||
/*
|
||||
* GPIO set/clear (these make assumptions about assembly details
|
||||
* below).
|
||||
|
@ -68,24 +50,27 @@ struct ws2812_gpio_cfg {
|
|||
#define SET_HIGH "str %[p], [%[r], #0]\n" /* OUTSET = BIT(LED_PIN) */
|
||||
#define SET_LOW "str %[p], [%[r], #4]\n" /* OUTCLR = BIT(LED_PIN) */
|
||||
|
||||
#define NOPS(i, _) "nop\n"
|
||||
#define NOP_N_TIMES(n) LISTIFY(n, NOPS, ())
|
||||
|
||||
/* Send out a 1 bit's pulse */
|
||||
#define ONE_BIT(base, pin) do { \
|
||||
__asm volatile (SET_HIGH \
|
||||
DELAY_T1H \
|
||||
SET_LOW \
|
||||
DELAY_TxL \
|
||||
:: \
|
||||
[r] "l" (base), \
|
||||
#define ONE_BIT(base, pin) do { \
|
||||
__asm volatile (SET_HIGH \
|
||||
NOP_N_TIMES(CONFIG_DELAY_T1H) \
|
||||
SET_LOW \
|
||||
NOP_N_TIMES(CONFIG_DELAY_T1L) \
|
||||
:: \
|
||||
[r] "l" (base), \
|
||||
[p] "l" (pin)); } while (false)
|
||||
|
||||
/* Send out a 0 bit's pulse */
|
||||
#define ZERO_BIT(base, pin) do { \
|
||||
__asm volatile (SET_HIGH \
|
||||
DELAY_T0H \
|
||||
SET_LOW \
|
||||
DELAY_TxL \
|
||||
:: \
|
||||
[r] "l" (base), \
|
||||
#define ZERO_BIT(base, pin) do { \
|
||||
__asm volatile (SET_HIGH \
|
||||
NOP_N_TIMES(CONFIG_DELAY_T0H) \
|
||||
SET_LOW \
|
||||
NOP_N_TIMES(CONFIG_DELAY_T0L) \
|
||||
:: \
|
||||
[r] "l" (base), \
|
||||
[p] "l" (pin)); } while (false)
|
||||
|
||||
static int send_buf(const struct device *dev, uint8_t *buf, size_t len)
|
||||
|
|
|
@ -6,9 +6,95 @@ description: |
|
|||
|
||||
Driver bindings for bit-banging a WS2812 or compatible LED strip.
|
||||
|
||||
The GPIO driver uses inline assembly, and isn't available for all
|
||||
boards.
|
||||
The CPU driver uses inline assembly, and isn't available for all
|
||||
boards. The timing is automatically derived from the CPU clock frequency,
|
||||
or can be provided by setting the delay-txx properties in the device
|
||||
tree, or can be manually provided by the Kconfig settings DELAY_Txx.
|
||||
|
||||
The four delays provided (calculated based on the clock frequency,
|
||||
provided by a dts, or Kconfig file) determine the delays as depicted
|
||||
below. The exact timings of the LED strip data line might vary on the
|
||||
type of LEDs used, consult the data-sheet for the precise timings.
|
||||
|
||||
0 code
|
||||
+-------+ +---
|
||||
| | |
|
||||
| T0H | T0L |
|
||||
| | |
|
||||
---+ +-----------------+
|
||||
|
||||
1 code
|
||||
+---------------+ +---
|
||||
| | |
|
||||
| T1H | T1L |
|
||||
| | |
|
||||
---+ +---------+
|
||||
|
||||
|
||||
Example dts file:
|
||||
/ {
|
||||
cpus {
|
||||
cpu@0 {
|
||||
clock-frequency = <64000000>;
|
||||
};
|
||||
};
|
||||
|
||||
rgb_led: ws2812 {
|
||||
compatible = "worldsemi,ws2812-gpio";
|
||||
chain-length = <1>;
|
||||
color-mapping = <LED_COLOR_ID_GREEN
|
||||
LED_COLOR_ID_RED
|
||||
LED_COLOR_ID_BLUE>;
|
||||
gpios = <&gpio0 16 0>;
|
||||
};
|
||||
};
|
||||
|
||||
Example dts file:
|
||||
/ {
|
||||
chosen {
|
||||
zephyr,led-strip = &rgb_led;
|
||||
};
|
||||
|
||||
rgb_led: ws2812 {
|
||||
compatible = "worldsemi,ws2812-gpio";
|
||||
chain-length = <1>;
|
||||
color-mapping = <LED_COLOR_ID_GREEN
|
||||
LED_COLOR_ID_RED
|
||||
LED_COLOR_ID_BLUE>;
|
||||
reset-delay = <50>;
|
||||
gpios = <&gpio0 16 0>;
|
||||
delay-t1h = <48>;
|
||||
delay-t1l = <32>;
|
||||
delay-t0h = <16>;
|
||||
delay-t0l = <32>;
|
||||
};
|
||||
};
|
||||
|
||||
compatible: "worldsemi,ws2812-gpio"
|
||||
|
||||
include: [base.yaml, ws2812-gpio.yaml]
|
||||
|
||||
properties:
|
||||
delay-t1h:
|
||||
type: int
|
||||
description: |
|
||||
Number of NOP assembly operations to create a delay for a 1 bit, high
|
||||
voltage period (default 700 nsec)
|
||||
|
||||
delay-t1l:
|
||||
type: int
|
||||
description: |
|
||||
Number of NOP assembly operations to create a delay for a 1 bit, low
|
||||
voltage period (default 600 nsec)
|
||||
|
||||
delay-t0h:
|
||||
type: int
|
||||
description: |
|
||||
Number of NOP assembly operations to create a delay for a 0 bit, high
|
||||
voltage period (default 350 nsec)
|
||||
|
||||
delay-t0l:
|
||||
type: int
|
||||
description: |
|
||||
Number of NOP assembly operations to create a delay for a 0 bit, low
|
||||
voltage period (default 800 nsec)
|
||||
|
|
4
samples/drivers/led_strip/boards/bbc_microbit.conf
Normal file
4
samples/drivers/led_strip/boards/bbc_microbit.conf
Normal file
|
@ -0,0 +1,4 @@
|
|||
CONFIG_DELAY_T1H=12
|
||||
CONFIG_DELAY_T1L=8
|
||||
CONFIG_DELAY_T0H=4
|
||||
CONFIG_DELAY_T0L=8
|
Loading…
Add table
Add a link
Reference in a new issue