Forum: Analoge Elektronik und Schaltungstechnik STM32F4 Disco. ADC Sample Time einstellen


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Justus Z. (justus_zeiger)


Bewertung
0 lesenswert
nicht lesenswert
Hallöchen,

ich habe eine Frage bzw. benötige etwas Hilfe beim Einstellen der 
SampleTime beim STM32F4 Disco. Vielleicht könnt ihr mir da ein wenig 
helfen.

Ich brauche eine stabile Abtastfrequenz von 160kHz. Aber ich finde 
nichts, wo man die Abtastfrequenz beim ADC selbst so genau einstellen 
kann. In den Registern gibt es leider immer nur Prescaler, Resolution, 
SampleTime(3,5,...,480 Cycles) und ADC_TwoSamplingDelay.
Wobei mir auch nicht 100% klar ist, wie sich letzendlich die komplette 
Zeit zusammensetzt.

Bisher habe ich es so gelöst, dass ich den ADC auf Channel 10 im 
continuouse mode laufen lasse, und mit einem Timer IRQ in einer Frequenz 
von 160kHz einen Interrupt auslöse und mir dann den momentanwert des 
AD-Wandlers hole. Aber das muss doch auch irgendiwe mit dem ADC selbst 
möglich sein! Wäre ja schon irgendwie ideal.


Schon mal Danke im Voraus. Wenn gewünscht, kann ich auch gerne mal 
meinen Code mitschicken.

Gruß
Justus

