led_ws2812b: replace timer with SPI being master
This commit is contained in:
parent
7030480482
commit
607ba7e9b7
|
@ -16,7 +16,7 @@
|
|||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @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 <libopencm3/stm32/rcc.h> // real-time control clock library
|
||||
#include <libopencm3/stm32/gpio.h> // general purpose input output library
|
||||
#include <libopencm3/stm32/spi.h> // SPI library
|
||||
#include <libopencm3/stm32/timer.h> // timer library
|
||||
#include <libopencm3/stm32/dma.h> // DMA library
|
||||
#include <libopencm3/cm3/nvic.h> // 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
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @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
|
||||
|
||||
|
|
Loading…
Reference in New Issue