Forum: Mikrocontroller und Digitale Elektronik STM32F4 Discovery: Problem mit DMA (ADC + Audio DAC)


von chillking (Gast)


Angehängte Dateien:

Lesenswert?

Tag zusammen,

ich verwende ein STM32F4 Discovery und programmiere mit CooCox.

Ich möchte immer 1000 Werte des ADC1 per DMA2 einlesen und in zwei 
Arrays abspeichern (Double Buffering).

Gleichzeitig soll DMA1 den externen AudioDAC befeuern (ebenfalls 
double-buffer mit jeweils 1000 Werten).

Meine Konfigurationen sehen folgender Maßen aus:
1
void setupDAC()
2
{
3
  //Clocks automatisch
4
5
  device.initI2C();  //I2C-Schnittstelle konfigurieren
6
  device.initI2S();  //I2S/SPI-Schnittstelle konfigurieren
7
8
9
  DMA_InitTypeDef  DMA_InitStructure;
10
  NVIC_InitTypeDef NVIC_InitStructure;
11
12
  //Enable DMA1 clock
13
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);
14
15
  //Enable the DMA1 global interrupt
16
  NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream7_IRQn;
17
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
18
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
19
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
20
  NVIC_Init(&NVIC_InitStructure);
21
22
  // SPI3_Tx = DMA1 Stream7 Channel0
23
  // DMA1 Stream7 Channel 0 configuration
24
  DMA_InitStructure.DMA_Channel = DMA_Channel_0;
25
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI3->DR;
26
  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&dac_buffer_0;
27
  DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
28
  DMA_InitStructure.DMA_BufferSize = DAC_BUFFER_SIZE;
29
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
30
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
31
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
32
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
33
  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
34
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
35
  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
36
  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
37
  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
38
  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
39
  DMA_Init(DMA1_Stream7, &DMA_InitStructure);
40
41
  //DoubleBuffer
42
  DMA_DoubleBufferModeConfig(DMA1_Stream7, (uint32_t)&dac_buffer_1, DMA_Memory_0);
43
  DMA_DoubleBufferModeCmd(DMA1_Stream7, ENABLE);
44
45
  DMA_Cmd(DMA1_Stream7, ENABLE);
46
47
  DMA_ITConfig(DMA1_Stream7, DMA_IT_TC, ENABLE);
48
49
  // Enable DMA request after last transfer
50
  //ADC_DMARequestAfterLastTransferCmd(SPI3, ENABLE);
51
52
53
  // Enable SPI DMA
54
  //ADC_DMACmd(SPI3, ENABLE);
55
  SPI_I2S_DMACmd(SPI3,SPI_I2S_DMAReq_Tx,ENABLE);
56
57
58
  device.initAudioDac();  //Audio-DAC per I2C konfigurieren
59
60
  setVol();  //Lautstärke einstellen damit mir nicht die Ohren abfallen
61
62
  I2S_Cmd(SPI3, ENABLE);  //I2S/SPI start
63
}
64
65
66
67
void setupADC1()
68
{
69
  ADC_InitTypeDef ADC_InitStructure;
70
  ADC_CommonInitTypeDef ADC_CommonInitStructure;
71
  DMA_InitTypeDef DMA_InitStructure;
72
  GPIO_InitTypeDef GPIO_InitStructure;
73
  NVIC_InitTypeDef NVIC_InitStructure;
74
  TIM_TimeBaseInitTypeDef TIM_TimeBase_InitStructure;
75
76
  // Clocks für DMA2, ADC1, TIM2, GPIOC->PC3
77
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2 | RCC_AHB1Periph_GPIOC, ENABLE);
78
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
79
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
80
81
82
  //Timer2 als Trigger des ADC1
83
  TIM_DeInit(TIM2);
84
  //RCC_PCLK1Config (RCC_HCLK_Div1);
85
  TIM_TimeBaseStructInit(&TIM_TimeBase_InitStructure);  //auf standard stellen
86
  TIM_TimeBase_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
87
  TIM_TimeBase_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;
88
  TIM_TimeBase_InitStructure.TIM_Period = 1750; //21e6/(((float)fa)*0.9114);  //-->84MHz bzw der Takt von TIM2 wird vorgeteilt auf 21MHz (glaub ich)
89
  TIM_TimeBase_InitStructure.TIM_Prescaler = 0;
90
  TIM_TimeBaseInit(TIM2, &TIM_TimeBase_InitStructure);
91
92
  //Timer noch nicht starten
93
  //TIM_Cmd(TIM2, ENABLE);
94
95
96
  TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);
97
98
  // DMA2 Stream0 channel0 --> ADC1   (alternativ DMA2 Stream4 Channel0)
99
  DMA_InitStructure.DMA_Channel = DMA_Channel_0;
100
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADC1_DR_ADDRESS;
101
  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&adc_buffer_0;
102
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
103
  DMA_InitStructure.DMA_BufferSize = ADC_BUFFER_SIZE;
104
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
105
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
106
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
107
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
108
  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
109
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
110
  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
111
  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
112
  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
113
  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
114
  DMA_Init(DMA2_Stream0, &DMA_InitStructure);
115
116
  //DMA2 Stream0 DoubleBuffer
117
  DMA_DoubleBufferModeConfig(DMA2_Stream0, (uint32_t)&adc_buffer_1, DMA_Memory_0);
