lib: port WS2812B to STM32F4

This commit is contained in:
King Kévin 2022-07-14 14:59:24 +02:00
parent d5e63eefd2
commit 68def6b234
2 changed files with 38 additions and 26 deletions

View File

@ -2,7 +2,7 @@
* @file
* @author King Kévin <kingkevin@cuvoodoo.info>
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
* @date 2016-2020
* @date 2016-2022
* @note peripherals used: SPI @ref led_ws2812b_spi, DMA
*/
@ -26,9 +26,16 @@
* @{
*/
/** SPI peripheral
* @note SPI2 is 5V tolerant and can be operated in open-drain mode will 1 kO pull-up resistor to 5V to get 5V signal level
* @note if pin is 5V tolerant, it can be operated in open-drain mode will 1 kOhm pull-up resistor to 5V to get 5V signal level
*/
#define LED_WS2812B_SPI 2 /**< SPI peripheral */
#define LED_WS2812B_SPI 1 /**< SPI peripheral */
#define LED_WS2812B_DR &SPI1_DR /**< SPI Data Register to transmit data */
#define LED_WS2812B_DOUT PB5 /**< SPI MOSI pin used for data output */
#define LED_WS2812B_AF GPIO_AF5 /**< alternate function for SPI pin */
#define LED_WS2812B_RCC_DMA RCC_DMA2 /**< RCC for DMA for SPI peripheral */
#define LED_WS2812B_DMA DMA2 /**< DMA for SPI peripheral */
#define LED_WS2812B_STREAM DMA_STREAM3 /**< DMA stream for SPI TX */
#define LED_WS2812B_CHANNEL DMA_SxCR_CHSEL_3 /**< DMA channel for SPI TX */
/** @} */
/** bit template to encode one byte to be shifted out by SPI to the WS2812B LEDs
@ -40,8 +47,8 @@
*/
#define LED_WS2812B_SPI_TEMPLATE 0x924924
/** data encoded to be shifted out by SPI for the WS2812B, plus the 50us reset (~40 data bits) */
uint8_t led_ws2812b_data[LED_WS2812B_LEDS * 3 * 3 + 40 * 3 / 8 + 1] = {0};
/** data encoded to be shifted out by SPI for the WS2812B, plus the 50us reset */
uint8_t led_ws2812b_data[LED_WS2812B_LEDS * 3 * 3 + 25] = {0};
void led_ws2812b_set_rgb(uint16_t led, uint8_t red, uint8_t green, uint8_t blue)
{
@ -94,13 +101,14 @@ void led_ws2812b_setup(void)
}
// setup SPI to transmit data
rcc_periph_clock_enable(RCC_SPI_MOSI_PORT(LED_WS2812B_SPI)); // enable clock for SPI output peripheral
gpio_set_mode(SPI_MOSI_PORT(LED_WS2812B_SPI), GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, SPI_MOSI_PIN(LED_WS2812B_SPI)); // set MOSI as output (push-pull needs to be shifted to 5V, open-drain consumes up to 5 mA with pull-up resistor of 1 kO)
rcc_periph_clock_enable(RCC_AFIO); // enable clock for SPI alternate function
rcc_periph_clock_enable(GPIO_RCC(LED_WS2812B_DOUT)); // enable clock for SPI output peripheral
gpio_mode_setup(GPIO_PORT(LED_WS2812B_DOUT), GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN(LED_WS2812B_DOUT)); // set SPI MOSI pin to alternate function
gpio_set_output_options(GPIO_PORT(LED_WS2812B_DOUT), GPIO_OTYPE_OD, GPIO_OSPEED_25MHZ, GPIO_PIN(LED_WS2812B_DOUT)); // set SPI MOSI pin output as open-drain
gpio_set_af(GPIO_PORT(LED_WS2812B_DOUT), LED_WS2812B_AF, GPIO_PIN(LED_WS2812B_DOUT)); // set alternate function to SPI
rcc_periph_clock_enable(RCC_SPI(LED_WS2812B_SPI)); // enable clock for SPI peripheral
spi_reset(SPI(LED_WS2812B_SPI)); // clear SPI values to default
spi_set_master_mode(SPI(LED_WS2812B_SPI)); // set SPI as master since we generate the clock
spi_set_baudrate_prescaler(SPI(LED_WS2812B_SPI), SPI_CR1_BR_FPCLK_DIV_16); // set clock to 0.44 us per bit
spi_set_baudrate_prescaler(SPI(LED_WS2812B_SPI), SPI_CR1_BR_FPCLK_DIV_32); // set clock to 0.30 us per bit
spi_set_clock_polarity_1(SPI(LED_WS2812B_SPI)); // clock is high when idle
spi_set_clock_phase_1(SPI(LED_WS2812B_SPI)); // output data on second edge (rising)
spi_set_dff_8bit(SPI(LED_WS2812B_SPI)); // use 8 bits for simpler encoding (but there will be more interrupts)
@ -109,20 +117,25 @@ void led_ws2812b_setup(void)
spi_set_unidirectional_mode(SPI(LED_WS2812B_SPI)); // we only need to transmit data
spi_enable_software_slave_management(SPI(LED_WS2812B_SPI)); // control the slave select in software (so we can start transmission)
spi_set_nss_high(SPI(LED_WS2812B_SPI)); // set NSS high so we can output
spi_enable_tx_dma(SPI(LED_WS2812B_SPI)); // use DMA to provide data stream to be transferred
spi_enable(SPI(LED_WS2812B_SPI)); // enable SPI
// configure DMA to provide the pattern to be shifted out from SPI to the WS2812B LEDs
rcc_periph_clock_enable(RCC_DMA_SPI(LED_WS2812B_SPI)); // enable clock for DMA peripheral
dma_channel_reset(DMA_SPI(LED_WS2812B_SPI), DMA_CHANNEL_SPI_TX(LED_WS2812B_SPI)); // start with fresh channel configuration
dma_set_read_from_memory(DMA_SPI(LED_WS2812B_SPI), DMA_CHANNEL_SPI_TX(LED_WS2812B_SPI)); // set direction from memory to peripheral
dma_set_memory_size(DMA_SPI(LED_WS2812B_SPI), DMA_CHANNEL_SPI_TX(LED_WS2812B_SPI), DMA_CCR_MSIZE_8BIT); // read 8 bits from memory
dma_set_memory_address(DMA_SPI(LED_WS2812B_SPI), DMA_CHANNEL_SPI_TX(LED_WS2812B_SPI), (uint32_t)led_ws2812b_data); // set bit pattern as source address
dma_enable_memory_increment_mode(DMA_SPI(LED_WS2812B_SPI), DMA_CHANNEL_SPI_TX(LED_WS2812B_SPI)); // go through bit pattern
dma_set_number_of_data(DMA_SPI(LED_WS2812B_SPI), DMA_CHANNEL_SPI_TX(LED_WS2812B_SPI), LENGTH(led_ws2812b_data)); // set the size of the data to transmit
dma_set_peripheral_address(DMA_SPI(LED_WS2812B_SPI), DMA_CHANNEL_SPI_TX(LED_WS2812B_SPI), (uint32_t)&SPI_DR(SPI(LED_WS2812B_SPI))); // set SPI as peripheral destination address
dma_set_peripheral_size(DMA_SPI(LED_WS2812B_SPI), DMA_CHANNEL_SPI_TX(LED_WS2812B_SPI), DMA_CCR_PSIZE_8BIT); // write 8 bits to peripheral
dma_set_priority(DMA_SPI(LED_WS2812B_SPI), DMA_CHANNEL_SPI_TX(LED_WS2812B_SPI), DMA_CCR_PL_HIGH); // set priority to high since time is crucial for the peripheral
dma_enable_circular_mode(DMA_SPI(LED_WS2812B_SPI), DMA_CHANNEL_SPI_TX(LED_WS2812B_SPI)); // always send the data
dma_enable_channel(DMA_SPI(LED_WS2812B_SPI), DMA_CHANNEL_SPI_TX(LED_WS2812B_SPI)); // enable DMA channel
spi_enable_tx_dma(SPI(LED_WS2812B_SPI)); // use DMA to provide data stream to be transferred
// configure DMA
rcc_periph_clock_enable(LED_WS2812B_RCC_DMA); // enable clock for DMA peripheral
dma_disable_stream(LED_WS2812B_DMA, LED_WS2812B_STREAM); // disable stream before re-configuring
while (DMA_SCR(LED_WS2812B_DMA, LED_WS2812B_STREAM) & DMA_SxCR_EN); // wait until transfer is finished before we can reconfigure
dma_stream_reset(LED_WS2812B_DMA, LED_WS2812B_STREAM); // use default values
dma_channel_select(LED_WS2812B_DMA, LED_WS2812B_STREAM, LED_WS2812B_CHANNEL); // set the channel for this stream
dma_set_transfer_mode(LED_WS2812B_DMA, LED_WS2812B_STREAM, DMA_SxCR_DIR_MEM_TO_PERIPHERAL); // set transfer from memory to memory
dma_set_memory_size(LED_WS2812B_DMA, LED_WS2812B_STREAM, DMA_SxCR_MSIZE_8BIT); // read 8 bits for transfer
dma_set_memory_address(LED_WS2812B_DMA, LED_WS2812B_STREAM, (uint32_t)led_ws2812b_data); // set memory address to read from
dma_enable_memory_increment_mode(LED_WS2812B_DMA, LED_WS2812B_STREAM); // increment address of memory to read
dma_set_peripheral_address(LED_WS2812B_DMA, LED_WS2812B_STREAM, (uint32_t)LED_WS2812B_DR); // set peripheral address to write data to
dma_set_peripheral_size(LED_WS2812B_DMA, LED_WS2812B_STREAM, DMA_SxCR_PSIZE_8BIT); // we only write the 8 first bit
dma_disable_peripheral_increment_mode(LED_WS2812B_DMA, LED_WS2812B_STREAM); // always write to the SPI data register
dma_set_number_of_data(LED_WS2812B_DMA, LED_WS2812B_STREAM, LENGTH(led_ws2812b_data)); // set transfer size
dma_set_priority(LED_WS2812B_DMA, LED_WS2812B_STREAM, DMA_SxCR_PL_HIGH); // set priority to high since time is crucial for the peripheral
dma_enable_circular_mode(LED_WS2812B_DMA, LED_WS2812B_STREAM); // always send the data
dma_enable_stream(LED_WS2812B_DMA, LED_WS2812B_STREAM); // enable DMA channel
}

View File

@ -2,14 +2,13 @@
* @file
* @author King Kévin <kingkevin@cuvoodoo.info>
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
* @date 2016-2020
* @date 2016-2022
* @note peripherals used: SPI @ref led_ws2812b_spi, DMA
*/
#pragma once
#error not converted for STM32F4
/** number of LEDs on the WS2812B strip */
#define LED_WS2812B_LEDS 48
#define LED_WS2812B_LEDS 3U
/** setup WS2812B LED driver
* @note this starts the continuous transmission