von Dr. Sommer (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Die ADC's haben keine präzisen Timer. Aber du kannst einen der normalen 
Timer auf 160 kHz einstellen und damit den ADC triggern lassen. Im 
Reference Manual steht wie das geht.

von Justus Z. (justus_zeiger)


Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> Die ADC's haben keine präzisen Timer. Aber du kannst einen der normalen
> Timer auf 160 kHz einstellen und damit den ADC triggern lassen. Im
> Reference Manual steht wie das geht.

Naja, das habe ich ja oben geschrieben. So mache ich es ja bisher. Ich 
dachte nur, dass müsste doch auch direkt mittels ADC gehen.
Oder hat jemand noch eine adequate Lösung, wie man das doch über den ADC 
direkt machen kann?


Aber vielleicht kann mir noch jemand behilflich sein, wie ich die Zeit 
berechne, die mein ADC benötigt, um einen Wert umzuwandeln. Und ja, ich 
weiß im HAnd-Manual steht diese Gleichung auf Seite 400. Aber die lässt 
mich da nicht ganz hintersteigen.

von Reginald L. (Firma: HS Ulm) (reggie)


Bewertung
0 lesenswert
nicht lesenswert
Du kannst den adc entweder einschalten oder ausschalten. Samplerate 
richtet sich dann nach der von dir erwähnten Gleichung.

Je schneller du dir die werte abholst, auf welchem Weg auch immer, desto 
schwieriger wird es sicherzustellen, dass du genau den Wert abgeholt 
hast, den du auch haben wolltest. Zumindest, wenn nebenher noch anderes 
zeug läuft. Da musst du dann die Priorität für den adc-teil hochsetzen.

von Dr. Sommer (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Justus Z. schrieb:
> Naja, das habe ich ja oben geschrieben

Nein, du hast continuous mode und interrupts geschrieben. Ich habe 
gesagt, dass der Timer automatisch eine ADC Konvertierung ohne 
Interrupt auslösen kann, über die interne Synchronisations Verschaltung 
im Controller. Nein, es gibt keine andere Möglichkeit, weil dies die 
dafür vorgesehene Möglichkeit ist. Zusammen mit DMA kann man auch ganz 
auf Interrupts verzichten.

von Justus Z. (justus_zeiger)


Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> Nein, du hast continuous mode und interrupts geschrieben. Ich habe
> gesagt, dass der Timer automatisch eine ADC Konvertierung ohne
> Interrupt auslösen kann, über die interne Synchronisations Verschaltung
> im Controller. Nein, es gibt keine andere Möglichkeit, weil dies die
> dafür vorgesehene Möglichkeit ist. Zusammen mit DMA kann man auch ganz
> auf Interrupts verzichten.

Und wo kann ich das genau nachlesen? Das würde mich auch nochmal 
interessieren, wenigstens um mal davon gehört zuhaben und noch eine 
ausweich Möglichkeit zuhaben.
Welche Vorzüge hätte denn diese Variante?

Edit: Oder würde das Gedanklich so funktionieren, dass der Timer dann 
alle 160kHz einen Signal an den ADC gibt eine Konvertierung zu starten? 
Würde mich über guten Input freuen.

Momentan habe ich das Problem, dass irgendwas mit der Zeit nicht so 
genau funktioniert. Ich habe den Timer 3 (84MHz) mit einem Prescaler von 
105 auf 800kHz. Einen Counter mit Auto-Reloader habe ich auf 800kHz / 
160kHz - 1 = 4 gestellt. Somit sollte der Timer einen Interrupt alle 
6,25µs auslösen. In dem Timer Interrupt steht folgender Code:
[...]
if( iBufCho ) {
  aiBuffer1[iBufPos] = ADC1->DR;
  iBufPos++;
  if( iBufPos == iAnzahlAbtast) {
    iBufPos = 0;
    iBufCho = 0;
    fFrequenz = iCalc();
  }
}
else {
  aiBuffer2[iBufPos] = ADC1->DR;
  iBufPos++;
  if( iBufPos == iAnzahlAbtast ) {
    iBufPos = 0;
    iBufCho = 1;
    fFrequenz = iCalc();
  }
}
[...]

Es wird also der konvertierte Wert des AD Wandlers in ein Puffer 
geschrieben.
Wenn der Puffer voll ist, wird eine Rechenfunktion aufgerufen. die ist 
etwas länger und benötigt ca. 60000 Takten. Habe gerade nicht im Kopf, 
wie groß die Rechenleistung beim F4 ist. Aber müsste diese Zeit dann 
noch irgendwie mit beachtet werden?
Weil ich habe das Gefühl, das der Timer nicht richtig läuft. in der 
Calc() lasse ich alle 50 Durchgänge (das müsste eigentlich eine Zeit von 
genau 1s entsprechen) eine LED togglen und die toggled um ca. das 
1.7-Fache zu langsam. Oder vergesse ich bei dem ganzen irgendwas.

: Bearbeitet durch User
von Reginald L. (Firma: HS Ulm) (reggie)


Bewertung
0 lesenswert
nicht lesenswert
Justus Z. schrieb:
> Aber müsste diese Zeit dann
> noch irgendwie mit beachtet werden?
Na klar, genauso wie die ADC_Conversion_Time. Der ADC sampled natürlich 
durchgehend. Aber du holst dir die Daten ja ab.

von Justus Z. (justus_zeiger)


Bewertung
0 lesenswert
nicht lesenswert
Reginald L. schrieb:
> Na klar, genauso wie die ADC_Conversion_Time. Der ADC sampled natürlich
> durchgehend. Aber du holst dir die Daten ja ab.

Gut. Hehe. Das dachte ich mir schon. Ich bin mir halt nur relativ 
unsicher, wie viele Dinge der Microcontroler parallel laufen haben kann, 
ohne das zeitlich etwas beeinflusst wird.

Also die Funktionen die bearbeitet werden sind:
- ADC
- Beschreiben zweier Buffer (abwechselnd)
- Verrechnen der Buffer

ADC:
Der ADC läuft meines Erachtens im Hintergrund (also die Wandlung 
selbst). Wenn diese vorrüber ist, wird ein Interrupt ausgelöst und der 
Wert in eine "Warte-Variable" geschrieben. Das dürfte mit einer 
Geschwindigkeit von t_ADC passieren. Der ADC läuft über den APB2 mit 
84MHz und wird mit einem Prescaler von 6 auf 14MHz runter "gedrosselt". 
Bedient wird nur ein Channel, ein ADC und eine Wandlung. Die Zeit der 
Conversion selbst berechnet sich mit:
T_conv = SamplingTime + 12 Cycles -> 12 Cycles werden denke mal die 
Resolution sein. Leider weiß ich nicht, woraus sich die SampleTime 
zusammen setzt. Dazu kommen noch die ADC_TwoSamplingDelay, die man 
einstellen kann (mind. 5 * T_ADCCLK). Auch hier weiß ich leider nicht, 
warum man die benötigt. Also berechnet sich die Zeit für einen 
kompletten Durchgang des AD-Wandlers mit:
T_ADC = T_conv + T_delay. Richtig? Wobei halt die Wandlung selbst im 
Hintergrund läuft. Die interrupt Funktion des ADC selbst beinhaltet wie 
erwähnt, nur das beschreiben einer Variable und das zurücksetzen eines 
Bits. Also 3 Takte
Timer/Verrechnen der Buffer:
Der läuft wie oben schon Berechnet mit einer Geschwindigkeit von 160kHz. 
Der Code darin benötigt 6 Takte, wenn der Buffer noch nicht gefüllt ist 
und ca. 50000 (nochmal genau nachgerechnet). Somit Berechnet sich die 
Zeit für den Timer zu:
T_timer = 6 bzw. 50000 * T_HCLK. Korrekt?

Und da sehe ich auch gleich eine Frage. Wenn diese timer Zeit über 
160kHz kommt, wird dann ein Interrupt in einen Interrupt ausgelöst?
Und wie könnte ich dieses Problem umgehen. Gibt es eine Möglichkeit den 
Buffer zu befüllen, währenddessen schön gerechnet wird?

Hoffentlich bekomme ich jetzt nicht zu stark den Kopf gewaschen.

Gruß
Justus

: Bearbeitet durch User
von Reginald L. (Firma: HS Ulm) (reggie)


Bewertung
0 lesenswert
nicht lesenswert
Ich würde sie sache ganz anders angehen:
Adc clock: würde ich versuchen auf das mögliche Maximum zu stellen. Je 
höher, desto höher wird die Chance, dass du genau den Sample abholst den 
du haben möchtest. Bei den relativ niedrigen sampling raten die du 
erzielen möchtest reichen die 14mhz aber alle mal.
Conversion time: je höher, desto länger wird vom adc die "anliegende 
Spannung gemessen und gemittelt". Die sollte man also auch auf das 
mögliche Maximum einstellen. Der wert hängt natürlich von deiner 
gewünschten frequenz und der adc clock ab.

Die Abholung der Daten würde ich bei der sampling rate schon fast dem 
dma überlassen. Double buffering und du kannst zwischendurch gemütlich 
deine Werte berechnen.

Justus Z. schrieb:
> Hoffentlich bekomme ich jetzt nicht zu stark den Kopf gewaschen.
Zu der Sorte microcontroller.net-Menschen gehöre ich nicht ;)

von Reginald L. (Firma: HS Ulm) (reggie)


Bewertung
0 lesenswert
nicht lesenswert
Hier mal ein alter Codeschnipsel von mir. Da triggert ein Timer den DMA, 
der die Daten abholt. Aber pass auf, da sind noch einige Fehler 
enthalten, so darf z.B. Timer.Prescaler nur 16bit groß sein. Aber vom 
Prinzip her hilft dir das vllt weiter.

Und der DMA läuft im manuellen DoubleBuffer, der kann das natürlich auch 
von selber.
1
bool _DAQ::Init()
2
{
3
  // Clocks
4
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
5
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
6
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
7
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
8
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
9
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
10
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC2, ENABLE);
11
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
12
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);
13
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM13, ENABLE);
14
15
  //Pins
