#include "ws2812.h" #include "tim.h" #include "stdlib.h" #include "bsp/pwm.h" /** * @file ws2812.c * @brief WS2812 LED驱动实现 * @note 使用PWM+DMA方式驱动WS2812灯带 */ /* 动态内存指针 */ static uint32_t* Pixel_Buf = NULL; static uint16_t Pixel_NUM = 0; static uint8_t Is_Initialized = 0; /* WS2812时序码定义 * WS2812 时序要求 (800kHz, 1.25μs per bit): * - 1码: 高电平 0.8μs (64%), 低电平 0.4μs (32%) * - 0码: 高电平 0.4μs (32%), 低电平 0.8μs (64%) * ARR = 89, 周期 = 90 计数 */ #define CODE_1 ((uint32_t)(90 * 0.64f)) /*!< 1码: 58 */ #define CODE_0 ((uint32_t)(90 * 0.32f)) /*!< 0码: 29 */ /** * @brief 初始化WS2812驱动,分配内存 * @param led_num: LED数量 * @retval 0成功,1失败 */ uint8_t WS2812_Init(uint16_t led_num) { if (led_num == 0 || led_num > 1024) return 1; // 限制最大LED数量 // 如果已经初始化,先释放旧的内存 if (Is_Initialized) { WS2812_DeInit(); } Pixel_NUM = led_num; uint32_t buf_size = (Pixel_NUM + 1) * 24; // 每个LED 24位 + 1行RESET Pixel_Buf = (uint32_t*)malloc(buf_size * sizeof(uint32_t)); if (Pixel_Buf == NULL) { Pixel_NUM = 0; return 1; } // 初始化缓冲区为0 for (uint32_t i = 0; i < buf_size; i++) { Pixel_Buf[i] = 0; } Is_Initialized = 1; // TIM1是高级定时器,必须使能主输出 __HAL_TIM_MOE_ENABLE(&htim1); return 0; } /** * @brief 释放内存,停止DMA * @retval None */ void WS2812_DeInit(void) { if (Pixel_Buf != NULL) { free(Pixel_Buf); Pixel_Buf = NULL; } Pixel_NUM = 0; Is_Initialized = 0; BSP_PWM_Stop(BSP_PWM_WS2812); } /** * @brief 设置单个LED的颜色 * @param LedId: LED序号 * @param R: 红色分量 0-255 * @param G: 绿色分量 0-255 * @param B: 蓝色分量 0-255 * @retval None */ void WS2812_SetColor(uint8_t LedId, uint8_t R, uint8_t G, uint8_t B) { if (!Is_Initialized || LedId >= Pixel_NUM || Pixel_Buf == NULL) return; uint32_t* led_data = Pixel_Buf + LedId * 24; // WS2812颜色顺序:GRB for (uint8_t i = 0; i < 8; i++) led_data[i] = (G & (1 << (7 - i))) ? CODE_1 : CODE_0; for (uint8_t i = 8; i < 16; i++) led_data[i] = (R & (1 << (15 - i))) ? CODE_1 : CODE_0; for (uint8_t i = 16; i < 24; i++) led_data[i] = (B & (1 << (23 - i))) ? CODE_1 : CODE_0; } /** * @brief 设置多个LED的颜色 * @param PixelArray: 颜色数组 * PixelArray[i][0]: LED的ID * PixelArray[i][1]: R值 * PixelArray[i][2]: G值 * PixelArray[i][3]: B值 * @retval None */ void WS2812_SetColors(uint8_t PixelArray[][4], uint16_t ArraySize) { if (!Is_Initialized || Pixel_Buf == NULL) return; for (uint16_t i = 0; i < ArraySize; i++) { uint8_t id = PixelArray[i][0]; uint8_t R = PixelArray[i][1]; uint8_t G = PixelArray[i][2]; uint8_t B = PixelArray[i][3]; if (id < Pixel_NUM) { WS2812_SetColor(id, R, G, B); } } } /** * @brief 发送数据到WS2812灯带 * @note 会自动添加RESET码 * @retval None */ void WS2812_SendArray(void) { if (!Is_Initialized || Pixel_Buf == NULL) return; // 设置RESET行(最后24个数据为0) uint32_t* reset_row = Pixel_Buf + Pixel_NUM * 24; for (uint8_t i = 0; i < 24; i++) { reset_row[i] = 0; } // 直接使用HAL库函数,绕过BSP层 // HAL_TIM_PWM_Start_DMA( // &htim1, // TIM_CHANNEL_4, // Pixel_Buf, // (Pixel_NUM + 1) * 24 // ); BSP_PWM_Start_DMA( BSP_PWM_WS2812, Pixel_Buf, (Pixel_NUM + 1) * 24 ); } /** * @brief 清空所有LED(设置为黑色) * @retval None */ void WS2812_Clear(void) { if (!Is_Initialized || Pixel_Buf == NULL) return; // 将所有LED数据清零 for (uint32_t i = 0; i < Pixel_NUM * 24; i++) { Pixel_Buf[i] = 0; } } /** * @brief 设置所有LED为同一颜色 * @param R: 红色分量 0-255 * @param G: 绿色分量 0-255 * @param B: 蓝色分量 0-255 * @retval None */ void WS2812_SetAll(uint8_t R, uint8_t G, uint8_t B) { if (!Is_Initialized || Pixel_Buf == NULL) return; for (uint16_t i = 0; i < Pixel_NUM; i++) { WS2812_SetColor(i, R, G, B); } } /** * @brief 获取当前LED数量 * @retval LED数量 */ uint16_t WS2812_GetLedCount(void) { return Pixel_NUM; } /*---------------------------------------------------------------------------- 状态指示灯功能实现 (低功耗/低干扰设计) *---------------------------------------------------------------------------*/ /* 状态指示灯控制结构 */ static WS2812_StatusCtrl_t StatusCtrl = { .status = WS2812_STATUS_IDLE, .lastStatus = WS2812_STATUS_IDLE, .blinkState = 0, .lastUpdateTick = 0, .lastCommTick = 0, .brightness = 25 /* 默认25%亮度,减少功耗对射频干扰 */ }; /* 通信超时时间(ms) */ static uint32_t CommTimeout_ms = 1000; /* 闪烁周期(ms) */ #define BLINK_PERIOD_MS 500 /* LED更新最小间隔(ms) - 避免频繁DMA操作 */ #define LED_UPDATE_MIN_INTERVAL_MS 50 /** * @brief 设置LED状态指示 * @param status: LED状态 (WS2812_Status_t枚举) */ void WS2812_SetStatus(WS2812_Status_t status) { StatusCtrl.status = status; } /** * @brief 获取当前LED状态 * @retval 当前状态 */ WS2812_Status_t WS2812_GetStatus(void) { return StatusCtrl.status; } /** * @brief 通信成功通知 */ void WS2812_NotifyCommSuccess(void) { StatusCtrl.lastCommTick = HAL_GetTick(); } /** * @brief 设置LED亮度 * @param brightness: 亮度百分比 (0-100) */ void WS2812_SetBrightness(uint8_t brightness) { if (brightness > 100) brightness = 100; StatusCtrl.brightness = brightness; } /** * @brief 设置通信超时时间 * @param timeout_ms: 超时时间(ms) */ void WS2812_SetCommTimeout(uint32_t timeout_ms) { CommTimeout_ms = timeout_ms; } /** * @brief 内部函数: 应用亮度并设置颜色 * @param R,G,B: 原始颜色值 */ static void WS2812_SetColorWithBrightness(uint8_t R, uint8_t G, uint8_t B) { if (!Is_Initialized || Pixel_Buf == NULL) return; /* 应用亮度缩放 */ uint8_t adjR = (uint8_t)((uint16_t)R * StatusCtrl.brightness / 100); uint8_t adjG = (uint8_t)((uint16_t)G * StatusCtrl.brightness / 100); uint8_t adjB = (uint8_t)((uint16_t)B * StatusCtrl.brightness / 100); WS2812_SetColor(0, adjR, adjG, adjB); } /** * @brief 状态指示灯任务(非阻塞) * @param currentTick: 当前系统tick(ms) * @note 自动管理闪烁、超时检测,对射频影响极小 */ void WS2812_StatusTask(uint32_t currentTick) { if (!Is_Initialized || Pixel_Buf == NULL) return; /* 控制更新频率,避免频繁DMA操作干扰射频 */ if (currentTick - StatusCtrl.lastUpdateTick < LED_UPDATE_MIN_INTERVAL_MS) { return; } /* 检测通信超时: TX_OK/RX_OK状态下超时自动切换为ERROR */ if (StatusCtrl.status == WS2812_STATUS_TX_OK || StatusCtrl.status == WS2812_STATUS_RX_OK) { if (currentTick - StatusCtrl.lastCommTick > CommTimeout_ms) { StatusCtrl.status = WS2812_STATUS_ERROR; } } /* 计算闪烁状态 */ uint8_t newBlinkState = ((currentTick / BLINK_PERIOD_MS) % 2) ? 1 : 0; /* 判断是否需要更新LED (状态变化或闪烁状态变化) */ uint8_t needUpdate = 0; if (StatusCtrl.status != StatusCtrl.lastStatus) { needUpdate = 1; StatusCtrl.lastStatus = StatusCtrl.status; } else if (StatusCtrl.status == WS2812_STATUS_TX_OK || StatusCtrl.status == WS2812_STATUS_RX_OK || StatusCtrl.status == WS2812_STATUS_DEBUG) { /* 闪烁状态: 检查是否需要切换 */ if (newBlinkState != StatusCtrl.blinkState) { needUpdate = 1; } } if (!needUpdate) return; StatusCtrl.blinkState = newBlinkState; StatusCtrl.lastUpdateTick = currentTick; /* 根据状态设置颜色 */ switch (StatusCtrl.status) { case WS2812_STATUS_IDLE: /* 空闲: LED熄灭 */ WS2812_SetColor(0, 0, 0, 0); break; case WS2812_STATUS_TX_OK: case WS2812_STATUS_RX_OK: /* 正常通信: 绿灯闪烁 */ if (StatusCtrl.blinkState) { WS2812_SetColorWithBrightness(0, 255, 0); /* 绿色 */ } else { WS2812_SetColor(0, 0, 0, 0); /* 熄灭 */ } break; case WS2812_STATUS_ERROR: /* 异常: 红灯常亮 */ WS2812_SetColorWithBrightness(255, 0, 0); /* 红色 */ break; case WS2812_STATUS_DEBUG: /* 调试: 黄灯闪烁 */ if (StatusCtrl.blinkState) { WS2812_SetColorWithBrightness(255, 180, 0); /* 黄色 */ } else { WS2812_SetColor(0, 0, 0, 0); /* 熄灭 */ } break; default: WS2812_SetColor(0, 0, 0, 0); break; } /* 发送数据到LED */ WS2812_SendArray(); }