Forum: Mikrocontroller und Digitale Elektronik STM32 ADC mit DMA für mehrere Kanäle


von xnager (Gast)


Lesenswert?

Hallo,

zur Zeit möchte ich über einen STM32F3 4 Kanäle eines ADCs zyklisch 
auslesen. Dazu wird der ADC über den TIM3 über TRGO getriggert.
1
void TIM3_Init()
2
{
3
  //Timer zum Triggern des ADCs bei bestimmter Frequenz über TRGO
4
  
5
  TIM3->PSC = 0;      //Prescaler
6
  TIM3->ARR = 72-1;    //Auto Reload
7
  TIM3->CNT = 0;      //Count
8
  TIM3->CR1 = 1;      //Control Reg
9
  TIM3->CCR1= 36;      //Capture Compare
10
  
11
  SET_BIT(TIM3->CR2, TIM_CR2_MMS_1);  //TRGO
12
13
  
14
  SET_BIT(TIM3->CR1, TIM_CR1_CEN);    //Timer Enable
15
}

Der ADC soll die Kanäle 1 bis 4 nacheinander auslesen. Nach jeder 
Messung soll der Wert mittels DMA in den Speicher abgelegt werden. Das 
soll für alle 4 Werte gemacht werden. Erst wenn TIM3 wieder auslöst soll 
der Zyklus erneut beginnen.
1
static void ADC1_Init(void)
2
{
3
  //sicher gehen, dass Clock gesetzt ist
4
  SET_BIT(RCC->AHBENR, RCC_AHBENR_ADC12EN);
5
  
6
  //ADC1 disablen
7
  if(READ_BIT(ADC1->ISR, ADC_ISR_ADRDY))
8
  {
9
    SET_BIT(ADC1->ISR, ADC_ISR_ADRDY);
10
  }
11
  if(READ_BIT(ADC1->CR, ADC_CR_ADEN))
12
  {
13
    SET_BIT(ADC1->CR, ADC_CR_ADDIS);
14
  }
15
  
16
  //Warten bis ADC1 komplett disabled ist
17
  while(READ_BIT(ADC1->CR, ADC_CR_ADEN)) {}
18
    
19
  //ADC1 voltage regulator enablen
20
  //Enable sequence im Reference sheet (Seite 219)
21
  MODIFY_REG(ADC1->CR, ADC_CR_ADVREGEN, 0);
22
  MODIFY_REG(ADC1->CR, ADC_CR_ADVREGEN, ADC_CR_ADVREGEN_0);
23
    
24
  //kurzer delay zum enablen  
25
  HAL_Delay(2);
26
  
27
  //ADC1 Clock = HCLK/1
28
  MODIFY_REG(ADC12_COMMON->CCR, ADC12_CCR_CKMODE, ADC12_CCR_CKMODE_0);
29
    
30
  //Single Ended Mode für alle Kanäle
31
  ADC1->DIFSEL = 0x0;
32
    
33
  //Kalibrierung für single ended Mode starten
34
  CLEAR_BIT(ADC1->CR, ADC_CR_ADCALDIF);
35
  SET_BIT(ADC1->CR, ADC_CR_ADCAL);
36
    
37
  //Warten bis Kalibrierung erfolgt ist
38
  while(READ_BIT(ADC1->CR, ADC_CR_ADCAL));
39
  
40
  //Ready Flag zurücksetzen
41
  SET_BIT(ADC1->ISR, ADC_ISR_ADRDY);
42
  
43
  SET_BIT(ADC1->SQR1, ADC_SQR1_L_1 + ADC_SQR1_L_0);          //Sequnz Laenge = 4 (0011)
44
  SET_BIT(ADC1->SQR1, ADC_SQR1_SQ1_1);                       //Erste Konv Channel 2 (0010) Vin
45
  SET_BIT(ADC1->SQR1, ADC_SQR1_SQ2_2);                       //Zweit Konv Channel 4 (0100) Vout
46
  SET_BIT(ADC1->SQR1, ADC_SQR1_SQ3_0);                       //Dritt Konv Channel 1 (0001) Iin
47
  SET_BIT(ADC1->SQR1, ADC_SQR1_SQ4_0 + ADC_SQR1_SQ4_1);      //Viert Konv Channel 3 (0011) Iout
48
  
49
  SET_BIT(ADC1->SMPR1, ADC_SMPR1_SMP2_2);                    //Sampling Time f. Channel 2 = 32 CLK
50
  SET_BIT(ADC1->SMPR1, ADC_SMPR1_SMP4_2);                    //Sampling Time f. Channel 2 = 32 CLK
51
  SET_BIT(ADC1->SMPR1, ADC_SMPR1_SMP1_2);                    //Sampling Time f. Channel 2 = 32 CLK
52
  SET_BIT(ADC1->SMPR1, ADC_SMPR1_SMP3_2);                    //Sampling Time f. Channel 2 = 32 CLK
53
  
54
  CLEAR_BIT(ADC1->CFGR, ADC_CFGR_CONT);                      //Single conversion mode auswählen
55
  
56
  SET_BIT(ADC1->CFGR, ADC_CFGR_EXTSEL_2);                    //External Trigger für TIM3_TRGO Event EXT4
57
  //SET_BIT(ADC1->CFGR, ADC_CFGR_EXTEN_0);                    //External Trigger auf Rising Edge
58
  
59
  SET_BIT(ADC1->CFGR, ADC_CFGR_DMAEN);                      //DMA einschalten
60
  SET_BIT(ADC1->CFGR, ADC_CFGR_DMACFG);                      //DMA im Circular Mode
61
  
62
  SET_BIT(ADC1->IER, ADC_IER_EOCIE);                        //Interrupt Enable für EOC
63
  SET_BIT(ADC1->IER, ADC_IER_EOSIE);                        //Interrupt Enable für EOS
64
  
65
  //ADC einschalten bis es funktioniert (aus Errata Sheet)
66
  do
67
  {
68
    SET_BIT(ADC1->CR, ADC_CR_ADEN);
69
  }
70
  while(!READ_BIT(ADC1->ISR, ADC_ISR_ADRDY));
71
72
  SET_BIT(ADC1->CR, ADC_CR_ADSTART);
73
  //togled1();
74
  
75
}//ADC1 Init
1
void DMA_Init()
2
{
3
  //1. Channel im SYSCFG CFGR1/3 bekannt geben
4
  //SYSCFG_CFGR1: Nichts für ADC
5
  //SYSCFG_CFGR3: Bit 9 == 1: ADC2 mapped on DMA1 Ch2
6
  SET_BIT(SYSCFG->CFGR3, SYSCFG_CFGR3_ADC2_DMA_RMP_1);
7
  
8
  //Vier Register sind wichtig
9
  //Configuration:            DMA_CCRx      MINC, PINK, CIRC, /MEM2MEM
10
  //Holding number of bytes:  DMA_CNDTRx
11
  //Source Adress:             DMA_CMARx
12
  //Destination Adress:       DMA_CPARx
13
  
14
  CLEAR_BIT(DMA1_Channel1->CCR, DMA_CCR_DIR);      //Data Transfer Direction: 0 = Read from Peripheral
15
  CLEAR_BIT(DMA1_Channel1->CCR, DMA_CCR_MEM2MEM);  //Nicht MEM 2 MEM
16
  CLEAR_BIT(DMA1_Channel1->CCR, DMA_CCR_CIRC);    //Circ Mode Disabeled, wird nur von ADC EOC ausgelöst
17
  
18
  CLEAR_BIT(DMA1_Channel1->CCR, DMA_CCR_PL_0);    //Priority level
19
  CLEAR_BIT(DMA1_Channel1->CCR, DMA_CCR_PL_1);    //00 low, 01 med, 10 high, 11 very high
20
  
21
  SET_BIT(DMA1_Channel1->CCR, DMA_CCR_MSIZE_0);      //Memory Size
22
  CLEAR_BIT(DMA1_Channel1->CCR, DMA_CCR_MSIZE_1);    //00 8bit, 01 16bit, 10 32 bit
23
  
24
  SET_BIT(DMA1_Channel1->CCR, DMA_CCR_PSIZE_0);      //Peripheral Size
25
  CLEAR_BIT(DMA1_Channel1->CCR, DMA_CCR_PSIZE_1);    //00 8bit, 01 16bit, 10 32 bit
26
27
  SET_BIT(DMA1_Channel1->CCR, DMA_CCR_MINC);        //Memory Increment
28
  CLEAR_BIT(DMA1_Channel1->CCR, DMA_CCR_PINC);      //NICHT Periph. increment
29
  
30
  //DMA Source und Destination Adressen setzen
31
  DMA1_Channel1->CMAR = ( uint32_t )&ADC_raw[0];
32
  DMA1_Channel1->CPAR = ( uint32_t )(&ADC1->DR);
33
  DMA1_Channel1->CNDTR = 4;
34
  
35
  SET_BIT(DMA1_Channel1->CCR, DMA_CCR_EN);          //DMA enablen
36
}

