mirror of
https://github.com/zephyrproject-rtos/zephyr
synced 2025-09-04 15:41:56 +00:00
Low frequency and high frequency clocks had separate devices while they are actually handled by single peripheral with single interrupt. The split was done probably because opaque subsys argument in the API was used for other purposes and there was no way to pass the information which clock should be controlled. Implementation changes some time ago and subsys parameter was no longer used. It now can be used to indicate which clock should be controlled. Change become necessary when nrf5340 is taken into account where there are more clocks and current approach would lead to create multiple devices - mess. Signed-off-by: Krzysztof Chruscinski <krzysztof.chruscinski@nordicsemi.no>
148 lines
3.3 KiB
C
148 lines
3.3 KiB
C
/*
|
|
* Copyright (c) 2018 Intel Corporation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <drivers/led_strip.h>
|
|
#include <string.h>
|
|
|
|
#define LOG_LEVEL CONFIG_LED_STRIP_LOG_LEVEL
|
|
#include <logging/log.h>
|
|
LOG_MODULE_REGISTER(ws2812b_sw);
|
|
|
|
#include <zephyr.h>
|
|
#include <soc.h>
|
|
#include <drivers/gpio.h>
|
|
#include <device.h>
|
|
#include <drivers/clock_control.h>
|
|
#include <drivers/clock_control/nrf_clock_control.h>
|
|
|
|
static int send_buf(u8_t *buf, size_t len)
|
|
{
|
|
/* Address of OUTSET. OUTCLR is OUTSET + 4 */
|
|
volatile u32_t *base = (u32_t *)(NRF_GPIO_BASE + 0x508);
|
|
u32_t pin = BIT(CONFIG_WS2812B_SW_GPIO_PIN);
|
|
struct device *clock;
|
|
unsigned int key;
|
|
/* Initilization of i is strictly not needed, but it avoids an
|
|
* uninitialized warning with the inline assembly.
|
|
*/
|
|
u32_t i = 0U;
|
|
|
|
clock = device_get_binding(DT_INST_0_NORDIC_NRF_CLOCK_LABEL);
|
|
if (!clock) {
|
|
LOG_ERR("Unable to get HF clock");
|
|
return -EIO;
|
|
}
|
|
|
|
/* The inline assembly further below is designed to work only with
|
|
* the 16 MHz clock enabled.
|
|
*/
|
|
clock_control_on(clock, CLOCK_CONTROL_NRF_SUBSYS_HF);
|
|
key = irq_lock();
|
|
|
|
while (len--) {
|
|
u32_t b = *buf++;
|
|
|
|
/* Generate signal out of the bits, MSB. 1-bit should be
|
|
* roughly 0.85us high, 0.4us low, whereas a 0-bit should be
|
|
* roughly 0.4us high, 0.85us low.
|
|
*/
|
|
__asm volatile ("movs %[i], #8\n" /* i = 8 */
|
|
".start_bit:\n"
|
|
|
|
/* OUTSET = BIT(LED_PIN) */
|
|
"strb %[p], [%[r], #0]\n"
|
|
|
|
/* if (b & 0x80) goto .long */
|
|
"tst %[b], %[m]\n"
|
|
"bne .long\n"
|
|
|
|
/* 0-bit */
|
|
"nop\nnop\n"
|
|
/* OUTCLR = BIT(LED_PIN) */
|
|
"strb %[p], [%[r], #4]\n"
|
|
"nop\nnop\nnop\n"
|
|
"b .next_bit\n"
|
|
|
|
/* 1-bit */
|
|
".long:\n"
|
|
"nop\nnop\nnop\nnop\nnop\nnop\nnop\n"
|
|
/* OUTCLR = BIT(LED_PIN) */
|
|
"strb %[p], [%[r], #4]\n"
|
|
|
|
".next_bit:\n"
|
|
/* b <<= 1 */
|
|
"lsl %[b], #1\n"
|
|
/* i-- */
|
|
"sub %[i], #1\n"
|
|
/* if (i > 0) goto .start_bit */
|
|
"bne .start_bit\n"
|
|
:
|
|
[i] "+r" (i)
|
|
:
|
|
[b] "l" (b),
|
|
[m] "l" (0x80),
|
|
[r] "l" (base),
|
|
[p] "r" (pin)
|
|
:);
|
|
}
|
|
|
|
irq_unlock(key);
|
|
clock_control_off(clock, CLOCK_CONTROL_NRF_SUBSYS_HF);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ws2812b_sw_update_rgb(struct device *dev, struct led_rgb *pixels,
|
|
size_t num_pixels)
|
|
{
|
|
u8_t *ptr = (u8_t *)pixels;
|
|
size_t i;
|
|
|
|
/* Convert from RGB to GRB format */
|
|
for (i = 0; i < num_pixels; i++) {
|
|
u8_t r = pixels[i].r;
|
|
u8_t b = pixels[i].b;
|
|
u8_t g = pixels[i].g;
|
|
|
|
*ptr++ = g;
|
|
*ptr++ = r;
|
|
*ptr++ = b;
|
|
}
|
|
|
|
return send_buf((u8_t *)pixels, num_pixels * 3);
|
|
}
|
|
|
|
static int ws2812b_sw_update_channels(struct device *dev, u8_t *channels,
|
|
size_t num_channels)
|
|
{
|
|
LOG_ERR("update_channels not implemented");
|
|
return -ENOSYS;
|
|
}
|
|
|
|
static int ws2812b_sw_init(struct device *dev)
|
|
{
|
|
struct device *gpio;
|
|
|
|
gpio = device_get_binding(CONFIG_WS2812B_SW_GPIO_NAME);
|
|
if (!gpio) {
|
|
LOG_ERR("Unable to find %s", CONFIG_WS2812B_SW_GPIO_NAME);
|
|
return -ENODEV;
|
|
}
|
|
|
|
gpio_pin_configure(gpio, CONFIG_WS2812B_SW_GPIO_PIN, GPIO_DIR_OUT);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct led_strip_driver_api ws2812b_sw_api = {
|
|
.update_rgb = ws2812b_sw_update_rgb,
|
|
.update_channels = ws2812b_sw_update_channels,
|
|
};
|
|
|
|
DEVICE_AND_API_INIT(ws2812b_sw, CONFIG_WS2812B_SW_NAME, ws2812b_sw_init, NULL,
|
|
NULL, POST_KERNEL, CONFIG_LED_STRIP_INIT_PRIORITY,
|
|
&ws2812b_sw_api);
|