118
  DMA_DoubleBufferModeCmd(DMA2_Stream0, ENABLE);
119
  //<--muss evtl nach DMA-Start?!??
120
121
  //DMA2 Stream0 starten
122
  DMA_Cmd(DMA2_Stream0, ENABLE);
123
124
125
  // ADC1 Channel13 pin (PC3)
126
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
127
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
128
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
129
  GPIO_Init(GPIOC, &GPIO_InitStructure);
130
131
  // ADC Common Init
132
  ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
133
  ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;
134
  ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
135
  ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
136
  ADC_CommonInit(&ADC_CommonInitStructure);
137
138
  // ADC1
139
  ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
140
  ADC_InitStructure.ADC_ScanConvMode = DISABLE;
141
  ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
142
  ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising;
143
  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO;
144
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
145
  ADC_InitStructure.ADC_NbrOfConversion = 1;
146
  ADC_Init(ADC1, &ADC_InitStructure);
147
148
  // ADC1 Channel13
149
  ADC_RegularChannelConfig(ADC1, ADC_Channel_13, 1, ADC_SampleTime_3Cycles);  //evtl auch ADC_SampleTime_15Cycles?!?
150
151
152
153
  // DMA2 Stream0 globaler Interrupt-Channel konfigurieren
154
  NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn;
155
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
156
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
157
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
158
  NVIC_Init(&NVIC_InitStructure);
159
160
  //DMA bestimmte Interrupts anschalten (Transmission complete)
161
  DMA_ITConfig(DMA2_Stream0, DMA_IT_TC | DMA_IT_HT, ENABLE);
162
  //DMA_ITConfig(DMA2_Stream0, DMA2_Stream0_IRQn, ENABLE);  //alle Interrupts an
163
164
165
  // Alles einschalten
166
167
  // DMA Request nachdem ADC1 fertig
168
  ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);
169
170
  // ADC1 DMA anschalten
171
  ADC_DMACmd(ADC1, ENABLE);
172
173
  // ADC1 anschalten
174
  ADC_Cmd(ADC1, ENABLE);
175
176
  //------------------------------------------------brauch ich das?!?
177
  //ADC_SoftwareStartConv(ADC1);
178
179
  //ich glaube ich muss nur den Timer starten
180
  TIM_Cmd(TIM2, ENABLE);
181
}
182
183
184
void setupSWI()
185
{
186
  NVIC_InitTypeDef NVIC_InitStructure;
187
  EXTI_InitTypeDef EXTI_InitStructure;
188
189
  // CLock
190
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
191
192
  // GPIO lass ich erstmal weg, da nur SWI
193
194
  // EXTI Line an Pin, auch weg
195
196
  // Exti Line 0
197
  EXTI_InitStructure.EXTI_Line = EXTI_Line0;
198
  EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
199
  EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
200
  EXTI_InitStructure.EXTI_LineCmd = ENABLE;
201
  EXTI_Init(&EXTI_InitStructure);
202
203
  // EXTI Interrupt
204
  NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
205
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
206
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
207
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
208
  NVIC_Init(&NVIC_InitStructure);
209
210
}

Hier meine Interrupts:
1
// DMA1 Stream7 Channel0 --> SPI3_Tx --> Audio-DAC
2
extern "C" void DMA1_Stream7_IRQHandler(void)
3
{
4
  GPIO_SetBits(GPIOD,GPIO_Pin_12);
5
  //GPIO_ToggleBits(GPIOD, GPIO_Pin_14);
6
  fZahl++;
7
8
  //Übertragung komplett fertig (ein Buffer vollständig beschrieben)
9
  if (DMA_GetITStatus(DMA1_Stream7, DMA_IT_TCIF7) != RESET)
10
  {
11
    //Bit zurücksetzen
12
    DMA_ClearITPendingBit(DMA1_Stream7, DMA_IT_TCIF7);
13
14
    //Flag setzten, welche Buffer jetzt bearbeitet werden darf
15
    if (flag_dac_buffer)
16
      flag_dac_buffer=0;
17
    else
18
      flag_dac_buffer=1;
19
20
    flag_dac_copy_ready=1;  //ADC ist fertig, buffer kann bearbeitet werden
21
22
    //-->und weiter gehts
23
  }
24
  GPIO_ResetBits(GPIOD,GPIO_Pin_12);
25
}
26
27
//DMA2 Stream0 Channel0 --> ADC1
28
extern "C" void DMA2_Stream0_IRQHandler(void)
29
{
30
  GPIO_SetBits(GPIOD,GPIO_Pin_13);
31
  //GPIO_ToggleBits(GPIOD, GPIO_Pin_15);
32
  Zahl++;
33
34
  //Übertragung komplett fertig (ein Buffer vollständig beschrieben)
35
  if (DMA_GetITStatus(DMA2_Stream0, DMA_IT_TCIF0) != RESET)
36
  {
37
    //ADC_DMACmd(ADC1, DISABLE);
38
39
    //Bit zurücksetzen
40
    DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF0);
41
42
    //Flag setzten, welche Buffer jetzt bearbeitet werden darf
43
    if (flag_adc_buffer)
44
      flag_adc_buffer=0;
45
    else
46
      flag_adc_buffer=1;
47
48
    flag_adc_copy_ready=1;  //ADC ist fertig, buffer kann bearbeitet werden
49
50
    //Software-Interrupt für Filter und Buffer kopieren starten
51
    EXTI_GenerateSWInterrupt(EXTI_Line0);
52
53
    //-->und weiter gehts
54
    //ADC_DMACmd(ADC1, ENABLE);
55
56
  }
