mikrocontroller.net

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


Autor: Justus Zeiger (justus_zeiger)
Datum:

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

Autor: Dr. Sommer (Gast)
Datum:

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.

Autor: Justus Zeiger (justus_zeiger)
Datum:

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.

Autor: Reginald Leonczuk (Firma: HS Ulm) (reggie)
Datum:

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.

Autor: Dr. Sommer (Gast)
Datum:

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.

Autor: Justus Zeiger (justus_zeiger)
Datum:

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
Autor: Reginald Leonczuk (Firma: HS Ulm) (reggie)
Datum:

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.

Autor: Justus Zeiger (justus_zeiger)
Datum:

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
Autor: Reginald Leonczuk (Firma: HS Ulm) (reggie)
Datum:

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 ;)

Autor: Reginald Leonczuk (Firma: HS Ulm) (reggie)
Datum:

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.
bool _DAQ::Init()
{
  // Clocks
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC2, ENABLE);
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM13, ENABLE);

  //Pins
  GPIO_InitTypeDef h_gpio;
  ZEROINIT(h_gpio);
  h_gpio.GPIO_Mode = GPIO_Mode_IN;
  h_gpio.GPIO_OType = GPIO_OType_OD;
  h_gpio.GPIO_PuPd = GPIO_PuPd_NOPULL;
  h_gpio.GPIO_Speed = GPIO_High_Speed;
  h_gpio.GPIO_Pin =
    GPIO_Pin_15;    // Trigger Input
  GPIO_Init(GPIOA, &h_gpio);
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource15);

  EXTI_InitTypeDef h_exti;
  ZEROINIT(h_exti);
  h_exti.EXTI_Line = EXTI_Line15;
  h_exti.EXTI_LineCmd = ENABLE;
  h_exti.EXTI_Mode = EXTI_Mode_Interrupt;
  h_exti.EXTI_Trigger = EXTI_Trigger_Rising;
  EXTI_Init(&h_exti);

  h_gpio.GPIO_Mode = GPIO_Mode_AN;
  h_gpio.GPIO_OType = GPIO_OType_OD;
  h_gpio.GPIO_PuPd = GPIO_PuPd_NOPULL;
  h_gpio.GPIO_Pin =
    GPIO_Pin_0;    // ADC1 Channel 8
  GPIO_Init(GPIOB, &h_gpio);
  h_gpio.GPIO_Pin =
    GPIO_Pin_1;    // ADC2 Channel 11
  GPIO_Init(GPIOC, &h_gpio);

  // DMA ADC
  DMA_InitTypeDef h_dma;
  ZEROINIT(h_dma);
  h_dma.DMA_BufferSize = 0;
  h_dma.DMA_Channel = DMA_Channel_0;
  h_dma.DMA_DIR = DMA_DIR_PeripheralToMemory;
  h_dma.DMA_FIFOMode = DMA_FIFOMode_Enable;
  h_dma.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull;
  h_dma.DMA_Memory0BaseAddr = (uint32_t)&rx_buffer1;
  h_dma.DMA_MemoryBurst = DMA_MemoryBurst_Single;
  h_dma.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
  h_dma.DMA_MemoryInc = DMA_MemoryInc_Enable;
  h_dma.DMA_Mode = DMA_Mode_Circular;
  h_dma.DMA_PeripheralBaseAddr = (uint32_t)&ADC->CDR;
  h_dma.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
  h_dma.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
  h_dma.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  h_dma.DMA_Priority = DAQ_ADC_DMA_PRIORITY;
  DMA_Init(DMA2_Stream0, &h_dma);
  DMA_DoubleBufferModeConfig(DMA2_Stream0, (uint32_t)&rx_buffer2, DMA_Memory_0);
  DMA_DoubleBufferModeCmd(DMA2_Stream0, ENABLE);
  DMA_ITConfig(DMA2_Stream0, DMA_IT_TC, ENABLE);

  // ADC1  
  ADC_InitTypeDef h_adc1;
  ZEROINIT(h_adc1);
  h_adc1.ADC_ContinuousConvMode = DISABLE;
  h_adc1.ADC_DataAlign = ADC_DataAlign_Right;
  h_adc1.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO;
  h_adc1.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising;
  h_adc1.ADC_NbrOfConversion = 1;
  h_adc1.ADC_Resolution = ADC_Resolution_12b;
  h_adc1.ADC_ScanConvMode = DISABLE;
  ADC_Init(ADC1, &h_adc1);
  ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 1, ADC_SampleTime_480Cycles);

  // ADC2
  ADC_InitTypeDef h_adc2;
  ZEROINIT(h_adc2);
  h_adc2.ADC_ContinuousConvMode = DISABLE;
  h_adc2.ADC_DataAlign = ADC_DataAlign_Right;
  h_adc2.ADC_ExternalTrigConv = 0;
  h_adc2.ADC_ExternalTrigConvEdge = 0;
  h_adc2.ADC_NbrOfConversion = 1;
  h_adc2.ADC_Resolution = ADC_Resolution_12b;
  h_adc2.ADC_ScanConvMode = DISABLE;
  ADC_Init(ADC2, &h_adc2);
  ADC_RegularChannelConfig(ADC2, ADC_Channel_11, 1, ADC_SampleTime_480Cycles);

  // ADC Multimode
  ADC_CommonInitTypeDef h_adc_multi;
  ZEROINIT(h_adc_multi);
  h_adc_multi.ADC_DMAAccessMode = ADC_DMAAccessMode_2;
  h_adc_multi.ADC_Mode = ADC_DualMode_RegSimult;
  h_adc_multi.ADC_Prescaler = ADC_Prescaler_Div4; // 22.5MHz at 90MHz APB
  h_adc_multi.ADC_TwoSamplingDelay = DISABLE;
  ADC_CommonInit(&h_adc_multi);

  ADC_Cmd(ADC1, ENABLE);
  ADC_Cmd(ADC2, ENABLE);
  ADC_MultiModeDMARequestAfterLastTransferCmd(ENABLE);

  // Timer 5 - Sync Timer for Data and Trigger
  TIM_TimeBaseInitTypeDef h_tim5;
  ZEROINIT(h_tim5);
  h_tim5.TIM_ClockDivision = TIM_CKD_DIV1;
  h_tim5.TIM_CounterMode = TIM_CounterMode_Up;
  h_tim5.TIM_Period = 0xFFFFFFFF;    // 5 days Period
  h_tim5.TIM_Prescaler = 0;      // Variable Interval
  TIM_TimeBaseInit(TIM5, &h_tim5);

  // Timer 13 - Trigger
  TIM_TimeBaseInitTypeDef h_tim13;
  ZEROINIT(h_tim13);
  h_tim13.TIM_ClockDivision = TIM_CKD_DIV1;
  h_tim13.TIM_CounterMode = TIM_CounterMode_Up;
  h_tim13.TIM_Period = 65536 - 1;    // ~1.3s Period
  h_tim13.TIM_Prescaler = 1800 - 1;  // Interval 20 µs
  TIM_TimeBaseInit(TIM13, &h_tim13);
  TIM_SelectOnePulseMode(TIM13, TIM_OPMode_Single);

  // Timer 2 - ADC Trigger
  TIM_TimeBaseInitTypeDef h_tim2;
  ZEROINIT(h_tim2);
  h_tim2.TIM_ClockDivision = TIM_CKD_DIV1;
  h_tim2.TIM_CounterMode = TIM_CounterMode_Up;
  h_tim2.TIM_Period = 0;      // Variable Period
  h_tim2.TIM_Prescaler = 0;    // Interval 0.011 µs
  TIM_TimeBaseInit(TIM2, &h_tim2);
  TIM_SelectMasterSlaveMode(TIM2, TIM_MasterSlaveMode_Enable);
  TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);

  // Interrupts
  NVIC_SetPriority(EXTI15_10_IRQn, NVIC_EncodePriority(NVIC_PriorityGroup_3, DAQ_TRIGGER_IT_PRIORITY, 1));
  NVIC_EnableIRQ(EXTI15_10_IRQn);
  NVIC_SetPriority(DMA2_Stream0_IRQn, NVIC_EncodePriority(NVIC_PriorityGroup_3, DAQ_BUFFERCOMPLETE_IT_PRIORITY, 1));
  NVIC_EnableIRQ(DMA2_Stream0_IRQn);
  
  return OK;
}
// On Trigger
void EXTI15_10_IRQHandler()
{
  // Clear IT
  EXTI->PR = EXTI_Line15;

  // Start Trigger Timer
  TIM13->CR1 |= TIM_CR1_CEN;

  // Check if Trigger dead time passed
  if (TIM13->CNT > 1500)
  {
    // Stop Trigger Timer 13
    TIM13->CR1 &= (uint16_t)~TIM_CR1_CEN;

    // Get data
    DAQ.roundcounter++;
    DAQ.triggervalue = TIM13->CNT;

    DAQ.p_triggerbufferempty[DAQ.triggerbuffercounter] = TIM5->CNT;
    DAQ.triggerbuffercounter++;

    // Set Flags
    DAQ.triggerready = true;

    // Reset and start Trigger Timer 13
    TIM13->CNT = 0;
    TIM13->CR1 |= TIM_CR1_CEN;
  }
}
// On Buffer written
void DMA2_Stream0_IRQHandler()
{
  // Clear IT
  DMA2->LIFCR |= 0x20;

  // Switch Buffers
  if ((DMA2_Stream0->CR & DMA_SxCR_CT) != 0)
  {
    DAQ.p_accdatabuffercomplete = (uint32_t*)DAQ.rx_buffer1;
    DAQ.p_triggerbuffercomplete = (uint32_t*)DAQ.triggerbuffer1;
    DAQ.p_triggerbufferempty = (uint32_t*)DAQ.triggerbuffer2;

    DAQ.dmatriggervalcompleteSOC = DAQ.dmatriggerval1;
    DAQ.dmatriggervalcompleteEOC = DAQ.dmatriggerval2;
    DAQ.dmatriggerval2 = TIM5->CNT;
  }
  else
  {
    DAQ.p_accdatabuffercomplete = (uint32_t*)DAQ.rx_buffer2;
    DAQ.p_triggerbuffercomplete = (uint32_t*)DAQ.triggerbuffer2;

    DAQ.p_triggerbufferempty = (uint32_t*)DAQ.triggerbuffer1;
    DAQ.dmatriggervalcompleteSOC = DAQ.dmatriggerval2;
    DAQ.dmatriggervalcompleteEOC = DAQ.dmatriggerval1;
    DAQ.dmatriggerval1 = TIM5->CNT;
  }

  DAQ.lasttriggerbuffercounter = DAQ.triggerbuffercounter;
  DAQ.triggerbuffercounter = 0;
  DAQ.accdataready = true;
}

