From a40997aa12029f24ef0904d9af6415c975c4322f Mon Sep 17 00:00:00 2001
From: guowenxue <guowenxue@gmail.com>
Date: Tue, 07 Jul 2026 23:17:44 +0800
Subject: [PATCH] Add ex1.ws2812b example code

---
 ex1.ws2812b/Board/ws2812b.c |  244 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 244 insertions(+), 0 deletions(-)

diff --git a/ex1.ws2812b/Board/ws2812b.c b/ex1.ws2812b/Board/ws2812b.c
new file mode 100644
index 0000000..07b8342
--- /dev/null
+++ b/ex1.ws2812b/Board/ws2812b.c
@@ -0,0 +1,244 @@
+/**********************************************************************
+ *   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"
+
+#define mdelay(ms) HAL_Delay(ms)
+
+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);
+        }
+    }
+}

--
Gitblit v1.10.0