16
  GPIO_InitTypeDef h_gpio;
17
  ZEROINIT(h_gpio);
18
  h_gpio.GPIO_Mode = GPIO_Mode_IN;
19
  h_gpio.GPIO_OType = GPIO_OType_OD;
20
  h_gpio.GPIO_PuPd = GPIO_PuPd_NOPULL;
21
  h_gpio.GPIO_Speed = GPIO_High_Speed;
22
  h_gpio.GPIO_Pin =
23
    GPIO_Pin_15;    // Trigger Input
24
  GPIO_Init(GPIOA, &h_gpio);
25
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource15);
26
27
  EXTI_InitTypeDef h_exti;
28
  ZEROINIT(h_exti);
29
  h_exti.EXTI_Line = EXTI_Line15;
30
  h_exti.EXTI_LineCmd = ENABLE;
31
  h_exti.EXTI_Mode = EXTI_Mode_Interrupt;
32
  h_exti.EXTI_Trigger = EXTI_Trigger_Rising;
33
  EXTI_Init(&h_exti);
34
35
  h_gpio.GPIO_Mode = GPIO_Mode_AN;
36
  h_gpio.GPIO_OType = GPIO_OType_OD;
37
  h_gpio.GPIO_PuPd = GPIO_PuPd_NOPULL;
