Ist es möglich, dass der DMA-Controller auf einem STM32 jedes Paket nur dann überträgt, wenn ein Timer-Aktualisierungsereignis auftritt, oder können Sie nur den Start eines ganzen DMA-Blocks steuern?
Der Anwendungsfall besteht darin (auf einem STM32 ohne HW-DAC), das PWM-Tastverhältnis von TIMER1 aus einem Block von Beispieldaten im Speicher in einem bestimmten Zeitintervall einzustellen. Derzeit generiere ich einen CPU-Interrupt (mit TIMER2) und fülle den PWM-Impulswert für TIMER1 manuell in die ISR, was funktioniert, aber ich möchte die CPU dort rausholen, wenn ich kann.
Ich habe im Referenzhandbuch nachgesehen und kann keine Standardmethode dafür finden (was ich möglicherweise übersehen habe), aber vielleicht gibt es eine hinterhältige Methode, die einen anderen DMA-Kanal verwendet, um den primären DMA-Ausgangskanal zu optimieren ... oder so...
Update: Code hinzugefügt (der kein DMA verwendet, sondern nur in eine Timer-ISR stopft)
#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)
{
}
}
Verwendete Variablen:
#define CHUNK_SIZE 255
// uint8_t buffer[CHUNK_SIZE]; optional!!!
uint16_t offset = 0;
Algorithmus
Initialisieren Sie Ihren DAC.
DMA initialisieren:
&waf + offset
((offset + CHUNK_SIZE) < ARRAY_SIZE(wav)) ? CHUNK_SIZE : (ARRAY_SIZE(wav) - offset )
offset
berechnete DMA-Länge für die nächste Verwendung hinzufügenInitialisieren Sie Ihren Timer:
Timer starten
Wenn DMA TC IRQ auftritt
offset == ARRAY_SIZE(wav)
dann Timer stoppen - Wiedergabe beendetDas ist alles! Sie müssen sicher sein, dass die Neuinitialisierung von DMA schneller erfolgt, als die Timer-Periode endet! Anderenfalls den Timer durch die Uhr des Gate-Timers anhalten, während der Prozess neu initialisiert wird. Wenn DMA nicht aus dem Flash-Speicher lesen kann, verwenden Sie den Puffer im RAM und kopieren Sie wav stückweise mit memcpy, bevor Sie jeden Chunck abspielen (bevor der DMA-Prozess initialisiert wird).
Entschuldigung für Grammatikfehler.
Eugen Sch.
brhans
Charlie Skilbeck
justieren
Charlie Skilbeck