57
  if (DMA_GetITStatus(DMA2_Stream0, DMA_IT_HTIF0) != RESET)
58
  {
59
    //Bit zurücksetzen
60
    DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_HTIF0);
61
  }
62
  GPIO_ResetBits(GPIOD,GPIO_Pin_13);
63
}
64
65
extern "C" void EXTI0_IRQHandler(void)
66
{
67
  GPIO_SetBits(GPIOD,GPIO_Pin_14);
68
  if (EXTI_GetITStatus(EXTI_Line0) != RESET)
69
  {
70
    //Bit zurücksetzen
71
    EXTI_ClearITPendingBit(EXTI_Line0);
72
73
    anz_swi++;
74
75
    //Filter bearbeiten
76
    if(flag_dac_copy_ready)
77
    {
78
      blabla++;
79
    if (flag_adc_buffer)
80
    {
81
      if (flag_dac_buffer)
82
      {
83
        aa++;
84
        memcpy(dac_buffer_0, adc_buffer_0, 2*DAC_BUFFER_SIZE);
85
      }
86
      else
87
      {
88
        bb++;
89
        memcpy(dac_buffer_0, adc_buffer_1, DAC_BUFFER_SIZE);
90
      }
91
    }
92
    else
93
    {
94
      if (flag_dac_buffer)
95
      {
96
        cc++;
97
        memcpy(dac_buffer_1, adc_buffer_0, DAC_BUFFER_SIZE);
98
      }
99
      else
100
      {
101
        dd++;
102
        memcpy(dac_buffer_1, adc_buffer_1, 2*DAC_BUFFER_SIZE);
103
      }
104
    }
105
    }
106
    /*
107
    if (flag_adc_buffer)
108
      memcpy(dac_buffer_0, adc_buffer_0, 2*DAC_BUFFER_SIZE);
109
    else
110
      memcpy(dac_buffer_1, adc_buffer_1, 2*DAC_BUFFER_SIZE);
111
    */
112
  }
113
  GPIO_ResetBits(GPIOD,GPIO_Pin_14);
114
}


EXTI_Line0 verwende ich als Software-Interrupt, hier sollen die 
adc_buffer in die dac_buffer kopiert werden (jeweils die Buffer, die 
nicht verwendet werden). Später wird hier noch ein bisschen 
Signalverarbeitung betrieben.

SystemInit() wird in main() aufgerufen, SYSCLK ist 168MHz.

Folgende Probleme habe ich:
1. Die Frequenz des Audio-DAC ist auf 48kHz eingestellt, ich messe 
allerdings nur ca 30kHz. Daraufhin habe ich "HSE_VALUE=16000000" als 
define eingefügt-->nun passt die Frequenz (gemessen mit GPIOs und Oszi).

2. Die Abtastfrequenz des ADC passt nicht, geplant war (erst einmal) 
fa=48kHz einzustellen (so wird die Variable uint16_t fa initialisiert 
wird),  ich messe allerdings ca 2kHz.

3. Der ADC liefert komische Werte, siehe Anhang. Ich habe ein 
Frequenzgenerator angeschlossen, eingestellt ist ein Sinus-Signal, 1V 
Spitze-Spitze, 1V Gleichanteil (da der ADC-Bereich zwischen 0V und 3V 
liegt).


4. Durch das define ist der SYSCLK nun 107MHz.



Sieht jemand einen Fehler bei der Konfiguration oder bei den Interrupts 
oder hat sonst irgend einen Tipp? Auch wie ich den Audio-DAC auf die 
richtige Frequenz bringe und trotzdem mit 168MHz fahren kann?

von Mar K. (mar_kus)


Lesenswert?

ich bin chillking, jetzt nur angemeldet :)

Nachtrag:
Das Bild habe ich mit Excel gemacht, indem ich im Debug-Modus den 
Buffer-Inhalt kopiert habe.

Der Ausgang was aus dem DAC rauskommt ist natürlich ebenfalls kein 
schöner Sinus, sondern ähnlich dem, was mir der ADC liefert. Komisch 
ist, dass bei Pausieren im Debug, der Ausgang des DAC ein reiner Sinus 
ist (bis auf ein paar Werte die nicht ganz passen). Sobald ich weiter 
laufen lasse kommt wieder Müll.
Ich denke das liegt auch an der Tatsache, dass die Frequenz des ADC 
nicht passt (deutlich zu hoch).


Danke schonmal!!!!

von Mar K. (mar_kus)


Lesenswert?

niemand einen Tipp?

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Habe deinen source jetzt nur mal gaaanz grob überflogen, paar 
Kleinigkeiten, Tipps, Gedanken und Ideen:

 - Guck mal ins ReferenceManual unter RCC, die Timer haben je nach 
APB-CLK-Prescaler eine Taktung von *2
 - Timer Prescaler immer -1 nehmen, also falls du tatsächlich 1750 
ausgerechnet hast, noch 1 abziehen
 - Guck mal ins ProgrammingManual unter ADC, mit welchem Tempo du den 
maximal extern triggern kannst
 - Stell HSE auf 8M und überprüfe in der system_irgendwas.c alle Teiler
 - Versuch erst mal nur den ADC mit DMA2 laufen zu lassen um zu schauen 