38
  h_gpio.GPIO_Pin =
39
    GPIO_Pin_0;    // ADC1 Channel 8
40
  GPIO_Init(GPIOB, &h_gpio);
41
  h_gpio.GPIO_Pin =
42
    GPIO_Pin_1;    // ADC2 Channel 11
43
  GPIO_Init(GPIOC, &h_gpio);
44
45
  // DMA ADC
46
  DMA_InitTypeDef h_dma;
47
  ZEROINIT(h_dma);
48
  h_dma.DMA_BufferSize = 0;
49
  h_dma.DMA_Channel = DMA_Channel_0;
50
  h_dma.DMA_DIR = DMA_DIR_PeripheralToMemory;
51
  h_dma.DMA_FIFOMode = DMA_FIFOMode_Enable;
52
  h_dma.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull;
53
  h_dma.DMA_Memory0BaseAddr = (uint32_t)&rx_buffer1;
54
  h_dma.DMA_MemoryBurst = DMA_MemoryBurst_Single;
55
  h_dma.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
56
  h_dma.DMA_MemoryInc = DMA_MemoryInc_Enable;
57
  h_dma.DMA_Mode = DMA_Mode_Circular;
58
  h_dma.DMA_PeripheralBaseAddr = (uint32_t)&ADC->CDR;
59
  h_dma.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
60
  h_dma.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
61
  h_dma.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
62
  h_dma.DMA_Priority = DAQ_ADC_DMA_PRIORITY;
63
  DMA_Init(DMA2_Stream0, &h_dma);
64
  DMA_DoubleBufferModeConfig(DMA2_Stream0, (uint32_t)&rx_buffer2, DMA_Memory_0);
65
  DMA_DoubleBufferModeCmd(DMA2_Stream0, ENABLE);
66
  DMA_ITConfig(DMA2_Stream0, DMA_IT_TC, ENABLE);
67
68
  // ADC1  
69
  ADC_InitTypeDef h_adc1;
70
  ZEROINIT(h_adc1);
71
  h_adc1.ADC_ContinuousConvMode = DISABLE;
72
  h_adc1.ADC_DataAlign = ADC_DataAlign_Right;
73
  h_adc1.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO;
74
  h_adc1.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising;
75
  h_adc1.ADC_NbrOfConversion = 1;
76
  h_adc1.ADC_Resolution = ADC_Resolution_12b;
77
  h_adc1.ADC_ScanConvMode = DISABLE;
78
  ADC_Init(ADC1, &h_adc1);
79
  ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 1, ADC_SampleTime_480Cycles);
80
81
  // ADC2
82
  ADC_InitTypeDef h_adc2;
83
  ZEROINIT(h_adc2);
84
  h_adc2.ADC_ContinuousConvMode = DISABLE;
85
  h_adc2.ADC_DataAlign = ADC_DataAlign_Right;
86
  h_adc2.ADC_ExternalTrigConv = 0;
87
  h_adc2.ADC_ExternalTrigConvEdge = 0;
88
  h_adc2.ADC_NbrOfConversion = 1;
89
  h_adc2.ADC_Resolution = ADC_Resolution_12b;
90
  h_adc2.ADC_ScanConvMode = DISABLE;
91
  ADC_Init(ADC2, &h_adc2);
92
  ADC_RegularChannelConfig(ADC2, ADC_Channel_11, 1, ADC_SampleTime_480Cycles);
93
94
  // ADC Multimode
95
  ADC_CommonInitTypeDef h_adc_multi;
96
  ZEROINIT(h_adc_multi);
97
  h_adc_multi.ADC_DMAAccessMode = ADC_DMAAccessMode_2;
98
  h_adc_multi.ADC_Mode = ADC_DualMode_RegSimult;
99
  h_adc_multi.ADC_Prescaler = ADC_Prescaler_Div4; // 22.5MHz at 90MHz APB
100
  h_adc_multi.ADC_TwoSamplingDelay = DISABLE;
101
  ADC_CommonInit(&h_adc_multi);
