¿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)
{
}
}