ob da alles passt, double buffer, DAC, etc. alles mal weg lassen

Drück dir die Daumen!

EDIT: Sehe gerade du hast nen ADC-Prescaler von 2. Den ADC kannst 
maximal mit 35MHz takten.

: Bearbeitet durch User
von Mar K. (mar_kus)


Lesenswert?

Hi!

Danke für die Antwort!!!

Ich hab jetzt nach weiterem langem Versuchen ein MinimalProgramm, in dem 
ich einfach nur ein Timer initialisiere und darin ein GPIO toggle.

In system_stm32f4xx.c habe ich
1
#if !defined  (HSE_VALUE) 
2
  #define HSE_VALUE    ((uint32_t)8000000) /*!< Value of the External oscillator in Hz */
3
#endif /* HSE_VALUE */
geändert. Vorher stand da 25000000 drin.

Die Teiler
1
/* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N */
2
#define PLL_M      25
3
#define PLL_N      336
4
5
/* SYSCLK = PLL_VCO / PLL_P */
6
#define PLL_P      2
sollten nach meiner Recherche auch passen.

Mein aktueller Code:
1
#include <stm32f4xx_gpio.h>
2
#include <stm32f4xx_tim.h>
3
#include <stm32f4xx_rcc.h>
4
#include <misc.h>
5
6
void InitializeLEDs()
7
{
8
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
9
10
    GPIO_InitTypeDef gpioStructure;
11
    gpioStructure.GPIO_Pin = GPIO_Pin_13;
12
    gpioStructure.GPIO_Mode = GPIO_Mode_OUT;
13
    gpioStructure.GPIO_Speed = GPIO_Speed_50MHz;
14
    GPIO_Init(GPIOD, &gpioStructure);
15
16
}
17
18
void InitializeTimer()
19
{
20
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
21
22
    TIM_TimeBaseInitTypeDef timerInitStructure;
23
    timerInitStructure.TIM_Prescaler = 83;
24
    timerInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
25
    timerInitStructure.TIM_Period = 999;
26
    timerInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
27
    timerInitStructure.TIM_RepetitionCounter = 0;
28
    TIM_TimeBaseInit(TIM2, &timerInitStructure);
29
    TIM_Cmd(TIM2, ENABLE);
30
  TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
31
}
32
33
void EnableTimerInterrupt()
34
{
35
    NVIC_InitTypeDef nvicStructure;
36
    nvicStructure.NVIC_IRQChannel = TIM2_IRQn;
37
    nvicStructure.NVIC_IRQChannelPreemptionPriority = 0;
38
    nvicStructure.NVIC_IRQChannelSubPriority = 1;
39
    nvicStructure.NVIC_IRQChannelCmd = ENABLE;
40
    NVIC_Init(&nvicStructure);
41
}
42
43
extern "C" void TIM2_IRQHandler()
44
{
45
    if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
46
    {
47
        TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
48
        GPIO_ToggleBits(GPIOD, GPIO_Pin_13);
49
    }
50
}
51
52
int main()
53
{
54
  SystemInit();
55
  RCC_ClocksTypeDef Clocks;
56
  RCC_GetClocksFreq(&Clocks);
57
58
    InitializeLEDs();
59
    InitializeTimer();
60
  EnableTimerInterrupt();
61
62
    for (;;)
63
    {
64
65
  }
66
}

Und nicht mal jetzt passt der Takt!
Ich messe am GPIO per Oszi 320,5Hz.

Im Debug werden mir folgende Werte angezeigt:
Clocks  {...}
  SYSCLK_Frequency  53760000
  HCLK_Frequency  53760000
  PCLK1_Frequency  13440000
  PCLK2_Frequency  26880000

: Bearbeitet durch User
von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Die Teiler stimmen nicht, guck mal hier:
http://stm32f4-discovery.com/2015/01/properly-set-clock-speed-stm32f4xx-devices/

EDIT: Schaffst du mit C++ oder warum das extern "C"?

: Bearbeitet durch User
von Mar K. (mar_kus)


Lesenswert?

Reginald L. schrieb:
> Die Teiler stimmen nicht, guck mal hier:
> http://stm32f4-discovery.com/2015/01/properly-set-...
>
> EDIT: Schaffst du mit C++ oder warum das extern "C"?

Okay top, nun bin ich mit dem SysClk auf 168MHz und der Ausgang passt 
nun auch! Hab die Seite vorher schon durchgeschaut, aber hab es da wohl 
falsch verstanden oder war zu blöd...Dank Dir!
Dann werde ich jetzt mal Schritt für Schritt weiter machen.

Ich sollte, ein paar Teile vom eigentlichen Code sind in C++, da musste 
ich nur feststellen, dass ich mit der Laufzeit nicht hingekommen bin und 
versuch mich nun an der DMA.

von Mar K. (mar_kus)


Lesenswert?

Reginald L. schrieb:
> EDIT: Sehe gerade du hast nen ADC-Prescaler von 2. Den ADC kannst
> maximal mit 35MHz takten.

In der stm32f4xx_adc.h ist ADC_Prescaler_Div2 der kleinste Wert oder 
geht das noch irgendwie anders?

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Mar K. schrieb:
> Reginald L. schrieb:
>> EDIT: Sehe gerade du hast nen ADC-Prescaler von 2. Den ADC kannst
>> maximal mit 35MHz takten.
>
> In der stm32f4xx_adc.h ist ADC_Prescaler_Div2 der kleinste Wert oder
> geht das noch irgendwie anders?

