Forum: Mikrocontroller und Digitale Elektronik STM32G4: minimale Sequenz zum Einschalten des ADC1


von Raoul D. (raoul_d219)


Lesenswert?

Hallo zusammen,

ich möchte den ADC1 eines Nucleo32G4-Boards mit dem G431 in Betrieb 
nehmen.

Dazu habe ich folgendes Vorgehen bisher (ohne HAL):
1
            RCC->AHB2ENR |= RCC_AHB2ENR_ADC12EN;
2
            
3
            MODIFY_REG(ADC1->CR, ADC_CR_DEEPPWD_Msk, 0x00 << ADC_CR_DEEPPWD_Pos); 
4
            ADC1->CR |= ADC_CR_ADVREGEN;
5
            
6
            wait_us(100);
7
8
            ADC1->ISR |= ADC_ISR_ADRDY; // clear flag
9
            
10
            // VREF ?
11
            
12
            ADC12_COMMON->CCR |= (0x0b << ADC_CCR_PRESC_Pos); // prescaler 256
13
            MODIFY_REG(ADC1->CFGR , ADC_CFGR_RES_Msk, (0x00 << ADC_CFGR_RES_Pos)); // 12 bit
14
            
15
            // temp sensor channel select (ADC1 INP16)
16
            
17
            ADC1->SQR1 = (0x00 << ADC_SQR1_L_Pos) | (16 << ADC_SQR1_SQ1_Pos);
18
            
19
            ADC1->CR |= ADC_CR_ADEN;
20
            
21
          while(!(ADC1->ISR & ADC_ISR_ADRDY)); //  ????

Das ADC_ISR_ADRDY geht allerding nie auf true.

Der Systemtakt und AHB Takt ist 170MHz.

Was mache ich falsch?

von Wastl (hartundweichware)


Lesenswert?

Wenn ich von STM alles so mundgerecht serviert bekomme dann
gebe ich mich als faulen Sack und lasse CubeMX den Code
generieren.

Wenn ich das beispielhaft für einen G431 mache bekome ich
den folgenden übersichtlichen Code der (vermutlich doch)
alles beinhaltet. Und das ohne HAL-Funktionen zu verwenden.

Daran könntest du dich orientieren wenn du wirklich auf
Registerebene alles zu Fuss machen möchtest. Das kann jedoch
- wie so oft - stark fehlerbehaftet sein. Also viel Erfolg,
welchen Weg du auch immer beschreiten willst .....
1
/* ADC1 init function */
2
void MX_ADC1_Init(void)
3
{
4
  LL_ADC_InitTypeDef ADC_InitStruct = {0};
5
  LL_ADC_REG_InitTypeDef ADC_REG_InitStruct = {0};
6
  LL_ADC_CommonInitTypeDef ADC_CommonInitStruct = {0};
7
8
  LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
9
10
  /* Peripheral clock enable */
11
  LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_ADC12);
12
  
13
  LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA);
14
  /**ADC1 GPIO Configuration  
15
  PA0   ------> ADC1_IN1 
16
  */
17
  GPIO_InitStruct.Pin = LL_GPIO_PIN_0;
18
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
19
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
20
  LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
21
22
  /* ADC1 interrupt Init */
