From 5f3473257d40f90d2de30a818084b003aa863d85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?King=20K=C3=A9vin?= Date: Mon, 9 Oct 2017 09:36:53 +0200 Subject: [PATCH] lib/led_ws2812b: now use global macros --- lib/led_ws2812b.c | 122 ++++++++++++++++++++++++++-------------------- lib/led_ws2812b.h | 36 +------------- 2 files changed, 71 insertions(+), 87 deletions(-) diff --git a/lib/led_ws2812b.c b/lib/led_ws2812b.c index 2d9c759..6df0e9b 100644 --- a/lib/led_ws2812b.c +++ b/lib/led_ws2812b.c @@ -15,8 +15,8 @@ /** 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 + * @date 2016-2017 + * @note peripherals used: SPI @ref led_ws2812b_spi, timer @ref led_ws2812b_timer, DMA (for SPI MISO) */ /* standard libraries */ @@ -24,17 +24,32 @@ #include // general utilities /* STM32 (including CM3) libraries */ +#include // Cortex M3 utilities #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 LED_WS2812B_SPI 1 /**< SPI peripheral */ +/** @} */ + +/** @defgroup led_ws2812b_timer timer peripheral used to generate SPI clock + * @{ + */ +#define LED_WS2812B_TIMER 3 /**< timer peripheral */ +#define LED_WS2812B_CLK_CH 3 /**< timer channel to output PWM (PB0), connect to SPI clock input */ +#define LED_WS2812B_TIMER_OC TIM_OC3 /**< timer output compare used to set PWM frequency */ +/** @} */ + /** 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. @@ -79,63 +94,64 @@ bool led_ws2812b_transmit(void) return false; } transmit_flag = true; // remember transmission started - dma_set_memory_address(LED_WS2812B_DMA, LED_WS2812B_DMA_CH, (uint32_t)led_ws2812b_data); - dma_set_number_of_data(LED_WS2812B_DMA, LED_WS2812B_DMA_CH, LENGTH(led_ws2812b_data)); // set the size of the data to transmit - dma_enable_transfer_complete_interrupt(LED_WS2812B_DMA, LED_WS2812B_DMA_CH); // warm when transfer is complete to stop transmission - dma_enable_channel(LED_WS2812B_DMA, LED_WS2812B_DMA_CH); // enable DMA channel + dma_set_memory_address(DMA_SPI(LED_WS2812B_SPI), DMA_CHANNEL_SPI_TX(LED_WS2812B_SPI), (uint32_t)led_ws2812b_data); + 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_enable_transfer_complete_interrupt(DMA_SPI(LED_WS2812B_SPI), DMA_CHANNEL_SPI_TX(LED_WS2812B_SPI)); // warm when transfer is complete to stop transmission + dma_enable_channel(DMA_SPI(LED_WS2812B_SPI), DMA_CHANNEL_SPI_TX(LED_WS2812B_SPI)); // enable DMA channel - spi_enable_tx_dma(LED_WS2812B_SPI); // use DMA to provide data stream to be transfered + spi_enable_tx_dma(SPI(LED_WS2812B_SPI)); // use DMA to provide data stream to be transfered - timer_set_counter(LED_WS2812B_TIMER, 0); // reset timer counter fro clean clock - timer_enable_counter(LED_WS2812B_TIMER); // start timer to generate clock + timer_set_counter(TIM(LED_WS2812B_TIMER), 0); // reset timer counter fro clean clock + timer_enable_counter(TIM(LED_WS2812B_TIMER)); // start timer to generate clock return true; } void led_ws2812b_setup(void) { // setup timer to generate clock of (using PWM): 800kHz*3 - rcc_periph_clock_enable(LED_WS2812B_CLK_RCC); // enable clock for GPIO peripheral - gpio_set_mode(LED_WS2812B_CLK_PORT, GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, LED_WS2812B_CLK_PIN); // set pin as output + rcc_periph_clock_enable(RCC_TIM_CH(LED_WS2812B_TIMER, LED_WS2812B_CLK_CH)); // enable clock for GPIO peripheral + gpio_set_mode(TIM_CH_PORT(LED_WS2812B_TIMER, LED_WS2812B_CLK_CH), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, TIM_CH_PIN(LED_WS2812B_TIMER, LED_WS2812B_CLK_CH)); // set pin as output rcc_periph_clock_enable(RCC_AFIO); // enable clock for alternate function (PWM) - rcc_periph_clock_enable(LED_WS2812B_TIMER_RCC); // enable clock for timer peripheral - timer_reset(LED_WS2812B_TIMER); // reset timer state - timer_set_mode(LED_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(LED_WS2812B_TIMER, 0); // no prescaler to keep most precise timer (72MHz/2^16=1099<800kHz) - timer_set_period(LED_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(LED_WS2812B_TIMER, LED_WS2812B_TIMER_OC, rcc_ahb_frequency/800000/3/2); // duty cycle to 50% - timer_set_oc_mode(LED_WS2812B_TIMER, LED_WS2812B_TIMER_OC, TIM_OCM_PWM1); // set timer to generate PWM (used as clock) - timer_enable_oc_output(LED_WS2812B_TIMER, LED_WS2812B_TIMER_OC); // enable output to generate the clock + rcc_periph_clock_enable(RCC_TIM(LED_WS2812B_TIMER)); // enable clock for timer peripheral + timer_reset(TIM(LED_WS2812B_TIMER)); // reset timer state + timer_set_mode(TIM(LED_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(TIM(LED_WS2812B_TIMER), 0); // no prescaler to keep most precise timer (72MHz/2^16=1099<800kHz) + timer_set_period(TIM(LED_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(TIM(LED_WS2812B_TIMER), LED_WS2812B_TIMER_OC, rcc_ahb_frequency/800000/3/2); // duty cycle to 50% + timer_set_oc_mode(TIM(LED_WS2812B_TIMER), LED_WS2812B_TIMER_OC, TIM_OCM_PWM1); // set timer to generate PWM (used as clock) + timer_enable_oc_output(TIM(LED_WS2812B_TIMER), LED_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(LED_WS2812B_SPI_PORT_RCC); // enable clock for SPI IO peripheral - gpio_set_mode(LED_WS2812B_SPI_PORT, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, LED_WS2812B_SPI_CLK); // set clock as input - gpio_set_mode(LED_WS2812B_SPI_PORT, GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, LED_WS2812B_SPI_DOUT); // set MISO as output + rcc_periph_clock_enable(RCC_SPI_SCK_PORT(LED_WS2812B_SPI)); // enable clock for SPI IO peripheral + gpio_set_mode(SPI_SCK_PORT(LED_WS2812B_SPI), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, SPI_SCK_PIN(LED_WS2812B_SPI)); // set clock as input + rcc_periph_clock_enable(RCC_SPI_MISO_PORT(LED_WS2812B_SPI)); // enable clock for SPI IO peripheral + gpio_set_mode(SPI_MISO_PORT(LED_WS2812B_SPI), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, SPI_MISO_PIN(LED_WS2812B_SPI)); // set MISO as output rcc_periph_clock_enable(RCC_AFIO); // enable clock for SPI alternate function - rcc_periph_clock_enable(LED_WS2812B_SPI_RCC); // enable clock for SPI peripheral - spi_reset(LED_WS2812B_SPI); // clear SPI values to default - spi_set_slave_mode(LED_WS2812B_SPI); // set SPI as slave (since we use the clock as input) - spi_set_bidirectional_transmit_only_mode(LED_WS2812B_SPI); // we won't receive data - spi_set_unidirectional_mode(LED_WS2812B_SPI); // we only need to transmit data - spi_set_dff_8bit(LED_WS2812B_SPI); // use 8 bits for simpler encoding (but there will be more interrupts) - spi_set_clock_polarity_1(LED_WS2812B_SPI); // clock is high when idle - spi_set_clock_phase_1(LED_WS2812B_SPI); // output data on second edge (rising) - spi_send_msb_first(LED_WS2812B_SPI); // send least significant bit first - spi_enable_software_slave_management(LED_WS2812B_SPI); // control the slave select in software (since there is no master) - spi_set_nss_low(LED_WS2812B_SPI); // set NSS low so we can output - spi_enable(LED_WS2812B_SPI); // enable 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_slave_mode(SPI(LED_WS2812B_SPI)); // set SPI as slave (since we use the clock as input) + spi_set_bidirectional_transmit_only_mode(SPI(LED_WS2812B_SPI)); // we won't receive data + spi_set_unidirectional_mode(SPI(LED_WS2812B_SPI)); // we only need to transmit data + spi_set_dff_8bit(SPI(LED_WS2812B_SPI)); // use 8 bits for simpler encoding (but there will be more interrupts) + 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_send_msb_first(SPI(LED_WS2812B_SPI)); // send least significant bit first + spi_enable_software_slave_management(SPI(LED_WS2812B_SPI)); // control the slave select in software (since there is no master) + spi_set_nss_low(SPI(LED_WS2812B_SPI)); // set NSS low so we can output + spi_enable(SPI(LED_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(LED_WS2812B_DMA_RCC); // enable clock for DMA peripheral - dma_channel_reset(LED_WS2812B_DMA, LED_WS2812B_DMA_CH); // start with fresh channel configuration - dma_set_memory_address(LED_WS2812B_DMA, LED_WS2812B_DMA_CH, (uint32_t)led_ws2812b_data); // set bit pattern as source address - dma_set_peripheral_address(LED_WS2812B_DMA, LED_WS2812B_DMA_CH, (uint32_t)&LED_WS2812B_SPI_DR); // set SPI as peripheral destination address - dma_set_read_from_memory(LED_WS2812B_DMA, LED_WS2812B_DMA_CH); // set direction from memory to peripheral - dma_enable_memory_increment_mode(LED_WS2812B_DMA, LED_WS2812B_DMA_CH); // go through bit pattern - dma_set_memory_size(LED_WS2812B_DMA, LED_WS2812B_DMA_CH, DMA_CCR_MSIZE_8BIT); // read 8 bits from memory - dma_set_peripheral_size(LED_WS2812B_DMA, LED_WS2812B_DMA_CH, DMA_CCR_PSIZE_8BIT); // write 8 bits to peripheral - dma_set_priority(LED_WS2812B_DMA, LED_WS2812B_DMA_CH, DMA_CCR_PL_HIGH); // set priority to high since time is crucial for the peripheral - nvic_enable_irq(LED_WS2812B_DMA_IRQ); // enable interrupts for this DMA channel + 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_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_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_read_from_memory(DMA_SPI(LED_WS2812B_SPI), DMA_CHANNEL_SPI_TX(LED_WS2812B_SPI)); // set direction from memory to peripheral + dma_enable_memory_increment_mode(DMA_SPI(LED_WS2812B_SPI), DMA_CHANNEL_SPI_TX(LED_WS2812B_SPI)); // go through bit pattern + 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_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 + nvic_enable_irq(DMA_IRQ_SPI_TX(LED_WS2812B_SPI)); // enable interrupts for this DMA channel // fill buffer with bit pattern for (uint16_t i=0; i - * @date 2016 - * @note peripherals used: SPI @ref led_ws2812b_spi, timer @ref led_ws2812b_timer, DMA @ref led_ws2812b_dma + * @date 2016-2017 + * @note peripherals used: SPI @ref led_ws2812b_spi, timer @ref led_ws2812b_timer, DMA (for SPI MISO) */ #pragma once /** number of LEDs on the WS2812B strip */ #define LED_WS2812B_LEDS 48 -/** peripheral configuration */ -/** @defgroup led_ws2812b_spi SPI peripheral used to control the WS2812B LEDs - * @{ - */ -#define LED_WS2812B_SPI SPI1 /**< SPI peripheral */ -#define LED_WS2812B_SPI_DR SPI1_DR /**< SPI data register for the DMA */ -#define LED_WS2812B_SPI_RCC RCC_SPI1 /**< SPI peripheral clock */ -#define LED_WS2812B_SPI_PORT_RCC RCC_GPIOA /**< SPI I/O peripheral clock */ -#define LED_WS2812B_SPI_PORT GPIOA /**< SPI port */ -#define LED_WS2812B_SPI_CLK GPIO_SPI1_SCK /**< SPI clock pin (PA5), connect to PWM output */ -#define LED_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 LED_WS2812B_TIMER TIM3 /**< timer peripheral */ -#define LED_WS2812B_TIMER_RCC RCC_TIM3 /**< timer peripheral clock */ -#define LED_WS2812B_TIMER_OC TIM_OC3 /**< timer output compare used to set PWM frequency */ -#define LED_WS2812B_CLK_RCC RCC_GPIOB /**< timer port peripheral clock */ -#define LED_WS2812B_CLK_PORT GPIOB /**< timer port */ -#define LED_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 LED_WS2812B_DMA DMA1 /**< DMA peripheral to put data for WS2812B LED in SPI queue (only DMA1 supports SPI1_TX interrupt) */ -#define LED_WS2812B_DMA_RCC RCC_DMA1 /**< DMA peripheral clock */ -#define LED_WS2812B_DMA_CH DMA_CHANNEL3 /**< DMA channel (only DMA1 channel 3 supports SPI1_TX interrupt) */ -#define LED_WS2812B_DMA_IRQ NVIC_DMA1_CHANNEL3_IRQ /**< DMA channel interrupt signal */ -#define LED_WS2812B_DMA_ISR dma1_channel3_isr /**< DMA channel interrupt service routine */ -/** @} */ - /** setup WS2812B LED driver */ void led_ws2812b_setup(void); /** set color of a single LED