84 / 2 = 42 ; 84 / 4 = 21 ;)

von Mar K. (mar_kus)


Angehängte Dateien:

Lesenswert?

Reginald L. schrieb:
> Mar K. schrieb:
>> Reginald L. schrieb:
>>> EDIT: Sehe gerade du hast nen ADC-Prescaler von 2. Den ADC kannst
>>> maximal mit 35MHz takten.
>>
>> In der stm32f4xx_adc.h ist ADC_Prescaler_Div2 der kleinste Wert oder
>> geht das noch irgendwie anders?
>
> 84 / 2 = 42 ; 84 / 4 = 21 ;)

Aber direkt mit 84MHz ohne Teiler (bzw Teiler 1) geht nicht?


EDIT: ADC + TIM2 geht...

EDIT EDIT: ADC + TIM2 + DMA funktioniert glaube ich auch. Habe das 
Transmission Complete-Bit abgefragt und darin wieder ein GPIO 
getogglet-->48Hz, da jeweils 1000 Werte eingelesen werden komme ich bei 
meinen gewünschten 48kHz raus (prescaler=35-1; period=50-1; 
ClockDivision=TIM_CLK_DIV1).
Es werden auf jeden Fall beide Buffer beschrieben, allerdings verstehe 
ich hier zwei Dinge noch nicht ganz.
Habe einen Ausschnitt der Daten aus einem Buffer wieder angehängt, hier 
sieht die Kurve auch ganz gut aus (Frequenzgenerator Sinus, f=1kHz, 
Amplitude=Offset=1V). Woher kommen die beiden Fehler am Ende bei den 
Werten 766 und 892? (Habe die Werte während eines Breakpoints nachdem 
das DMA_IT_TCIF0 kam ausgelesen)

Zweitens bin ich mir mit den Interrupts nicht ganz sicher. Ich dachte, 
das TC kommt, wenn die DMA beide Buffer beschrieben hat und das HT wenn 
die DMA mit dem ersten Buffer fertig ist. Nun sieht es im Debugger aber 
so aus, dass beide Buffer sowohl bei HT als auch bei TC neu beschrieben 
wurden. Hast du da eine Ahnung wann welches Flag kommt?

Vielen Dank für deine Hilfe!

: Bearbeitet durch User
von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Mar K. schrieb:
> Aber direkt mit 84MHz ohne Teiler (bzw Teiler 1) geht nicht?
Der ADC Controller arbeitet mit maximal 35MHz, dh. bei nem APB von 84MHz 
musst du mindestens nen Prescaler von 4 reinhaun. Zumindest, wenn man 
nach Spezifikation geht.

Mar K. schrieb:
> Habe einen Ausschnitt der Daten aus einem Buffer wieder angehängt, hier
> sieht die Kurve auch ganz gut aus (Frequenzgenerator Sinus, f=1kHz,
> Amplitude=Offset=1V). Woher kommen die beiden Fehler am Ende bei den
> Werten 766 und 892? (Habe die Werte während eines Breakpoints nachdem
> das DMA_IT_TCIF0 kam ausgelesen)
Fülle die Buffer vor jeder Aufnahme mal mit 0en und schau mal wie der 
Buffer dann in Excel aussieht. Und wie gesagt, mach erst mal 
SingleBuffer. Zur Fehlersuche würde ich persönlich den DMA nicht in 
Circular laufen lassen sondern manuell zünden. Ach und ich lasse meine 
ADCs in meinem Projekt durchgehend auf Enabled und steuere die Füllung 
des Buffers mit dem DMA. Dürfte aber ansich eigentlich egal sein.

Mar K. schrieb:
> Zweitens bin ich mir mit den Interrupts nicht ganz sicher. Ich dachte,
> das TC kommt, wenn die DMA beide Buffer beschrieben hat und das HT wenn
> die DMA mit dem ersten Buffer fertig ist. Nun sieht es im Debugger aber
> so aus, dass beide Buffer sowohl bei HT als auch bei TC neu beschrieben
> wurden. Hast du da eine Ahnung wann welches Flag kommt?
TC kommt nach jedem CounterReset. ReferenceManual.


Das HT-Flag habe ich bisher noch nie benutzt. Ausser in irgendwelchen 
Sonderfällen (bspw. SingleBuffer und anderweitige Verwendung des Buffers 
während der Aufnahme) brauchst das normalerweise nicht.

von Mar K. (mar_kus)


Lesenswert?

Reginald L. schrieb:
> Der ADC Controller arbeitet mit maximal 35MHz, dh. bei nem APB von 84MHz
> musst du mindestens nen Prescaler von 4 reinhaun. Zumindest, wenn man
> nach Spezifikation geht.

Jetzt hab auch ich es verstanden, so gesehen macht das natürlich Sinn.

Reginald L. schrieb:
> Fülle die Buffer vor jeder Aufnahme mal mit 0en und schau mal wie der
> Buffer dann in Excel aussieht. Und wie gesagt, mach erst mal
> SingleBuffer. Zur Fehlersuche würde ich persönlich den DMA nicht in
> Circular laufen lassen sondern manuell zünden. Ach und ich lasse meine
> ADCs in meinem Projekt durchgehend auf Enabled und steuere die Füllung
> des Buffers mit dem DMA. Dürfte aber ansich eigentlich egal sein.