Alle Clock Signale werden woanders eingeschaltet.
Allerdings werden keine Werte ins ADC_raw[] abgelegt. Der Timer und ADC 
arbeiten aber. Fallen euch Ungereimtheiten auf?
Bitte entschuldigt grobe Schnitzer, das ist mein erstes STM32 Projekt.

Vielen Dank!

von xnager (Gast)


Lesenswert?

Okay, ich bin vielleicht ein wenig doof.
Habe vergessen die DMA_Init() aufzurufen. Der Code funktioniert - ein 
wenig.

So wie der Code da steht wird mir nach dem Startup jeweils einmal alle 4 
Kanäle gelesen und per DMA ins RAM gelegt von wo aus ich die 4 Werte 
auch auswerten kann. Allerdings soll dies ja dauerhaft im Hintergrund 
geschehen mit der durch den TIM3 festgelegten Frequenz. Das passiert 
allerdings nicht.

Kann sich jemand vorstellen warum?

von PowerHannes (Gast)


Lesenswert?

Hallo xnager,
bei dem Stm32f4 hatte ich das gleiche Problem. Wenn man den DMA nicht im 
Circular Mode betreibt, muss man den DMA nach jedem auslesen komplett 
neu initialisieren. Der DMA muss im Circular Mode laufen. Dann musst du 
mit dem Timer den ADC triggern, wodurch die Werte mittels DMA geladen 
werden.

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.