Forum: Mikrocontroller und Digitale Elektronik STM32F4 HAL Timer+ADC+DMA


von Martin (Gast)


Lesenswert?

Guten Tag,

ich möchte einen ADC im Burst Modus betreiben und dazu die HAL-Treiber 
verwenden.

Ich habe mich zunächst an das Standardbeispiel gehalten, aber irgendwie 
springt er selbst da schon nicht in die DMA2_Stream0_IRQHandler 
Funktion.
Eventuell könnte es mit dem Trigger des TIM3 zu tun haben? Ich habe 
ADC_EXTERNALTRIGCONV_T3_TRGO gewählt...

Und eigentlich möchte ich den ADC ja im Burst-Modus verwenden. D.h. ich 
möchte alle 50 ms eine Messung mit maximaler ADC-Frequenz durchführen 
und 10 Sample aufnehmen und über DMA abspeichern. Nach weiteren 50 ms 
dann die nächste Messung.
Wie sage ich dem ADC, dass er nach 10 Werten stoppen soll?

1
bool MainFunctions::InitADCs(){
2
    /*
3
    -----------------------
4
    Pin      ADC      Channel
5
    -----------------------
6
    PC04    ADC1    CH14
7
    PC05    ADC1    CH15
8
    */
9
    //PC_4 und PC_5 sind schon in InitPwrLupGPIOs aktiviert
10
  
11
    /* 1. Init Timer for Interrupt trigger ****************************/
12
    m_hTIM3 = new TIM_HandleTypeDef; 
13
  
14
    // TIM3 clock enable    
15
    __TIM3_CLK_ENABLE();
16
    
17
    //Init TIM3 Base
18
    m_hTIM3->Instance               = TIM3;                          // Timer TIM3: Only Up, No DMA, MaxClock 84 MHz
19
    m_hTIM3->Init.ClockDivision     = TIM_CLOCKDIVISION_DIV1;
20
    m_hTIM3->Init.Prescaler         = 8400;                          // Systemtakt:84 MHz / 8400 = 10 kHz => 100 µs resolution            
21
    m_hTIM3->Init.CounterMode       = TIM_COUNTERMODE_UP;
22
    m_hTIM3->Init.Period             = 500;                          // Time = 500 * 100µs = 50 ms 
23
    m_hTIM3->Init.RepetitionCounter = 0;
24
    
25
    if(HAL_TIM_Base_Init(m_hTIM3) != HAL_OK){
26
      return false;
27
    }    
28
    //Start timer
29
    if(HAL_TIM_Base_Start(m_hTIM3) != HAL_OK){
30
      return false;
31
    }      
32
    
33
    /* 2. Enable ADC1 Clk  ****************************/
34
    // Enable ADC Clk before DMA2 Clk
35
    m_hADC1 = new ADC_HandleTypeDef;
36
    __ADC1_CLK_ENABLE();
37
38
    /* 3. DMA Konfigurieren ****************************/
39
    //For ADC1 => DMA2 Channel 0  
40
    m_hDMA2s0c0 = new DMA_HandleTypeDef;
41
    __DMA2_CLK_ENABLE();
42
  
43
    m_hDMA2s0c0->Instance                 = DMA2_Stream0;  
44
    m_hDMA2s0c0->Init.Channel             = DMA_CHANNEL_0;
45
    m_hDMA2s0c0->Init.Direction           = DMA_PERIPH_TO_MEMORY;
46
    m_hDMA2s0c0->Init.PeriphInc           = DMA_PINC_DISABLE;                  //Always the same source
47
    m_hDMA2s0c0->Init.MemInc               = DMA_MINC_ENABLE;                  //Inc. Memory address
48
    m_hDMA2s0c0->Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;              //Half DataWord = 16 Bit...
49
    m_hDMA2s0c0->Init.MemDataAlignment     = DMA_MDATAALIGN_WORD;
50
    m_hDMA2s0c0->Init.Mode                 = DMA_CIRCULAR;                      //Normal Mode: if the register reaches zero, the stream is disabled
51
    m_hDMA2s0c0->Init.Priority             = DMA_PRIORITY_LOW;
52
    m_hDMA2s0c0->Init.FIFOMode             = DMA_FIFOMODE_DISABLE;
53
    m_hDMA2s0c0->Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_HALFFULL;
54
    m_hDMA2s0c0->Init.MemBurst             = DMA_MBURST_SINGLE;
55
    m_hDMA2s0c0->Init.PeriphBurst         = DMA_PBURST_SINGLE;
56
    
57
    HAL_DMA_Init(m_hDMA2s0c0);
58
    
59
    /* 4. DMA mit ADC verlinken ****************************/
60
    //__HAL_LINKDMA(Peripheral Handle , Memory Handle of ADC1, Pointer to DMA Controller Handle) -> Figure 1 in AN4031
61
    __HAL_LINKDMA(m_hADC1, DMA_Handle, *m_hDMA2s0c0);
62
    
63
    /* 5. DMA Interrupts verknüpfen ****************************/
64
    // Priority of SysTick IR = 0 (Delay function) 
65
    HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 1, 1);
66
    HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
67
    
68
    /* 6. Init ADC ****************************/
69
    m_hADC1->Instance                    = ADC1;
70
    m_hADC1->Init.ClockPrescaler        = ADC_CLOCKPRESCALER_PCLK_DIV4;            // Frequency = 84 MHz / 4 = 21 MHz
