make API more consistent

This commit is contained in:
King Kévin 2016-05-01 15:03:41 +02:00
parent b60135d54a
commit 9594304c46
3 changed files with 106 additions and 137 deletions

View File

@ -13,10 +13,10 @@
* *
*/ */
/** @brief library to drive a WS2812b LED chain (code) /** @brief library to drive a WS2812b LED chain (code)
* @file led_ws2812b.c * @file led_led_ws2812b.c
* @author King Kévin <kingkevin@cuvoodoo.info> * @author King Kévin <kingkevin@cuvoodoo.info>
* @date 2016 * @date 2016
* @note peripherals used: SPI @ref led_ws2812b_spi, timer @ref led_ws2812b_timer, DMA @ref led_ws2812b_dma * @note peripherals used: SPI @ref led_led_ws2812b_spi, timer @ref led_led_ws2812b_timer, DMA @ref led_led_ws2812b_dma
*/ */
/* standard libraries */ /* standard libraries */
@ -35,37 +35,6 @@
#include "led_ws2812b.h" // LED WS2812b library API #include "led_ws2812b.h" // LED WS2812b library API
#include "global.h" // common methods #include "global.h" // common methods
/** peripheral configuration */
/** @defgroup led_ws2812b_spi SPI peripheral used to control the WS2812b LEDs
* @{
*/
#define WS2812B_SPI SPI1 /**< SPI peripheral */
#define WS2812B_SPI_DR SPI1_DR /**< SPI data register for the DMA */
#define WS2812B_SPI_RCC RCC_SPI1 /**< SPI peripheral clock */
#define WS2812B_SPI_PORT GPIOA /**< SPI port */
#define WS2812B_SPI_CLK GPIO_SPI1_SCK /**< SPI clock pin (PA5), connect to PWM output */
#define 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 WS2812B_TIMER TIM3 /**< timer peripheral */
#define WS2812B_TIMER_RCC RCC_TIM3 /**< timer peripheral clock */
#define WS2812B_TIMER_OC TIM_OC3 /**< timer output compare used to set PWM frequency */
#define WS2812B_CLK_RCC RCC_GPIOB /**< timer port peripheral clock */
#define WS2812B_CLK_PORT GPIOB /**< timer port */
#define 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 WS2812B_DMA DMA1 /**< DMA peripheral to put data for WS2812b LED in SPI queue (only DMA1 supports SPI1_TX interrupt) */
#define WS2812B_DMA_RCC RCC_DMA1 /**< DMA peripheral clock */
#define WS2812B_DMA_CH DMA_CHANNEL3 /**< DMA channel (only DMA1 channel 3 supports SPI1_TX interrupt) */
#define WS2812B_DMA_IRQ NVIC_DMA1_CHANNEL3_IRQ /**< DMA channel interrupt signal */
#define WS2812B_DMA_ISR dma1_channel3_isr /**< DMA channel interrupt service routine */
/** @} */
/** @brief bit template to encode one byte to be shifted out by SPI to the WS2812b LEDs /** @brief 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 transfered we require to transfer 3 SPI bits.
* The first SPI bit is the high start of the WS2812b bit frame. * The first SPI bit is the high start of the WS2812b bit frame.
@ -73,15 +42,15 @@
* The third SPI bit is the last part of the WS2812b bit frame, which is always low. * The third SPI bit is the last part of the WS2812b bit frame, which is always low.
* The binary pattern is 0b100100100100100100100100 * The binary pattern is 0b100100100100100100100100
*/ */
#define WS2812B_SPI_TEMPLATE 0x924924 #define LED_WS2812B_SPI_TEMPLATE 0x924924
uint8_t ws2812b_data[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) */ 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 */ static volatile bool transmit_flag = false; /**< flag set in software when transmission started, clear by interrupt when transmission completed */
void ws2812b_set_rgb(uint16_t led, uint8_t red, uint8_t green, uint8_t blue) void led_ws2812b_set_rgb(uint16_t led, uint8_t red, uint8_t green, uint8_t blue)
{ {
// verify the led exists // verify the led exists
if (led>=WS2812B_LEDS) { if (led>=LED_WS2812B_LEDS) {
return; return;
} }
// wait for transmission to complete before changing the color // wait for transmission to complete before changing the color
@ -96,99 +65,99 @@ void ws2812b_set_rgb(uint16_t led, uint8_t red, uint8_t green, uint8_t blue)
// fill the middle bit (fixed is faster than calculating it) // fill the middle bit (fixed is faster than calculating it)
for (uint8_t bit=0; bit<8; bit++) { // bit from the color to set/clear for (uint8_t bit=0; bit<8; bit++) { // bit from the color to set/clear
if (colors[color]&(1<<bit)) { // setting bit if (colors[color]&(1<<bit)) { // setting bit
ws2812b_data[led*3*3+color*3+pattern_byte[bit]] |= pattern_bit[bit]; // setting bit is pattern led_ws2812b_data[led*3*3+color*3+pattern_byte[bit]] |= pattern_bit[bit]; // setting bit is pattern
} else { // clear bit } else { // clear bit
ws2812b_data[led*3*3+color*3+pattern_byte[bit]] &= ~pattern_bit[bit]; // clearing bit is pattern led_ws2812b_data[led*3*3+color*3+pattern_byte[bit]] &= ~pattern_bit[bit]; // clearing bit is pattern
} }
} }
} }
} }
bool ws2812b_transmit(void) bool led_ws2812b_transmit(void)
{ {
if (transmit_flag) { // a transmission is already ongoing if (transmit_flag) { // a transmission is already ongoing
return false; return false;
} }
transmit_flag = true; // remember transmission started transmit_flag = true; // remember transmission started
dma_set_memory_address(WS2812B_DMA, WS2812B_DMA_CH, (uint32_t)ws2812b_data); dma_set_memory_address(LED_WS2812B_DMA, LED_WS2812B_DMA_CH, (uint32_t)led_ws2812b_data);
dma_set_number_of_data(WS2812B_DMA, WS2812B_DMA_CH, LENGTH(ws2812b_data)); // set the size of the data to transmit 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(WS2812B_DMA, WS2812B_DMA_CH); // warm when transfer is complete to stop transmission dma_enable_transfer_complete_interrupt(LED_WS2812B_DMA, LED_WS2812B_DMA_CH); // warm when transfer is complete to stop transmission
dma_enable_channel(WS2812B_DMA, WS2812B_DMA_CH); // enable DMA channel dma_enable_channel(LED_WS2812B_DMA, LED_WS2812B_DMA_CH); // enable DMA channel
spi_enable_tx_dma(WS2812B_SPI); // use DMA to provide data stream to be transfered spi_enable_tx_dma(LED_WS2812B_SPI); // use DMA to provide data stream to be transfered
timer_set_counter(WS2812B_TIMER, 0); // reset timer counter fro clean clock timer_set_counter(LED_WS2812B_TIMER, 0); // reset timer counter fro clean clock
timer_enable_counter(WS2812B_TIMER); // start timer to generate clock timer_enable_counter(LED_WS2812B_TIMER); // start timer to generate clock
return true; return true;
} }
void ws2812b_setup(void) void led_ws2812b_setup(void)
{ {
/* setup timer to generate clock of (using PWM): 800kHz*3 */ /* setup timer to generate clock of (using PWM): 800kHz*3 */
rcc_periph_clock_enable(WS2812B_CLK_RCC); // enable clock for GPIO peripheral rcc_periph_clock_enable(LED_WS2812B_CLK_RCC); // enable clock for GPIO peripheral
gpio_set_mode(WS2812B_CLK_PORT, GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, WS2812B_CLK_PIN); // set pin as output 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_AFIO); // enable clock for alternate function (PWM) rcc_periph_clock_enable(RCC_AFIO); // enable clock for alternate function (PWM)
rcc_periph_clock_enable(WS2812B_TIMER_RCC); // enable clock for timer peripheral rcc_periph_clock_enable(LED_WS2812B_TIMER_RCC); // enable clock for timer peripheral
timer_reset(WS2812B_TIMER); // reset timer state timer_reset(LED_WS2812B_TIMER); // reset timer state
timer_set_mode(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_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(WS2812B_TIMER, 0); // no prescaler to keep most precise timer (72MHz/2^16=1099<800kHz) timer_set_prescaler(LED_WS2812B_TIMER, 0); // no prescaler to keep most precise timer (72MHz/2^16=1099<800kHz)
timer_set_period(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_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(WS2812B_TIMER, WS2812B_TIMER_OC, rcc_ahb_frequency/800000/3/2); // duty cycle to 50% 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(WS2812B_TIMER, WS2812B_TIMER_OC, TIM_OCM_PWM1); // set timer to generate PWM (used as clock) 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(WS2812B_TIMER, WS2812B_TIMER_OC); // enable output to generate the clock timer_enable_oc_output(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 */ /* 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(WS2812B_SPI_RCC); // enable clock for SPI peripheral rcc_periph_clock_enable(LED_WS2812B_SPI_RCC); // enable clock for SPI peripheral
gpio_set_mode(WS2812B_SPI_PORT, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, WS2812B_SPI_CLK); // set clock as input 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(WS2812B_SPI_PORT, GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, WS2812B_SPI_DOUT); // set MISO as output 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
spi_reset(WS2812B_SPI); // clear SPI values to default spi_reset(LED_WS2812B_SPI); // clear SPI values to default
spi_set_slave_mode(WS2812B_SPI); // set SPI as slave (since we use the clock as input) spi_set_slave_mode(LED_WS2812B_SPI); // set SPI as slave (since we use the clock as input)
spi_set_bidirectional_transmit_only_mode(WS2812B_SPI); // we won't receive data spi_set_bidirectional_transmit_only_mode(LED_WS2812B_SPI); // we won't receive data
spi_set_unidirectional_mode(WS2812B_SPI); // we only need to transmit data spi_set_unidirectional_mode(LED_WS2812B_SPI); // we only need to transmit data
spi_set_dff_8bit(WS2812B_SPI); // use 8 bits for simpler encoding (but there will be more interrupts) spi_set_dff_8bit(LED_WS2812B_SPI); // use 8 bits for simpler encoding (but there will be more interrupts)
spi_set_clock_polarity_1(WS2812B_SPI); // clock is high when idle spi_set_clock_polarity_1(LED_WS2812B_SPI); // clock is high when idle
spi_set_clock_phase_1(WS2812B_SPI); // output data on second edge (rising) spi_set_clock_phase_1(LED_WS2812B_SPI); // output data on second edge (rising)
spi_send_msb_first(WS2812B_SPI); // send least significant bit first spi_send_msb_first(LED_WS2812B_SPI); // send least significant bit first
spi_enable_software_slave_management(WS2812B_SPI); // control the slave select in software (since there is no master) spi_enable_software_slave_management(LED_WS2812B_SPI); // control the slave select in software (since there is no master)
spi_set_nss_low(WS2812B_SPI); // set NSS low so we can output spi_set_nss_low(LED_WS2812B_SPI); // set NSS low so we can output
spi_enable(WS2812B_SPI); // enable SPI spi_enable(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 // 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 */ /* configure DMA to provide the pattern to be shifted out from SPI to the WS2812b LEDs */
rcc_periph_clock_enable(WS2812B_DMA_RCC); // enable clock for DMA peripheral rcc_periph_clock_enable(LED_WS2812B_DMA_RCC); // enable clock for DMA peripheral
dma_channel_reset(WS2812B_DMA, WS2812B_DMA_CH); // start with fresh channel configuration dma_channel_reset(LED_WS2812B_DMA, LED_WS2812B_DMA_CH); // start with fresh channel configuration
dma_set_memory_address(WS2812B_DMA, WS2812B_DMA_CH, (uint32_t)ws2812b_data); // set bit pattern as source address 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(WS2812B_DMA, WS2812B_DMA_CH, (uint32_t)&WS2812B_SPI_DR); // set SPI as peripheral destination 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(WS2812B_DMA, WS2812B_DMA_CH); // set direction from memory to peripheral dma_set_read_from_memory(LED_WS2812B_DMA, LED_WS2812B_DMA_CH); // set direction from memory to peripheral
dma_enable_memory_increment_mode(WS2812B_DMA, WS2812B_DMA_CH); // go through bit pattern dma_enable_memory_increment_mode(LED_WS2812B_DMA, LED_WS2812B_DMA_CH); // go through bit pattern
dma_set_memory_size(WS2812B_DMA, WS2812B_DMA_CH, DMA_CCR_MSIZE_8BIT); // read 8 bits from memory dma_set_memory_size(LED_WS2812B_DMA, LED_WS2812B_DMA_CH, DMA_CCR_MSIZE_8BIT); // read 8 bits from memory
dma_set_peripheral_size(WS2812B_DMA, WS2812B_DMA_CH, DMA_CCR_PSIZE_8BIT); // write 8 bits to peripheral dma_set_peripheral_size(LED_WS2812B_DMA, LED_WS2812B_DMA_CH, DMA_CCR_PSIZE_8BIT); // write 8 bits to peripheral
dma_set_priority(WS2812B_DMA, WS2812B_DMA_CH, DMA_CCR_PL_HIGH); // set priority to high since time is crucial for the 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(WS2812B_DMA_IRQ); // enable interrupts for this DMA channel nvic_enable_irq(LED_WS2812B_DMA_IRQ); // enable interrupts for this DMA channel
// fill buffer with bit pattern // fill buffer with bit pattern
for (uint16_t i=0; i<WS2812B_LEDS*3; i++) { for (uint16_t i=0; i<LED_WS2812B_LEDS*3; i++) {
ws2812b_data[i*3+0] = (uint8_t)(WS2812B_SPI_TEMPLATE>>16); led_ws2812b_data[i*3+0] = (uint8_t)(LED_WS2812B_SPI_TEMPLATE>>16);
ws2812b_data[i*3+1] = (uint8_t)(WS2812B_SPI_TEMPLATE>>8); led_ws2812b_data[i*3+1] = (uint8_t)(LED_WS2812B_SPI_TEMPLATE>>8);
ws2812b_data[i*3+2] = (uint8_t)(WS2812B_SPI_TEMPLATE>>0); led_ws2812b_data[i*3+2] = (uint8_t)(LED_WS2812B_SPI_TEMPLATE>>0);
} }
// fill remaining with with 0 to encode the reset code // fill remaining with with 0 to encode the reset code
for (uint16_t i=WS2812B_LEDS*3*3; i<LENGTH(ws2812b_data); i++) { for (uint16_t i=LED_WS2812B_LEDS*3*3; i<LENGTH(led_ws2812b_data); i++) {
ws2812b_data[i] = 0; led_ws2812b_data[i] = 0;
} }
ws2812b_transmit(); // set LEDs led_ws2812b_transmit(); // set LEDs
} }
/** @brief DMA interrupt service routine to stop transmission after it finished */ /** @brief DMA interrupt service routine to stop transmission after it finished */
void WS2812B_DMA_ISR(void) void LED_WS2812B_DMA_ISR(void)
{ {
if (dma_get_interrupt_flag(WS2812B_DMA, WS2812B_DMA_CH, DMA_TCIF)) { // transfer completed if (dma_get_interrupt_flag(LED_WS2812B_DMA, LED_WS2812B_DMA_CH, DMA_TCIF)) { // transfer completed
dma_clear_interrupt_flags(WS2812B_DMA, WS2812B_DMA_CH, DMA_TCIF); // clear flag dma_clear_interrupt_flags(LED_WS2812B_DMA, LED_WS2812B_DMA_CH, DMA_TCIF); // clear flag
dma_disable_transfer_complete_interrupt(WS2812B_DMA, WS2812B_DMA_CH); // stop warning transfer completed dma_disable_transfer_complete_interrupt(LED_WS2812B_DMA, LED_WS2812B_DMA_CH); // stop warning transfer completed
spi_disable_tx_dma(WS2812B_SPI); // stop SPI asking for more data spi_disable_tx_dma(LED_WS2812B_SPI); // stop SPI asking for more data
while (SPI_SR(WS2812B_SPI) & SPI_SR_BSY); // wait for data to be shifted out while (SPI_SR(LED_WS2812B_SPI) & SPI_SR_BSY); // wait for data to be shifted out
timer_disable_counter(WS2812B_TIMER); // stop clock timer_disable_counter(LED_WS2812B_TIMER); // stop clock
dma_disable_channel(WS2812B_DMA, WS2812B_DMA_CH); // stop using DMA dma_disable_channel(LED_WS2812B_DMA, LED_WS2812B_DMA_CH); // stop using DMA
transmit_flag = false; // transmission completed transmit_flag = false; // transmission completed
} }
} }

View File

@ -21,41 +21,41 @@
#pragma once #pragma once
/** number of LEDs on the WS2812b strip */ /** number of LEDs on the WS2812b strip */
#define WS2812B_LEDS 48 #define LED_WS2812B_LEDS 48
/** peripheral configuration */ /** peripheral configuration */
/** @defgroup led_ws2812b_spi SPI peripheral used to control the WS2812b LEDs /** @defgroup led_ws2812b_spi SPI peripheral used to control the WS2812b LEDs
* @{ * @{
*/ */
#define WS2812B_SPI SPI1 /**< SPI peripheral */ #define LED_WS2812B_SPI SPI1 /**< SPI peripheral */
#define WS2812B_SPI_DR SPI1_DR /**< SPI data register for the DMA */ #define LED_WS2812B_SPI_DR SPI1_DR /**< SPI data register for the DMA */
#define WS2812B_SPI_RCC RCC_SPI1 /**< SPI peripheral clock */ #define LED_WS2812B_SPI_RCC RCC_SPI1 /**< SPI peripheral clock */
#define WS2812B_SPI_PORT GPIOA /**< SPI port */ #define LED_WS2812B_SPI_PORT GPIOA /**< SPI port */
#define WS2812B_SPI_CLK GPIO_SPI1_SCK /**< SPI clock pin (PA5), connect to PWM output */ #define LED_WS2812B_SPI_CLK GPIO_SPI1_SCK /**< SPI clock pin (PA5), connect to PWM output */
#define WS2812B_SPI_DOUT GPIO_SPI1_MISO /**< SPI data pin (PA6), connect to WS2812b DIN */ #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 /** @defgroup led_ws2812b_timer timer peripheral used to generate SPI clock
* @{ * @{
*/ */
#define WS2812B_TIMER TIM3 /**< timer peripheral */ #define LED_WS2812B_TIMER TIM3 /**< timer peripheral */
#define WS2812B_TIMER_RCC RCC_TIM3 /**< timer peripheral clock */ #define LED_WS2812B_TIMER_RCC RCC_TIM3 /**< timer peripheral clock */
#define WS2812B_TIMER_OC TIM_OC3 /**< timer output compare used to set PWM frequency */ #define LED_WS2812B_TIMER_OC TIM_OC3 /**< timer output compare used to set PWM frequency */
#define WS2812B_CLK_RCC RCC_GPIOB /**< timer port peripheral clock */ #define LED_WS2812B_CLK_RCC RCC_GPIOB /**< timer port peripheral clock */
#define WS2812B_CLK_PORT GPIOB /**< timer port */ #define LED_WS2812B_CLK_PORT GPIOB /**< timer port */
#define WS2812B_CLK_PIN GPIO_TIM3_CH3 /**< timer pin to output PWM (PB0), connect to SPI clock input */ #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 /** @defgroup led_ws2812b_dma DMA peripheral used to send the data
* @{ * @{
*/ */
#define WS2812B_DMA DMA1 /**< DMA peripheral to put data for WS2812b LED in SPI queue (only DMA1 supports SPI1_TX interrupt) */ #define LED_WS2812B_DMA DMA1 /**< DMA peripheral to put data for WS2812b LED in SPI queue (only DMA1 supports SPI1_TX interrupt) */
#define WS2812B_DMA_RCC RCC_DMA1 /**< DMA peripheral clock */ #define LED_WS2812B_DMA_RCC RCC_DMA1 /**< DMA peripheral clock */
#define WS2812B_DMA_CH DMA_CHANNEL3 /**< DMA channel (only DMA1 channel 3 supports SPI1_TX interrupt) */ #define LED_WS2812B_DMA_CH DMA_CHANNEL3 /**< DMA channel (only DMA1 channel 3 supports SPI1_TX interrupt) */
#define WS2812B_DMA_IRQ NVIC_DMA1_CHANNEL3_IRQ /**< DMA channel interrupt signal */ #define LED_WS2812B_DMA_IRQ NVIC_DMA1_CHANNEL3_IRQ /**< DMA channel interrupt signal */
#define WS2812B_DMA_ISR dma1_channel3_isr /**< DMA channel interrupt service routine */ #define LED_WS2812B_DMA_ISR dma1_channel3_isr /**< DMA channel interrupt service routine */
/** @} */ /** @} */
/** @brief setup WS2812b LED driver */ /** @brief setup WS2812b LED driver */
void ws2812b_setup(void); void led_ws2812b_setup(void);
/** @brief set color of a single LED /** @brief set color of a single LED
* @param[in] led the LED number to set the color * @param[in] led the LED number to set the color
* @param[in] red the red color value to set on the LED * @param[in] red the red color value to set on the LED
@ -63,8 +63,8 @@ void ws2812b_setup(void);
* @param[in] blue the blue color value to set on the LED * @param[in] blue the blue color value to set on the LED
* @note transmission needs to be done separately * @note transmission needs to be done separately
*/ */
void ws2812b_set_rgb(uint16_t led, uint8_t red, uint8_t green, uint8_t blue); void led_ws2812b_set_rgb(uint16_t led, uint8_t red, uint8_t green, uint8_t blue);
/** @brief transmit color values to WS2812b LEDs /** @brief transmit color values to WS2812b LEDs
* @return true if transmission started, false if another transmission is already ongoing * @return true if transmission started, false if another transmission is already ongoing
*/ */
bool ws2812b_transmit(void); bool led_ws2812b_transmit(void);

38
main.c
View File

@ -56,7 +56,7 @@ volatile bool photoresistor_flag = false; /**< flag set when ambient luminosity
* @note I have to use type variables because defines would be stored in signed integers, leading to an overflow it later calculations * @note I have to use type variables because defines would be stored in signed integers, leading to an overflow it later calculations
* @{ * @{
*/ */
/** the number of ticks in one second (greater than 256*WS2812B_LEDS/60) */ /** the number of ticks in one second (greater than 256*LED_WS2812B_LEDS/60) */
#define TICKS_PER_SECOND SQUARE_WAVE_FREQUENCY/SQUARE_WAVE_TICKS #define TICKS_PER_SECOND SQUARE_WAVE_FREQUENCY/SQUARE_WAVE_TICKS
/** number of ticks in one second */ /** number of ticks in one second */
const uint32_t ticks_second = TICKS_PER_SECOND; const uint32_t ticks_second = TICKS_PER_SECOND;
@ -79,7 +79,7 @@ const uint32_t ticks_midday = 12*60*60*TICKS_PER_SECOND;
/** RGB values for the WS2812b clock LEDs */ /** RGB values for the WS2812b clock LEDs */
uint8_t clock_leds[WS2812B_LEDS*3] = {0}; uint8_t clock_leds[LED_WS2812B_LEDS*3] = {0};
/** user input command */ /** user input command */
char command[32] = {0}; char command[32] = {0};
/** user input command index */ /** user input command index */
@ -171,15 +171,15 @@ static void clock_clear(void)
*/ */
static void clock_show_time(uint32_t time) static void clock_show_time(uint32_t time)
{ {
uint32_t led_hour = (WS2812B_LEDS*(256*(uint64_t)(time%ticks_midday)))/ticks_midday; // scale to LED brightnesses for hours uint32_t led_hour = (LED_WS2812B_LEDS*(256*(uint64_t)(time%ticks_midday)))/ticks_midday; // scale to LED brightnesses for hours
uint32_t led_minute = (WS2812B_LEDS*(256*(uint64_t)(time%ticks_hour)))/ticks_hour; // scale to LED brightnesses for minutes uint32_t led_minute = (LED_WS2812B_LEDS*(256*(uint64_t)(time%ticks_hour)))/ticks_hour; // scale to LED brightnesses for minutes
if (led_hour>=WS2812B_LEDS*256 || led_minute>=WS2812B_LEDS*256) { // a calculation error occurred if (led_hour>=LED_WS2812B_LEDS*256 || led_minute>=LED_WS2812B_LEDS*256) { // a calculation error occurred
return; return;
} }
// show hours and minutes on LEDs // show hours and minutes on LEDs
if (led_hour>led_minute) { if (led_hour>led_minute) {
// show hours in blue (and clear other LEDs) // show hours in blue (and clear other LEDs)
for (uint16_t led=0; led<WS2812B_LEDS; led++) { for (uint16_t led=0; led<LED_WS2812B_LEDS; led++) {
clock_leds[led*3+0] = 0; clock_leds[led*3+0] = 0;
clock_leds[led*3+1] = 0; clock_leds[led*3+1] = 0;
if (led_hour>=0xff) { // full hours if (led_hour>=0xff) { // full hours
@ -190,7 +190,7 @@ static void clock_show_time(uint32_t time)
led_hour -= clock_leds[led*3+2]; led_hour -= clock_leds[led*3+2];
} }
// show minutes in green (override hours) // show minutes in green (override hours)
for (uint16_t led=0; led<WS2812B_LEDS && led_minute>0; led++) { for (uint16_t led=0; led<LED_WS2812B_LEDS && led_minute>0; led++) {
clock_leds[led*3+0] = 0; clock_leds[led*3+0] = 0;
if (led_minute>=0xff) { // full minutes if (led_minute>=0xff) { // full minutes
clock_leds[led*3+1] = 0xff; clock_leds[led*3+1] = 0xff;
@ -202,7 +202,7 @@ static void clock_show_time(uint32_t time)
} }
} else { } else {
// show minutes in green (and clear other LEDs) // show minutes in green (and clear other LEDs)
for (uint16_t led=0; led<WS2812B_LEDS; led++) { for (uint16_t led=0; led<LED_WS2812B_LEDS; led++) {
clock_leds[led*3+0] = 0; clock_leds[led*3+0] = 0;
if (led_minute>=0xff) { // full minutes if (led_minute>=0xff) { // full minutes
clock_leds[led*3+1] = 0xff; clock_leds[led*3+1] = 0xff;
@ -213,7 +213,7 @@ static void clock_show_time(uint32_t time)
clock_leds[led*3+2] = 0; clock_leds[led*3+2] = 0;
} }
// show hours in blue (override minutes) // show hours in blue (override minutes)
for (uint16_t led=0; led<WS2812B_LEDS && led_hour>0; led++) { for (uint16_t led=0; led<LED_WS2812B_LEDS && led_hour>0; led++) {
clock_leds[led*3+0] = 0; clock_leds[led*3+0] = 0;
clock_leds[led*3+1] = 0; clock_leds[led*3+1] = 0;
if (led_hour>=0xff) { // full hours if (led_hour>=0xff) { // full hours
@ -228,15 +228,15 @@ static void clock_show_time(uint32_t time)
if (time%ticks_minute==0) { if (time%ticks_minute==0) {
return; return;
} }
uint32_t led_second = (WS2812B_LEDS*(256*(uint64_t)(time%ticks_minute)))/ticks_minute; // scale to LED brightnesses for seconds uint32_t led_second = (LED_WS2812B_LEDS*(256*(uint64_t)(time%ticks_minute)))/ticks_minute; // scale to LED brightnesses for seconds
uint8_t brightness_second = led_second%256; // get brightness for seconds for last LED uint8_t brightness_second = led_second%256; // get brightness for seconds for last LED
uint16_t second_led = (WS2812B_LEDS*(time%ticks_minute))/ticks_minute; // get LED for seconds (we only use the last LED as runner instead of all LEDs as arc) uint16_t second_led = (LED_WS2812B_LEDS*(time%ticks_minute))/ticks_minute; // get LED for seconds (we only use the last LED as runner instead of all LEDs as arc)
// set seconds LED // set seconds LED
clock_leds[second_led*3+0] = brightness_second; clock_leds[second_led*3+0] = brightness_second;
//clock_leds[second_led*3+1] = 0; // clear other colors (minutes/hours indication) //clock_leds[second_led*3+1] = 0; // clear other colors (minutes/hours indication)
//clock_leds[second_led*3+2] = 0; // clear other colors (minutes/hours indication) //clock_leds[second_led*3+2] = 0; // clear other colors (minutes/hours indication)
// set previous seconds LED // set previous seconds LED
second_led = ((second_led==0) ? WS2812B_LEDS-1 : second_led-1); // previous LED second_led = ((second_led==0) ? LED_WS2812B_LEDS-1 : second_led-1); // previous LED
clock_leds[second_led*3+0] = 0xff-brightness_second; clock_leds[second_led*3+0] = 0xff-brightness_second;
//clock_leds[second_led*3+1] = 0; // clear other colors (minutes/hours indication) //clock_leds[second_led*3+1] = 0; // clear other colors (minutes/hours indication)
//clock_leds[second_led*3+2] = 0; // clear other colors (minutes/hours indication) //clock_leds[second_led*3+2] = 0; // clear other colors (minutes/hours indication)
@ -249,7 +249,7 @@ static void clock_show_time(uint32_t time)
static void clock_leds_set(void) static void clock_leds_set(void)
{ {
for (uint16_t i=0; i<LENGTH(clock_leds)/3; i++) { for (uint16_t i=0; i<LENGTH(clock_leds)/3; i++) {
ws2812b_set_rgb(i,gamma_correction_lut[(uint8_t)(clock_leds[i*3+0]*clock_brightness)],gamma_correction_lut[(uint8_t)(clock_leds[i*3+1]*clock_brightness)],gamma_correction_lut[(uint8_t)(clock_leds[i*3+2]*clock_brightness)]); // set new value (this costs time) led_ws2812b_set_rgb(i,gamma_correction_lut[(uint8_t)(clock_leds[i*3+0]*clock_brightness)],gamma_correction_lut[(uint8_t)(clock_leds[i*3+1]*clock_brightness)],gamma_correction_lut[(uint8_t)(clock_leds[i*3+2]*clock_brightness)]); // set new value (this costs time)
} }
} }
@ -260,7 +260,7 @@ static void clock_set_time(uint32_t time)
{ {
clock_show_time(time); // set time clock_show_time(time); // set time
clock_leds_set(); // set the colors of all LEDs clock_leds_set(); // set the colors of all LEDs
ws2812b_transmit(); // transmit set color led_ws2812b_transmit(); // transmit set color
} }
/** @brief incrementally set the time on the LEDs /** @brief incrementally set the time on the LEDs
@ -295,13 +295,13 @@ static void clock_hours(void)
for (uint16_t i=0; i<512; i++) { // fade in and out for (uint16_t i=0; i<512; i++) { // fade in and out
uint8_t brightness = (i>255 ? 512-i-1 : i); // get fade brightness uint8_t brightness = (i>255 ? 512-i-1 : i); // get fade brightness
for (uint8_t hour=0; hour<12; hour++) { // set all hour colors for (uint8_t hour=0; hour<12; hour++) { // set all hour colors
uint16_t led = WS2812B_LEDS/12*hour; // get LED four hour mark uint16_t led = LED_WS2812B_LEDS/12*hour; // get LED four hour mark
clock_leds[led*3+0] = brightness; // set brightness clock_leds[led*3+0] = brightness; // set brightness
clock_leds[led*3+1] = brightness; // set brightness clock_leds[led*3+1] = brightness; // set brightness
clock_leds[led*3+2] = brightness; // set brightness clock_leds[led*3+2] = brightness; // set brightness
} }
clock_leds_set(); // set the colors of all LEDs clock_leds_set(); // set the colors of all LEDs
ws2812b_transmit(); // transmit set color led_ws2812b_transmit(); // transmit set color
// delay some time for the animation // delay some time for the animation
for (uint32_t j=0; j<40000; j++) { for (uint32_t j=0; j<40000; j++) {
__asm__("nop"); __asm__("nop");
@ -383,10 +383,10 @@ int main(void)
} }
// setup WS2812b LEDs // setup WS2812b LEDs
ws2812b_setup(); // setup WS2812b LEDs led_ws2812b_setup(); // setup WS2812b LEDs
clock_clear(); // clear all LEDs clock_clear(); // clear all LEDs
clock_leds_set(); // set the colors of all LEDs clock_leds_set(); // set the colors of all LEDs
ws2812b_transmit(); // transmit set color led_ws2812b_transmit(); // transmit set color
// setup ADC to photo-resistor voltage // setup ADC to photo-resistor voltage
rcc_periph_clock_enable(PHOTORESISTOR_PORT_RCC); // enable clock for photo-resistor GPIO peripheral rcc_periph_clock_enable(PHOTORESISTOR_PORT_RCC); // enable clock for photo-resistor GPIO peripheral
@ -487,7 +487,7 @@ int main(void)
adc_start_conversion_regular(ADC1); // start measuring ambient luminosity adc_start_conversion_regular(ADC1); // start measuring ambient luminosity
} }
if ((rtc_ticks%ticks_second)==0) { // one second passed if ((rtc_ticks%ticks_second)==0) { // one second passed
//led_toggle(); // don't use the LED, this confuses the 32.768 kHz oscillator led_toggle(); // don't use the LED, this confuses the 32.768 kHz oscillator
} }
if ((rtc_ticks%ticks_minute)==0) { // one minute passed if ((rtc_ticks%ticks_minute)==0) { // one minute passed
printf("%02lu:%02lu:%02lu\n", rtc_get_counter_val()/ticks_hour, (rtc_get_counter_val()%ticks_hour)/ticks_minute, (rtc_get_counter_val()%ticks_minute)/ticks_second); // display time printf("%02lu:%02lu:%02lu\n", rtc_get_counter_val()/ticks_hour, (rtc_get_counter_val()%ticks_hour)/ticks_minute, (rtc_get_counter_val()%ticks_minute)/ticks_second); // display time