├── .github └── FUNDING.yml ├── LICENSE.TXT ├── NimaLTD.I-CUBE-WS28XX_conf.h ├── README.md ├── ws28xx.c └── ws28xx.h /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | github: [nimaltd] 3 | patreon: # Replace with a single Patreon username 4 | open_collective: # Replace with a single Open Collective username 5 | ko_fi: nimaltd 6 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 7 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 8 | liberapay: #nimaltd 9 | issuehunt: #nimaltd 10 | otechie: # Replace with a single Otechie username 11 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 12 | -------------------------------------------------------------------------------- /LICENSE.TXT: -------------------------------------------------------------------------------- 1 | Software License Terms 2 | 3 | This software is dual-licensed: 4 | 5 | 1. GNU General Public License v2 (GPLv2): 6 | - You may redistribute and/or modify this software under the terms of GPLv2 as published by the Free Software Foundation. 7 | - This license does not provide any warranty of any kind, including but not limited to MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 8 | - See for details. 9 | 10 | 2. Commercial License: 11 | - The commercial license removes all GPLv2 restrictions and allows you to redistribute your closed-source products with the Library embedded. 12 | - This license includes access to software maintenance, such as updates and upgrades. Customers who purchase this license are eligible to receive updates whenever 13 | they are released. However, the software provider is not obligated to release updates on a specific schedule. 14 | - The commercial license is available in two models: 15 | - Single-Product License: Allows usage in one specific product. 16 | - Company-Wide License: Allows usage across all products of the company. 17 | - No Warranty: The software is provided "AS IS" without any warranties, express or implied, including but not limited to the implied warranties of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, or NON-INFRINGEMENT. 18 | - Liability Disclaimer: In no event shall the copyright holder or contributors be liable for any claim, damages, or other liability, whether in an action of contract, tort, or otherwise, arising from, out of, or in connection with the software or the use or other dealings in the software. 19 | 20 | By using this software under the commercial license, you agree to these terms and acknowledge that no additional guarantees or obligations are provided beyond what is explicitly stated in this document. 21 | 22 | --- 23 | 24 | Copyright © Nima Askari, 2025. All rights reserved. 25 | For inquiries, contact: nima.askari@gmail.com All rights reserved 26 | -------------------------------------------------------------------------------- /NimaLTD.I-CUBE-WS28XX_conf.h: -------------------------------------------------------------------------------- 1 | /** 2 | ****************************************************************************** 3 | * File Name : NimaLTD.I-CUBE-WS28XX_conf.h 4 | * Description : This file provides code for the configuration 5 | * of the NimaLTD.I-CUBE-WS28XX_conf.h instances. 6 | ****************************************************************************** 7 | * @attention 8 | * 9 | * Copyright (c) 2024 STMicroelectronics. 10 | * All rights reserved. 11 | * 12 | * This software is licensed under terms that can be found in the LICENSE file 13 | * in the root directory of this software component. 14 | * If no LICENSE file comes with this software, it is provided AS-IS. 15 | * 16 | ****************************************************************************** 17 | */ 18 | /* Define to prevent recursive inclusion -------------------------------------*/ 19 | #ifndef _NIMALTD_I_CUBE_WS28XX_CONF_H_ 20 | #define _NIMALTD_I_CUBE_WS28XX_CONF_H_ 21 | 22 | #ifdef __cplusplus 23 | extern "C" { 24 | #endif 25 | 26 | #define WS28XX_ORDER_RGB 0 27 | #define WS28XX_ORDER_BGR 1 28 | #define WS28XX_ORDER_GRB 2 29 | 30 | #define WS28XX_RTOS_DISABLE 0 31 | #define WS28XX_RTOS_CMSIS_V1 1 32 | #define WS28XX_RTOS_CMSIS_V2 2 33 | #define WS28XX_RTOS_THREADX 3 34 | 35 | /** 36 | MiddleWare name : NimaLTD.I-CUBE-WS28XX.3.0.0 37 | MiddleWare fileName : NimaLTD.I-CUBE-WS28XX_conf.h 38 | */ 39 | /*---------- WS28XX_PIXEL_MAX -----------*/ 40 | #define WS28XX_PIXEL_MAX 256 41 | 42 | /*---------- WS28XX_PULSE_LENGTH_NS -----------*/ 43 | #define WS28XX_PULSE_LENGTH_NS 1250 44 | 45 | /*---------- WS28XX_PULSE_0_NS -----------*/ 46 | #define WS28XX_PULSE_0_NS 400 47 | 48 | /*---------- WS28XX_PULSE_1_NS -----------*/ 49 | #define WS28XX_PULSE_1_NS 800 50 | 51 | /*---------- WS28XX_ORDER -----------*/ 52 | #define WS28XX_ORDER WS28XX_ORDER_GRB 53 | 54 | /*---------- WS28XX_GAMMA -----------*/ 55 | #define WS28XX_GAMMA true 56 | 57 | /*---------- WS28XX_RTOS -----------*/ 58 | #define WS28XX_RTOS WS28XX_RTOS_DISABLE 59 | 60 | #ifdef __cplusplus 61 | } 62 | #endif 63 | #endif /* _NIMALTD_I_CUBE_WS28XX_CONF_H_ */ 64 | 65 | /** 66 | * @} 67 | */ 68 | 69 | /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ 70 | 71 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WS28XX (WS2811,WS2812,WS2812B,WS2815) LED Series Library for STM32 (PWM + DMA) 2 | --- 3 | ## Please Do not Forget to get STAR, DONATE and support me on social networks. Thank you. :sparkling_heart: 4 | --- 5 | - Author: Nima Askari 6 | - Github: https://www.github.com/NimaLTD 7 | - Youtube: https://www.youtube.com/@nimaltd 8 | - LinkedIn: https://www.linkedin.com/in/nimaltd 9 | - Instagram: https://instagram.com/github.NimaLTD 10 | --- 11 | * Install Library from https://github.com/nimaltd/STM32-PACK/raw/main/WS28XX/NimaLTD.I-CUBE-WS28XX.pdsc 12 | * Add and enable it. 13 | * Configure Bit Length and ... . 14 | * Enable a TIMER with Internal clock and one channel PWM as default mode. 15 | * Enable DMA for PWM channel, set Direction Memory to Peripheral, select Increment Memory, set Memory Data Width to Byte, and set Peripheral Data Width to Half-Word. 16 | * Select 'Generate peripheral initialization as a pair of .c/.h files per peripheral' on the Code Generator Tab. 17 | * Generate Code. 18 | * Define a structure of `WS28XX_HandleTypeDef`. 19 | * Call `WS28XX_Init()` and enjoy. 20 | --- 21 | # Watch the Video: 22 |
23 | Video 24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /ws28xx.c: -------------------------------------------------------------------------------- 1 | 2 | /************************************************************************************************************ 3 | ************** Include Headers 4 | ************************************************************************************************************/ 5 | 6 | #include "ws28xx.h" 7 | #include 8 | 9 | #if WS28XX_RTOS == WS28XX_RTOS_DISABLE 10 | #elif WS28XX_RTOS == WS28XX_RTOS_CMSIS_V1 11 | #include "cmsis_os.h" 12 | #include "freertos.h" 13 | #elif WS28XX_RTOS == WS28XX_RTOS_CMSIS_V2 14 | #include "cmsis_os2.h" 15 | #include "freertos.h" 16 | #elif WS28XX_RTOS == WS28XX_RTOS_THREADX 17 | #include "app_threadx.h" 18 | #endif 19 | 20 | /************************************************************************************************************ 21 | ************** Private Definitions 22 | ************************************************************************************************************/ 23 | 24 | // none 25 | 26 | /************************************************************************************************************ 27 | ************** Private Variables 28 | ************************************************************************************************************/ 29 | 30 | #if (WS28XX_GAMMA == true) 31 | const uint8_t WS28XX_GammaTable[] = 32 | {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33 | 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 34 | 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 35 | 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 11, 11, 36 | 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 37 | 19, 19, 20, 21, 21, 22, 22, 23, 23, 24, 25, 25, 26, 27, 27, 28, 38 | 29, 29, 30, 31, 31, 32, 33, 34, 34, 35, 36, 37, 37, 38, 39, 40, 39 | 40, 41, 42, 43, 44, 45, 46, 46, 47, 48, 49, 50, 51, 52, 53, 54, 40 | 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 41 | 71, 72, 73, 74, 76, 77, 78, 79, 80, 81, 83, 84, 85, 86, 88, 89, 42 | 90, 91, 93, 94, 95, 96, 98, 99,100,102,103,104,106,107,109,110, 43 | 111,113,114,116,117,119,120,121,123,124,126,128,129,131,132,134, 44 | 135,137,138,140,142,143,145,146,148,150,151,153,155,157,158,160, 45 | 162,163,165,167,169,170,172,174,176,178,179,181,183,185,187,189, 46 | 191,193,194,196,198,200,202,204,206,208,210,212,214,216,218,220, 47 | 222,224,227,229,231,233,235,237,239,241,244,246,248,250,252,255 48 | }; 49 | #endif 50 | 51 | /************************************************************************************************************ 52 | ************** Private Functions 53 | ************************************************************************************************************/ 54 | 55 | void WS28XX_Delay(uint32_t Delay); 56 | void WS28XX_Lock(WS28XX_HandleTypeDef *hLed); 57 | void WS28XX_UnLock(WS28XX_HandleTypeDef *hLed); 58 | 59 | /***********************************************************************************************************/ 60 | 61 | void WS28XX_Delay(uint32_t Delay) 62 | { 63 | #if WS28XX_RTOS == WS28XX_RTOS_DISABLE 64 | HAL_Delay(Delay); 65 | #elif (WS28XX_RTOS == WS28XX_RTOS_CMSIS_V1) || (WS28XX_RTOS == WS28XX_RTOS_CMSIS_V2) 66 | uint32_t d = (configTICK_RATE_HZ * Delay) / 1000; 67 | if (d == 0) 68 | d = 1; 69 | osDelay(d); 70 | #elif WS28XX_RTOS == WS28XX_RTOS_THREADX 71 | uint32_t d = (TX_TIMER_TICKS_PER_SECOND * Delay) / 1000; 72 | if (d == 0) 73 | d = 1; 74 | tx_thread_sleep(d); 75 | #endif 76 | } 77 | 78 | /***********************************************************************************************************/ 79 | 80 | void WS28XX_Lock(WS28XX_HandleTypeDef *hLed) 81 | { 82 | while (hLed->Lock) 83 | { 84 | WS28XX_Delay(1); 85 | } 86 | hLed->Lock = 1; 87 | } 88 | 89 | /***********************************************************************************************************/ 90 | 91 | void WS28XX_UnLock(WS28XX_HandleTypeDef *hLed) 92 | { 93 | hLed->Lock = 0; 94 | } 95 | 96 | /************************************************************************************************************ 97 | ************** Public Functions 98 | ************************************************************************************************************/ 99 | 100 | /** 101 | * @brief Initialize WS28XX hLed 102 | * @note Initialize WS28XX hLed and set the Channel and number of Pixels 103 | * 104 | * @param *hLed: Pointer to WS28XX_hLedTypeDef structure 105 | * @param *hTim: Pointer to TIM_hLedTypeDef structure 106 | * @param Channel: Selected PWM channel 107 | * TIM_CHANNEL_1 108 | * TIM_CHANNEL_2 109 | * TIM_CHANNEL_3 110 | * TIM_CHANNEL_4 111 | * TIM_CHANNEL_5 112 | * TIM_CHANNEL_6 113 | * @param Pixel: Number of pixels 114 | * 115 | * @retval bool: true or false 116 | */ 117 | bool WS28XX_Init(WS28XX_HandleTypeDef *hLed, TIM_HandleTypeDef *hTim, 118 | uint16_t TimerBusFrequencyMHz, uint8_t Channel, uint16_t Pixel) 119 | { 120 | bool answer = false; 121 | uint32_t aar_value; 122 | do 123 | { 124 | if (hLed == NULL || hTim == NULL) 125 | { 126 | break; 127 | } 128 | if (Pixel > WS28XX_PIXEL_MAX) 129 | { 130 | break; 131 | } 132 | hLed->Channel = Channel; 133 | hLed->MaxPixel = Pixel; 134 | hLed->hTim = hTim; 135 | aar_value = (TimerBusFrequencyMHz / (1.0f / (WS28XX_PULSE_LENGTH_NS / 1000.0f))) - 1; 136 | __HAL_TIM_SET_AUTORELOAD(hLed->hTim ,aar_value); 137 | __HAL_TIM_SET_PRESCALER(hLed->hTim, 0); 138 | hLed->Pulse0 = ((WS28XX_PULSE_0_NS / 1000.0f) * aar_value) / (WS28XX_PULSE_LENGTH_NS / 1000.0f); 139 | hLed->Pulse1 = ((WS28XX_PULSE_1_NS / 1000.0f) * aar_value) / (WS28XX_PULSE_LENGTH_NS / 1000.0f); 140 | memset(hLed->Pixel, 0, sizeof(hLed->Pixel)); 141 | memset(hLed->Buffer, 0, sizeof(hLed->Buffer)); 142 | HAL_TIM_PWM_Start_DMA(hLed->hTim, hLed->Channel, (const uint32_t*)hLed->Buffer, Pixel); 143 | answer = true; 144 | } 145 | while (0); 146 | 147 | return answer; 148 | } 149 | 150 | /***********************************************************************************************************/ 151 | 152 | /** 153 | * @brief Set Pixel 154 | * @note Fill the pixel By RGB Values 155 | * 156 | * @param *hLed: Pointer to WS28XX_hLedTypeDef structure 157 | * @param Pixel: Pixel Starts from 0 to Max - 1 158 | * @param Red: Red Value, 0 to 255 159 | * @param Green: Green Value, 0 to 255 160 | * @param Blue: Blue Value, 0 to 255 161 | * 162 | * @retval bool: true or false 163 | */ 164 | bool WS28XX_SetPixel_RGB(WS28XX_HandleTypeDef *hLed, uint16_t Pixel, uint8_t Red, uint8_t Green, uint8_t Blue) 165 | { 166 | bool answer = true; 167 | do 168 | { 169 | if (Pixel >= hLed->MaxPixel) 170 | { 171 | answer = false; 172 | break; 173 | } 174 | #if (WS28XX_GAMMA == false) 175 | #if WS28XX_ORDER == WS28XX_ORDER_RGB 176 | hLed->Pixel[Pixel][0] = Red; 177 | hLed->Pixel[Pixel][1] = Green; 178 | hLed->Pixel[Pixel][2] = Blue; 179 | #elif WS28XX_ORDER == WS28XX_ORDER_BGR 180 | hLed->Pixel[Pixel][0] = Blue; 181 | hLed->Pixel[Pixel][1] = Green; 182 | hLed->Pixel[Pixel][2] = Red; 183 | #elif WS28XX_ORDER == WS28XX_ORDER_GRB 184 | hLed->Pixel[Pixel][0] = Green; 185 | hLed->Pixel[Pixel][1] = Red; 186 | hLed->Pixel[Pixel][2] = Blue; 187 | #endif 188 | #else 189 | #if WS28XX_ORDER == WS28XX_ORDER_RGB 190 | hLed->Pixel[Pixel][0] = WS28XX_GammaTable[Red]; 191 | hLed->Pixel[Pixel][1] = WS28XX_GammaTable[Green]; 192 | hLed->Pixel[Pixel][2] = WS28XX_GammaTable[Blue]; 193 | #elif WS28XX_ORDER == WS28XX_ORDER_BGR 194 | hLed->Pixel[Pixel][0] = WS28XX_GammaTable[Blue]; 195 | hLed->Pixel[Pixel][1] = WS28XX_GammaTable[Green]; 196 | hLed->Pixel[Pixel][2] = WS28XX_GammaTable[Red]; 197 | #elif WS28XX_ORDER == WS28XX_ORDER_GRB 198 | hLed->Pixel[Pixel][0] = WS28XX_GammaTable[Green]; 199 | hLed->Pixel[Pixel][1] = WS28XX_GammaTable[Red]; 200 | hLed->Pixel[Pixel][2] = WS28XX_GammaTable[Blue]; 201 | #endif 202 | #endif 203 | } 204 | 205 | while (0); 206 | 207 | return answer; 208 | } 209 | 210 | /***********************************************************************************************************/ 211 | 212 | /** 213 | * @brief Set Pixel 214 | * @note Fill the pixel By RGB Values 215 | * 216 | * @param *hLed: Pointer to WS28XX_hLedTypeDef structure 217 | * @param Pixel: Pixel Starts from 0 to Max - 1 218 | * @param Color: RGB565 Color Code 219 | * 220 | * @retval bool: true or false 221 | */ 222 | bool WS28XX_SetPixel_RGB_565(WS28XX_HandleTypeDef *hLed, uint16_t Pixel, uint16_t Color) 223 | { 224 | bool answer = true; 225 | uint8_t Red, Green, Blue; 226 | do 227 | { 228 | if (Pixel >= hLed->MaxPixel) 229 | { 230 | answer = false; 231 | break; 232 | } 233 | Red = ((Color >> 8) & 0xF8); 234 | Green = ((Color >> 3) & 0xFC); 235 | Blue = ((Color << 3) & 0xF8); 236 | #if (WS28XX_GAMMA == false) 237 | #if WS28XX_ORDER == WS28XX_ORDER_RGB 238 | hLed->Pixel[Pixel][0] = Red; 239 | hLed->Pixel[Pixel][1] = Green; 240 | hLed->Pixel[Pixel][2] = Blue; 241 | #elif WS28XX_ORDER == WS28XX_ORDER_BGR 242 | hLed->Pixel[Pixel][0] = Blue; 243 | hLed->Pixel[Pixel][1] = Green; 244 | hLed->Pixel[Pixel][2] = Red; 245 | #elif WS28XX_ORDER == WS28XX_ORDER_GRB 246 | hLed->Pixel[Pixel][0] = Green; 247 | hLed->Pixel[Pixel][1] = Red; 248 | hLed->Pixel[Pixel][2] = Blue; 249 | #endif 250 | #else 251 | #if WS28XX_ORDER == WS28XX_ORDER_RGB 252 | hLed->Pixel[Pixel][0] = WS28XX_GammaTable[Red]; 253 | hLed->Pixel[Pixel][1] = WS28XX_GammaTable[Green]; 254 | hLed->Pixel[Pixel][2] = WS28XX_GammaTable[Blue]; 255 | #elif WS28XX_ORDER == WS28XX_ORDER_BGR 256 | hLed->Pixel[Pixel][0] = WS28XX_GammaTable[Blue]; 257 | hLed->Pixel[Pixel][1] = WS28XX_GammaTable[Green]; 258 | hLed->Pixel[Pixel][2] = WS28XX_GammaTable[Red]; 259 | #elif WS28XX_ORDER == WS28XX_ORDER_GRB 260 | hLed->Pixel[Pixel][0] = WS28XX_GammaTable[Green]; 261 | hLed->Pixel[Pixel][1] = WS28XX_GammaTable[Red]; 262 | hLed->Pixel[Pixel][2] = WS28XX_GammaTable[Blue]; 263 | #endif 264 | #endif 265 | } 266 | 267 | while (0); 268 | 269 | return answer; 270 | } 271 | 272 | /***********************************************************************************************************/ 273 | 274 | /** 275 | * @brief Set Pixel 276 | * @note Fill the pixel By RGB Values 277 | * 278 | * @param *hLed: Pointer to WS28XX_hLedTypeDef structure 279 | * @param Pixel: Pixel Starts from 0 to Max - 1 280 | * @param Color: RGB888 Color Code 281 | * 282 | * @retval bool: true or false 283 | */ 284 | bool WS28XX_SetPixel_RGB_888(WS28XX_HandleTypeDef *hLed, uint16_t Pixel, uint32_t Color) 285 | { 286 | bool answer = true; 287 | uint8_t Red, Green, Blue; 288 | do 289 | { 290 | if (Pixel >= hLed->MaxPixel) 291 | { 292 | answer = false; 293 | break; 294 | } 295 | Red = ((Color & 0xFF0000) >> 16); 296 | Green = ((Color & 0x00FF00) >> 8); 297 | Blue = (Color & 0x0000FF); 298 | #if (WS28XX_GAMMA == false) 299 | #if WS28XX_ORDER == WS28XX_ORDER_RGB 300 | hLed->Pixel[Pixel][0] = Red; 301 | hLed->Pixel[Pixel][1] = Green; 302 | hLed->Pixel[Pixel][2] = Blue; 303 | #elif WS28XX_ORDER == WS28XX_ORDER_BGR 304 | hLed->Pixel[Pixel][0] = Blue; 305 | hLed->Pixel[Pixel][1] = Green; 306 | hLed->Pixel[Pixel][2] = Red; 307 | #elif WS28XX_ORDER == WS28XX_ORDER_GRB 308 | hLed->Pixel[Pixel][0] = Green; 309 | hLed->Pixel[Pixel][1] = Red; 310 | hLed->Pixel[Pixel][2] = Blue; 311 | #endif 312 | #else 313 | #if WS28XX_ORDER == WS28XX_ORDER_RGB 314 | hLed->Pixel[Pixel][0] = WS28XX_GammaTable[Red]; 315 | hLed->Pixel[Pixel][1] = WS28XX_GammaTable[Green]; 316 | hLed->Pixel[Pixel][2] = WS28XX_GammaTable[Blue]; 317 | #elif WS28XX_ORDER == WS28XX_ORDER_BGR 318 | hLed->Pixel[Pixel][0] = WS28XX_GammaTable[Blue]; 319 | hLed->Pixel[Pixel][1] = WS28XX_GammaTable[Green]; 320 | hLed->Pixel[Pixel][2] = WS28XX_GammaTable[Red]; 321 | #elif WS28XX_ORDER == WS28XX_ORDER_GRB 322 | hLed->Pixel[Pixel][0] = WS28XX_GammaTable[Green]; 323 | hLed->Pixel[Pixel][1] = WS28XX_GammaTable[Red]; 324 | hLed->Pixel[Pixel][2] = WS28XX_GammaTable[Blue]; 325 | #endif 326 | #endif 327 | } 328 | 329 | while (0); 330 | 331 | return answer; 332 | } 333 | 334 | /***********************************************************************************************************/ 335 | 336 | /** 337 | * @brief Set Pixel and Brightness 338 | * @note Fill the pixel By RGB Values 339 | * 340 | * @param *hLed: Pointer to WS28XX_hLedTypeDef structure 341 | * @param Pixel: Pixel Starts from 0 to Max - 1 342 | * @param Color: RGB565 Color Code 343 | * @param Brightness: Brightness level, 0 to 255 344 | * 345 | * @retval bool: true or false 346 | */ 347 | bool WS28XX_SetPixel_RGBW_565(WS28XX_HandleTypeDef *hLed, uint16_t Pixel, uint16_t Color, uint8_t Brightness) 348 | { 349 | bool answer = true; 350 | uint8_t Red, Green, Blue; 351 | do 352 | { 353 | if (Pixel >= hLed->MaxPixel) 354 | { 355 | answer = false; 356 | break; 357 | } 358 | Red = ((Color >> 8) & 0xF8) * Brightness / 255; 359 | Green = ((Color >> 3) & 0xFC)* Brightness / 255; 360 | Blue = ((Color << 3) & 0xF8) * Brightness / 255; 361 | #if (WS28XX_GAMMA == false) 362 | #if WS28XX_ORDER == WS28XX_ORDER_RGB 363 | hLed->Pixel[Pixel][0] = Red; 364 | hLed->Pixel[Pixel][1] = Green; 365 | hLed->Pixel[Pixel][2] = Blue; 366 | #elif WS28XX_ORDER == WS28XX_ORDER_BGR 367 | hLed->Pixel[Pixel][0] = Blue; 368 | hLed->Pixel[Pixel][1] = Green; 369 | hLed->Pixel[Pixel][2] = Red; 370 | #elif WS28XX_ORDER == WS28XX_ORDER_GRB 371 | hLed->Pixel[Pixel][0] = Green; 372 | hLed->Pixel[Pixel][1] = Red; 373 | hLed->Pixel[Pixel][2] = Blue; 374 | #endif 375 | #else 376 | #if WS28XX_ORDER == WS28XX_ORDER_RGB 377 | hLed->Pixel[Pixel][0] = WS28XX_GammaTable[Red]; 378 | hLed->Pixel[Pixel][1] = WS28XX_GammaTable[Green]; 379 | hLed->Pixel[Pixel][2] = WS28XX_GammaTable[Blue]; 380 | #elif WS28XX_ORDER == WS28XX_ORDER_BGR 381 | hLed->Pixel[Pixel][0] = WS28XX_GammaTable[Blue]; 382 | hLed->Pixel[Pixel][1] = WS28XX_GammaTable[Green]; 383 | hLed->Pixel[Pixel][2] = WS28XX_GammaTable[Red]; 384 | #elif WS28XX_ORDER == WS28XX_ORDER_GRB 385 | hLed->Pixel[Pixel][0] = WS28XX_GammaTable[Green]; 386 | hLed->Pixel[Pixel][1] = WS28XX_GammaTable[Red]; 387 | hLed->Pixel[Pixel][2] = WS28XX_GammaTable[Blue]; 388 | #endif 389 | #endif 390 | } 391 | 392 | while (0); 393 | 394 | return answer; 395 | } 396 | 397 | /***********************************************************************************************************/ 398 | 399 | /** 400 | * @brief Set Pixel and Brightness 401 | * @note Fill the pixel By RGB Values 402 | * 403 | * @param *hLed: Pointer to WS28XX_hLedTypeDef structure 404 | * @param Pixel: Pixel Starts from 0 to Max - 1 405 | * @param Color: RGB888 Color Code 406 | * @param Brightness: Brightness level, 0 to 255 407 | * 408 | * @retval bool: true or false 409 | */ 410 | bool WS28XX_SetPixel_RGBW_888(WS28XX_HandleTypeDef *hLed, uint16_t Pixel, uint32_t Color, uint8_t Brightness) 411 | { 412 | bool answer = true; 413 | uint8_t Red, Green, Blue; 414 | do 415 | { 416 | if (Pixel >= hLed->MaxPixel) 417 | { 418 | answer = false; 419 | break; 420 | } 421 | Red = ((Color & 0xFF0000) >> 16) * Brightness / 255 ; 422 | Green = ((Color & 0x00FF00) >> 8) * Brightness / 255; 423 | Blue = (Color & 0x0000FF) * Brightness / 255; 424 | #if (WS28XX_GAMMA == false) 425 | #if WS28XX_ORDER == WS28XX_ORDER_RGB 426 | hLed->Pixel[Pixel][0] = Red; 427 | hLed->Pixel[Pixel][1] = Green; 428 | hLed->Pixel[Pixel][2] = Blue; 429 | #elif WS28XX_ORDER == WS28XX_ORDER_BGR 430 | hLed->Pixel[Pixel][0] = Blue; 431 | hLed->Pixel[Pixel][1] = Green; 432 | hLed->Pixel[Pixel][2] = Red; 433 | #elif WS28XX_ORDER == WS28XX_ORDER_GRB 434 | hLed->Pixel[Pixel][0] = Green; 435 | hLed->Pixel[Pixel][1] = Red; 436 | hLed->Pixel[Pixel][2] = Blue; 437 | #endif 438 | #else 439 | #if WS28XX_ORDER == WS28XX_ORDER_RGB 440 | hLed->Pixel[Pixel][0] = WS28XX_GammaTable[Red]; 441 | hLed->Pixel[Pixel][1] = WS28XX_GammaTable[Green]; 442 | hLed->Pixel[Pixel][2] = WS28XX_GammaTable[Blue]; 443 | #elif WS28XX_ORDER == WS28XX_ORDER_BGR 444 | hLed->Pixel[Pixel][0] = WS28XX_GammaTable[Blue]; 445 | hLed->Pixel[Pixel][1] = WS28XX_GammaTable[Green]; 446 | hLed->Pixel[Pixel][2] = WS28XX_GammaTable[Red]; 447 | #elif WS28XX_ORDER == WS28XX_ORDER_GRB 448 | hLed->Pixel[Pixel][0] = WS28XX_GammaTable[Green]; 449 | hLed->Pixel[Pixel][1] = WS28XX_GammaTable[Red]; 450 | hLed->Pixel[Pixel][2] = WS28XX_GammaTable[Blue]; 451 | #endif 452 | #endif 453 | } 454 | 455 | while (0); 456 | 457 | return answer; 458 | } 459 | 460 | /***********************************************************************************************************/ 461 | 462 | /** 463 | * @brief Send Buffer to LEDs 464 | * @note This function use PWM+DMA for Sending data 465 | * 466 | * @param *hLed: Pointer to WS28XX_hLedTypeDef structure 467 | * 468 | * @retval bool: true or false 469 | */ 470 | bool WS28XX_Update(WS28XX_HandleTypeDef *hLed) 471 | { 472 | bool answer = true; 473 | uint32_t i = 2; 474 | WS28XX_Lock(hLed); 475 | for (uint16_t pixel = 0; pixel < hLed->MaxPixel; pixel++) 476 | { 477 | for (int rgb = 0; rgb < 3; rgb ++) 478 | { 479 | for (int b = 7; b >= 0 ; b--) 480 | { 481 | if ((hLed->Pixel[pixel][rgb] & (1 << b)) == 0) 482 | { 483 | hLed->Buffer[i] = hLed->Pulse0; 484 | } 485 | else 486 | { 487 | hLed->Buffer[i] = hLed->Pulse1; 488 | } 489 | i++; 490 | } 491 | } 492 | } 493 | if (HAL_TIM_PWM_Start_DMA(hLed->hTim, hLed->Channel, (const uint32_t*)hLed->Buffer, (hLed->MaxPixel * 24) + 4) != HAL_OK) 494 | { 495 | answer = false; 496 | } 497 | WS28XX_UnLock(hLed); 498 | return answer; 499 | } 500 | 501 | /***********************************************************************************************************/ 502 | -------------------------------------------------------------------------------- /ws28xx.h: -------------------------------------------------------------------------------- 1 | #ifndef _WS28XX_H_ 2 | #define _WS28XX_H_ 3 | 4 | /*********************************************************************************************************** 5 | 6 | Author: Nima Askari 7 | Github: https://www.github.com/NimaLTD 8 | LinkedIn: https://www.linkedin.com/in/nimaltd 9 | Youtube: https://www.youtube.com/@nimaltd 10 | Instagram: https://instagram.com/github.NimaLTD 11 | 12 | Version: 3.0.1 13 | 14 | History: 15 | 3.0.1 16 | - Fixed Blinker on first pixel 17 | 18 | 3.0.0 19 | - Rewrite again 20 | - Support STM32CubeMx Packet installer 21 | 22 | ***********************************************************************************************************/ 23 | 24 | #ifdef __cplusplus 25 | extern "C" 26 | { 27 | #endif 28 | 29 | /************************************************************************************************************ 30 | ************** Include Headers 31 | ************************************************************************************************************/ 32 | 33 | #include 34 | #include "tim.h" 35 | #include "NimaLTD.I-CUBE-WS28XX_conf.h" 36 | 37 | /************************************************************************************************************ 38 | ************** Public Definitions 39 | ************************************************************************************************************/ 40 | 41 | #define COLOR_RGB565_BLACK 0x0000 /* 0, 0, 0 */ 42 | #define COLOR_RGB565_WHITE 0xFFFF /* 255, 255, 255 */ 43 | #define COLOR_RGB565_RED 0xF800 /* 255, 0, 0 */ 44 | #define COLOR_RGB565_GREEN 0x07E0 /* 0, 255, 0 */ 45 | #define COLOR_RGB565_BLUE 0x001F /* 0, 0, 255 */ 46 | #define COLOR_RGB565_CYAN 0x07FF /* 0, 255, 255 */ 47 | #define COLOR_RGB565_MAGENTA 0xF81F /* 255, 0, 255 */ 48 | #define COLOR_RGB565_YELLOW 0xFFE0 /* 255, 255, 0 */ 49 | #define COLOR_RGB565_ORANGE 0xFD20 /* 255, 165, 0 */ 50 | #define COLOR_RGB565_PINK 0xFC18 /* 255, 192, 203 */ 51 | #define COLOR_RGB565_PURPLE 0x780F /* 128, 0, 128 */ 52 | #define COLOR_RGB565_TEAL 0x0438 /* 0, 128, 128 */ 53 | #define COLOR_RGB565_LIME 0x07E0 /* 0, 255, 0 */ 54 | #define COLOR_RGB565_AQUA 0x5D1C /* 0, 255, 255 */ 55 | #define COLOR_RGB565_MAROON 0x7800 /* 128, 0, 0 */ 56 | #define COLOR_RGB565_NAVY 0x000F /* 0, 0, 128 */ 57 | #define COLOR_RGB565_OLIVE 0x7BE0 /* 128, 128, 0 */ 58 | #define COLOR_RGB565_SILVER 0xC618 /* 192, 192, 192 */ 59 | #define COLOR_RGB565_GRAY 0x8410 /* 128, 128, 128 */ 60 | #define COLOR_RGB565_SKYBLUE 0x867D /* 135, 206, 235 */ 61 | #define COLOR_RGB565_VIOLET 0x915C /* 138, 43, 226 */ 62 | #define COLOR_RGB565_BROWN 0xA145 /* 165, 42, 42 */ 63 | #define COLOR_RGB565_GOLD 0xFEA0 /* 255, 215, 0 */ 64 | #define COLOR_RGB565_TAN 0xD5B1 /* 210, 180, 140 */ 65 | #define COLOR_RGB565_FORESTGREEN 0x2444 /* 34, 139, 34 */ 66 | #define COLOR_RGB565_SEAGREEN 0x2C4A /* 46, 139, 87 */ 67 | #define COLOR_RGB565_CRIMSON 0xD8A7 /* 220, 20, 60 */ 68 | #define COLOR_RGB565_LAVENDER 0xE73B /* 230, 230, 250 */ 69 | #define COLOR_RGB565_INDIGO 0x4810 /* 75, 0, 130 */ 70 | #define COLOR_RGB565_DARKORANGE 0xFC60 /* 255, 140, 0 */ 71 | #define COLOR_RGB565_CHOCOLATE 0xCB6D /* 210, 105, 30 */ 72 | #define COLOR_RGB565_FIREBRICK 0xB104 /* 178, 34, 34 */ 73 | #define COLOR_RGB565_CORAL 0xFBEA /* 255, 127, 80 */ 74 | #define COLOR_RGB565_TOMATO 0xFB08 /* 255, 99, 71 */ 75 | #define COLOR_RGB565_SLATEGRAY 0x7412 /* 112, 128, 144 */ 76 | #define COLOR_RGB565_DARKSLATEGRAY 0x2A69 /* 47, 79, 79 */ 77 | #define COLOR_RGB565_DIMGRAY 0x6B4D /* 105, 105, 105 */ 78 | 79 | #define COLOR_RGB888_BLACK 0x000000 /* 0, 0, 0 */ 80 | #define COLOR_RGB888_WHITE 0xFFFFFF /* 255, 255, 255 */ 81 | #define COLOR_RGB888_RED 0xFF0000 /* 255, 0, 0 */ 82 | #define COLOR_RGB888_GREEN 0x00FF00 /* 0, 255, 0 */ 83 | #define COLOR_RGB888_BLUE 0x0000FF /* 0, 0, 255 */ 84 | #define COLOR_RGB888_CYAN 0x00FFFF /* 0, 255, 255 */ 85 | #define COLOR_RGB888_MAGENTA 0xFF00FF /* 255, 0, 255 */ 86 | #define COLOR_RGB888_YELLOW 0xFFFF00 /* 255, 255, 0 */ 87 | #define COLOR_RGB888_ORANGE 0xFFA500 /* 255, 165, 0 */ 88 | #define COLOR_RGB888_PINK 0xFFC0CB /* 255, 192, 203 */ 89 | #define COLOR_RGB888_PURPLE 0x800080 /* 128, 0, 128 */ 90 | #define COLOR_RGB888_TEAL 0x008080 /* 0, 128, 128 */ 91 | #define COLOR_RGB888_LIME 0x00FF00 /* 0, 255, 0 */ 92 | #define COLOR_RGB888_AQUA 0x00FFFF /* 0, 255, 255 */ 93 | #define COLOR_RGB888_MAROON 0x800000 /* 128, 0, 0 */ 94 | #define COLOR_RGB888_NAVY 0x000080 /* 0, 0, 128 */ 95 | #define COLOR_RGB888_OLIVE 0x808000 /* 128, 128, 0 */ 96 | #define COLOR_RGB888_SILVER 0xC0C0C0 /* 192, 192, 192 */ 97 | #define COLOR_RGB888_GRAY 0x808080 /* 128, 128, 128 */ 98 | #define COLOR_RGB888_SKYBLUE 0x87CEEB /* 135, 206, 235 */ 99 | #define COLOR_RGB888_VIOLET 0x8A2BE2 /* 138, 43, 226 */ 100 | #define COLOR_RGB888_BROWN 0xA52A2A /* 165, 42, 42 */ 101 | #define COLOR_RGB888_GOLD 0xFFD700 /* 255, 215, 0 */ 102 | #define COLOR_RGB888_TAN 0xD2B48C /* 210, 180, 140 */ 103 | #define COLOR_RGB888_FORESTGREEN 0x228B22 /* 34, 139, 34 */ 104 | #define COLOR_RGB888_SEAGREEN 0x2E8B57 /* 46, 139, 87 */ 105 | #define COLOR_RGB888_CRIMSON 0xDC143C /* 220, 20, 60 */ 106 | #define COLOR_RGB888_LAVENDER 0xE6E6FA /* 230, 230, 250 */ 107 | #define COLOR_RGB888_INDIGO 0x4B0082 /* 75, 0, 130 */ 108 | #define COLOR_RGB888_DARKORANGE 0xFF8C00 /* 255, 140, 0 */ 109 | #define COLOR_RGB888_CHOCOLATE 0xD2691E /* 210, 105, 30 */ 110 | #define COLOR_RGB888_FIREBRICK 0xB22222 /* 178, 34, 34 */ 111 | #define COLOR_RGB888_CORAL 0xFF7F50 /* 255, 127, 80 */ 112 | #define COLOR_RGB888_TOMATO 0xFF6347 /* 255, 99, 71 */ 113 | #define COLOR_RGB888_SLATEGRAY 0x708090 /* 112, 128, 144 */ 114 | #define COLOR_RGB888_DARKSLATEGRAY 0x2F4F4F /* 47, 79, 79 */ 115 | #define COLOR_RGB888_DIMGRAY 0x696969 /* 105, 105, 105 */ 116 | 117 | /************************************************************************************************************ 118 | ************** Public struct/enum 119 | ************************************************************************************************************/ 120 | 121 | typedef struct 122 | { 123 | TIM_HandleTypeDef *hTim; 124 | uint8_t Channel; 125 | uint8_t Lock; 126 | uint16_t Pulse0; 127 | uint16_t Pulse1; 128 | uint16_t MaxPixel; 129 | uint8_t Pixel[WS28XX_PIXEL_MAX][3]; 130 | uint8_t Buffer[(WS28XX_PIXEL_MAX * 24) + 4]; 131 | 132 | } WS28XX_HandleTypeDef; 133 | 134 | /************************************************************************************************************ 135 | ************** Public Functions 136 | ************************************************************************************************************/ 137 | 138 | bool WS28XX_Init(WS28XX_HandleTypeDef *hLed, TIM_HandleTypeDef *hTim, 139 | uint16_t TimerBusFrequencyMHz, uint8_t Channel, uint16_t Pixel); 140 | bool WS28XX_SetPixel_RGB(WS28XX_HandleTypeDef *hLed, uint16_t Pixel, uint8_t Red, uint8_t Green, uint8_t Blue); 141 | bool WS28XX_SetPixel_RGB_565(WS28XX_HandleTypeDef *hLed, uint16_t Pixel, uint16_t Color); 142 | bool WS28XX_SetPixel_RGB_888(WS28XX_HandleTypeDef *hLed, uint16_t Pixel, uint32_t Color); 143 | bool WS28XX_SetPixel_RGBW_565(WS28XX_HandleTypeDef *hLed, uint16_t Pixel, uint16_t Color, uint8_t Brightness); 144 | bool WS28XX_SetPixel_RGBW_888(WS28XX_HandleTypeDef *hLed, uint16_t Pixel, uint32_t Color, uint8_t Brightness); 145 | bool WS28XX_Update(WS28XX_HandleTypeDef *hLed); 146 | 147 | #ifdef __cplusplus 148 | } 149 | #endif 150 | #endif 151 | --------------------------------------------------------------------------------