1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
|
#include "ws2812b.h"
#include <string.h>
#include "spi.h"
// either 0b110 when (n) is true, otherwise 0b100
#define BIT_TO_SPI_DATA(v, n) ((BIT_N(v, n)) ? 6 : 4)
#define BIT_N(v, n) ((v) & (1 << (n)))
#define BYTE_1(n) ((n) << 0)
#define BYTE_2(n) ((n) << 8)
#define BYTE_3(n) ((n) << 16)
#define BYTE_4(n) ((n) << 24)
#define COLOR_BYTE_1(c) \
(BIT_TO_SPI_DATA((c), 7) << 5 | BIT_TO_SPI_DATA((c), 6) << 2 | \
BIT_TO_SPI_DATA((c), 5) >> 1)
#define COLOR_BYTE_2(c) \
((BIT_TO_SPI_DATA((c), 5) & 1) << 7 | BIT_TO_SPI_DATA((c), 4) << 4 | \
BIT_TO_SPI_DATA((c), 3) << 1 | BIT_TO_SPI_DATA((c), 2) >> 2)
#define COLOR_BYTE_3(c) \
((BIT_TO_SPI_DATA((c), 2) & 3) << 6 | BIT_TO_SPI_DATA((c), 1) << 3 | \
BIT_TO_SPI_DATA((c), 0) << 0)
// Clips a pointer the bottom 14 bits for comparison with the DMA.
#define CLIP_TO_DMA_BITS(ptr) ((uint16_t)(((uint32_t)(ptr)) & 0x3fff))
inline static void complie_color_inline(
rgb_t color, struct rgb_compiled* out, enum ws2812b_byte_order byte_order)
{
if (byte_order == BYTE_ORDER_GBR) {
uint8_t tmp = color.b;
color.b = color.g;
color.g = tmp;
}
out->first_bits =
BYTE_1(COLOR_BYTE_1(color.b)) | BYTE_2(COLOR_BYTE_2(color.b)) |
BYTE_3(COLOR_BYTE_3(color.b)) | BYTE_4(COLOR_BYTE_1(color.g));
out->second_bits =
BYTE_1(COLOR_BYTE_2(color.g)) | BYTE_2(COLOR_BYTE_3(color.g)) |
BYTE_3(COLOR_BYTE_1(color.r)) | BYTE_4(COLOR_BYTE_2(color.r));
out->last_bits = COLOR_BYTE_3(color.r);
}
void compile_color(
rgb_t color, struct rgb_compiled* out, enum ws2812b_byte_order byte_order)
{
complie_color_inline(color, out, byte_order);
}
int write_rgb(struct ws2812b_buf* out, rgb_t color)
{
struct rgb_compiled comp;
complie_color_inline(color, &comp, out->byte_order);
// Make sure there's enough memory.
if (out->total_alloc < out->cur + TOTAL_BYTES_PER_LED) {
return 1;
}
// Wait for DMA if it's running.
volatile uint16_t* dma_now = out->dma_now_reg;
// If the dma_now is not set, we won't wait for the DMA.
if (dma_now) {
uint16_t dma_loc = *dma_now;
uint16_t dma_end = *out->dma_end_reg;
while ((dma_loc - CLIP_TO_DMA_BITS(out->buf + out->cur) <
TOTAL_BYTES_PER_LED) &&
(dma_loc < dma_end)) {
dma_loc = *dma_now;
}
}
memcpy(out->buf + out->cur, comp.buf, WIRE_BYTES_PER_COLOR);
memset(out->buf + out->cur + WIRE_BYTES_PER_COLOR, 0, PADDING_BYTES);
out->cur += TOTAL_BYTES_PER_LED;
return 0;
}
void start_dma(struct ws2812b_buf* buf)
{
buf->dma_end_reg = &SPI0->dma_end;
buf->dma_now_reg = &SPI0->dma_now;
dma_transfer(buf->buf, buf->cur);
}
void make_wsb2812b(struct ws2812b_buf* out, void* buf, size_t n_alloc)
{
memset(out, 0, sizeof(*out));
out->buf = buf;
out->total_alloc = n_alloc;
}
void compiled_color_dbg_str(struct rgb_compiled* c, char* out)
{
uint8_t cur = 0;
for (int i = 0; i < 9; ++i) {
cur = c->buf[i];
for (int j = 0; j < 8; ++j) {
*(out++) = (cur & 0x80) ? '1' : '0';
cur <<= 1;
}
}
*(out++) = 0;
}
|