├── .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 |

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 |
--------------------------------------------------------------------------------