From: Martin Mareš Date: Fri, 12 Sep 2025 19:50:41 +0000 (+0200) Subject: Neopixel square: Double-buffered DMA X-Git-Url: http://mj.ucw.cz/gitweb/?a=commitdiff_plain;h=309cd67264553a1d451deda32fc6643425a99ae7;p=home-hw.git Neopixel square: Double-buffered DMA --- diff --git a/test-neopixel-square/main.c b/test-neopixel-square/main.c index 7474061..9605a44 100644 --- a/test-neopixel-square/main.c +++ b/test-neopixel-square/main.c @@ -91,31 +91,34 @@ static void delay_ms(uint ms) /*** Neopixels ***/ #define NPIX_PERIOD 90 // timer runs on 72 MHz, so 90 periods = 1250 ns -#define NPIX_RESET 128 // the chip needs longer reset pulse than documented #define NPIX_NUM_LEDS 64 +#define NPIX_RESET 6 // length of reset pulse in LED slots + // The chip needs longer reset pulse than documented. #define B0 30 #define B1 60 -byte neopixel_buf[NPIX_RESET + 24*NPIX_NUM_LEDS]; +static byte neopixel_leds[NPIX_NUM_LEDS][3]; +static byte neopixel_buf[2*24]; +static uint neopixel_index; static void neopixel_set(uint led, byte r, byte g, byte b) { - byte *buf = &neopixel_buf[NPIX_RESET + 24*led]; - - for (uint m=0x80; m; m >>= 1) - *buf++ = (g & m) ? B1 : B0; - for (uint m=0x80; m; m >>= 1) - *buf++ = (r & m) ? B1 : B0; - for (uint m=0x80; m; m >>= 1) - *buf++ = (b & m) ? B1 : B0; + cm_disable_interrupts(); + neopixel_leds[led][0] = r; + neopixel_leds[led][1] = g; + neopixel_leds[led][2] = b; + cm_enable_interrupts(); } - static void neopixel_init(void) { for (uint i=0; i < NPIX_NUM_LEDS; i++) neopixel_set(i, 7, 7, 7); + // neopixel_buf is zero-initialized, which is a start of the reset sequence + memset(neopixel_buf, 0, sizeof(neopixel_buf)); + neopixel_index = NPIX_NUM_LEDS; + // TIM4 update is connected to DMA1 channel 7 // FIXME: Strange programming sequence as specified in manual @@ -136,6 +139,10 @@ static void neopixel_init(void) dma_set_peripheral_size(DMA1, 7, DMA_CCR_PSIZE_16BIT); dma_disable_peripheral_increment_mode(DMA1, 7); + dma_enable_half_transfer_interrupt(DMA1, 7); + dma_enable_transfer_complete_interrupt(DMA1, 7); + nvic_enable_irq(NVIC_DMA1_CHANNEL7_IRQ); + dma_enable_channel(DMA1, 7); timer_set_prescaler(TIM4, 0); @@ -154,6 +161,39 @@ static void neopixel_init(void) timer_enable_counter(TIM4); } +static inline void next_led(byte *buf) +{ + if (neopixel_index < NPIX_NUM_LEDS) { + byte *led = neopixel_leds[neopixel_index++]; + byte r = led[0], g = led[1], b = led[2]; + for (uint m=0x80; m; m >>= 1) + *buf++ = (g & m) ? B1 : B0; + for (uint m=0x80; m; m >>= 1) + *buf++ = (r & m) ? B1 : B0; + for (uint m=0x80; m; m >>= 1) + *buf++ = (b & m) ? B1 : B0; + } else { + for (uint i=0; i < 24; i++) + *buf++ = 0; + neopixel_index++; + if (neopixel_index == NPIX_NUM_LEDS + NPIX_RESET) + neopixel_index = 0; + } +} + +void dma1_channel7_isr(void) +{ + if (DMA1_ISR & DMA_ISR_HTIF7) { + // Half transfer + DMA1_IFCR |= DMA_IFCR_CHTIF7; + next_led(neopixel_buf); + } else if (DMA1_ISR & DMA_ISR_TCIF7) { + // Transfer completed + DMA1_IFCR |= DMA_IFCR_CTCIF7; + next_led(neopixel_buf + 24); + } +} + /*** Game of Life ***/ #define LN 8