]> git.donarmstrong.com Git - qmk_firmware.git/blob - keyboards/matrix/noah/ws2812_f4.c
adding noah keyboard (#7432)
[qmk_firmware.git] / keyboards / matrix / noah / ws2812_f4.c
1 /**
2  * @file    ws2812.c
3  * @author  Austin Glaser <austin.glaser@gmail.com>, Joerg Wangemann <joerg.wangemann@gmail.com>
4  * @brief   WS2812 LED driver
5  *
6  * Copyright (C) 2016 Austin Glaser, 2017 Joerg Wangemann
7  *
8  * This software may be modified and distributed under the terms
9  * of the MIT license.  See the LICENSE file for details.
10  *
11  * @todo    Put in names and descriptions of variables which need to be defined to use this file
12  *
13  * @addtogroup WS2812
14  * @{
15  */
16
17 /* --- PRIVATE DEPENDENCIES ------------------------------------------------- */
18
19 // This Driver
20 #include "ws2812_f4.h"
21
22 // Standard
23 #include <stdint.h>
24
25 // ChibiOS
26 #include "ch.h"
27 #include "hal.h"
28
29 #include "wait.h"
30 // Application
31 //#include "board.h"
32
33 // TODO: Add these #define's to the headers of your project.
34 // Pin, timer and dma are all connected, check them all if you change one.
35 // Tested with STM32F4, working at 144 or 168 MHz.
36 //#define WS2812_LED_N    2 // Number of LEDs
37 //#define PORT_WS2812     GPIOB
38 //#define PIN_WS2812      9
39 //#define WS2812_TIM_N    4  // timer, 1-11
40 //#define WS2812_TIM_CH   3  // timer channel, 0-3
41 //#define WS2812_DMA_STREAM STM32_DMA1_STREAM2  // DMA stream for TIMx_UP (look up in reference manual under DMA Channel selection)
42 //#define WS2812_DMA_CHANNEL 6                  // DMA channel for TIMx_UP
43 // The WS2812 expects 5V signal level (or at least 0.7 * VDD). Sometimes it works
44 // with a 3V signal level, otherwise the easiest way to get the signal level to 5V
45 // is to add an external pullup resistor from the DI pin to 5V (10k will do) and
46 // configure the pin as open drain.
47 // (An SMD resistor is easily solders on the connections of a light strip)
48 // Uncomment the next line if an external pullup resistor is used.
49 //#define WS2812_EXTERNAL_PULLUP
50
51 /* --- CONFIGURATION CHECK -------------------------------------------------- */
52
53 #if !defined(WS2812_LED_N)
54     #error WS2812 LED chain length not specified
55 #elif WS2812_LED_N <= 0
56     #error WS2812 LED chain length set to invalid value
57 #endif
58
59 #if !defined(WS2812_TIM_N)
60     #error WS2812 timer not specified
61 #endif
62 #if defined(STM32F2XX) || defined(STM32F4XX) || defined(STM32F7XX)
63     #if WS2812_TIM_N <= 2
64         #define WS2812_AF 1
65     #elif WS2812_TIM_N <= 5
66         #define WS2812_AF 2
67     #elif WS2812_TIM_N <= 11
68         #define WS2812_AF 3
69     #endif
70 #elif !defined(WS2812_AF)
71     #error WS2812_AF timer alternate function not specified
72 #endif
73
74 #if !defined(WS2812_TIM_CH)
75     #error WS2812 timer channel not specified
76 #elif WS2812_TIM_CH >= 4
77     #error WS2812 timer channel set to invalid value
78 #endif
79
80 /* --- PRIVATE CONSTANTS ---------------------------------------------------- */
81
82 #define WS2812_PWM_FREQUENCY    (STM32_SYSCLK/2)                /**< Clock frequency of PWM, must be valid with respect to system clock! */
83 #define WS2812_PWM_PERIOD       (WS2812_PWM_FREQUENCY/800000)   /**< Clock period in ticks. 1 / 800kHz = 1.25 uS (as per datasheet) */
84
85 /**
86  * @brief   Number of bit-periods to hold the data line low at the end of a frame
87  *
88  * The reset period for each frame must be at least 50 uS; so we add in 50 bit-times
89  * of zeroes at the end. (50 bits)*(1.25 uS/bit) = 62.5 uS, which gives us some
90  * slack in the timing requirements
91  */
92 #define WS2812_RESET_BIT_N      (50)
93 #define WS2812_COLOR_BIT_N      (WS2812_LED_N*24)                           /**< Number of data bits */
94 #define WS2812_BIT_N            (WS2812_COLOR_BIT_N + WS2812_RESET_BIT_N)   /**< Total number of bits in a frame */
95
96 /**
97  * @brief   High period for a zero, in ticks
98  *
99  * Per the datasheet:
100  * WS2812:
101  * - T0H: 200 nS to 500 nS, inclusive
102  * - T0L: 650 nS to 950 nS, inclusive
103  * WS2812B:
104  * - T0H: 200 nS to 500 nS, inclusive
105  * - T0L: 750 nS to 1050 nS, inclusive
106  *
107  * The duty cycle is calculated for a high period of 350 nS.
108  */
109 #define WS2812_DUTYCYCLE_0      (WS2812_PWM_FREQUENCY/(1000000000/450))
110
111 /**
112  * @brief   High period for a one, in ticks
113  *
114  * Per the datasheet:
115  * WS2812:
116  * - T1H: 550 nS to 850 nS, inclusive
117  * - T1L: 450 nS to 750 nS, inclusive
118  * WS2812B:
119  * - T1H: 750 nS to 1050 nS, inclusive
120  * - T1L: 200 nS to 500 nS, inclusive
121  *
122  * The duty cycle is calculated for a high period of 800 nS.
123  * This is in the middle of the specifications of the WS2812 and WS2812B.
124  */
125 #define WS2812_DUTYCYCLE_1      (WS2812_PWM_FREQUENCY/(1000000000/900))
126
127 /* --- PRIVATE MACROS ------------------------------------------------------- */
128
129 /**
130  * @brief   Generates a reference to a numbered PWM driver
131  *
132  * @param[in] n:            The driver (timer) number
133  *
134  * @return                  A reference to the driver
135  */
136 #define PWMD(n)                             CONCAT_EXPANDED_SYMBOLS(PWMD, n)
137
138 #define WS2812_PWMD                         PWMD(WS2812_TIM_N)      /**< The PWM driver to use for the LED chain */
139
140 /**
141  * @brief   Determine the index in @ref ws2812_frame_buffer "the frame buffer" of a given bit
142  *
143  * @param[in] led:                  The led index [0, @ref WS2812_LED_N)
144  * @param[in] byte:                 The byte number [0, 2]
145  * @param[in] bit:                  The bit number [0, 7]
146  *
147  * @return                          The bit index
148  */
149 #define WS2812_BIT(led, byte, bit)          (24*(led) + 8*(byte) + (7 - (bit)))
150
151 /**
152  * @brief   Determine the index in @ref ws2812_frame_buffer "the frame buffer" of a given red bit
153  *
154  * @note    The red byte is the middle byte in the color packet
155  *
156  * @param[in] led:                  The led index [0, @ref WS2812_LED_N)
157  * @param[in] bit:                  The bit number [0, 7]
158  *
159  * @return                          The bit index
160  */
161 #define WS2812_RED_BIT(led, bit)            WS2812_BIT((led), 1, (bit))
162
163 /**
164  * @brief   Determine the index in @ref ws2812_frame_buffer "the frame buffer" of a given green bit
165  *
166  * @note    The green byte is the first byte in the color packet
167  *
168  * @param[in] led:                  The led index [0, @ref WS2812_LED_N)
169  * @param[in] bit:                  The bit number [0, 7]
170  *
171  * @return                          The bit index
172  */
173 #define WS2812_GREEN_BIT(led, bit)          WS2812_BIT((led), 0, (bit))
174
175 /**
176  * @brief   Determine the index in @ref ws2812_frame_buffer "the frame buffer" of a given blue bit
177  *
178  * @note    The blue byte is the last byte in the color packet
179  *
180  * @param[in] led:                  The led index [0, @ref WS2812_LED_N)
181  * @param[in] bit:                  The bit index [0, 7]
182  *
183  * @return                          The bit index
184  */
185 #define WS2812_BLUE_BIT(led, bit)           WS2812_BIT((led), 2, (bit))
186
187 /* --- PRIVATE VARIABLES ---------------------------------------------------- */
188
189 static uint32_t ws2812_frame_buffer[WS2812_BIT_N + 1];                             /**< Buffer for a frame */
190
191 /* --- PUBLIC FUNCTIONS ----------------------------------------------------- */
192 /*
193  * Gedanke: Double-buffer type transactions: double buffer transfers using two memory pointers for
194 the memory (while the DMA is reading/writing from/to a buffer, the application can
195 write/read to/from the other buffer).
196  */
197
198 void ws2812_init(void)
199 {
200     // Initialize led frame buffer
201     uint32_t i;
202     for (i = 0; i < WS2812_COLOR_BIT_N; i++) ws2812_frame_buffer[i]                       = WS2812_DUTYCYCLE_0;   // All color bits are zero duty cycle
203     for (i = 0; i < WS2812_RESET_BIT_N; i++) ws2812_frame_buffer[i + WS2812_COLOR_BIT_N]  = 0;                    // All reset bits are zero
204     // Configure pin as AF output. If there's an external pull up resistor the signal level is brought to 5V using open drain mode.
205 #ifdef WS2812_EXTERNAL_PULLUP
206     palSetPadMode(PORT_WS2812, PIN_WS2812, PAL_MODE_ALTERNATE(WS2812_AF) | PAL_STM32_OTYPE_OPENDRAIN);
207 #else
208     palSetPadMode(PORT_WS2812, PIN_WS2812, PAL_MODE_ALTERNATE(WS2812_AF) | PAL_STM32_OTYPE_PUSHPULL); //PAL_MODE_STM32_ALTERNATE_PUSHPULL);
209 #endif
210     //palClearPad(PORT_WS2812, PIN_WS2812);
211     //wait_ms(1);
212     // PWM Configuration
213     #pragma GCC diagnostic ignored "-Woverride-init"                                        // Turn off override-init warning for this struct. We use the overriding ability to set a "default" channel config
214     static const PWMConfig ws2812_pwm_config = {
215         .frequency          = WS2812_PWM_FREQUENCY,
216         .period             = WS2812_PWM_PERIOD, //Mit dieser Periode wird UDE-Event erzeugt und ein neuer Wert (Länge WS2812_BIT_N) vom DMA ins CCR geschrieben
217         .callback           = NULL,
218         .channels = {
219             [0 ... 3]       = {.mode = PWM_OUTPUT_DISABLED,     .callback = NULL},          // Channels default to disabled
220             [WS2812_TIM_CH] = {.mode = PWM_OUTPUT_ACTIVE_HIGH,  .callback = NULL},          // Turn on the channel we care about
221         },
222         .cr2                = 0,
223         .dier               = TIM_DIER_UDE,                                                 // DMA on update event for next period
224     };
225     #pragma GCC diagnostic pop                                                              // Restore command-line warning options
226
227     // Configure DMA
228     //dmaInit(); // Joe added this
229     dmaStreamAllocate(WS2812_DMA_STREAM, 10, NULL, NULL);
230     dmaStreamSetPeripheral(WS2812_DMA_STREAM, &(WS2812_PWMD.tim->CCR[WS2812_TIM_CH]));  // Ziel ist der An-Zeit im Cap-Comp-Register
231     dmaStreamSetMemory0(WS2812_DMA_STREAM, ws2812_frame_buffer);
232     dmaStreamSetTransactionSize(WS2812_DMA_STREAM, WS2812_BIT_N);
233     dmaStreamSetMode(WS2812_DMA_STREAM,
234       STM32_DMA_CR_CHSEL(WS2812_DMA_CHANNEL) | STM32_DMA_CR_DIR_M2P | STM32_DMA_CR_PSIZE_WORD | STM32_DMA_CR_MSIZE_WORD |
235       STM32_DMA_CR_MINC | STM32_DMA_CR_CIRC | STM32_DMA_CR_PL(3));
236     // M2P: Memory 2 Periph; PL: Priority Level
237
238     // Start DMA
239     dmaStreamEnable(WS2812_DMA_STREAM);
240
241     // Configure PWM
242     // NOTE: It's required that preload be enabled on the timer channel CCR register. This is currently enabled in the
243     // ChibiOS driver code, so we don't have to do anything special to the timer. If we did, we'd have to start the timer,
244     // disable counting, enable the channel, and then make whatever configuration changes we need.
245     pwmStart(&WS2812_PWMD, &ws2812_pwm_config);
246     pwmEnableChannel(&WS2812_PWMD, WS2812_TIM_CH, 0);     // Initial period is 0; output will be low until first duty cycle is DMA'd in
247 }
248
249 ws2812_err_t ws2812_write_led(uint32_t led_number, uint8_t r, uint8_t g, uint8_t b)
250 {
251     // Check for valid LED
252     if (led_number > WS2812_LED_N) return WS2812_LED_INVALID;
253
254     // Write color to frame buffer
255     for (uint32_t bit = 0; bit < 8; bit++) {
256         ws2812_frame_buffer[WS2812_RED_BIT(led_number, bit)]      = ((r >> bit) & 0x01) ? WS2812_DUTYCYCLE_1 : WS2812_DUTYCYCLE_0;
257         ws2812_frame_buffer[WS2812_GREEN_BIT(led_number, bit)]    = ((g >> bit) & 0x01) ? WS2812_DUTYCYCLE_1 : WS2812_DUTYCYCLE_0;
258         ws2812_frame_buffer[WS2812_BLUE_BIT(led_number, bit)]     = ((b >> bit) & 0x01) ? WS2812_DUTYCYCLE_1 : WS2812_DUTYCYCLE_0;
259     }
260
261     // Success
262     return WS2812_SUCCESS;
263 }
264
265 void ws2812_setleds(LED_TYPE *ledarray, uint16_t number_of_leds)
266 {
267     for(int i = 0; i < number_of_leds; i++) {
268         ws2812_write_led(i, ledarray[i].r, ledarray[i].g, ledarray[i].b);
269     }
270 }
271
272 /** @} addtogroup WS2812 */