102
103
  ADC_Cmd(ADC1, ENABLE);
104
  ADC_Cmd(ADC2, ENABLE);
105
  ADC_MultiModeDMARequestAfterLastTransferCmd(ENABLE);
106
107
  // Timer 5 - Sync Timer for Data and Trigger
108
  TIM_TimeBaseInitTypeDef h_tim5;
109
  ZEROINIT(h_tim5);
110
  h_tim5.TIM_ClockDivision = TIM_CKD_DIV1;
111
  h_tim5.TIM_CounterMode = TIM_CounterMode_Up;
112
  h_tim5.TIM_Period = 0xFFFFFFFF;    // 5 days Period
113
  h_tim5.TIM_Prescaler = 0;      // Variable Interval
114
  TIM_TimeBaseInit(TIM5, &h_tim5);
115
116
  // Timer 13 - Trigger
117
  TIM_TimeBaseInitTypeDef h_tim13;
118
  ZEROINIT(h_tim13);
119
  h_tim13.TIM_ClockDivision = TIM_CKD_DIV1;
120
  h_tim13.TIM_CounterMode = TIM_CounterMode_Up;
121
  h_tim13.TIM_Period = 65536 - 1;    // ~1.3s Period
122
  h_tim13.TIM_Prescaler = 1800 - 1;  // Interval 20 µs
123
  TIM_TimeBaseInit(TIM13, &h_tim13);
124
  TIM_SelectOnePulseMode(TIM13, TIM_OPMode_Single);
125
126
  // Timer 2 - ADC Trigger
127
  TIM_TimeBaseInitTypeDef h_tim2;
128
  ZEROINIT(h_tim2);
129
  h_tim2.TIM_ClockDivision = TIM_CKD_DIV1;
130
  h_tim2.TIM_CounterMode = TIM_CounterMode_Up;
131
  h_tim2.TIM_Period = 0;      // Variable Period
132
  h_tim2.TIM_Prescaler = 0;    // Interval 0.011 µs
133
  TIM_TimeBaseInit(TIM2, &h_tim2);
134
  TIM_SelectMasterSlaveMode(TIM2, TIM_MasterSlaveMode_Enable);
135
  TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);
136
137
  // Interrupts
138
  NVIC_SetPriority(EXTI15_10_IRQn, NVIC_EncodePriority(NVIC_PriorityGroup_3, DAQ_TRIGGER_IT_PRIORITY, 1));
139
  NVIC_EnableIRQ(EXTI15_10_IRQn);
140
  NVIC_SetPriority(DMA2_Stream0_IRQn, NVIC_EncodePriority(NVIC_PriorityGroup_3, DAQ_BUFFERCOMPLETE_IT_PRIORITY, 1));
141
  NVIC_EnableIRQ(DMA2_Stream0_IRQn);
142
  
143
  return OK;
144
}
145
// On Trigger
146
void EXTI15_10_IRQHandler()
147
{
148
  // Clear IT
149
  EXTI->PR = EXTI_Line15;
150
151
  // Start Trigger Timer
152
  TIM13->CR1 |= TIM_CR1_CEN;
153
154
  // Check if Trigger dead time passed
155
  if (TIM13->CNT > 1500)
156
  {
157
    // Stop Trigger Timer 13
158
    TIM13->CR1 &= (uint16_t)~TIM_CR1_CEN;
159
160
    // Get data
161
    DAQ.roundcounter++;
162
    DAQ.triggervalue = TIM13->CNT;
163
164
    DAQ.p_triggerbufferempty[DAQ.triggerbuffercounter] = TIM5->CNT;
165
    DAQ.triggerbuffercounter++;
166
167
    // Set Flags
168
    DAQ.triggerready = true;
169
170
    // Reset and start Trigger Timer 13
171
    TIM13->CNT = 0;
172
    TIM13->CR1 |= TIM_CR1_CEN;
173
  }
174
}
175
// On Buffer written
176
void DMA2_Stream0_IRQHandler()
177
{
178
  // Clear IT
179
  DMA2->LIFCR |= 0x20;
180
181
  // Switch Buffers
182
  if ((DMA2_Stream0->CR & DMA_SxCR_CT) != 0)
183
  {
184
    DAQ.p_accdatabuffercomplete = (uint32_t*)DAQ.rx_buffer1;
185
    DAQ.p_triggerbuffercomplete = (uint32_t*)DAQ.triggerbuffer1;
186
    DAQ.p_triggerbufferempty = (uint32_t*)DAQ.triggerbuffer2;
187
188
    DAQ.dmatriggervalcompleteSOC = DAQ.dmatriggerval1;
189
    DAQ.dmatriggervalcompleteEOC = DAQ.dmatriggerval2;
190
    DAQ.dmatriggerval2 = TIM5->CNT;
191
  }
192
  else
193
  {
194
    DAQ.p_accdatabuffercomplete = (uint32_t*)DAQ.rx_buffer2;
195
    DAQ.p_triggerbuffercomplete = (uint32_t*)DAQ.triggerbuffer2;
196
197
    DAQ.p_triggerbufferempty = (uint32_t*)DAQ.triggerbuffer1;
198
    DAQ.dmatriggervalcompleteSOC = DAQ.dmatriggerval2;
199
    DAQ.dmatriggervalcompleteEOC = DAQ.dmatriggerval1;
200
    DAQ.dmatriggerval1 = TIM5->CNT;
201
  }
202
203
  DAQ.lasttriggerbuffercounter = DAQ.triggerbuffercounter;
204
  DAQ.triggerbuffercounter = 0;
205
  DAQ.accdataready = true;
206
}

