From 68def6b2343af9e9713e177d3165b362a86358b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?King=20K=C3=A9vin?= Date: Thu, 14 Jul 2022 14:59:24 +0200 Subject: [PATCH] lib: port WS2812B to STM32F4 --- lib/led_ws2812b.c | 59 +++++++++++++++++++++++++++++------------------ lib/led_ws2812b.h | 5 ++-- 2 files changed, 38 insertions(+), 26 deletions(-) diff --git a/lib/led_ws2812b.c b/lib/led_ws2812b.c index f80e7cf..5d9c5f1 100644 --- a/lib/led_ws2812b.c +++ b/lib/led_ws2812b.c @@ -2,7 +2,7 @@ * @file * @author King Kévin * @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 } diff --git a/lib/led_ws2812b.h b/lib/led_ws2812b.h index ed1f726..37add1d 100644 --- a/lib/led_ws2812b.h +++ b/lib/led_ws2812b.h @@ -2,14 +2,13 @@ * @file * @author King Kévin * @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