guowenxue
8 days ago 6c1a5eb1a9891898552bb9320ba973349ca4bffa
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
/**********************************************************************
 *   Copyright: (C)2024 LingYun IoT System Studio
 *      Author: GuoWenxue<guowenxue@gmail.com>
 *
 * Description: WS2812B strip lights driver on ISKBoard
 *
 * Reference  : https://blog.csdn.net/Lennon8_8/article/details/108980808
 *
 * COMPILER OPTIMIZATION WARNING:
 *   This driver uses NOP-based delays for precise WS2812B pulse timing. Keil5
 *   Arm Compiler(AC6) default '-O3' optimization breaks this by altering loop
 *   assembly and branch structures, causing timing collapse.
 *   Fix: Options for Target -> C/C++ (AC6)  Set Optimization to -O1.
 *  Note: GCC compiler (STM32CubeIDE/VSCode) is NOT affected.
 *
 *   ChangeLog:
 *        Version    Date       Author            Description
 *        V1.0.0  2024.08.29    GuoWenxue      Release initial version
 *
 ***********************************************************************/
#include <string.h>
#include <stdint.h>
#include "ws2812b.h"
#include "miscdev.h"
 
static color_t g_ColorPanel[WS2812_NUM];
 
typedef struct ws_gpio_s
{
    GPIO_TypeDef        *group;
    uint16_t             pin;
} ws_gpio_t;
 
/* 使用 ws_gpio 结构体进行 GPIO 操作,便于移植 */
static ws_gpio_t   ws_gpio =
{
    .group = GPIOC,
    .pin   = GPIO_PIN_2,
};
#define ws_set_high()   (ws_gpio.group->BSRR = ws_gpio.pin)
#define ws_set_low()    (ws_gpio.group->BRR = ws_gpio.pin)
 
 
/* 配置 WS2812B 灯带的 GPIO 口 */
void ws2812b_init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
 
    GPIO_InitStruct.Pin = ws_gpio.pin;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
 
    HAL_GPIO_Init(ws_gpio.group, &GPIO_InitStruct);
 
    ws2812b_reset();
 
    return ;
}
 
/*
 * 使用内联汇编 NOP 实现纳秒级精确延时 @ 80MHz: 1 NOP = 12.5ns
 *
 * 根据数据手册时序:
 * T0H: 220~380ns  (recommend 300ns = 24 cycles)
 * T0L: 580~1000ns (recommend 800ns = 64 cycles)
 * T1H: 580~1600ns (recommend 1100ns = 88 cycles)
 * T1L: 220~420ns  (recommend 320ns = 26 cycles)
 * RES: > 280us
 *
 * 实际测试建议:
 * - 考虑 GPIO 操作本身耗时约 2-3 个时钟周期
 * - 考虑函数调用、判断等开销
 * - 以下数值已预留开销,可根据实测微调 ±2~4 个 NOP
 */
 
/* T0H: 300ns ≈ 24 cycles
 * 减去 GPIO 和判断开销约 6 cycles,使用 18 NOPs
 */
#define delay_T0H() __ASM volatile( \
    "NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t" \
    "NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t" \
    "NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t" \
    "NOP\n\t NOP\n\t NOP\n\t" \
)
 
/* T0L: 800ns ≈ 64 cycles
 * 减去开销约 6 cycles,使用 58 NOPs
 */
#define delay_T0L() __ASM volatile( \
    "NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t" \
    "NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t" \
    "NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t" \
    "NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t" \
    "NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t" \
    "NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t" \
)
 
/* T1H: 1100ns ≈ 88 cycles
 * 减去开销约 6 cycles,使用 82 NOPs
 */
#define delay_T1H() __ASM volatile( \
    "NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t" \
    "NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t" \
    "NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t" \
    "NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t" \
    "NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t" \
    "NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t" \
    "NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t" \
    "NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t" \
    "NOP\n\t NOP\n\t" \
)
 
/* T1L: 320ns ≈ 26 cycles
 * 减去开销约 6 cycles,使用 20 NOPs
 */
#define delay_T1L() __ASM volatile( \
    "NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t" \
    "NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t" \
)
 
static inline void ws2812b_WriteByte(uint8_t byte)
{
    int i;
 
    for(i=0; i<8; i++)
    {
        if(byte & 0x80)
        {
            /* Sequence Send 1:
             *
             * +--------------+         +
             * |<-   T1H    ->|<- T1L ->|
             * |              +---------+
             */
            ws_set_high();
            delay_T1H();
 
            ws_set_low();
            delay_T1L();
        }
        else
        {
            /* Sequence Send 0:
             *
             * +---------+              +
             * |<- T0H ->|<-    T0L   ->|
             * |         +--------------+
             */
            ws_set_high();
            delay_T0H();
 
            ws_set_low();
            delay_T0L();
        }
 
        byte <<= 1;
    }
 
    return ;
}
 
void ws2812b_reset(void)
{
    /* RES: low voltage time > 280us */
    ws_set_low();
    udelay(300);
 
    memset(g_ColorPanel, 0, sizeof(g_ColorPanel));
 
    return ;
}
 
static inline void ws2812b_SendColor(color_t color)
{
    ws2812b_WriteByte(color.pixel.green);
    ws2812b_WriteByte(color.pixel.red);
    ws2812b_WriteByte(color.pixel.blue);
 
    return ;
}
 
static inline void ws2812b_Refresh(void)
{
    int i;
    uint32_t primask;
 
    /* 关闭中断保证时序准确 */
    primask = __get_PRIMASK();
    __disable_irq();
 
    for(i=0; i<WS2812_NUM; i++)
    {
        ws2812b_SendColor(g_ColorPanel[i]);
    }
 
    /* 恢复中断状态 */
    __set_PRIMASK(primask);
 
    return ;
}
 
static inline int ws2812b_SetPixel(int which, color_t color)
{
    if( which<0 || which>=WS2812_NUM )
        return -1;
 
    g_ColorPanel[which].pixel.red   = color.pixel.red;
    g_ColorPanel[which].pixel.green = color.pixel.green;
    g_ColorPanel[which].pixel.blue  = color.pixel.blue;
 
    return 0;
}
 
int ws2812b_turn(int which, color_t color)
{
    if( which<0 || which>=WS2812_NUM )
        return -1;
 
    ws2812b_SetPixel(which, color);
    ws2812b_Refresh();
 
    return 0;
}
 
void ws2812b_blink(void)
{
    int      c, i;
    color_t  colors[4] = {[0].data=PIXEL_B, [1].data=PIXEL_R, [2].data=PIXEL_G, [3].data=PIXEL_W};
    color_t  color_off = {.data = PIXEL_OFF};
 
    for(c=0; c<4; c++) /* color */
    {
        for(i=0; i<WS2812_NUM; i++)
        {
            ws2812b_turn(i, colors[c]);
            mdelay(300);
            ws2812b_turn(i, color_off);
            mdelay(300);
        }
    }
}