|
8 | 8 | * |
9 | 9 | * WS/LRCK frequency: |
10 | 10 | * This refers to the "I2S word or channel select" clock. |
11 | | - * The I2C peripheral sends two 16-bit channel values for each clock period. |
| 11 | + * The I2S peripheral sends two 16-bit channel values for each clock period. |
12 | 12 | * A single LED color (8 data bits) will take up one 32-bit word or one LRCK |
13 | 13 | * period. This means a standard RGB led will take 3 LRCK periods to transmit. |
14 | 14 | * |
|
17 | 17 |
|
18 | 18 | #define DT_DRV_COMPAT worldsemi_ws2812_i2s |
19 | 19 |
|
20 | | -#include <string.h> |
21 | | - |
22 | 20 | #include <zephyr/drivers/led_strip.h> |
23 | 21 |
|
24 | 22 | #define LOG_LEVEL CONFIG_LED_STRIP_LOG_LEVEL |
@@ -51,43 +49,31 @@ struct ws2812_i2s_cfg { |
51 | 49 | /* Serialize an 8-bit color channel value into two 16-bit I2S values (or 1 32-bit |
52 | 50 | * word). |
53 | 51 | */ |
54 | | -static inline void ws2812_i2s_ser(uint32_t *word, uint8_t color, const uint8_t sym_one, |
55 | | - const uint8_t sym_zero) |
| 52 | +static inline uint32_t ws2812_i2s_ser(uint8_t color, const uint8_t sym_one, const uint8_t sym_zero) |
56 | 53 | { |
57 | | - *word = 0; |
58 | | - for (uint16_t i = 0; i < 8; i++) { |
59 | | - if ((1 << i) & color) { |
60 | | - *word |= sym_one << (i * 4); |
61 | | - } else { |
62 | | - *word |= sym_zero << (i * 4); |
63 | | - } |
| 54 | + uint32_t word = 0; |
| 55 | + |
| 56 | + for (uint_fast8_t mask = 0x80; mask != 0; mask >>= 1) { |
| 57 | + word <<= 4; |
| 58 | + word |= (color & mask) ? sym_one : sym_zero; |
64 | 59 | } |
65 | 60 |
|
66 | 61 | /* Swap the two I2S values due to the (audio) channel TX order. */ |
67 | | - *word = (*word >> 16) | (*word << 16); |
| 62 | + return (word >> 16) | (word << 16); |
68 | 63 | } |
69 | 64 |
|
70 | 65 | static int ws2812_strip_update_rgb(const struct device *dev, struct led_rgb *pixels, |
71 | 66 | size_t num_pixels) |
72 | 67 | { |
73 | 68 | const struct ws2812_i2s_cfg *cfg = dev->config; |
74 | | - uint8_t sym_one, sym_zero; |
75 | | - uint32_t reset_word; |
| 69 | + const uint8_t sym_one = cfg->nibble_one; |
| 70 | + const uint8_t sym_zero = cfg->nibble_zero; |
| 71 | + const uint32_t reset_word = cfg->active_low ? ~0 : 0; |
76 | 72 | uint32_t *tx_buf; |
77 | 73 | uint32_t flush_time_us; |
78 | 74 | void *mem_block; |
79 | 75 | int ret; |
80 | 76 |
|
81 | | - if (cfg->active_low) { |
82 | | - sym_one = (~cfg->nibble_one) & 0x0F; |
83 | | - sym_zero = (~cfg->nibble_zero) & 0x0F; |
84 | | - reset_word = 0xFFFFFFFF; |
85 | | - } else { |
86 | | - sym_one = cfg->nibble_one & 0x0F; |
87 | | - sym_zero = cfg->nibble_zero & 0x0F; |
88 | | - reset_word = 0; |
89 | | - } |
90 | | - |
91 | 77 | /* Acquire memory for the I2S payload. */ |
92 | 78 | ret = k_mem_slab_alloc(cfg->mem_slab, &mem_block, K_SECONDS(10)); |
93 | 79 | if (ret < 0) { |
@@ -127,7 +113,7 @@ static int ws2812_strip_update_rgb(const struct device *dev, struct led_rgb *pix |
127 | 113 | default: |
128 | 114 | return -EINVAL; |
129 | 115 | } |
130 | | - ws2812_i2s_ser(tx_buf, pixel, sym_one, sym_zero); |
| 116 | + *tx_buf = ws2812_i2s_ser(pixel, sym_one, sym_zero) ^ reset_word; |
131 | 117 | tx_buf++; |
132 | 118 | } |
133 | 119 | } |
@@ -221,15 +207,12 @@ static const struct led_strip_driver_api ws2812_i2s_api = { |
221 | 207 | .length = ws2812_strip_length, |
222 | 208 | }; |
223 | 209 |
|
224 | | -/* Integer division, but always rounds up: e.g. 10/3 = 4 */ |
225 | | -#define WS2812_ROUNDED_DIVISION(x, y) ((x + (y - 1)) / y) |
226 | | - |
227 | 210 | #define WS2812_I2S_LRCK_PERIOD_US(idx) DT_INST_PROP(idx, lrck_period) |
228 | 211 |
|
229 | 212 | #define WS2812_RESET_DELAY_US(idx) DT_INST_PROP(idx, reset_delay) |
230 | 213 | /* Rounds up to the next 20us. */ |
231 | | -#define WS2812_RESET_DELAY_WORDS(idx) WS2812_ROUNDED_DIVISION(WS2812_RESET_DELAY_US(idx), \ |
232 | | - WS2812_I2S_LRCK_PERIOD_US(idx)) |
| 214 | +#define WS2812_RESET_DELAY_WORDS(idx) \ |
| 215 | + DIV_ROUND_UP(WS2812_RESET_DELAY_US(idx), WS2812_I2S_LRCK_PERIOD_US(idx)) |
233 | 216 |
|
234 | 217 | #define WS2812_NUM_COLORS(idx) (DT_INST_PROP_LEN(idx, color_mapping)) |
235 | 218 |
|
|
0 commit comments