| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159 |
- /*
- * LEDDriver.c
- *
- * Created on: Aug 26, 2013
- * Author: Omri Iluz
- */
- #include "ws2812.h"
- #include "stdlib.h"
- static uint8_t *fb;
- static int sLeds;
- static stm32_gpio_t *sPort;
- static uint32_t sMask;
- uint8_t* dma_source;
- void setColor(uint8_t color, uint8_t *buf,uint32_t mask){
- int i;
- for (i=0;i<8;i++){
- buf[i]=((color<<i)&0b10000000?0x0:mask);
- }
- }
- void setColorRGB(Color c, uint8_t *buf, uint32_t mask){
- setColor(c.G,buf, mask);
- setColor(c.R,buf+8, mask);
- setColor(c.B,buf+16, mask);
- }
- /**
- * @brief Initialize Led Driver
- * @details Initialize the Led Driver based on parameters.
- * Following initialization, the frame buffer would automatically be
- * exported to the supplied port and pins in the right timing to drive
- * a chain of WS2812B controllers
- * @note The function assumes the controller is running at 72Mhz
- * @note Timing is critical for WS2812. While all timing is done in hardware
- * need to verify memory bandwidth is not exhausted to avoid DMA delays
- *
- * @param[in] leds length of the LED chain controlled by each pin
- * @param[in] port which port would be used for output
- * @param[in] mask Which pins would be used for output, each pin is a full chain
- * @param[out] o_fb initialized frame buffer
- *
- */
- void ledDriverInit(int leds, stm32_gpio_t *port, uint32_t mask, uint8_t **o_fb) {
- sLeds=leds;
- sPort=port;
- sMask=mask;
- palSetGroupMode(port, sMask, 0, PAL_MODE_OUTPUT_PUSHPULL|PAL_STM32_OSPEED_HIGHEST|PAL_STM32_PUPDR_FLOATING);
- // configure pwm timers -
- // timer 2 as master, active for data transmission and inactive to disable transmission during reset period (50uS)
- // timer 3 as slave, during active time creates a 1.25 uS signal, with duty cycle controlled by frame buffer values
- static PWMConfig pwmc2 = {72000000 / 90, /* 800Khz PWM clock frequency. 1/90 of PWMC3 */
- (72000000 / 90) * 0.05, /*Total period is 50ms (20FPS), including sLeds cycles + reset length for ws2812b and FB writes */
- NULL,
- { {PWM_OUTPUT_ACTIVE_HIGH, NULL},
- {PWM_OUTPUT_DISABLED, NULL},
- {PWM_OUTPUT_DISABLED, NULL},
- {PWM_OUTPUT_DISABLED, NULL}},
- TIM_CR2_MMS_2, /* master mode selection */
- 0, };
- /* master mode selection */
- static PWMConfig pwmc3 = {72000000,/* 72Mhz PWM clock frequency. */
- 90, /* 90 cycles period (1.25 uS per period @72Mhz */
- NULL,
- { {PWM_OUTPUT_ACTIVE_HIGH, NULL},
- {PWM_OUTPUT_ACTIVE_HIGH, NULL},
- {PWM_OUTPUT_ACTIVE_HIGH, NULL},
- {PWM_OUTPUT_ACTIVE_HIGH, NULL}},
- 0,
- 0,
- };
- dma_source = chHeapAlloc(NULL, 1);
- fb = chHeapAlloc(NULL, ((sLeds) * 24)+10);
- *o_fb=fb;
- int j;
- for (j = 0; j < (sLeds) * 24; j++) fb[j] = 0;
- dma_source[0] = sMask;
- // DMA stream 2, triggered by channel3 pwm signal. if FB indicates, reset output value early to indicate "0" bit to ws2812
- dmaStreamAllocate(STM32_DMA1_STREAM2, 10, NULL, NULL);
- dmaStreamSetPeripheral(STM32_DMA1_STREAM2, &(sPort->BSRR.H.clear));
- dmaStreamSetMemory0(STM32_DMA1_STREAM2, fb);
- dmaStreamSetTransactionSize(STM32_DMA1_STREAM2, (sLeds) * 24);
- dmaStreamSetMode(
- STM32_DMA1_STREAM2,
- STM32_DMA_CR_DIR_M2P | STM32_DMA_CR_MINC | STM32_DMA_CR_PSIZE_BYTE
- | STM32_DMA_CR_MSIZE_BYTE | STM32_DMA_CR_CIRC | STM32_DMA_CR_PL(2));
- // DMA stream 3, triggered by pwm update event. output high at beginning of signal
- dmaStreamAllocate(STM32_DMA1_STREAM3, 10, NULL, NULL);
- dmaStreamSetPeripheral(STM32_DMA1_STREAM3, &(sPort->BSRR.H.set));
- dmaStreamSetMemory0(STM32_DMA1_STREAM3, dma_source);
- dmaStreamSetTransactionSize(STM32_DMA1_STREAM3, 1);
- dmaStreamSetMode(
- STM32_DMA1_STREAM3, STM32_DMA_CR_TEIE |
- STM32_DMA_CR_DIR_M2P | STM32_DMA_CR_PSIZE_BYTE | STM32_DMA_CR_MSIZE_BYTE
- | STM32_DMA_CR_CIRC | STM32_DMA_CR_PL(3));
- // DMA stream 6, triggered by channel1 update event. reset output value late to indicate "1" bit to ws2812.
- // always triggers but no affect if dma stream 2 already change output value to 0
- dmaStreamAllocate(STM32_DMA1_STREAM6, 10, NULL, NULL);
- dmaStreamSetPeripheral(STM32_DMA1_STREAM6, &(sPort->BSRR.H.clear));
- dmaStreamSetMemory0(STM32_DMA1_STREAM6, dma_source);
- dmaStreamSetTransactionSize(STM32_DMA1_STREAM6, 1);
- dmaStreamSetMode(
- STM32_DMA1_STREAM6,
- STM32_DMA_CR_DIR_M2P | STM32_DMA_CR_PSIZE_BYTE | STM32_DMA_CR_MSIZE_BYTE
- | STM32_DMA_CR_CIRC | STM32_DMA_CR_PL(3));
- pwmStart(&PWMD2, &pwmc2);
- pwmStart(&PWMD3, &pwmc3);
- // set pwm3 as slave, triggerd by pwm2 oc1 event. disables pwmd2 for synchronization.
- PWMD3.tim->SMCR |= TIM_SMCR_SMS_0 | TIM_SMCR_SMS_2 | TIM_SMCR_TS_0;
- PWMD2.tim->CR1 &= ~TIM_CR1_CEN;
- // set pwm values.
- // 28 (duty in ticks) / 90 (period in ticks) * 1.25uS (period in S) = 0.39 uS
- pwmEnableChannel(&PWMD3, 2, 28);
- // 58 (duty in ticks) / 90 (period in ticks) * 1.25uS (period in S) = 0.806 uS
- pwmEnableChannel(&PWMD3, 0, 58);
- // active during transfer of 90 cycles * sLeds * 24 bytes * 1/90 multiplier
- pwmEnableChannel(&PWMD2, 0, 90 * sLeds * 24 / 90);
- // stop and reset counters for synchronization
- PWMD2.tim->CNT = 0;
- // Slave (TIM3) needs to "update" immediately after master (TIM2) start in order to start in sync.
- // this initial sync is crucial for the stability of the run
- PWMD3.tim->CNT = 89;
- PWMD3.tim->DIER |= TIM_DIER_CC3DE | TIM_DIER_CC1DE | TIM_DIER_UDE;
- dmaStreamEnable(STM32_DMA1_STREAM3);
- dmaStreamEnable(STM32_DMA1_STREAM6);
- dmaStreamEnable(STM32_DMA1_STREAM2);
- // all systems go! both timers and all channels are configured to resonate
- // in complete sync without any need for CPU cycles (only DMA and timers)
- // start pwm2 for system to start resonating
- PWMD2.tim->CR1 |= TIM_CR1_CEN;
- }
- void ledDriverWaitCycle(void){
- while (PWMD2.tim->CNT < 90 * sLeds * 24 / 90){chThdSleepMicroseconds(1);};
- }
- void testPatternFB(uint8_t *fb){
- int i;
- Color tmpC = {rand()%256, rand()%256, rand()%256};
- for (i=0;i<sLeds;i++){
- setColorRGB(tmpC,fb+24*i, sMask);
- }
- }
- void ws2812_setleds(LED_TYPE *ledarray, uint16_t number_of_leds) {
- // uint8_t i = 0;
- // while (i < number_of_leds) {
- // ws2812_write_led(i, ledarray[i].r, ledarray[i].g, ledarray[i].b);
- // i++;
- // }
- }
- void ws2812_setleds_rgbw(LED_TYPE *ledarray, uint16_t number_of_leds) {
- }
|