Das werde ich morgen früh testen und berichten, bin jetzt grad daheim 
angekommen und hab hier kein Frequenzgenerator usw.
Zum weiteren Testen mach ich den double Buffer wieder aus, wollte nur 
testen ob das noch funktioniert.
Heißt in der Konfiguration DMA_Mode = DMA_Mode_Normal und dann im 
Interrupt bei Bedarf starten, nur wie startest den dann? Ich hab das mal 
mit ADC_DMACmd(ADC1, DISABLE); und ADC_DMACmd(ADC1, ENABLE); versucht, 
das hat aber nicht so richtig funktioniert. Hab mir dann gedacht (und 
meine auch irgendwo gelesen), dass es dem ADC nicht gefällt, wenn er vom 
Timer getriggert wird, der alter Wert aber noch nicht ausgelesen wurde.

Reginald L. schrieb:
> TC kommt nach jedem CounterReset. ReferenceManual.

Okay, kommt also immer wenn ein Buffer vollständig beschrieben wurde. 
Ist mir auch grad eingefallen, dass ich das auch schon einmal getestet 
hatte. Ist für mich ziemlich verwirrend, dass der DMA auch weiterläuft, 
wenn der µC eigentlich an einem Breakpoint hängt...das hat mich vorhin 
dann wohl wieder erwischt :D
Ja dieses ReferenceManual und auch ProgrammingManual und die ganze 
ApplicationManual...so richtig durchsteigen tu ich da noch nicht. Da hab 
ich entweder das Gefühl, etwas total sinnloses und unwichtiges 
durchzulesen, oder ich blicks nicht. Fällt mir sehr schwer da was 
brauchbares rauszubekommen, fehlt mir wohl (hoffentlich) noch die Übung.

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Mar K. schrieb:
> Heißt in der Konfiguration DMA_Mode = DMA_Mode_Normal und dann im
> Interrupt bei Bedarf starten, nur wie startest den dann? Ich hab das mal
> mit ADC_DMACmd(ADC1, DISABLE); und ADC_DMACmd(ADC1, ENABLE); versucht,
> das hat aber nicht so richtig funktioniert. Hab mir dann gedacht (und
> meine auch irgendwo gelesen), dass es dem ADC nicht gefällt, wenn er vom
> Timer getriggert wird, der alter Wert aber noch nicht ausgelesen wurde.
Alles, bis auf das DMA EN Bit auf Enabled setzen, mit dem EN Bit dann 
starten und / oder stoppen. Ist vielleicht nicht die feine englische Art 
aber funktioniert, zumindest bei mir, hervorragend. Bei mir ändern sich 
so viele Variablen, von denen der DMA und der ADC abhängen, dass dies in 
meinem Projekt, zumindest meiner Ansicht nach, den schnellsten 
ausführbaren Code darstellt. Je nach Projekt / Anforderung ist die feine 
Englische Art vllt eher zu bevorzugen. Dabei musst du noch beachten, wie 
du schon gelesen hast, dass du die OVR Flags und / oder die DR-Register 
ausließt, bevor du den DMA wieder startest, sonst könnte es sein, dass 
der ADC den DMA sofort nach Aktivierung, noch mit dem alten DR-Wert, 
triggert. Siehe hierzu ReferenceManual (das lesen nehme ich dir nicht 
ab).
Hierzu sei noch gesagt: Ich hantiere immer wieder mal mit den OVR-Flags. 
Ich habe gemerkt, dass man die nicht als "Error" sehen darf sondern 
vielmehr als Information. Je nach Anwendung, vor allem, wenn man sehr 
individuelle Algorithmen für eine Lösung schreibt, schießt einem gerne 
das OVR-Flag um die Ohren.

Mar K. schrieb:
> Okay, kommt also immer wenn ein Buffer vollständig beschrieben wurde.
> Ist mir auch grad eingefallen, dass ich das auch schon einmal getestet
> hatte. Ist für mich ziemlich verwirrend, dass der DMA auch weiterläuft,
> wenn der µC eigentlich an einem Breakpoint hängt...das hat mich vorhin
> dann wohl wieder erwischt :D
Ist dem so? Zu der Thematik konnte ich mich bisher noch nicht wirklich 
einlesen, lediglich ein paar Abschnitte zu den Timern. Bei denen kann 
man das Stoppen beim Debuggen aber über ein Register erzwingen.
Wobei, jetzt wo du es sagst, der LTDC läuft ja auch weiter beim 
Debuggen, ist mir aufgefallen. Andererseits stoppt der DMA2D 
(augenscheinlich) beim Debuggen.

Mar K. schrieb:
> Ja dieses ReferenceManual und auch ProgrammingManual und die ganze
> ApplicationManual...so richtig durchsteigen tu ich da noch nicht. Da hab
> ich entweder das Gefühl, etwas total sinnloses und unwichtiges
> durchzulesen, oder ich blicks nicht. Fällt mir sehr schwer da was
> brauchbares rauszubekommen, fehlt mir wohl (hoffentlich) noch die Übung.
Wie lange bist du denn schon dabei? Ich habe vor etwa einem Jahr mit dem 
STM und C angefangen. Es hat etwa 1-2 Monate gedauert bis ich die 
Manuals zu schätzen und benutzen gelernt habe.
Wenn du mal was absolut individuelles mit dem STM anfangen willst, 
kommst du um die Dinger nicht herum. Klar, für Standardzeugs kopiert man 
sich meist von irgendwoher Code, bzw. tippt ab. So habe ich es ganz am 
Anfang auch gemacht. Inzwischen schau ich mir kurz ein Beispiel zu einer 
Peripherie an und gehe dann gleich in die Manuals über. Oft ist es dann 
auch so, dass die SPL bestimmte Bits nicht so setzt wie du es möchtest, 
oder du direkt auf Register zugreifen möchtest, zb. bei zeitkritischen 
Anwendungen. Ohne Manual geht das eben nicht.

