diff --git a/lib/led_ws2812b.c b/lib/led_ws2812b.c index 4e25e64..6774240 100644 --- a/lib/led_ws2812b.c +++ b/lib/led_ws2812b.c @@ -16,7 +16,7 @@ * @file * @author King Kévin * @date 2016-2020 - * @note peripherals used: SPI @ref led_ws2812b_spi, timer @ref led_ws2812b_timer, DMA (for SPI MISO) + * @note peripherals used: SPI @ref led_ws2812b_spi, DMA */ /* standard libraries */ @@ -28,7 +28,6 @@ #include // real-time control clock library #include // general purpose input output library #include // SPI library -#include // timer library #include // DMA library #include // interrupt handler @@ -39,19 +38,11 @@ /** @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 */ +#define LED_WS2812B_SPI 2 /**< SPI peripheral */ /** @} */ /** 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. + * @details For each WS2812B bit which needs to be transferred 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. @@ -99,47 +90,30 @@ bool led_ws2812b_transmit(void) 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(SPI(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 transferred - 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(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(RCC_TIM(LED_WS2812B_TIMER)); // enable clock for timer peripheral - rcc_periph_reset_pulse(RST_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(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 + // setup SPI to transmit data + rcc_periph_clock_enable(RCC_SPI_MISO_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_PUSHPULL, SPI_MOSI_PIN(LED_WS2812B_SPI)); // set MISO as output (needs to be shifted to 5V) rcc_periph_clock_enable(RCC_AFIO); // enable clock for SPI alternate function 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_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_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_set_dff_8bit(SPI(LED_WS2812B_SPI)); // use 8 bits for simpler encoding (but there will be more interrupts) + spi_send_msb_first(SPI(LED_WS2812B_SPI)); // send most significant bit first + 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_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(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(RCC_DMA_SPI(LED_WS2812B_SPI)); // enable clock for DMA peripheral @@ -174,7 +148,6 @@ void DMA_ISR_SPI_TX(LED_WS2812B_SPI)(void) dma_disable_transfer_complete_interrupt(DMA_SPI(LED_WS2812B_SPI), DMA_CHANNEL_SPI_TX(LED_WS2812B_SPI)); // stop warning transfer completed spi_disable_tx_dma(SPI(LED_WS2812B_SPI)); // stop SPI asking for more data while (SPI_SR(SPI(LED_WS2812B_SPI)) & SPI_SR_BSY); // wait for data to be shifted out - timer_disable_counter(TIM(LED_WS2812B_TIMER)); // stop clock dma_disable_channel(DMA_SPI(LED_WS2812B_SPI), DMA_CHANNEL_SPI_TX(LED_WS2812B_SPI)); // stop using DMA transmit_flag = false; // transmission completed } diff --git a/lib/led_ws2812b.h b/lib/led_ws2812b.h index 9a5901e..e36584f 100644 --- a/lib/led_ws2812b.h +++ b/lib/led_ws2812b.h @@ -16,7 +16,7 @@ * @file * @author King Kévin * @date 2016-2020 - * @note peripherals used: SPI @ref led_ws2812b_spi, timer @ref led_ws2812b_timer, DMA (for SPI MISO) + * @note peripherals used: SPI @ref led_ws2812b_spi, DMA */ #pragma once