ws2812.c 6.6 KB

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