/********************************************************************** * Copyright: (C)2024 LingYun IoT System Studio * Author: GuoWenxue * * 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 #include #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 ) 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