Forum: Mikrocontroller und Digitale Elektronik STM32 zwei DMA-Streams mit gleicher Triggerquelle


von Paul S. (mrpaul)


Lesenswert?

Hallo,

ich möchte in meiner Anwendung die Ergebnisse einer ADC-Messung 
GLEICHZEITIG in zwei Puffer (bufA, bufB) schreiben.
Beim ADC nutze ich den "Dual Regular Simultaneous"-Mode, d.h. ADC1 & 
ADC2 werden zeitgleich getriggert. Das Ergebnis (2x16Bit) soll dann 
parallel in die beiden Puffer geschrieben werden. Das Schreiben in den 
Puffer A funktioniert, nur im Puffer B kommt nichts an.
Zur Überprüfung habe ich die "DMA-HalfTransfer"-Interrupts genutzt, um 
mir den Inhalt der Puffer auszugeben. Der Interrupt für den DMA-Stream, 
der Puffer A füttert, wird generiert, der für den anderen Stream/Puffer 
hingegen nicht.
Aktiviere ich jedoch nur den Zweiten und lasse den Stream für den ersten 
Puffer deaktiviert, wird der zweite Puffer ordnungsgemäß gefüllt.
Bei den Streams handelt es sich um DMA2_Stream0 und DMA2_Stream4, 
jeweils mit Channel 0 (=ADC1).

Nun meine Frage:
Kann es sein, dass eine Quelle (ADC1) nicht mehrere DMA-Streams 
gleichzeitig triggern kann? Ich habe nicht wirklich einen Hinweis 
gefunden, dass man das nicht darf.
Aber sobald ich beide Streams nutze, gewinnt DMA2_Stream0.
Scheinbar setzt die Ausführung des DMA2_Stream0 den Trigger zurück, 
sodass bei DMA2_Stream4 nichts mehr ankommt. Ist dem so?

Gäbe es eine Alternative zu meinem Ansatz?

Eine Idee wäre noch, dass der Transfer des ersten Streams den zweiten 
triggert und so die Daten von Puffer A nach B kopiert anstatt diese 
direkt vom ADC zu lesen. Wäre das möglich?

Hier mein Programmcode:
1
void ADCInit(){
2
  GPIO_InitTypeDef GPIO_InitStructure;
3
  ADC_CommonInitTypeDef ADC_CommonInitStructure;
4
  ADC_InitTypeDef ADC_InitStructure;
5
  DMA_InitTypeDef DMA_InitStructure;
6
  NVIC_InitTypeDef NVIC_InitStructure;
7
8
  adcBuffer.bufFFT=0;  // no buffer assigned -> no execution
9
  for(int i=0;i<ADC_BUFFER_LENGTH;i++){
10
    adcBuffer.bufA[i]=0;
11
    adcBuffer.bufB[i]=0;
12
  }
13
  adcBuffer.streamAEnabled = FALSE;
14
  adcBuffer.streamBEnabled = FALSE;
15
16
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
17
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC2, ENABLE);
18
19
  // initialize I/O's
20
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
21
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
22
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
23
  GPIO_Init(GPIOA, &GPIO_InitStructure);
24
25
  // initialize common ADC
26
  ADC_CommonInitStructure.ADC_Mode = ADC_DualMode_RegSimult;
27
  ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4;
28
  ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_1;
29
  ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
30
  ADC_CommonInit(&ADC_CommonInitStructure);
31
32
  // initialize ADC1 & ADC2
33
  ADC_InitStructure.ADC_Resolution = ADC_Resolution_10b;
34
  ADC_InitStructure.ADC_ScanConvMode = DISABLE;
35
  ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
36
  ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising;
37
  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO;
38
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
39
  ADC_InitStructure.ADC_NbrOfConversion = 1;
40
  ADC_Init(ADC1, &ADC_InitStructure);
41
  ADC_Init(ADC2, &ADC_InitStructure);
42
43
  // ADC1 & ADC2 regular channel configuration
44
  // ADC1: CH0 at pin A0
45
  // ADC2: CH1 at pin A1
46
  ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_3Cycles);
47
  ADC_RegularChannelConfig(ADC2, ADC_Channel_1, 1, ADC_SampleTime_3Cycles);
48
49
  // enable DMA request after last transfer
50
  ADC_MultiModeDMARequestAfterLastTransferCmd(ENABLE);
51
52
  // initialize DMA streams
53
  // both streams are filling the buffers circular
54
  // the half transfer interrupt is used to switch the pointers
55
  // second DMA stream starts, when first stream has filled half buffer
56
  DMA_Cmd(DMA2_Stream0, DISABLE);
57
  DMA_Cmd(DMA2_Stream4, DISABLE);
58
  DMA_DeInit(DMA2_Stream0);
59
  DMA_DeInit(DMA2_Stream4);
60
    DMA_InitStructure.DMA_Channel = DMA_Channel_0;
61
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC->CDR;
62
    DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)(&adcBuffer.bufA);
63
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
64
    DMA_InitStructure.DMA_BufferSize = ADC_BUFFER_LENGTH;
65
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
66
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
67
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; // 16 bit
68
    DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_HalfWord;    // 16bit per Transfer
