led_driver.c 9.7 KB

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