Conducir WS2812 usando DMA en STM32F302

Estoy tratando de manejar algunos LED WS2812 que requieren una señal (400 ns alto + 800 ns bajo) o (800 ns alto + 400 ns bajo) para indicar un 0 o un 1.

Entonces, esencialmente, estoy tratando de tomar una serie de datos y enviarlos directamente a un GPIO a aproximadamente 2.5MHz. Nunca antes había usado el DMA, pero por lo que entiendo, debería poder enviar datos directamente desde la memoria a un GPIO.

¿Alguien puede ofrecer alguna orientación sobre cómo configurar el DMA para hacer esto, o si hay mejores formas de generar datos en un GPIO en un reloj constante?

Respuestas (3)

Debería considerar usar una interfaz UART, SPI o I2S, usando un byte para generar la forma de onda para un bit. El punto importante es que dichas interfaces tienen un búfer FIFO, por lo que las actividades de la CPU no necesitan estar exactamente sincronizadas con la señal de salida.

Un estudiante mío hizo esto con éxito en un LPC1114.

Tal vez pueda usar DMA para alimentar la interfaz serial, pero tendría que 'ampliar' los datos a un byte por bit.

La hoja de datos confirma que es posible alternar GPIO a 2,5 MHz

El manejo rápido de E/S permite alternar E/S hasta 36 MHz

Es posible escribir DMA en GPIO (observando el diagrama de bloques de la sección DMA). Si pudiera escribir a GPIO directamente a los registros de GPIO_data.

DMA no se puede usar debido a su restricción en el ciclo de trabajo para la lógica baja y la lógica alta. Sugeriría probar la codificación en C, impulsando los GPIO directamente (cambie a asm o en línea, si es compatible con la cadena de herramientas) si se requiere una velocidad de 1/400 ns = 2,5 MHz y tiempo adicional en el medio.
Editar: para usar DMA, según el comentario a continuación para que el procesador pueda estar libre, el ciclo de trabajo sigue siendo 66.66% y 33.33% según el requisito en OP. Pero lo único es que la tarifa aumentará a 2,5 MHz. Todavía tengo que explorar la configuración de DMA para comprobar la posibilidad.ingrese la descripción de la imagen aquí

Eh, DC no es un problema a menos que haya retrasos o vueltas a cero entre bytes. Dado que la señal es bastante regular, puede usar 3 bits de salida por bit WS281X en la señal (100 para 0, 110 para 1).
No aumentará a 7,5 MHz; la señal base es de 800 kHz, por lo que 2,5 MHz es aproximadamente correcta.
Gracias por la buena idea de generar el PWM con señales digitales. Lo he corregido a 2,5 MHz y sigo con la configuración

1. Con un STM32, configure la velocidad del reloj a 40/80/120 MHz y use el SPI con salida a MOSI. Utilice el canal SPI y DMA. El pulso estará en 400us. A 80Mhz, el divisor SPI será 32.

  1. Configure el SPI así:

    hdma_spi1_tx.Instance = DMA1_Channel3;

    hdma_spi1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;

    hdma_spi1_tx.Init.PeriphInc = DMA_PINC_DISABLE;

    hdma_spi1_tx.Init.MemInc = DMA_MINC_ENABLE;

    hdma_spi1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;

    hdma_spi1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;

    hdma_spi1_tx.Init.Mode = DMA_NORMAL;

    hdma_spi1_tx.Init.Priority = DMA_PRIORITY_LOW;

3. Configure el puerto DMA así:

DMA1_Channel3->CPAR = (uint32_t)&(SPI1->DR);

DMA1_Channel3->CMAR = (uint32_t)píxeles; // para enviar a ws2812

DMA1_Canal3->CCR |= DMA_CCR_DIR | DMA_CCR_MINC;

NVIC_SetPriority(DMA1_Channel2_3_IRQn, 0);

NVIC_EnableIRQ(DMA1_Channel2_3_IRQn);

4. Codifique los datos correctamente antes de enviarlos. El Bit 1 será 110 y el Bit 0: 100. Entonces, si tienes 10 leds WS2812 x 3 bytes (24 bits) x 3 pulsos = 90 bytes

  1. Deje el primer y último byte de flujo para enviar a WS2812 en blanco (0). El primer byte se utilizará para restablecer la tira.

6.Envíe datos al WS2812 con DMA HAL_SPI_Transmit_DMA( &hspi1, pixels, numBytes );

7. Espere 1,25 ms x leds antes de enviar de nuevo