23
  NVIC_SetPriority(ADC1_2_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
24
  NVIC_EnableIRQ(ADC1_2_IRQn);
25
26
  /** Common config 
27
  */
28
  ADC_InitStruct.Resolution = LL_ADC_RESOLUTION_12B;
29
  ADC_InitStruct.DataAlignment = LL_ADC_DATA_ALIGN_RIGHT;
30
  ADC_InitStruct.LowPowerMode = LL_ADC_LP_MODE_NONE;
31
  LL_ADC_Init(ADC1, &ADC_InitStruct);
32
33
  ADC_REG_InitStruct.TriggerSource = LL_ADC_REG_TRIG_SOFTWARE;
34
  ADC_REG_InitStruct.SequencerLength = LL_ADC_REG_SEQ_SCAN_DISABLE;
35
  ADC_REG_InitStruct.SequencerDiscont = LL_ADC_REG_SEQ_DISCONT_DISABLE;
36
  ADC_REG_InitStruct.ContinuousMode = LL_ADC_REG_CONV_SINGLE;
37
  ADC_REG_InitStruct.DMATransfer = LL_ADC_REG_DMA_TRANSFER_NONE;
38
  ADC_REG_InitStruct.Overrun = LL_ADC_REG_OVR_DATA_PRESERVED;
39
  LL_ADC_REG_Init(ADC1, &ADC_REG_InitStruct);
40
41
  LL_ADC_SetGainCompensation(ADC1, 0);
42
  LL_ADC_SetOverSamplingScope(ADC1, LL_ADC_OVS_DISABLE);
43
  LL_ADC_DisableDeepPowerDown(ADC1);
44
  LL_ADC_EnableInternalRegulator(ADC1);
45
  ADC_CommonInitStruct.CommonClock = LL_ADC_CLOCK_ASYNC_DIV1;
46
  ADC_CommonInitStruct.Multimode = LL_ADC_MULTI_INDEPENDENT;
47
  LL_ADC_CommonInit(__LL_ADC_COMMON_INSTANCE(ADC1), &ADC_CommonInitStruct);
48
  LL_ADC_EnableIT_EOC(ADC1);
49
  LL_ADC_DisableIT_EOS(ADC1);
50
  /** Configure Regular Channel 
51
  */
52
  LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_1, LL_ADC_CHANNEL_1);
53
  LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_1, LL_ADC_SAMPLINGTIME_2CYCLES_5);
54
  LL_ADC_SetChannelSingleDiff(ADC1, LL_ADC_CHANNEL_1, LL_ADC_SINGLE_ENDED);
55
}

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Versuch mal:
1
RCC->AHB2ENR |= RCC_AHB2ENR_ADC12EN;
2
__DSB ();
3
ADC1->CR = ADC_CR_ADVREGEN;
4
            
5
wait_us(100);
6
ADC1->ISR = ADC_ISR_ADRDY; // clear flag

Schau auch mal im Debugger was jeweils in den Registern steht (CR1, 
ISR).

Die Bits in ISR sind "rc_w1", also geht löschen wie oben gezeigt (kein 
OR).

von Irgend W. (Firma: egal) (irgendwer)


Lesenswert?

Raoul D. schrieb:
1
> 0x00 << ADC_CR_DEEPPWD_Pos
2
> (0x00 << ADC_CFGR_RES_Pos)
3
> (0x00 << ADC_SQR1_L_Pos)

Kann mir mal jemand erklären was das sein soll?
Ich hätte eigentlich gedacht das hier eine Null n mal nach links 
geschiftet wird und dann immer noch Null ist. Aber zusammen mit dem mir 
unbekannten Makro "MODIFY_REG" scheint das wohl was anderes zu bewirken, 
sonst wäre es ja sinnlos und verwirrend?

von Raoul D. (raoul_d219)


Lesenswert?

Niklas G. schrieb:
> Versuch mal:
>
>
1
RCC->AHB2ENR |= RCC_AHB2ENR_ADC12EN;
2
> __DSB ();
3
> ADC1->CR = ADC_CR_ADVREGEN;
4
> 
5
> wait_us(100);
6
> ADC1->ISR = ADC_ISR_ADRDY; // clear flag
7
> 
8
>
>
> Schau auch mal im Debugger was jeweils in den Registern steht (CR1,
> ISR).

CR1 gibt es ja nicht, Du meinst sicher CR

> Die Bits in ISR sind "rc_w1", also geht löschen wie oben gezeigt (kein
> OR).

Danke, das habe ich übersehen.

Die MB mit __DSB() ist m.E. unnötig und führt auch nicht zum Erfolg.

von N. M. (mani)


Lesenswert?

Irgend W. schrieb:
> Kann mir mal jemand erklären was das sein soll?

Vermutlich spuckt der Code Generator immer den gleichen Code aus und 
trägt falls notwendig nur eine 1 ein.

Irgend W. schrieb:
> Ich hätte eigentlich gedacht das hier eine Null n mal nach links
> geschiftet wird und dann immer noch Null ist.

Ja, würde ich auch sagen.

Irgend W. schrieb:
> Aber zusammen mit dem mir unbekannten Makro "MODIFY_REG" scheint das
> wohl was anderes zu bewirken, sonst wäre es ja sinnlos und verwirrend?

