diff options
Diffstat (limited to 'keyboards/matrix/noah/ws2812_f4.c')
| -rw-r--r-- | keyboards/matrix/noah/ws2812_f4.c | 272 |
1 files changed, 0 insertions, 272 deletions
diff --git a/keyboards/matrix/noah/ws2812_f4.c b/keyboards/matrix/noah/ws2812_f4.c deleted file mode 100644 index 7536d6d8c..000000000 --- a/keyboards/matrix/noah/ws2812_f4.c +++ /dev/null | |||
| @@ -1,272 +0,0 @@ | |||
| 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 */ | ||