EDIT: Das hier habe ich vor kurzem mal hier im Forum gepostet, da hatte 
ich ein Problem mit SPI ;)
"In den Interrupts
wird ausschließlich mit "DMAx_Streamx->CR |= (uint32_t)DMA_SxCR_EN"
gearbeitet. Mein Denkfehler war, dass SPI anfängt mit OVR's um sich zu
schmeißen und mich dann nicht mehr mag, wenn ich ihm den DMA wegnehme.
Mit OVR's schmeißt er zwar rum, wenn ihm keiner das DR-Register leert,
aber mögen tut er mich trotzdem :)"

: Bearbeitet durch User
von Mar K. (mar_kus)


Lesenswert?

Reginald L. schrieb:
> Ist dem so? Zu der Thematik konnte ich mich bisher noch nicht wirklich
> einlesen, lediglich ein paar Abschnitte zu den Timern. Bei denen kann
> man das Stoppen beim Debuggen aber über ein Register erzwingen.
> Wobei, jetzt wo du es sagst, der LTDC läuft ja auch weiter beim
> Debuggen, ist mir aufgefallen. Andererseits stoppt der DMA2D
> (augenscheinlich) beim Debuggen.

Also zumindest der DMA1, der bei mir die SPI3-Schnittstelle befeuert 
arbeitet während eines Breakpoints weiter, ich seh am Oszi die selbe 
Kurve wie im normalen Durchlauf.


Reginald L. schrieb:
> Wie lange bist du denn schon dabei? Ich habe vor etwa einem Jahr mit dem
> STM und C angefangen. Es hat etwa 1-2 Monate gedauert bis ich die
> Manuals zu schätzen und benutzen gelernt habe.

Dann dauert das bei mir schon einmal länger. Ich mach meine 
Projektarbeit mit dem Board, heißt im November15 hab ich mit Cortex-M4 
angefangen. Im Labor hab ich eine Lib bekommen, die sie selbst 
geschrieben haben, mit der der Einstieg recht einfach war (dass sich 
überhaupt mal was getan hat). Sobald man aber ein bisschen was anderes 
machen will, muss man wieder alles selbst machen, wodurch sich der 
richtige Einstieg bei mir nur etwas verschoben hat.
Bin aber echt beeindruckt, wie viel Leistung einmal hier zur Verfügung 
steht, hätte ich nicht gedacht. Aber war auch klar, dass man dafür eben 
auch mehr Arbeit reinstecken muss. Aber irgendwann wird das schon.

Reginald L. schrieb:
> Alles, bis auf das DMA EN Bit auf Enabled setzen, mit dem EN Bit dann
> starten und / oder stoppen. Ist vielleicht nicht die feine englische Art
> aber funktioniert, zumindest bei mir, hervorragend. Bei mir ändern sich
> so viele Variablen, von denen der DMA und der ADC abhängen, dass dies in
> meinem Projekt, zumindest meiner Ansicht nach, den schnellsten
> ausführbaren Code darstellt. Je nach Projekt / Anforderung ist die feine
> Englische Art vllt eher zu bevorzugen. Dabei musst du noch beachten, wie
> du schon gelesen hast, dass du die OVR Flags und / oder die DR-Register
> ausließt, bevor du den DMA wieder startest, sonst könnte es sein, dass
> der ADC den DMA sofort nach Aktivierung, noch mit dem alten DR-Wert,
> triggert. Siehe hierzu ReferenceManual (das lesen nehme ich dir nicht
> ab).
> Hierzu sei noch gesagt: Ich hantiere immer wieder mal mit den OVR-Flags.
> Ich habe gemerkt, dass man die nicht als "Error" sehen darf sondern
> vielmehr als Information. Je nach Anwendung, vor allem, wenn man sehr
> individuelle Algorithmen für eine Lösung schreibt, schießt einem gerne
> das OVR-Flag um die Ohren.

Also nur per DMA_Cmd(DMA2_Stream0, DISABLE); bzw ENABLE und ADC-Wert 
auslesen funktioniert es schon mal nicht (Mode=Normal), werde mich da 
mal durch das Manual wühlen.

Vielen Dank für deine Hilfe!

von Mar K. (mar_kus)


Angehängte Dateien:

Lesenswert?