: Bearbeitet durch User
Autor: Dr. Sommer (Gast)
Datum:

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).

Autor: Ingo Less (corrtexx)
Datum:

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:
void Init_ADC ( void )
{
  /* Set clock */
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2 , ENABLE);
  RCC_APB2PeriphClockCmd( RCC_APB2Periph_ADC1, ENABLE);
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

  GPIO_InitTypeDef GPIO_InitStructure;
  GPIO_StructInit(&GPIO_InitStructure);
  ADC_InitTypeDef ADC_InitStructure;
  ADC_StructInit(&ADC_InitStructure);
  ADC_CommonInitTypeDef ADC_CommonInitStructure;
  ADC_CommonStructInit(&ADC_CommonInitStructure);
  DMA_InitTypeDef DMA_InitStructure;
  DMA_StructInit(&DMA_InitStructure);
  TIM_TimeBaseInitTypeDef TIM_TimeBase_InitStructure;
  TIM_TimeBaseStructInit (&TIM_TimeBase_InitStructure);
  NVIC_InitTypeDef NVIC_InitStructure;
  NVIC_Init(&NVIC_InitStructure);

  TIM_TimeBase_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
  TIM_TimeBase_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;
  TIM_TimeBase_InitStructure.TIM_Period = 840 - 1;
  TIM_TimeBase_InitStructure.TIM_Prescaler = 0;
  TIM_TimeBaseInit(TIM2, &TIM_TimeBase_InitStructure);

  TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
  TIM_SelectOutputTrigger(TIM2,TIM_TRGOSource_Update);
  TIM_ARRPreloadConfig(TIM2, ENABLE);

  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
  NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);

  /* ADC Kanäle */
  /* IO initialisieren */
  GPIO_StructInit(&GPIO_InitStructure);
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
  GPIO_Init(GPIOC, &GPIO_InitStructure);

  /* DMA Controll */
  DMA_InitStructure.DMA_Channel = DMA_Channel_0;
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(ADC1->DR);
  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)DataBuffer;
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
  DMA_InitStructure.DMA_BufferSize = BUFFERSIZE;
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
  DMA_Init(DMA2_Stream0, &DMA_InitStructure);
  DMA_ITConfig(DMA2_Stream0,DMA_IT_TC,ENABLE );


  /* ADC */
  ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
  ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div8;
  ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
  ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
  ADC_CommonInit(&ADC_CommonInitStructure);

  ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
  ADC_InitStructure.ADC_ScanConvMode = DISABLE;
  ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
  ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising;
  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO;
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
  ADC_InitStructure.ADC_NbrOfConversion = 1;
  ADC_Init(ADC1, &ADC_InitStructure);

  ADC_EOCOnEachRegularChannelCmd(ADC1, ENABLE);
  ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 1, ADC_SampleTime_28Cycles);

  DMA_Cmd(DMA2_Stream0, ENABLE);
  ADC_Cmd(ADC1, ENABLE);
  ADC_DMACmd(ADC1, ENABLE);
  ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);
}

