¿El temporizador puede controlar la velocidad DMA del STM32?

1

¿Es posible que el controlador DMA en un STM32 transfiera cada paquete solo cuando ocurre un evento de actualización del temporizador o solo puede controlar el inicio de una parte completa de DMA?

El caso de uso es (en un STM32 sin HW DAC) para configurar el ciclo de trabajo PWM de TIMER1 de un bloque de datos de muestra en la memoria en un intervalo de tiempo específico. Actualmente genero una interrupción de la CPU (usando TIMER2) y relleno manualmente el valor de pulso PWM para TIMER1 en el ISR, que funciona, pero me gustaría sacar la CPU de allí si puedo.

He mirado el manual de referencia y no puedo encontrar una forma estándar de hacer esto (lo que puede que haya pasado por alto), pero quizás haya un método astuto que usaría otro canal DMA para ajustar el canal DMA de salida principal ... o algo ...

Actualización: código agregado (que no usa DMA, solo lo mete en un ISR con temporizador)

#define WAV

extern "C" {
    #include "misc.h"
    #include "GPIO_stm32f10x.h"
    #include "stm32f10x_tim.h"
    #include "TIM_ex.h"
    #include "wav.h"
    #include "stm32f10x_dma.h"
}

#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))

extern "C" void TIM2_IRQHandler()
{
    static uint32_t i = 1;
    static uint32_t j = 0;

    // clear timer2 irq status
    TIM2->SR = (uint16_t)~TIM_IT_Update;

    // heartbeat toggle PORTA:0 every tick
    GPIOA->BSRR = i;
    i ^= 0x10001;

    #ifdef WAV
    TIM_SetChannel1Pulse(TIM1, wav[j]);
    if(++j >= ARRAY_SIZE(wav))
    {
        j = 0;
    }
    #else
    static uint32_t k = 0;
    TIM1->CCR1 = sine[j & 0xff];
    j += (sine[(k >> 6) & 0xff] >> 1) + 32;
    k += 1;
    #endif
}

int main()
{
    // switch on some peripheral clocks
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);      // DMA1
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);    // TIMER2
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA +
                            RCC_APB2Periph_AFIO +
                            RCC_APB2Periph_TIM1, ENABLE);   // PORTA, AFIO, TIMER1

    // set PORTA:0 to output
    GPIO_InitTypeDef a0Init;
    a0Init.GPIO_Pin = GPIO_Pin_0;
    a0Init.GPIO_Mode = GPIO_Mode_Out_PP;
    a0Init.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &a0Init);

    // setup timer2 @ 8KHz
    TIM_TimeBaseInitTypeDef t2Init;
    t2Init.TIM_CounterMode = TIM_CounterMode_Up;
    t2Init.TIM_Prescaler = 0;
    t2Init.TIM_Period = 72000000 / 8000 - 1;
    t2Init.TIM_ClockDivision = TIM_CKD_DIV1;
    t2Init.TIM_RepetitionCounter = 0;
    TIM_TimeBaseInit(TIM2, &t2Init);

    // enable TIMER2 IRQs
    NVIC_InitTypeDef nvicInit;
    nvicInit.NVIC_IRQChannel = TIM2_IRQn;
    nvicInit.NVIC_IRQChannelPreemptionPriority = 0;
    nvicInit.NVIC_IRQChannelSubPriority = 1;
    nvicInit.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&nvicInit);

    // switch on TIMER2 update IRQs
    TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);

    // setup timer1 for 7 bit PWM
    TIM_TimeBaseInitTypeDef t1Init;
    t1Init.TIM_Prescaler = 0;
    t1Init.TIM_CounterMode = TIM_CounterMode_Up;
    t1Init.TIM_Period = 256;
    t1Init.TIM_ClockDivision = TIM_CKD_DIV1;
    t1Init.TIM_RepetitionCounter = 0;
    TIM_TimeBaseInit(TIM1, &t1Init);

    // setup timer1 output channel for PWM
    TIM_OCInitTypeDef  t1_OCInit;
    t1_OCInit.TIM_OCMode = TIM_OCMode_PWM2;   
    t1_OCInit.TIM_OutputState = TIM_OutputState_Enable;   
    t1_OCInit.TIM_OutputNState = TIM_OutputNState_Enable;   
    t1_OCInit.TIM_Pulse = 0;
    t1_OCInit.TIM_OCPolarity = TIM_OCPolarity_Low;   
    t1_OCInit.TIM_OCNPolarity = TIM_OCNPolarity_Low;   
    t1_OCInit.TIM_OCIdleState = TIM_OCIdleState_Set;   
    t1_OCInit.TIM_OCNIdleState = TIM_OCIdleState_Reset;   
    TIM_OC1Init(TIM1, &t1_OCInit);

    // switch timer1 to PWM mode
    TIM_EnablePWMOutputs(TIM1);

    // set PORTA:8 to alt. function output (ie timer1 PWM)
    GPIO_InitTypeDef a8Init;
    a8Init.GPIO_Pin = GPIO_Pin_8;
    a8Init.GPIO_Mode = GPIO_Mode_AF_PP;
    a8Init.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &a8Init);

    TIM_Cmd(TIM1, ENABLE);
    TIM_Cmd(TIM2, ENABLE);                              // start timers

    while(1)
    {
    }
}
    
pregunta Charlie Skilbeck

1 respuesta

2

Variables utilizadas:

#define CHUNK_SIZE 255
// uint8_t buffer[CHUNK_SIZE]; optional!!!
uint16_t offset = 0;

Algorithm

Inicializa tu DAC.

Inicializar DMA:

  1. configure el flujo / canal requerido para el DMA seleccionado de acuerdo con el evento de actualización del temporizador seleccionado
  2. establece la dirección de datos DMA en &waf + offset
  3. establezca la dirección periferal DMA en DAC_DATA_REGISTER
  4. establezca la longitud DMA en ((offset + CHUNK_SIZE) < ARRAY_SIZE(wav)) ? CHUNK_SIZE : (ARRAY_SIZE(wav) - offset )
  5. agregue la longitud de DMA calculada a offset para el próximo uso
  6. habilitar solo el total IRQ completo

Inicializa tu temporizador:

  1. configuración de frecuencia requerida
  2. no habilita el IRQ de ningún temporizador
  3. habilitar el disparo DMA por el evento de actualización del temporizador

Iniciar temporizador

Cuando se producirá DMA TC IRQ

  1. si offset == ARRAY_SIZE(wav) entonces detiene el temporizador: la reproducción terminó
  2. de lo contrario, reinicialice DMA con un nuevo desplazamiento

Eso es todo! ¡Debe asegurarse de reinicializar DMA más rápido que el final del período del temporizador! De lo contrario, haga una pausa en el temporizador con el reloj del temporizador de la puerta mientras se reinicia. Si DMA no puede leer desde la memoria flash, use el búfer en la memoria RAM y copie wav por partes con memcpy antes de cada reproducción (antes del proceso de inicialización de DMA).

Lo siento por errores gramaticales.

    
respondido por el imbearr

Lea otras preguntas en las etiquetas