Ja wunderbar, mit
1
//DMA2 Stream0 Channel0 --> ADC1
2
extern "C" void DMA2_Stream0_IRQHandler(void)
3
{
4
  GPIO_SetBits(GPIOD,GPIO_Pin_13);
5
  //GPIO_ToggleBits(GPIOD, GPIO_Pin_15);
6
  Zahl++;
7
8
  //Übertragung komplett fertig (ein Buffer vollständig beschrieben)
9
  if (DMA_GetITStatus(DMA2_Stream0, DMA_IT_TCIF0) != RESET)
10
  {
11
    DMA_Cmd(DMA2_Stream0, DISABLE);
12
13
    TIM_Cmd(TIM2, DISABLE);
14
    //GPIO_ToggleBits(GPIOD,GPIO_Pin_13);
15
    //Bit zurücksetzen
16
    DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF0);
17
18
    //Flag setzten, welche Buffer jetzt bearbeitet werden darf
19
    if (flag_adc_buffer)
20
      flag_adc_buffer=0;
21
    else
22
      flag_adc_buffer=1;
23
24
    flag_adc_copy_ready=1;  //ADC ist fertig, buffer kann bearbeitet werden
25
26
    //Software-Interrupt für Filter und Buffer kopieren starten
27
    //EXTI_GenerateSWInterrupt(EXTI_Line0);
28
29
    //-->und weiter gehts
30
    //TIM_Cmd(TIM2, ENABLE);
31
32
33
    //convValue=ADC_GetConversionValue(ADC1);
34
35
    memcpy(adc_buffer_0, adc_buffer_1, ADC_BUFFER_SIZE);
36
37
    TIM_Cmd(TIM2, ENABLE);
38
    DMA_Cmd(DMA2_Stream0, ENABLE);
39
  }
40
41
  GPIO_ResetBits(GPIOD,GPIO_Pin_13);
42
}
funktionierts und ich kann den Buffer überprüfen. Sieht gut aus würde 
ich sagen (siehe Anhang). Die Unterbrechungen zwischendrin sind 
lediglich ein Kopierfehler der Daten von Coocox in Excel (eine Leerzeile 
immer nach 100 Werten).

Würdest du generell die Buffer vor dem neuen Beschreiben mit 0er füllen, 
oder machst du das nur zum Testen?

: Bearbeitet durch User
von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Na also.
Noch was: Versuche in Abhängigkeit deiner Samplingfrequenz das Maximum 
an ADC-Sampling-Time einzustellen. Je länger eine ADC Wandlung läuft 
desto genauer ist sie (Mittelwert).

Die 0er waren nur eine Anregung zwecks Test.

: Bearbeitet durch User
von Mar K. (mar_kus)


Lesenswert?

Reginald L. schrieb:
> Na also.
> Noch was: Versuche in Abhängigkeit deiner Samplingfrequenz das Maximum
> an ADC-Sampling-Time einzustellen. Je länger eine ADC Wandlung läuft
> desto genauer ist sie (Mittelwert).
>
> Die 0er waren nur eine Anregung zwecks Test.

Ja das kommt jetzt noch dazu, die Samplingfrequenz soll nämlich 
einstellbar sein. Hat (zumindest vor meine DMA-Exzess) auch 
funktioniert, nur muss ich jetzt dann mal schauen wie ich das mit der 
Anzahl der Werte mache. Denn die Frequenz des Audio-DACs sollte ja 
konstant sein, bzw wäre auch nur in recht großen Schritten einstellbar.

Habe bis jetzt im Kopf dass ich z.B. bei fs=24kHz einfach jeden Wert 
zwei mal verwende (dac_buffer[0]=dac_buffer[1]=adc_buffer[0])...
fa soll aber in 1kHz oder 0,5kHz Schritten einstellbar sein.

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Das kannst du allein über die Periode des Timers bewerkstelligen.

von Mar K. (mar_kus)


Lesenswert?

Reginald L. schrieb:
> Das kannst du allein über die Periode des Timers bewerkstelligen.

Ja das hab ich auch so gemacht, nur weis ich noch nicht genau wie ich 
das mit der Anzahl der Werte mache.
Wenn die Buffer des DAC 1000 Werte beinhalten sind das ja ca. 20ms. Und 
wenn ich die Samplefrequenz des ADCs veränder, passt bei 1000 Werten die 
Dauer ja nicht mehr. Also muss ich (glaub ich zumindest) so viel Werte 
aufnehmen, dass ich wieder bei 20ms bin, und dann je nachdem ein paar 
Werte wegschmeißen oder doppelt nehmen.

Oder hast du da eine andere Idee?

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Mar K. schrieb:
> Reginald L. schrieb:
>> Das kannst du allein über die Periode des Timers bewerkstelligen.
>
> Ja das hab ich auch so gemacht, nur weis ich noch nicht genau wie ich
> das mit der Anzahl der Werte mache.
> Wenn die Buffer des DAC 1000 Werte beinhalten sind das ja ca. 20ms. Und
> wenn ich die Samplefrequenz des ADCs veränder, passt bei 1000 Werten die
> Dauer ja nicht mehr. Also muss ich (glaub ich zumindest) so viel Werte
> aufnehmen, dass ich wieder bei 20ms bin, und dann je nachdem ein paar
> Werte wegschmeißen oder doppelt nehmen.
>
> Oder hast du da eine andere Idee?

Sry, vllt kann dir da jemand anders helfen. Mein Projekt bereitet mir 
schon genug Kopfschmerzen. Weisst ja, vom Denken bekommt man Kopfweh ;)

von Mar K. (mar_kus)


Lesenswert?

Reginald L. schrieb:
> Sry, vllt kann dir da jemand anders helfen. Mein Projekt bereitet mir
> schon genug Kopfschmerzen. Weisst ja, vom Denken bekommt man Kopfweh ;)

Okay klar, trotzdem vielen Dank für deine Hilfe!

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.