: Bearbeitet durch User
von Dr. Sommer (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Justus Z. schrieb:
> Und wo kann ich das genau nachlesen?
Im Reference Manual natürlich. S. 400-402.

Justus Z. schrieb:
> Edit: Oder würde das Gedanklich so funktionieren, dass der Timer dann
> alle 160kHz einen Signal an den ADC gibt eine Konvertierung zu starten?
Ja.

Justus Z. schrieb:
> Welche Vorzüge hätte denn diese Variante?
Dass du so die Samplerate auf fast jeden beliebigen Wert einstellen 
kannst (alle Teiler des Systemtakts von max. 168MHz). Durch Kaskadierung 
mehrerer Timer auch auf extrem langsame Werte, falls nötig.

Reginald L. schrieb:
> Adc clock: würde ich versuchen auf das mögliche Maximum zu stellen. Je
> höher, desto höher wird die Chance, dass du genau den Sample abholst den
> du haben möchtest.
Dadurch Jittert die Sample-Rate aber im Endeffekt. Nicht gut für 
DSP-Anwendungen.

Dafür ist die Timer-Synchronisation da, für exakte Sample-Rates, warum 
die nicht einfach benutzen?!

Reginald L. schrieb:
> Aber pass auf, da sind noch einige Fehler
> enthalten
Ja, z.B. ist ein Klassenname von _DAQ in C++ verboten (Unterstrich + 
Großbuchstabe, oder Doppelunterstrich, am Anfang, sind der Standard 
Library vorbehalten).

von Ingo L. (corrtexx)


Bewertung
0 lesenswert
nicht lesenswert
Hier mal ein Codeschnippsel, hier wird Timer2 dazu genutzt den ADC mit 
100kHz zu trigger (Samplerate), der DMA holt die Daten ab und wenn der 
Buffer voll ist gibts n Interrupt:
1
void Init_ADC ( void )
2
{
3
  /* Set clock */
4
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
5
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2 , ENABLE);
6
  RCC_APB2PeriphClockCmd( RCC_APB2Periph_ADC1, ENABLE);
7
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
8
9
  GPIO_InitTypeDef GPIO_InitStructure;
10
  GPIO_StructInit(&GPIO_InitStructure);
11
  ADC_InitTypeDef ADC_InitStructure;
12
  ADC_StructInit(&ADC_InitStructure);
13
  ADC_CommonInitTypeDef ADC_CommonInitStructure;
14
  ADC_CommonStructInit(&ADC_CommonInitStructure);
15
  DMA_InitTypeDef DMA_InitStructure;
16
  DMA_StructInit(&DMA_InitStructure);
17
  TIM_TimeBaseInitTypeDef TIM_TimeBase_InitStructure;
18
  TIM_TimeBaseStructInit (&TIM_TimeBase_InitStructure);
19
  NVIC_InitTypeDef NVIC_InitStructure;
20
  NVIC_Init(&NVIC_InitStructure);
21
22
  TIM_TimeBase_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
23
  TIM_TimeBase_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;
24
  TIM_TimeBase_InitStructure.TIM_Period = 840 - 1;
25
  TIM_TimeBase_InitStructure.TIM_Prescaler = 0;
26
  TIM_TimeBaseInit(TIM2, &TIM_TimeBase_InitStructure);
27
28
  TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
29
  TIM_SelectOutputTrigger(TIM2,TIM_TRGOSource_Update);
30
  TIM_ARRPreloadConfig(TIM2, ENABLE);
31
32
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
33
  NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn;
34
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
35
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
36
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
37
  NVIC_Init(&NVIC_InitStructure);
38
39
  /* ADC Kanäle */
40
  /* IO initialisieren */
41
  GPIO_StructInit(&GPIO_InitStructure);
42
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2;
43
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
44
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
45
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
46
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
47
  GPIO_Init(GPIOC, &GPIO_InitStructure);
48
49
  /* DMA Controll */
50
  DMA_InitStructure.DMA_Channel = DMA_Channel_0;
51
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(ADC1->DR);
52
  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)DataBuffer;