Man könnte sagen es ist sinnlos. Man könnte auch sagen dass man dadurch 
direkt sieht was auf dem entsprechenden Bit steht. Manche Bit sind 
Default 0, manche 1.
Ich denke also das ist Ansichtssache.

von Raoul D. (raoul_d219)


Lesenswert?

Wastl schrieb:
> Wenn ich von STM alles so mundgerecht serviert bekomme dann
> gebe ich mich als faulen Sack und lasse CubeMX den Code
> generieren.

Habe ich jetzt auch gemacht: geht trotzdem nicht. ADC_ISR_ADRDY bleibt 
false.

von Harry L. (mysth)


Lesenswert?

Raoul D. schrieb:
> Habe ich jetzt auch gemacht: geht trotzdem nicht. ADC_ISR_ADRDY bleibt
> false.

Du sollst ja auch die dafür vorgesehen Callback-Funktionen nutzen!

von Andreas B. (abm)


Lesenswert?

Hm, erstens fehlt die Kalibrierungsphase und zweitens ist der notwendige 
Ablauf im RM genau beschrieben, ADC on-off control 21.4.9. Beim G0 geht 
es bei mir so, wie im (zugehörigen) RM beschrieben (ohne diesen ganzen 
HAL-Krempel), und soweit ich auf den ersten Blick sehe, gibt's da auch 
keine wesentlichen Unterschiede zw. G0 und G4.

von Harry L. (mysth)


Lesenswert?

Andreas B. schrieb:
> wie im (zugehörigen) RM beschrieben (ohne diesen ganzen
> HAL-Krempel),

Dieser ganz "HAL-Krempel" funktioniert aber i.d.R und bis auf ganz, ganz 
wenige Ausnahmen zuverlässig, frei von Nebenwirkungen und das sogar 
unter Berücksichtigung der letzten Eratas.

Während die Superchecker, die sich für schlauer halten als die Ings bei 
ST noch an ihrer Initialisierung herumfrickeln kann man sich 
HAL-sei-Dank bereits auf die eigentliche Aufgabe/Anwendung 
konzentrieren.

von Andreas B. (abm)


Lesenswert?

Harry L. schrieb:
> Dieser ganz "HAL-Krempel" funktioniert aber i.d.R und bis auf ganz, ganz
> wenige Ausnahmen zuverlässig, frei von Nebenwirkungen und das sogar
> unter Berücksichtigung der letzten Eratas.
>
> Während die Superchecker, die sich für schlauer halten als die Ings bei
> ST noch an ihrer Initialisierung herumfrickeln kann man sich
> HAL-sei-Dank bereits auf die eigentliche Aufgabe/Anwendung
> konzentrieren.

Ach Gottchen, da regt sich mal wieder einer auf. Sei's drum, wer es so 
machen will (und für die simple GPIO-Init. gleich wer weiß wieviel Flash 
verbraten will), soll's doch gerne machen, was juckt's mich? Wenn jemand 
nicht mal die ADC-Initialisierung nach RM hin bekommt, ist das Ganze 
ohnehin ziemlich akademisch ...

von Harry L. (mysth)


Lesenswert?

Andreas B. schrieb:
> und für die simple GPIO-Init. gleich wer weiß wieviel Flash
> verbraten will

Allein mit diesem Satz hast du dich vollkommen disqualifiziert.
Man sollte nicht über Dinge schwadronieren, die man nicht kennt/von 
denen man keine Ahnung hat/ die man nicht verstanden hat.

Flash ist i.d.R auch reichlich vorhanden, und für ungenutzten Speicher 
gibts auch kein Geld zurück, und auch, wenn der generierte Code etwas 
länger erscheint, gilt das nicht zwangläufig für das, was der 
Compiler/Linker daraus macht.

Der generierte Code ist jedenfalls gut verständlich und dokumentiert - 
im Gegensatz zu den meisten Frickler-Ejakulaten.

von Raoul D. (raoul_d219)


Lesenswert?

Alle bisherigen Antworten waren leider falsch.

Es fehlte ganz simpel:
1
            RCC->CCIPR |= 0x02 << RCC_CCIPR_ADC12SEL_Pos;

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.