timer_enable_counter(WS2812B_TIMER);// start timer to generate clock
}
/* setup WS2812b LED controller */
voidws2812b_setup(void)
{
/* setup timer to generate clock of (using PWM): 800kHz*3 */
rcc_periph_clock_enable(WS2812B_CLK_RCC);// enable clock for GPIO peripheral
gpio_set_mode(WS2812B_CLK_PORT,GPIO_MODE_OUTPUT_10_MHZ,GPIO_CNF_OUTPUT_ALTFN_PUSHPULL,WS2812B_CLK_PIN);// set pin a output
rcc_periph_clock_enable(RCC_AFIO);// enable clock for alternate function (PWM)
rcc_periph_clock_enable(WS2812B_TIMER_RCC);// enable clock for timer peripheral
timer_reset(WS2812B_TIMER);// reset timer state
timer_set_mode(WS2812B_TIMER,TIM_CR1_CKD_CK_INT,TIM_CR1_CMS_EDGE,TIM_CR1_DIR_UP);// set timer mode, use undivided timer clock, edge alignment (simple count), and count up
timer_set_prescaler(WS2812B_TIMER,0);// no prescaler to keep most precise timer (72MHz/2^16=1099<800kHz)
timer_set_period(WS2812B_TIMER,rcc_ahb_frequency/800000/3-1);// set the clock frequency to 800kHz*3bit since we need to send 3 bits to output a 800kbps stream
timer_set_oc_value(WS2812B_TIMER,WS2812B_TIMER_OC,rcc_ahb_frequency/800000/3/2);// duty cycle to 50%
timer_set_oc_mode(WS2812B_TIMER,WS2812B_TIMER_OC,TIM_OCM_PWM1);// set timer to generate PWM (used as clock)
timer_enable_oc_output(WS2812B_TIMER,WS2812B_TIMER_OC);// enable output to generate the clock
/* setup SPI to transmit data (we are slave and the clock comes from the above PWM): 3 SPI bits for 1 WS2812b bit */
rcc_periph_clock_enable(WS2812B_SPI_RCC);// enable clock for SPI peripheral
gpio_set_mode(WS2812B_SPI_PORT,GPIO_MODE_INPUT,GPIO_CNF_INPUT_FLOAT,WS2812B_SPI_CLK);// set clock as input
gpio_set_mode(WS2812B_SPI_PORT,GPIO_MODE_OUTPUT_10_MHZ,GPIO_CNF_OUTPUT_ALTFN_PUSHPULL,WS2812B_SPI_DOUT);// set MISO as output
spi_reset(WS2812B_SPI);// clear SPI values to default
spi_set_slave_mode(WS2812B_SPI);// set SPI as slave (since we use the clock as input)
spi_set_bidirectional_transmit_only_mode(WS2812B_SPI);// we won't receive data
spi_set_unidirectional_mode(WS2812B_SPI);// we only need to transmit data
spi_set_dff_8bit(WS2812B_SPI);// use 8 bits for simpler encoding (but there will be more interrupts)
spi_set_clock_polarity_1(WS2812B_SPI);// clock is high when idle
spi_set_clock_phase_1(WS2812B_SPI);// output data on second edge (rising)
spi_send_msb_first(WS2812B_SPI);// send least significant bit first
spi_enable_software_slave_management(WS2812B_SPI);// control the slave select in software (since there is no master)
spi_set_nss_low(WS2812B_SPI);// set NSS low so we can output
spi_enable(WS2812B_SPI);// enable SPI
// do not disable SPI or set NSS high since it will put MISO high, breaking the beginning of the next transmission
/* configure DMA to provide the pattern to be shifted out from SPI to the WS2812b LEDs */
rcc_periph_clock_enable(WS2812B_DMA_RCC);// enable clock for DMA peripheral
dma_channel_reset(WS2812B_DMA,WS2812B_DMA_CH);// start with fresh channel configuration
dma_set_memory_address(WS2812B_DMA,WS2812B_DMA_CH,(uint32_t)ws2812b_data);// set bit pattern as source address
dma_set_peripheral_address(WS2812B_DMA,WS2812B_DMA_CH,(uint32_t)&WS2812B_SPI_DR);// set SPI as peripheral destination address
dma_set_read_from_memory(WS2812B_DMA,WS2812B_DMA_CH);// set direction from memory to peripheral
dma_enable_memory_increment_mode(WS2812B_DMA,WS2812B_DMA_CH);// go through bit pattern
dma_set_memory_size(WS2812B_DMA,WS2812B_DMA_CH,DMA_CCR_MSIZE_8BIT);// read 8 bits from memory
dma_set_peripheral_size(WS2812B_DMA,WS2812B_DMA_CH,DMA_CCR_PSIZE_8BIT);// write 8 bits to peripheral
dma_set_priority(WS2812B_DMA,WS2812B_DMA_CH,DMA_CCR_PL_HIGH);// set priority to high since time is crucial for the peripheral
nvic_enable_irq(WS2812B_DMA_IRQ);// enable interrupts for this DMA channel
// reset color
for(uint16_tled=0;led<WS2812B_LEDS;led++){
ws2812b_set_rgb(led,0x00,0x00,0x00);// switch off (set to black)
}
ws2812b_transmit();// set LEDs
}
/* data transmission finished */
voidWS2812B_DMA_ISR(void)
{
if(dma_get_interrupt_flag(WS2812B_DMA,WS2812B_DMA_CH,DMA_TCIF)){// transfer completed
dma_clear_interrupt_flags(WS2812B_DMA,WS2812B_DMA_CH,DMA_TCIF);// clear flag
dma_disable_transfer_complete_interrupt(WS2812B_DMA,WS2812B_DMA_CH);// stop warning transfer completed
spi_disable_tx_dma(WS2812B_SPI);// stop SPI asking for more data
while(SPI_SR(WS2812B_SPI)&SPI_SR_BSY);// wait for data to be shifted out