53
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
54
  DMA_InitStructure.DMA_BufferSize = BUFFERSIZE;
55
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
56
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
57
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
58
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
59
  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
60
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
61
  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
62
  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
63
  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
64
  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
65
  DMA_Init(DMA2_Stream0, &DMA_InitStructure);
66
  DMA_ITConfig(DMA2_Stream0,DMA_IT_TC,ENABLE );
67
68
69
  /* ADC */
70
  ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
71
  ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div8;
72
  ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
73
  ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
74
  ADC_CommonInit(&ADC_CommonInitStructure);
75
76
  ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
77
  ADC_InitStructure.ADC_ScanConvMode = DISABLE;
78
  ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
79
  ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising;
80
  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO;
81
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
82
  ADC_InitStructure.ADC_NbrOfConversion = 1;
83
  ADC_Init(ADC1, &ADC_InitStructure);
84
85
  ADC_EOCOnEachRegularChannelCmd(ADC1, ENABLE);
86
  ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 1, ADC_SampleTime_28Cycles);
87
88
  DMA_Cmd(DMA2_Stream0, ENABLE);
89
  ADC_Cmd(ADC1, ENABLE);
90
  ADC_DMACmd(ADC1, ENABLE);
91
  ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);
92
}
93
94
void DMA2_Stream0_IRQHandler ( void )
95
{
96
  DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF0);
97
  TIM_Cmd(TIM2, DISABLE);
98
  Parameter.Messtatus = ENDED;
99
}

Zum Starten einfach
1
TIM_Cmd(TIM2, ENABLE);

Timer2 läuft "nur" auf 84MHz, daher Prescaler 840-1 (84MHz/840 = 
100kHz).

von Hanspeter S. (kendo1)


Bewertung
0 lesenswert
nicht lesenswert
Damit der Code von  Ingo  oben bei mir lief, musste ich einige 
Anpassungen vornehmen.  siehe im Kommentar  // muths
1
#include "main.h"
2
#include "stm32f4xx_tim.h"
3
#include "misc.h"
4
#include "stm32f4xx_gpio.h"
5
#include "stm32f4xx_rcc.h"
6
#include "stm32f4xx_adc.h"
7
#include "stm32f4xx_dma.h"
8
9
#define BUFFERSIZE 3
10
volatile uint32_t DataBuffer[BUFFERSIZE];
11
12
volatile uint8_t Messtatus = 0;
13
14
15
void Init_ADC ( void )
16
{
17
  /* Set clock */
18
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
19
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2 , ENABLE);
20
  RCC_APB2PeriphClockCmd( RCC_APB2Periph_ADC1, ENABLE);
21
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
22
23
  GPIO_InitTypeDef GPIO_InitStructure;
24
  GPIO_StructInit(&GPIO_InitStructure);
25
  ADC_InitTypeDef ADC_InitStructure;
26
  ADC_StructInit(&ADC_InitStructure);
27
  ADC_CommonInitTypeDef ADC_CommonInitStructure;
28
  ADC_CommonStructInit(&ADC_CommonInitStructure);
29
  DMA_InitTypeDef DMA_InitStructure;