71
    m_hADC1->Init.Resolution            = ADC_RESOLUTION8b;                  
72
    m_hADC1->Init.DataAlign              = ADC_DATAALIGN_RIGHT;
73
    m_hADC1->Init.ScanConvMode          = DISABLE;
74
    m_hADC1->Init.EOCSelection          = DISABLE;                                //Interrupt flag after sequence
75
    m_hADC1->Init.ContinuousConvMode    = ENABLE;
76
    m_hADC1->Init.DMAContinuousRequests = ENABLE;
77
    m_hADC1->Init.NbrOfConversion        = 1;                                      // Number of used ADC Channels
78
    m_hADC1->Init.DiscontinuousConvMode  = DISABLE;
79
    m_hADC1->Init.NbrOfDiscConversion    =  0;  
80
    m_hADC1->Init.ExternalTrigConv      = ADC_EXTERNALTRIGCONV_T3_TRGO;            // Messung durch den Trigger des Timer 3 gestartet! CC = Capture/Compare Channel x
81
    m_hADC1->Init.ExternalTrigConvEdge  = ADC_EXTERNALTRIGCONVEDGE_NONE;
82
    
83
    if(HAL_ADC_Init(m_hADC1) != HAL_OK){
84
      return false;
85
    }
86
    
87
    //continuous triple Interleaved mode:
88
    //The ADC is configured in continuous triple Interleaved mode. In this mode, it converts
89
    //continuously one analog input channel at the maximum ADC speed (36 MHz).
90
    //http://www.st.com/web/en/resource/technical/document/application_note/DM00046011.pdf
91
    
92
    /* 7. Config ADC Channel ****************************/
93
    ADC_ChannelConfTypeDef  sConfig;
94
    
95
    sConfig.Channel       = ADC_CHANNEL_14;
96
    sConfig.Rank           = 1;
97
    sConfig.SamplingTime   = ADC_SAMPLETIME_15CYCLES;
98
    sConfig.Offset         = 0;
99
    
100
    if(HAL_ADC_ConfigChannel(m_hADC1, &sConfig) != HAL_OK){
101
      return false;
102
    }
103
  
104
    /* 8. Start ADC1 ****************************/
105
    if (HAL_ADC_Start_DMA(m_hADC1, m_iValuesADC1, 15) != HAL_OK) {
106
      return false; 
107
    }
108
    return true;
109
}

DMA IRQ:
1
/* DMA (ADC) interrupt handler ****************************/
2
void DMA2_Stream0_IRQHandler(void)
3
{  
4
    HAL_DMA_IRQHandler(IRQHandler->m_hADC1->DMA_Handle);
5
    //m_iValuesADC1 Auswerten
6
}

von Christoph S. (mixer) Benutzerseite


Lesenswert?

Martin schrieb:
> Eventuell könnte es mit dem Trigger des TIM3 zu tun haben? Ich habe
> ADC_EXTERNALTRIGCONV_T3_TRGO gewählt...


Du musst bei dem Timer auch den Triggerevent einschalten


> Und eigentlich möchte ich den ADC ja im Burst-Modus verwenden. D.h. ich
> möchte alle 50 ms eine Messung mit maximaler ADC-Frequenz durchführen
> und 10 Sample aufnehmen und über DMA abspeichern. Nach weiteren 50 ms
> dann die nächste Messung.
> Wie sage ich dem ADC, dass er nach 10 Werten stoppen soll?


Den gleichen Kanal 10mal konfigurieren

von Martin (Gast)


Lesenswert?

Hallo Christoph,

der Trigger des Timer ist doch schon automatisch aktiviert, oder was 
genau meinst du?

Und den Kanal 10 mal Konfigurieren? Gibts da nicht andere Methoden?

von Christoph (Gast)


Lesenswert?

Hallo,

schau dir mal das Timer-Register CR2 mit den MMS-Bits an, hier musst du 
den Trigger-Event noch aktivieren

Ich weiss keine andere Lösung für die 10mal wandeln...

Gruss
CS

von Martin (Gast)



Lesenswert?

Hey,
hab es hinbekommen. Die nötige Hilfe habe ich in einem halbwegs 
passablen Beispiel gefunden (siehe Anhang).
Was ich aber nicht nicht ganz verstehen ist, wertet man die DMA-Daten im 
Interupt-Handler aus oder in der Calback-Funktion wie es im HAL-Manual 
steht?! Hab noch kein Bsp-Code gesehen, wo es in der 
HAL_ADC_ConvCpltCallback() Funktion gemacht wird. Weiß darüber jemand 
mehr?

Grüße

von Martin (Gast)


Lesenswert?

Mit DMA-Daten, meine ich natürlich die Ergebnisse der 
AD-Konvertierung...

von Vincent H. (vinci)


Lesenswert?

Das bleibt dir überlassen.
Sobald der DMA IRQ auslöst stehn die Daten auf jedem Fall in dem dafür 
vorgesehenen Buffer.

Der Witz der Callback Funktionen besteht ja lediglich darin vom 
Controller komplett unabhängig zu werden. Der Interrupt zw. etwa einem 
F3 und einem F7 könnte durchaus anders ausschaun (andere Register, 
andere Flags) während die Callback Funktion stets gleich bleibt.

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.