app: use timer to update RGB panel lines

This commit is contained in:
King Kévin 2022-05-25 19:37:17 +02:00
parent 34d44c2a88
commit c73600c91f
1 changed files with 27 additions and 25 deletions

View File

@ -99,6 +99,7 @@ static volatile uint32_t dial_steps = 0; /**< set to drv8825_steps when dial is
#define RGBMATRIX_HEIGHT 32 /**< number of rows in the RGB matrix */
#define RGBMATRIX_WIDTH 64 /**< number of columns in the RGB matrix */
static uint8_t rgbmatrix_data[RGBMATRIX_HEIGHT / 2][RGBMATRIX_WIDTH * 2]; /**< data to be sent to RGB matrix (one byte includes upper and lower half values, each byte has 2 clock edges) */
#define RGBMATRIX_TIMER 3 /**< timer to update lines */
/** set motor speed and direction
* @param[in] speed speed (in Hz) and direction (sign)
@ -762,7 +763,7 @@ void main(void)
dma_disable_stream(RGBMATRIX_DMA, RGBMATRIX_STREAM); // disable stream before re-configuring
while (DMA_SCR(RGBMATRIX_DMA, RGBMATRIX_STREAM) & DMA_SxCR_EN); // wait until transfer is finished before we can reconfigure
dma_stream_reset(RGBMATRIX_DMA, RGBMATRIX_STREAM); // use default values
dma_set_peripheral_address(RGBMATRIX_DMA, RGBMATRIX_STREAM, (uint32_t)&rgbmatrix_data[0]); // set memory to read from (for memory-to-memory transfer, the source is the peripheral)
//dma_set_peripheral_address(RGBMATRIX_DMA, RGBMATRIX_STREAM, (uint32_t)&rgbmatrix_data[0]); // set memory to read from (for memory-to-memory transfer, the source is the peripheral)
dma_set_peripheral_size(RGBMATRIX_DMA, RGBMATRIX_STREAM, DMA_SxCR_PSIZE_8BIT); // we only write the 8 first bit
dma_enable_peripheral_increment_mode(RGBMATRIX_DMA, RGBMATRIX_STREAM); // increment address of memory to read
dma_set_memory_address(RGBMATRIX_DMA, RGBMATRIX_STREAM, (uint32_t) &GPIOA_ODR); // set GPIOA as destination (for memory-to-memory transfer, the destination is the memory)
@ -772,11 +773,21 @@ void main(void)
dma_channel_select(RGBMATRIX_DMA, RGBMATRIX_STREAM, RGBMATRIX_CHANNEL); // set the channel for this stream
dma_set_transfer_mode(RGBMATRIX_DMA, RGBMATRIX_STREAM, DMA_SxCR_DIR_MEM_TO_MEM); // set transfer from memory to memory
dma_set_priority(RGBMATRIX_DMA, RGBMATRIX_STREAM, DMA_SxCR_PL_LOW); // there is no need to rush
dma_enable_transfer_complete_interrupt(RGBMATRIX_DMA, RGBMATRIX_STREAM); // interrupt when line transfer is complete
nvic_enable_irq(RGBMATRIX_IRQ); // enable interrupt
rgbmatrix_clear(); // clear matrix
gpio_clear(GPIO_PORT(RGBMATRIX_OE_PIN), GPIO_PIN(RGBMATRIX_OE_PIN)); // enable output
dma_enable_stream(RGBMATRIX_DMA, RGBMATRIX_STREAM); // start DMA to continuously send data
// configure timer to go through rows/lines
rcc_periph_clock_enable(RCC_TIM(RGBMATRIX_TIMER)); // enable clock for timer domain
rcc_periph_reset_pulse(RST_TIM(RGBMATRIX_TIMER)); // reset timer state
timer_set_mode(TIM(RGBMATRIX_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(RGBMATRIX_TIMER), 2 - 1); // hand tuned prescale to minimize inter-line ghosting
timer_set_period(TIM(RGBMATRIX_TIMER), 0xcfff - 1); // hand tuned period to minimize inter-line ghosting
timer_clear_flag(TIM(RGBMATRIX_TIMER), TIM_SR_UIF); // clear update (overflow) flag
timer_update_on_overflow(TIM(RGBMATRIX_TIMER)); // only use counter overflow as UEV source (use overflow as next line update indication)
timer_enable_irq(TIM(RGBMATRIX_TIMER), TIM_DIER_UIE); // enable update interrupt for timer
nvic_enable_irq(NVIC_TIM_IRQ(RGBMATRIX_TIMER)); // catch interrupt in service routine
timer_enable_counter(TIM(RGBMATRIX_TIMER)); // start timer to update RGB matrix
puts_debug("OK\n");
// setup terminal
@ -810,7 +821,6 @@ void main(void)
led_toggle(); // toggle LED to indicate if main function is stuck
rgbmatrix_set(matrix_led % RGBMATRIX_WIDTH, matrix_led / RGBMATRIX_WIDTH, false, false, false); // disable current LED
matrix_led += 4; // go to next LED
//GPIOB_ODR = (GPIOB_ODR & 0xfff0) + ((matrix_led / RGBMATRIX_WIDTH) % RGBMATRIX_HEIGHT);
rgbmatrix_set(matrix_led % RGBMATRIX_WIDTH, matrix_led / RGBMATRIX_WIDTH, true, false, false); // enable next LED
}
if (0 == gpio_get(GPIO_PORT(DRV8825_FAULT_PIN), GPIO_PIN(DRV8825_FAULT_PIN))) { // DRV8825 stepper motor error reports error
@ -875,30 +885,22 @@ void GPIO_EXTI_ISR(DIAL_SWITCH_PIN)(void)
}
}
/** ISR triggered when the data for the line of the RGB matrix has been transferred
/** interrupt service routine called to update next line of RGB matrix
* @note ideally the next line should be updated when the current one is complete, but the DMA is too fast.
* @note switching lines too fast causes inter-line ghosting of the LEDs (on the same column), due to capacitance and driver switching limitations
*/
void RGBMATRIX_ISR(void)
void TIM_ISR(RGBMATRIX_TIMER)(void)
{
static uint8_t rgbmatrix_line = 0; // line being transferred
if (dma_get_interrupt_flag(RGBMATRIX_DMA, RGBMATRIX_STREAM, DMA_TCIF)) {
dma_clear_interrupt_flags(RGBMATRIX_DMA, RGBMATRIX_STREAM, DMA_TCIF);
if (timer_get_flag(TIM(RGBMATRIX_TIMER), TIM_SR_UIF)) { // update event happened
timer_clear_flag(TIM(RGBMATRIX_TIMER), TIM_SR_UIF); // clear flag
if (DMA_SCR(RGBMATRIX_DMA, RGBMATRIX_STREAM) & DMA_SxCR_EN) { // DMA is not complete
return;
}
static uint8_t rgbmatrix_line = 0; // line being transferred
rgbmatrix_line = (rgbmatrix_line + 1) % (RGBMATRIX_HEIGHT / 2); // go to next line (two lines are updated at once)
// data already latched by last pixel
//gpio_set(GPIO_PORT(RGBMATRIX_LAT_PIN), GPIO_PIN(RGBMATRIX_LAT_PIN)); // latch data on rising edge
//gpio_clear(GPIO_PORT(RGBMATRIX_LAT_PIN), GPIO_PIN(RGBMATRIX_LAT_PIN)); // release latch
for (volatile uint32_t i = 0; i < 4000; i++);
GPIOB_ODR = (GPIOB_ODR & 0xfff0) + rgbmatrix_line; // select line (line on lower and upper half are updated at once)
dma_set_peripheral_address(RGBMATRIX_DMA, RGBMATRIX_STREAM, (uint32_t)&rgbmatrix_data[rgbmatrix_line]); // set memory containing line data to be transferred
dma_set_peripheral_address(RGBMATRIX_DMA, RGBMATRIX_STREAM, (uint32_t)&rgbmatrix_data[rgbmatrix_line]); // set memory containing line data to be transferred (for memory-to-memory transfer, the source is the peripheral)
dma_clear_interrupt_flags(RGBMATRIX_DMA, RGBMATRIX_STREAM, DMA_TCIF); // even if interrupts are not enabled, clearing this flag is required to start the DMA (I don't know why)
dma_enable_stream(RGBMATRIX_DMA, RGBMATRIX_STREAM); // start sending next line
}
}
/* I don't why, but I have a ghosting issue between the lines.
- it only affects the configured number of lines (e.g. it does not appear when I only use 2 lines)
- in affects both halves independently
- having the latch on the last - 1 or last + 1 row clock does not solve the issue
- I see no ringing in the lines selection signal, and all LEDs in the line are affected the same way
- the voltage level does not seem to be the issue (3.3V signal vs 5V panel): each LED color is set correctly, and lowering the panel voltage does not change the ghosting
- the LED brightness is different across the lines, but identical across the line
- adding sleep time between line selections decreases the effect a bit
*/