led_ws2812b: replace transmit with continous circlar DMA transfer
This commit is contained in:
parent
607ba7e9b7
commit
cfbe9234b1
|
@ -50,8 +50,8 @@
|
|||
*/
|
||||
#define LED_WS2812B_SPI_TEMPLATE 0x924924
|
||||
|
||||
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 (~40 data bits) */
|
||||
static volatile bool transmit_flag = false; /**< flag set in software when transmission started, clear by interrupt when transmission completed */
|
||||
/** 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};
|
||||
|
||||
void led_ws2812b_set_rgb(uint16_t led, uint8_t red, uint8_t green, uint8_t blue)
|
||||
{
|
||||
|
@ -59,10 +59,6 @@ void led_ws2812b_set_rgb(uint16_t led, uint8_t red, uint8_t green, uint8_t blue)
|
|||
if (led >= LED_WS2812B_LEDS) {
|
||||
return;
|
||||
}
|
||||
// wait for transmission to complete before changing the color
|
||||
while (transmit_flag) {
|
||||
__WFI();
|
||||
}
|
||||
|
||||
const uint8_t colors[] = {green, red, blue}; // color order for the WS2812B
|
||||
const uint8_t pattern_bit[] = {0x02, 0x10, 0x80, 0x04, 0x20, 0x01, 0x08, 0x40}; // which bit to change in the pattern
|
||||
|
@ -77,28 +73,24 @@ void led_ws2812b_set_rgb(uint16_t led, uint8_t red, uint8_t green, uint8_t blue)
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool led_ws2812b_transmit(void)
|
||||
{
|
||||
if (transmit_flag) { // a transmission is already ongoing
|
||||
return false;
|
||||
}
|
||||
transmit_flag = true; // remember transmission started
|
||||
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(SPI(LED_WS2812B_SPI)); // use DMA to provide data stream to be transferred
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void led_ws2812b_setup(void)
|
||||
{
|
||||
// fill buffer with bit pattern
|
||||
for (uint16_t i = 0; i < LED_WS2812B_LEDS * 3; i++) {
|
||||
led_ws2812b_data[i * 3 + 0] = (uint8_t)(LED_WS2812B_SPI_TEMPLATE >> 16);
|
||||
led_ws2812b_data[i * 3 + 1] = (uint8_t)(LED_WS2812B_SPI_TEMPLATE >> 8);
|
||||
led_ws2812b_data[i * 3 + 2] = (uint8_t)(LED_WS2812B_SPI_TEMPLATE >> 0);
|
||||
}
|
||||
// fill remaining with with 0 to encode the reset code
|
||||
for (uint16_t i = LED_WS2812B_LEDS * 3 * 3; i < LENGTH(led_ws2812b_data); i++) {
|
||||
led_ws2812b_data[i] = 0;
|
||||
}
|
||||
|
||||
// setup SPI to transmit data
|
||||
rcc_periph_clock_enable(RCC_SPI_MISO_PORT(LED_WS2812B_SPI)); // enable clock for SPI output peripheral
|
||||
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_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
|
||||
|
@ -118,37 +110,15 @@ void led_ws2812b_setup(void)
|
|||
// 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_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_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
|
||||
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 < LED_WS2812B_LEDS * 3; i++) {
|
||||
led_ws2812b_data[i * 3 + 0] = (uint8_t)(LED_WS2812B_SPI_TEMPLATE >> 16);
|
||||
led_ws2812b_data[i * 3 + 1] = (uint8_t)(LED_WS2812B_SPI_TEMPLATE >> 8);
|
||||
led_ws2812b_data[i * 3 + 2] = (uint8_t)(LED_WS2812B_SPI_TEMPLATE >> 0);
|
||||
}
|
||||
// fill remaining with with 0 to encode the reset code
|
||||
for (uint16_t i = LED_WS2812B_LEDS * 3 * 3; i < LENGTH(led_ws2812b_data); i++) {
|
||||
led_ws2812b_data[i] = 0;
|
||||
}
|
||||
led_ws2812b_transmit(); // set LEDs
|
||||
}
|
||||
|
||||
/** DMA interrupt service routine to stop transmission after it finished */
|
||||
void DMA_ISR_SPI_TX(LED_WS2812B_SPI)(void)
|
||||
{
|
||||
if (dma_get_interrupt_flag(DMA_SPI(LED_WS2812B_SPI), DMA_CHANNEL_SPI_TX(LED_WS2812B_SPI), DMA_TCIF)) { // transfer completed
|
||||
dma_clear_interrupt_flags(DMA_SPI(LED_WS2812B_SPI), DMA_CHANNEL_SPI_TX(LED_WS2812B_SPI), DMA_TCIF); // clear flag
|
||||
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
|
||||
dma_disable_channel(DMA_SPI(LED_WS2812B_SPI), DMA_CHANNEL_SPI_TX(LED_WS2812B_SPI)); // stop using DMA
|
||||
transmit_flag = false; // transmission completed
|
||||
}
|
||||
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
|
||||
}
|
||||
|
|
|
@ -33,7 +33,3 @@ void led_ws2812b_setup(void);
|
|||
* @note transmission needs to be done separately
|
||||
*/
|
||||
void led_ws2812b_set_rgb(uint16_t led, uint8_t red, uint8_t green, uint8_t blue);
|
||||
/** transmit color values to WS2812B LEDs
|
||||
* @return true if transmission started, false if another transmission is already ongoing
|
||||
*/
|
||||
bool led_ws2812b_transmit(void);
|
||||
|
|
Loading…
Reference in New Issue