30
  DMA_StructInit(&DMA_InitStructure);
31
  TIM_TimeBaseInitTypeDef TIM_TimeBase_InitStructure;
32
  TIM_TimeBaseStructInit (&TIM_TimeBase_InitStructure);
33
  NVIC_InitTypeDef NVIC_InitStructure;
34
  NVIC_Init(&NVIC_InitStructure);
35
36
  /* Timer2 läuft "nur" auf 84MHz, daher Prescaler 840-1 (84MHz/840 = 100kHz).*/
37
38
  TIM_TimeBase_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
39
  TIM_TimeBase_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;
40
  TIM_TimeBase_InitStructure.TIM_Period = 840 - 1;
41
  TIM_TimeBase_InitStructure.TIM_Prescaler = 0;
42
  TIM_TimeBaseInit(TIM2, &TIM_TimeBase_InitStructure);
43
44
  TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
45
  TIM_SelectOutputTrigger(TIM2,TIM_TRGOSource_Update);
46
  TIM_ARRPreloadConfig(TIM2, ENABLE);
47
48
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
49
  NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn;
50
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
51
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
52
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
53
  NVIC_Init(&NVIC_InitStructure);
54
55
  /* ADC Kanäle */
56
  /* IO initialisieren */
57
  GPIO_StructInit(&GPIO_InitStructure);
58
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2;
59
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
60
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
61
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
62
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
63
  GPIO_Init(GPIOC, &GPIO_InitStructure);
64
65
  /* DMA Controll */
66
  DMA_InitStructure.DMA_Channel = DMA_Channel_0;
67
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(ADC1->DR);
68
  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)DataBuffer;   // muths   DataBuffer
69
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
70
  DMA_InitStructure.DMA_BufferSize = BUFFERSIZE;
71
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
72
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
73
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;    // muths DMA_PeripheralDataSize_HalfWord;
74
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;      // muths DMA_MemoryDataSize_HalfWord;
75
  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
76
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
77
  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
78
  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
79
  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
80
  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
81
  DMA_Init(DMA2_Stream0, &DMA_InitStructure);
82
  DMA_ITConfig(DMA2_Stream0,DMA_IT_TC,ENABLE );
83
84
85
  /* ADC */
86
  ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
87
  ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div8;
88
  ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
89
  ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
90
  ADC_CommonInit(&ADC_CommonInitStructure);
91
92
  ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
93
  ADC_InitStructure.ADC_ScanConvMode = ENABLE;    // muths    DISABLE;
94
  ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
95
  ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising;
96
  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO;
97
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
98
  ADC_InitStructure.ADC_NbrOfConversion = BUFFERSIZE; // muths   1;
99
  ADC_Init(ADC1, &ADC_InitStructure);
100
101
  ADC_EOCOnEachRegularChannelCmd(ADC1, ENABLE);
102
  ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_28Cycles);     // muths
103
  ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 2, ADC_SampleTime_28Cycles);
104
  ADC_RegularChannelConfig(ADC1, ADC_Channel_12, 3, ADC_SampleTime_28Cycles);    // muths
105
// für alle 6 benötigte Zeit= 1 us
106
107
  DMA_Cmd(DMA2_Stream0, ENABLE);
108
  ADC_Cmd(ADC1, ENABLE);
109
  ADC_DMACmd(ADC1, ENABLE);
110
  ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);
111
}
112
113
void DMA2_Stream0_IRQHandler ( void )
114
{
115
  DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF0);
116
  TIM_Cmd(TIM2, DISABLE);
117
  Messtatus = 1;
118
}
119
120
121
122
int main(void){
123
//----------------------------------------------------
124
125
126
  SystemInit(); // Quarz Einstellungen aktivieren
127
128
  Init_ADC();
129
  TIM_Cmd(TIM2, ENABLE); // Zum Starten
130
  /*
131
  Timer2 läuft "nur" auf 84MHz, daher Prescaler 840-1 (84MHz/840 =
132
  100kHz).
133
  */
134
  while(1)
135
  {
136
137
    if (Messtatus == 1) {
138
      // auswerten etc.
139
    }
140
141
142
143
144
  }
145
}

: Bearbeitet durch User
von m.n. (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Bei gut kommentierten Programmen kann man doch gleich erkennen, wie es 
funktionieren soll.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.