/* This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /** @brief library to drive a WS2812b LED chain (code) * @file led_ws2812b.c * @author King Kévin * @date 2016 * @note peripherals used: SPI @ref led_ws2812b_spi, timer @ref led_ws2812b_timer, DMA @ref led_ws2812b_dma * @todo improve ws2812b_set_rgb speed */ /* standard libraries */ #include // standard integer types #include // general utilities /* STM32 (including CM3) libraries */ #include // real-time control clock library #include // general purpose input output library #include // SPI library #include // timer library #include // DMA library #include // interrupt handler #include // Cortex M3 utilities #include "led_ws2812b.h" // LED WS2812b library API #include "global.h" // common methods /** peripheral configuration */ /** @defgroup led_ws2812b_spi SPI peripheral used to control the WS2812b LEDs * @{ */ #define WS2812B_SPI SPI1 /**< SPI peripheral */ #define WS2812B_SPI_DR SPI1_DR /**< SPI data register for the DMA */ #define WS2812B_SPI_RCC RCC_SPI1 /**< SPI peripheral clock */ #define WS2812B_SPI_PORT GPIOA /**< SPI port */ #define WS2812B_SPI_CLK GPIO_SPI1_SCK /**< SPI clock pin (PA5), connect to PWM output */ #define WS2812B_SPI_DOUT GPIO_SPI1_MISO /**< SPI data pin (PA6), connect to WS2812b DIN */ /** @} */ /** @defgroup led_ws2812b_timer timer peripheral used to generate SPI clock * @{ */ #define WS2812B_TIMER TIM3 /**< timer peripheral */ #define WS2812B_TIMER_RCC RCC_TIM3 /**< timer peripheral clock */ #define WS2812B_TIMER_OC TIM_OC3 /**< timer output compare used to set PWM frequency */ #define WS2812B_CLK_RCC RCC_GPIOB /**< timer port peripheral clock */ #define WS2812B_CLK_PORT GPIOB /**< timer port */ #define WS2812B_CLK_PIN GPIO_TIM3_CH3 /**< timer pin to output PWM (PB0), connect to SPI clock input */ /** @} */ /** @defgroup led_ws2812b_dma DMA peripheral used to send the data * @{ */ #define WS2812B_DMA DMA1 /**< DMA peripheral to put data for WS2812b LED in SPI queue (only DMA1 supports SPI1_TX interrupt) */ #define WS2812B_DMA_RCC RCC_DMA1 /**< DMA peripheral clock */ #define WS2812B_DMA_CH DMA_CHANNEL3 /**< DMA channel (only DMA1 channel 3 supports SPI1_TX interrupt) */ #define WS2812B_DMA_IRQ NVIC_DMA1_CHANNEL3_IRQ /**< DMA channel interrupt signal */ #define WS2812B_DMA_ISR dma1_channel3_isr /**< DMA channel interrupt service routine */ /** @} */ /** @brief bit template to encode one byte to be shifted out by SPI to the WS2812b LEDs * @details For each WS2812b bit which needs to be transfered we require to transfer 3 SPI bits. * The first SPI bit is the high start of the WS2812b bit frame. * The second SPI bit determines if the WS2812b bit is a 0 or 1. * The third SPI bit is the last part of the WS2812b bit frame, which is always low. * The binary pattern is 0b100100100100100100100100 */ #define WS2812B_SPI_TEMPLATE 0x924924 uint8_t ws2812b_data[WS2812B_LEDS*3*3+40*3/8+1] = {0}; /**< data encoded to be shifted out by SPI for the WS2812b, plus the 50us reset (~40 data bits) */ static volatile bool transmit_flag = false; /**< flag set in software when transmission started, clear by interrupt when transmission completed */ void ws2812b_set_rgb(uint16_t led, uint8_t red, uint8_t green, uint8_t blue) { // verify the led exists if (led>=WS2812B_LEDS) { return; } // wait for transmission to complete before changing the color while (transmit_flag) { __WFI(); } const uint8_t colors[] = {green, red, blue}; // color order for the WS2812b const uint8_t pattern_bit[] = {0x02, 0x10, 0x80, 0x04, 0x20, 0x01, 0x08, 0x40}; // which bit to change in the pattern const uint8_t pattern_byte[] = {2,2,2,1,1,0,0,0}; // in which byte in the pattern to write the pattern bit for (uint8_t color=0; color>16); ws2812b_data[i*3+1] = (uint8_t)(WS2812B_SPI_TEMPLATE>>8); ws2812b_data[i*3+2] = (uint8_t)(WS2812B_SPI_TEMPLATE>>0); } // fill remaining with with 0 to encode the reset code for (uint16_t i=WS2812B_LEDS*3*3; i