Primary Git Repository for the Zephyr Project. Zephyr is a new generation, scalable, optimized, secure RTOS for multiple hardware architectures.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

135 lines
3.3 KiB

/*
* Copyright (c) 2018 Google LLC.
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT apa_apa102
#include <errno.h>
#include <zephyr/drivers/led_strip.h>
#include <zephyr/drivers/spi.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/sys/util.h>
struct apa102_config {
struct spi_dt_spec bus;
size_t length;
uint8_t *const end_frame;
const size_t end_frame_size;
};
static int apa102_update(const struct device *dev, void *buf, size_t size)
{
const struct apa102_config *config = dev->config;
static const uint8_t zeros[] = { 0, 0, 0, 0 };
const struct spi_buf tx_bufs[] = {
{
/* Start frame: at least 32 zeros */
.buf = (uint8_t *)zeros,
.len = sizeof(zeros),
},
{
/* LED data itself */
.buf = buf,
.len = size,
},
{
/* End frame: at least 32 ones to clock the
* remaining bits to the LEDs at the end of
* the strip.
*/
.buf = (uint8_t *)config->end_frame,
.len = config->end_frame_size,
},
};
const struct spi_buf_set tx = {
.buffers = tx_bufs,
.count = ARRAY_SIZE(tx_bufs)
};
return spi_write_dt(&config->bus, &tx);
}
static int apa102_update_rgb(const struct device *dev, struct led_rgb *pixels,
size_t count)
{
uint8_t *p = (uint8_t *)pixels;
size_t i;
/* SOF (3 bits) followed by the 0 to 31 global dimming level */
uint8_t prefix = 0xE0 | 31;
/* Rewrite to the on-wire format */
for (i = 0; i < count; i++) {
uint8_t r = pixels[i].r;
uint8_t g = pixels[i].g;
uint8_t b = pixels[i].b;
*p++ = prefix;
*p++ = b;
*p++ = g;
*p++ = r;
}
BUILD_ASSERT(sizeof(struct led_rgb) == 4);
return apa102_update(dev, pixels, sizeof(struct led_rgb) * count);
}
static size_t apa102_length(const struct device *dev)
{
const struct apa102_config *config = dev->config;
return config->length;
}
static int apa102_init(const struct device *dev)
{
const struct apa102_config *config = dev->config;
if (!spi_is_ready_dt(&config->bus)) {
return -ENODEV;
}
memset(config->end_frame, 0xFF, config->end_frame_size);
return 0;
}
static DEVICE_API(led_strip, apa102_api) = {
.update_rgb = apa102_update_rgb,
.length = apa102_length,
};
/*
* The "End frame" is statically allocated, as a sequence of 0xFF bytes
* The only function of the “End frame” is to supply more clock pulses
* to the string until the data has permeated to the last LED. The
* number of clock pulses required is exactly half the total number
* of LEDs in the string. See below `end_frame`.
*/
#define APA102_DEVICE(idx) \
static uint8_t apa102_end_frame_##idx \
[(DT_INST_PROP(idx, chain_length) / \
sizeof(struct led_rgb) / 2) + 1]; \
static const struct apa102_config apa102_##idx##_config = { \
.bus = SPI_DT_SPEC_INST_GET( \
idx, \
SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | SPI_WORD_SET(8), \
0), \
.length = DT_INST_PROP(idx, chain_length), \
.end_frame = apa102_end_frame_##idx, \
.end_frame_size = (DT_INST_PROP(idx, chain_length) / \
sizeof(struct led_rgb) / 2) + 1, \
}; \
\
DEVICE_DT_INST_DEFINE(idx, \
apa102_init, \
NULL, \
NULL, \
&apa102_##idx##_config, \
POST_KERNEL, \
CONFIG_LED_STRIP_INIT_PRIORITY, \
&apa102_api);
DT_INST_FOREACH_STATUS_OKAY(APA102_DEVICE)