ws2812.c 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. /*
  2. * LEDDriver.c
  3. *
  4. * Created on: Aug 26, 2013
  5. * Author: Omri Iluz
  6. */
  7. #include "ws2812.h"
  8. #include "stdlib.h"
  9. #include "quantum.h"
  10. static uint8_t *fb;
  11. static int sLeds;
  12. static stm32_gpio_t *sPort;
  13. static uint32_t sMask;
  14. uint8_t* dma_source;
  15. static LED_TYPE led_array[RGBLED_NUM];
  16. void setColor(uint8_t color, uint8_t *buf,uint32_t mask){
  17. int i;
  18. for (i=0;i<8;i++){
  19. buf[i]=((color<<i)&0b10000000?0x0:mask);
  20. }
  21. }
  22. void setColorRGB(Color c, uint8_t *buf, uint32_t mask) {
  23. setColor(c.G,buf, mask);
  24. setColor(c.R,buf+8, mask);
  25. setColor(c.B,buf+16, mask);
  26. }
  27. /**
  28. * @brief Initialize Led Driver
  29. * @details Initialize the Led Driver based on parameters.
  30. * Following initialization, the frame buffer would automatically be
  31. * exported to the supplied port and pins in the right timing to drive
  32. * a chain of WS2812B controllers
  33. * @note The function assumes the controller is running at 72Mhz
  34. * @note Timing is critical for WS2812. While all timing is done in hardware
  35. * need to verify memory bandwidth is not exhausted to avoid DMA delays
  36. *
  37. * @param[in] leds length of the LED chain controlled by each pin
  38. * @param[in] port which port would be used for output
  39. * @param[in] mask Which pins would be used for output, each pin is a full chain
  40. * @param[out] o_fb initialized frame buffer
  41. *
  42. */
  43. void WS2812_init(void) {
  44. static uint8_t * p;
  45. //uint32_t port = RGBLED_PORT;
  46. //ledDriverInit(RGBLED_NUM, (stm32_gpio_t *)(port & 0xFFF0), 1 << (port & 0xF), &p);
  47. pin_t rgb_pin = RGB_DI_PIN;
  48. ledDriverInit(RGBLED_NUM, PAL_PORT(rgb_pin), 1 << PAL_PAD(rgb_pin), &p);
  49. }
  50. void ledDriverInit(int leds, stm32_gpio_t *port, uint32_t mask, uint8_t **o_fb) {
  51. sLeds=leds;
  52. sPort=port;
  53. sMask=mask;
  54. palSetGroupMode(port, sMask, 0, PAL_MODE_OUTPUT_PUSHPULL|PAL_STM32_OSPEED_HIGHEST|PAL_STM32_PUPDR_FLOATING);
  55. // maybe don't do whole port?
  56. // palSetPadMode(port, 8, PAL_MODE_OUTPUT_PUSHPULL|PAL_STM32_OSPEED_HIGHEST|PAL_STM32_PUPDR_FLOATING);
  57. // configure pwm timers -
  58. // timer 2 as master, active for data transmission and inactive to disable transmission during reset period (50uS)
  59. // timer 3 as slave, during active time creates a 1.25 uS signal, with duty cycle controlled by frame buffer values
  60. static PWMConfig pwmc2 = {72000000 / 90, /* 800Khz PWM clock frequency. 1/90 of PWMC3 */
  61. (72000000 / 90) * 0.05, /*Total period is 50ms (20FPS), including sLeds cycles + reset length for ws2812b and FB writes */
  62. NULL,
  63. { {PWM_OUTPUT_ACTIVE_HIGH, NULL},
  64. {PWM_OUTPUT_DISABLED, NULL},
  65. {PWM_OUTPUT_DISABLED, NULL},
  66. {PWM_OUTPUT_DISABLED, NULL}},
  67. TIM_CR2_MMS_2, /* master mode selection */
  68. 0, };
  69. /* master mode selection */
  70. static PWMConfig pwmc3 = {72000000,/* 72Mhz PWM clock frequency. */
  71. 90, /* 90 cycles period (1.25 uS per period @72Mhz */
  72. NULL,
  73. { {PWM_OUTPUT_ACTIVE_HIGH, NULL},
  74. {PWM_OUTPUT_ACTIVE_HIGH, NULL},
  75. {PWM_OUTPUT_ACTIVE_HIGH, NULL},
  76. {PWM_OUTPUT_ACTIVE_HIGH, NULL}},
  77. 0,
  78. 0,
  79. };
  80. dma_source = chHeapAlloc(NULL, 1);
  81. fb = chHeapAlloc(NULL, ((sLeds) * 24)+10);
  82. *o_fb=fb;
  83. int j;
  84. for (j = 0; j < (sLeds) * 24; j++) fb[j] = 0;
  85. dma_source[0] = sMask;
  86. // DMA stream 2, triggered by channel3 pwm signal. if FB indicates, reset output value early to indicate "0" bit to ws2812
  87. dmaStreamAllocate(STM32_DMA1_STREAM2, 10, NULL, NULL);
  88. dmaStreamSetPeripheral(STM32_DMA1_STREAM2, &(sPort->BSRR.H.clear));
  89. dmaStreamSetMemory0(STM32_DMA1_STREAM2, fb);
  90. dmaStreamSetTransactionSize(STM32_DMA1_STREAM2, (sLeds) * 24);
  91. dmaStreamSetMode(
  92. STM32_DMA1_STREAM2,
  93. STM32_DMA_CR_DIR_M2P | STM32_DMA_CR_MINC | STM32_DMA_CR_PSIZE_BYTE
  94. | STM32_DMA_CR_MSIZE_BYTE | STM32_DMA_CR_CIRC | STM32_DMA_CR_PL(2));
  95. // DMA stream 3, triggered by pwm update event. output high at beginning of signal
  96. dmaStreamAllocate(STM32_DMA1_STREAM3, 10, NULL, NULL);
  97. dmaStreamSetPeripheral(STM32_DMA1_STREAM3, &(sPort->BSRR.H.set));
  98. dmaStreamSetMemory0(STM32_DMA1_STREAM3, dma_source);
  99. dmaStreamSetTransactionSize(STM32_DMA1_STREAM3, 1);
  100. dmaStreamSetMode(
  101. STM32_DMA1_STREAM3, STM32_DMA_CR_TEIE |
  102. STM32_DMA_CR_DIR_M2P | STM32_DMA_CR_PSIZE_BYTE | STM32_DMA_CR_MSIZE_BYTE
  103. | STM32_DMA_CR_CIRC | STM32_DMA_CR_PL(3));
  104. // DMA stream 6, triggered by channel1 update event. reset output value late to indicate "1" bit to ws2812.
  105. // always triggers but no affect if dma stream 2 already change output value to 0
  106. dmaStreamAllocate(STM32_DMA1_STREAM6, 10, NULL, NULL);
  107. dmaStreamSetPeripheral(STM32_DMA1_STREAM6, &(sPort->BSRR.H.clear));
  108. dmaStreamSetMemory0(STM32_DMA1_STREAM6, dma_source);
  109. dmaStreamSetTransactionSize(STM32_DMA1_STREAM6, 1);
  110. dmaStreamSetMode(
  111. STM32_DMA1_STREAM6,
  112. STM32_DMA_CR_DIR_M2P | STM32_DMA_CR_PSIZE_BYTE | STM32_DMA_CR_MSIZE_BYTE
  113. | STM32_DMA_CR_CIRC | STM32_DMA_CR_PL(3));
  114. pwmStart(&PWMD2, &pwmc2);
  115. pwmStart(&PWMD3, &pwmc3);
  116. // set pwm3 as slave, triggerd by pwm2 oc1 event. disables pwmd2 for synchronization.
  117. PWMD3.tim->SMCR |= TIM_SMCR_SMS_0 | TIM_SMCR_SMS_2 | TIM_SMCR_TS_0;
  118. PWMD2.tim->CR1 &= ~TIM_CR1_CEN;
  119. // set pwm values.
  120. // 28 (duty in ticks) / 90 (period in ticks) * 1.25uS (period in S) = 0.39 uS
  121. pwmEnableChannel(&PWMD3, 2, 28);
  122. // 58 (duty in ticks) / 90 (period in ticks) * 1.25uS (period in S) = 0.806 uS
  123. pwmEnableChannel(&PWMD3, 0, 58);
  124. // active during transfer of 90 cycles * sLeds * 24 bytes * 1/90 multiplier
  125. pwmEnableChannel(&PWMD2, 0, 90 * sLeds * 24 / 90);
  126. // stop and reset counters for synchronization
  127. PWMD2.tim->CNT = 0;
  128. // Slave (TIM3) needs to "update" immediately after master (TIM2) start in order to start in sync.
  129. // this initial sync is crucial for the stability of the run
  130. PWMD3.tim->CNT = 89;
  131. PWMD3.tim->DIER |= TIM_DIER_CC3DE | TIM_DIER_CC1DE | TIM_DIER_UDE;
  132. dmaStreamEnable(STM32_DMA1_STREAM3);
  133. dmaStreamEnable(STM32_DMA1_STREAM6);
  134. dmaStreamEnable(STM32_DMA1_STREAM2);
  135. // all systems go! both timers and all channels are configured to resonate
  136. // in complete sync without any need for CPU cycles (only DMA and timers)
  137. // start pwm2 for system to start resonating
  138. PWMD2.tim->CR1 |= TIM_CR1_CEN;
  139. }
  140. void ledDriverWaitCycle(void){
  141. while (PWMD2.tim->CNT < 90 * sLeds * 24 / 90){chThdSleepMicroseconds(1);};
  142. }
  143. void testPatternFB(uint8_t *fb){
  144. int i;
  145. Color tmpC = {rand()%256, rand()%256, rand()%256};
  146. for (i=0;i<sLeds;i++){
  147. setColorRGB(tmpC,fb+24*i, sMask);
  148. }
  149. }
  150. void ws2812_setleds(LED_TYPE *ledarray, uint16_t number_of_leds) {
  151. // uint8_t i = 0;
  152. // while (i < number_of_leds) {
  153. // ws2812_write_led(i, ledarray[i].r, ledarray[i].g, ledarray[i].b);
  154. // i++;
  155. // }
  156. uint8_t i = 0;
  157. while (i < number_of_leds) {
  158. setColor(ledarray[i].g, (fb+24*i), sMask);
  159. setColor(ledarray[i].r, (fb+24*i)+8, sMask);
  160. setColor(ledarray[i].b, (fb+24*i)+16, sMask);
  161. i++;
  162. }
  163. }
  164. void ws2812_setleds_rgbw(LED_TYPE *ledarray, uint16_t number_of_leds) {
  165. }
  166. void WS2812_send_color( uint8_t index ) {
  167. setColor(led_array[index].g, (fb+24*index), sMask);
  168. setColor(led_array[index].r, (fb+24*index)+8, sMask);
  169. setColor(led_array[index].b, (fb+24*index)+16, sMask);
  170. }
  171. void WS2812_set_color( int index, uint8_t red, uint8_t green, uint8_t blue ) {
  172. led_array[index].r = red;
  173. led_array[index].g = green;
  174. led_array[index].b = blue;
  175. }
  176. void WS2812_set_color_all( uint8_t red, uint8_t green, uint8_t blue ) {
  177. for (int i = 0; i < RGBLED_NUM; i++) {
  178. WS2812_set_color( i, red, green, blue );
  179. }
  180. }
  181. void WS2812_send_colors(void) {
  182. for (int i = 0; i < RGBLED_NUM; i++) {
  183. WS2812_send_color( i );
  184. }
  185. }