69
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
70
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
71
    DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
72
    DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull;  // may replace by FULL
73
    DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
74
    DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
75
    DMA_Init(DMA2_Stream0, &DMA_InitStructure);
76
    DMA_InitStructure.DMA_Channel = DMA_Channel_0;
77
    DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)(&adcBuffer.bufB);
78
    DMA_Init(DMA2_Stream4, &DMA_InitStructure);
79
80
    // configure DMA half transfer complete interrupt
81
  NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn;
82
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
83
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = NVIC_PRIORITY_DMA2_STREAM0;
84
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = NVIC_SUBPRIORITY_DMA2_STREAM0;
85
  NVIC_Init(&NVIC_InitStructure);
86
87
  NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream4_IRQn;
88
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
89
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = NVIC_PRIORITY_DMA2_STREAM4;
90
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = NVIC_SUBPRIORITY_DMA2_STREAM4;
91
  NVIC_Init(&NVIC_InitStructure);
92
93
  // enable DMA half transfer complete interrupt
94
  DMA_ITConfig(DMA2_Stream0, DMA_IT_HT, ENABLE);
95
  DMA_ITConfig(DMA2_Stream4, DMA_IT_HT, ENABLE);
96
97
  // enable DMA streams
98
  adcBuffer.streamAEnabled = TRUE;
99
  DMA_Cmd(DMA2_Stream0, ENABLE);
100
  DMA_Cmd(DMA2_Stream4, ENABLE);    // enabled during HT interrupt of other channel
101
102
  // enable ADC
103
  ADC_Cmd(ADC1, ENABLE);
104
  ADC_Cmd(ADC2, ENABLE);
105
106
  // sampling will start as soon as timer generates trigger
107
}
108
109
void DMA2_Stream0_IRQHandler(void){
110
  // transfer complete interrupt
111
  if(DMA_GetITStatus(DMA2_Stream0, DMA_IT_HTIF0)==SET){
112
    // clear pending interrupt
113
    DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_HTIF0);
114
    // enable other channel
115
    DMA_Cmd(DMA2_Stream4, ENABLE);
116
    // change buffer pointer to corresponding buffer
117
    adcBuffer.bufFFT = adcBuffer.bufA;
118
119
    // print buffer
120
    usart2Printf("bufA HT\n\r");
121
    for(int i=0;i<ADC_BUFFER_LENGTH;i++){
122
      itoa(adcBuffer.bufA[i],tmp,10);
123
      usart2Printf(tmp);
124
      usart2Printf("\t");
125
      itoa(adcBuffer.bufB[i],tmp,10);
126
      usart2Printf(tmp);
127
      usart2Printf("\n\r");
128
    }
129
  }
130
}
131
132
void DMA2_Stream4_IRQHandler(void){
133
  // transfer complete interrupt
134
  if(DMA_GetITStatus(DMA2_Stream4, DMA_IT_HTIF4)==SET){
135
    // clear pending interrupt
136
    DMA_ClearITPendingBit(DMA2_Stream4, DMA_IT_HTIF4);
137
    // enable other channel
138
    DMA_Cmd(DMA2_Stream0, ENABLE);
139
    // change buffer pointer to corresponding buffer
140
    adcBuffer.bufFFT = adcBuffer.bufB;
141
142
    // print buffer
143
    usart2Printf("bufB HT\n\r");
144
    for(int i=0;i<ADC_BUFFER_LENGTH;i++){
145
      itoa(adcBuffer.bufA[i],tmp,10);
146
      usart2Printf(tmp);
147
      usart2Printf("\t");
148
      itoa(adcBuffer.bufB[i],tmp,10);
149
      usart2Printf(tmp);
150
      usart2Printf("\n\r");
151
    }
152
  }
153
}

: Bearbeitet durch User
von Paul S. (mrpaul)


Lesenswert?

Habe das "Problem" selbst lösen können.

Es funktioniert nicht, zwei DMA-Streams mit der gleichen Interruptquelle 
zu nutzen. Im Manual steht:
1
After an event, the peripheral sends a request signal to the DMA controller. The DMA controller serves the request depending on the channel priorities. As soon as the DMA controller accesses the peripheral, an Acknowledge signal is sent to the peripheral by the DMA controller. The peripheral releases its request as soon as it gets the Acknowledge signal from the DMA controller. Once the request has been deasserted by the peripheral, the DMA controller releases the Acknowledge signal.
Ich interpretiere das so, dass der Request sofort zurückgesetzt wird, 
sobald der erste Stream beginnt. Demzufolge "sieht" der zweite Stream 
den Request nicht mehr.

von Matthias L. (Gast)


Lesenswert?

Paul S. schrieb:
> Ich interpretiere das so, dass der Request sofort zurückgesetzt wird,
> sobald der erste Stream beginnt. Demzufolge "sieht" der zweite Stream
> den Request nicht mehr.

Das macht ja auch Sinn. Immerhin sind die Daten ja korrekt per DMA 
übertragen worden. Warum dann nochmal? Du kannst sie ja von der neuen 
Position weiterverwenden.

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.