void DMA2_Stream0_IRQHandler ( void )
{
  DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF0);
  TIM_Cmd(TIM2, DISABLE);
  Parameter.Messtatus = ENDED;
}

Zum Starten einfach
TIM_Cmd(TIM2, ENABLE);

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

Autor: Hanspeter S. (kendo1)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Damit der Code von  Ingo  oben bei mir lief, musste ich einige 
Anpassungen vornehmen.  siehe im Kommentar  // muths
#include "main.h"
#include "stm32f4xx_tim.h"
#include "misc.h"
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_adc.h"
#include "stm32f4xx_dma.h"

#define BUFFERSIZE 3
volatile uint32_t DataBuffer[BUFFERSIZE];

volatile uint8_t Messtatus = 0;


void Init_ADC ( void )
{
  /* Set clock */
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2 , ENABLE);
  RCC_APB2PeriphClockCmd( RCC_APB2Periph_ADC1, ENABLE);
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

  GPIO_InitTypeDef GPIO_InitStructure;
  GPIO_StructInit(&GPIO_InitStructure);
  ADC_InitTypeDef ADC_InitStructure;
  ADC_StructInit(&ADC_InitStructure);
  ADC_CommonInitTypeDef ADC_CommonInitStructure;
  ADC_CommonStructInit(&ADC_CommonInitStructure);
  DMA_InitTypeDef DMA_InitStructure;
  DMA_StructInit(&DMA_InitStructure);
  TIM_TimeBaseInitTypeDef TIM_TimeBase_InitStructure;
  TIM_TimeBaseStructInit (&TIM_TimeBase_InitStructure);
  NVIC_InitTypeDef NVIC_InitStructure;
  NVIC_Init(&NVIC_InitStructure);

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

  TIM_TimeBase_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
  TIM_TimeBase_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;
  TIM_TimeBase_InitStructure.TIM_Period = 840 - 1;
  TIM_TimeBase_InitStructure.TIM_Prescaler = 0;
  TIM_TimeBaseInit(TIM2, &TIM_TimeBase_InitStructure);

  TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
  TIM_SelectOutputTrigger(TIM2,TIM_TRGOSource_Update);
  TIM_ARRPreloadConfig(TIM2, ENABLE);

  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
  NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);

  /* ADC Kanäle */
  /* IO initialisieren */
  GPIO_StructInit(&GPIO_InitStructure);
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
  GPIO_Init(GPIOC, &GPIO_InitStructure);

  /* DMA Controll */
  DMA_InitStructure.DMA_Channel = DMA_Channel_0;
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(ADC1->DR);
  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)DataBuffer;   // muths   DataBuffer
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
  DMA_InitStructure.DMA_BufferSize = BUFFERSIZE;
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;    // muths DMA_PeripheralDataSize_HalfWord;
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;      // muths DMA_MemoryDataSize_HalfWord;
  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
  DMA_Init(DMA2_Stream0, &DMA_InitStructure);
  DMA_ITConfig(DMA2_Stream0,DMA_IT_TC,ENABLE );


  /* ADC */
  ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
  ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div8;
  ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
  ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
  ADC_CommonInit(&ADC_CommonInitStructure);

  ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
  ADC_InitStructure.ADC_ScanConvMode = ENABLE;    // muths    DISABLE;
  ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
  ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising;
  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO;
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
  ADC_InitStructure.ADC_NbrOfConversion = BUFFERSIZE; // muths   1;
  ADC_Init(ADC1, &ADC_InitStructure);

  ADC_EOCOnEachRegularChannelCmd(ADC1, ENABLE);
  ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_28Cycles);     // muths
  ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 2, ADC_SampleTime_28Cycles);
  ADC_RegularChannelConfig(ADC1, ADC_Channel_12, 3, ADC_SampleTime_28Cycles);    // muths
// für alle 6 benötigte Zeit= 1 us

  DMA_Cmd(DMA2_Stream0, ENABLE);
  ADC_Cmd(ADC1, ENABLE);
  ADC_DMACmd(ADC1, ENABLE);
  ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);
}

void DMA2_Stream0_IRQHandler ( void )
{
  DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF0);
  TIM_Cmd(TIM2, DISABLE);
  Messtatus = 1;
}



int main(void){
//----------------------------------------------------


  SystemInit(); // Quarz Einstellungen aktivieren

  Init_ADC();
  TIM_Cmd(TIM2, ENABLE); // Zum Starten
  /*
  Timer2 läuft "nur" auf 84MHz, daher Prescaler 840-1 (84MHz/840 =
  100kHz).
  */
  while(1)
  {

    if (Messtatus == 1) {
      // auswerten etc.
    }




  }
}

: Bearbeitet durch User
Autor: m.n. (Gast)
Datum:

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]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [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.