led_driver.c 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. /**
  2. * @file led_driver.c
  3. * @brief WS2812B LED Driver
  4. * @details This driver uses DMA and TIM2 AAR to drive WS2812B LEDs. There is no circular buffer,
  5. * so all of the data is loaded into memory at the beginning. We are able to driver 256 LEDs.
  6. *
  7. */
  8. #include <stm32wbxx_ll_dma.h>
  9. #include "led_driver.h"
  10. // We store the HIGH/LOW durations (2 values) for each color bit (24 bits per LED)
  11. #define LED_DRIVER_BUFFER_SIZE (MAX_LED_COUNT * 2 * 24)
  12. // We use a setinel value to figure out when the timer is complete.
  13. #define LED_DRIVER_TIMER_SETINEL 0xFFFFU
  14. /** 64 transitions per us @ 64MHz. Our timing is in NANO_SECONDS */
  15. #define LED_DRIVER_TIMER_NANOSECOND (1000U / (SystemCoreClock / 1000000U))
  16. // Timings for WS2812B
  17. #define LED_DRIVER_T0H 400U
  18. #define LED_DRIVER_T1H 800U
  19. #define LED_DRIVER_T0L 850U
  20. #define LED_DRIVER_T1L 450U
  21. #define LED_DRIVER_TRESETL 55 * 1000U
  22. #define LED_DRIVER_TDONE 2000U
  23. // Wait for 35ms for the DMA to complete. NOTE: 1000 leds*(850ns+450ns)*24 = 32ms
  24. #define LED_DRIVER_SETINEL_WAIT_MS 35
  25. struct LedDriver {
  26. LL_DMA_InitTypeDef dma_gpio_update;
  27. LL_DMA_InitTypeDef dma_led_transition_timer;
  28. const GpioPin* gpio;
  29. uint32_t gpio_buf[2]; // On/Off for GPIO
  30. uint16_t timer_buffer[LED_DRIVER_BUFFER_SIZE + 2];
  31. uint32_t write_pos;
  32. uint32_t read_pos;
  33. bool dirty;
  34. uint32_t count_leds;
  35. uint32_t* led_data;
  36. };
  37. static void led_driver_init_dma_gpio_update(LedDriver* led_driver, const GpioPin* gpio) {
  38. led_driver->gpio = gpio;
  39. // Memory to Peripheral
  40. led_driver->dma_gpio_update.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
  41. // Peripheral (GPIO - We populate GPIO port's BSRR register)
  42. led_driver->dma_gpio_update.PeriphOrM2MSrcAddress = (uint32_t)&gpio->port->BSRR;
  43. led_driver->dma_gpio_update.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
  44. led_driver->dma_gpio_update.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD;
  45. // Memory (State to set GPIO)
  46. led_driver->dma_gpio_update.MemoryOrM2MDstAddress = (uint32_t)led_driver->gpio_buf;
  47. led_driver->dma_gpio_update.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
  48. led_driver->dma_gpio_update.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD;
  49. // Data
  50. led_driver->dma_gpio_update.Mode = LL_DMA_MODE_CIRCULAR;
  51. led_driver->dma_gpio_update.NbData = 2; // We cycle between two (HIGH/LOW)values
  52. // When to perform data exchange
  53. led_driver->dma_gpio_update.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP;
  54. led_driver->dma_gpio_update.Priority = LL_DMA_PRIORITY_VERYHIGH;
  55. }
  56. static void led_driver_init_dma_led_transition_timer(LedDriver* led_driver) {
  57. // Timer that triggers based on user data.
  58. led_driver->dma_led_transition_timer.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
  59. // Peripheral (Timer - We populate TIM2's ARR register)
  60. led_driver->dma_led_transition_timer.PeriphOrM2MSrcAddress = (uint32_t)&TIM2->ARR;
  61. led_driver->dma_led_transition_timer.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
  62. led_driver->dma_led_transition_timer.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD;
  63. // Memory (Timings)
  64. led_driver->dma_led_transition_timer.MemoryOrM2MDstAddress =
  65. (uint32_t)led_driver->timer_buffer;
  66. led_driver->dma_led_transition_timer.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
  67. led_driver->dma_led_transition_timer.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_HALFWORD;
  68. // Data
  69. led_driver->dma_led_transition_timer.Mode = LL_DMA_MODE_NORMAL;
  70. led_driver->dma_led_transition_timer.NbData = LED_DRIVER_BUFFER_SIZE;
  71. // When to perform data exchange
  72. led_driver->dma_led_transition_timer.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP;
  73. led_driver->dma_led_transition_timer.Priority = LL_DMA_PRIORITY_HIGH;
  74. }
  75. LedDriver* led_driver_alloc(int count_leds, const GpioPin* gpio) {
  76. furi_assert(gpio);
  77. furi_assert(count_leds && count_leds <= MAX_LED_COUNT);
  78. LedDriver* led_driver = malloc(sizeof(LedDriver));
  79. led_driver_init_dma_gpio_update(led_driver, gpio);
  80. led_driver_init_dma_led_transition_timer(led_driver);
  81. led_driver->led_data = malloc(MAX_LED_COUNT * sizeof(uint32_t));
  82. led_driver->dirty = true;
  83. led_driver->count_leds = count_leds;
  84. return led_driver;
  85. }
  86. void led_driver_free(LedDriver* led_driver) {
  87. furi_assert(led_driver);
  88. free(led_driver->led_data);
  89. free(led_driver);
  90. }
  91. void led_driver_set_pin(LedDriver* led_driver, const GpioPin* gpio) {
  92. if(led_driver->gpio == gpio) {
  93. return;
  94. }
  95. led_driver_init_dma_gpio_update(led_driver, gpio);
  96. led_driver->dirty = true;
  97. }
  98. uint32_t led_driver_set_led(LedDriver* led_driver, uint32_t index, uint32_t rrggbb) {
  99. furi_assert(led_driver);
  100. if(index >= led_driver->count_leds) {
  101. return 0xFFFFFFFF;
  102. }
  103. uint32_t previous = led_driver->led_data[index];
  104. led_driver->led_data[index] = rrggbb;
  105. led_driver->dirty |= previous != rrggbb;
  106. return previous;
  107. }
  108. uint32_t led_driver_get_led(LedDriver* led_driver, uint32_t index) {
  109. furi_assert(led_driver);
  110. if(index >= led_driver->count_leds) {
  111. return 0xFFFFFFFF;
  112. }
  113. return led_driver->led_data[index];
  114. }
  115. static void led_driver_start_dma(LedDriver* led_driver) {
  116. furi_assert(led_driver);
  117. LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &led_driver->dma_gpio_update);
  118. LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &led_driver->dma_led_transition_timer);
  119. LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1);
  120. LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2);
  121. }
  122. static void led_driver_start_timer() {
  123. furi_hal_bus_enable(FuriHalBusTIM2);
  124. LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP);
  125. LL_TIM_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1);
  126. LL_TIM_SetPrescaler(TIM2, 0);
  127. // Updated by led_driver->dma_led_transition_timer.PeriphOrM2MSrcAddress
  128. LL_TIM_SetAutoReload(TIM2, LED_DRIVER_TIMER_SETINEL);
  129. LL_TIM_SetCounter(TIM2, 0);
  130. LL_TIM_EnableCounter(TIM2);
  131. LL_TIM_EnableUpdateEvent(TIM2);
  132. LL_TIM_EnableDMAReq_UPDATE(TIM2);
  133. LL_TIM_GenerateEvent_UPDATE(TIM2);
  134. }
  135. static void led_driver_stop_timer() {
  136. LL_TIM_DisableCounter(TIM2);
  137. LL_TIM_DisableUpdateEvent(TIM2);
  138. LL_TIM_DisableDMAReq_UPDATE(TIM2);
  139. furi_hal_bus_disable(FuriHalBusTIM2);
  140. }
  141. static void led_driver_stop_dma() {
  142. LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1);
  143. LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2);
  144. LL_DMA_ClearFlag_TC1(DMA1);
  145. LL_DMA_ClearFlag_TC2(DMA1);
  146. }
  147. static void led_driver_spin_lock(LedDriver* led_driver) {
  148. const uint32_t prev_timer = DWT->CYCCNT;
  149. const uint32_t wait_time = LED_DRIVER_SETINEL_WAIT_MS * SystemCoreClock / 1000;
  150. do {
  151. /* Make sure it's started (allow 100 ticks), but then check for sentinel value. */
  152. if(TIM2->ARR == LED_DRIVER_TIMER_SETINEL && DWT->CYCCNT - prev_timer > 100) {
  153. break;
  154. }
  155. // 0xFF is fairly quick, make sure we didn't miss it.
  156. if((DWT->CYCCNT - prev_timer > wait_time)) {
  157. FURI_LOG_D(
  158. "Demo", "0xFF not found (ARR 0x%08lx, read %lu)", TIM2->ARR, led_driver->read_pos);
  159. led_driver->read_pos = led_driver->write_pos - 1;
  160. break;
  161. }
  162. } while(true);
  163. }
  164. static void led_driver_add_period_length(LedDriver* led_driver, uint32_t length) {
  165. led_driver->timer_buffer[led_driver->write_pos++] = length;
  166. led_driver->timer_buffer[led_driver->write_pos] = LED_DRIVER_TIMER_SETINEL;
  167. }
  168. static void led_driver_add_period(LedDriver* led_driver, uint16_t duration_ns) {
  169. furi_assert(led_driver);
  170. uint32_t reload_value = duration_ns / LED_DRIVER_TIMER_NANOSECOND;
  171. if(reload_value > 255) {
  172. FURI_LOG_E("Demo", "reload_value: %ld", reload_value);
  173. }
  174. furi_check(reload_value > 0);
  175. furi_check(reload_value < 256 * 256);
  176. led_driver_add_period_length(led_driver, reload_value - 1);
  177. }
  178. static void led_driver_add_color(LedDriver* led_driver, uint32_t rrggbb) {
  179. UNUSED(rrggbb);
  180. uint32_t ggrrbb = (rrggbb & 0xFF) | ((rrggbb & 0xFF00) << 8) | ((rrggbb & 0xFF0000) >> 8);
  181. for(int i = 23; i >= 0; i--) {
  182. if(ggrrbb & (1 << i)) {
  183. led_driver_add_period(led_driver, LED_DRIVER_T0L);
  184. led_driver_add_period(led_driver, LED_DRIVER_T1L);
  185. } else {
  186. led_driver_add_period(led_driver, LED_DRIVER_T0H);
  187. led_driver_add_period(led_driver, LED_DRIVER_T1H);
  188. }
  189. }
  190. }
  191. void led_driver_transmit(LedDriver* led_driver, bool transmit_if_clean) {
  192. furi_assert(led_driver);
  193. furi_assert(!led_driver->read_pos);
  194. furi_assert(!led_driver->write_pos);
  195. if(!transmit_if_clean && !led_driver->dirty) {
  196. FURI_LOG_D("LED_DRIVER", "Skipping transmit");
  197. return;
  198. }
  199. FURI_LOG_D("LED_DRIVER", "Transmit");
  200. furi_hal_gpio_init(led_driver->gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
  201. furi_hal_gpio_write(led_driver->gpio, false);
  202. const uint32_t bit_set = led_driver->gpio->pin << GPIO_BSRR_BS0_Pos;
  203. const uint32_t bit_reset = led_driver->gpio->pin << GPIO_BSRR_BR0_Pos;
  204. // Our initial state is LOW, so first pulse is HIGH (set)
  205. led_driver->gpio_buf[0] = bit_set;
  206. led_driver->gpio_buf[1] = bit_reset;
  207. for(size_t i = 0; i < LED_DRIVER_BUFFER_SIZE; i++) {
  208. led_driver->timer_buffer[i] = LED_DRIVER_TIMER_SETINEL;
  209. }
  210. for(size_t i = 0; i < led_driver->count_leds; i++) {
  211. led_driver_add_color(led_driver, led_driver->led_data[i]);
  212. }
  213. led_driver_add_period(led_driver, LED_DRIVER_TDONE);
  214. led_driver->dma_led_transition_timer.NbData = led_driver->write_pos + 1;
  215. FURI_CRITICAL_ENTER();
  216. led_driver_start_dma(led_driver);
  217. led_driver_start_timer();
  218. led_driver_spin_lock(led_driver);
  219. led_driver_stop_timer();
  220. led_driver_stop_dma();
  221. FURI_CRITICAL_EXIT();
  222. memset(led_driver->timer_buffer, LED_DRIVER_TIMER_SETINEL, LED_DRIVER_BUFFER_SIZE);
  223. led_driver->read_pos = 0;
  224. led_driver->write_pos = 0;
  225. led_driver->dirty = false;
  226. }