Forum: Mikrocontroller und Digitale Elektronik STM32F446 - Taktkonfiguration


von Wühlhase (Gast)


Lesenswert?

So...ich habe mich mal an der Taktkonfiguration versucht.

Eine Frage vorweg: Kann ich bei der Taktkonfiguration irgendetwas so 
zerhacken, daß der nachher nicht mehr lauffähig ist? Ich meine sowas wie 
bei den AVRs, wenn man da die Fusebits unpassend wählt wars das-bis man 
die eingestellte Taktquelle zuführt.

Ich habe mich an diesem Tutorial hier entlanggehangelt, allerdings mit 
ein paar kleine Abweichungen.
https://github.com/jkerdels/stm32edu/blob/master/src/rcc.c
So läuft mein STM mit 180MHz, und nutzt den internen RC-Oszillator als 
Taktquelle.

Meint ihr das läuft so? Ich hab vor der Taktkonfiguration doch noch 
etwas Respekt.
1
//Initialize clock config
2
void initClock(){
3
  //dont touch clock at the moment, use standard settings
4
  return;
5
6
  //Siehe: https://github.com/jkerdels/stm32edu/blob/master/src/rcc.c
7
8
  //Enable HSI-clock -> 16MHz
9
  RCC -> CR |= RCC_CR_HSION;
10
11
  //Wait for HSI is stable running
12
  while(RCC -> CR & RCC_CR_HSIRDY){}
13
14
  //Switch SYSCLK to HSI, SW -> 00
15
  RCC -> CFGR &= ~RCC_CFGR_SW_0;
16
  RCC -> CFGR &= ~RCC_CFGR_SW_1;
17
18
  //Wait for switching SYSCLK is ready
19
  while((RCC -> CFGR & RCC_CFGR_SWS_0 == false) && (RCC -> CFGR & 
20
  RCC_CFGR_SWS_1 == false)){}
21
22
  //Config waitstates for flash memory, cpu should run with nearly
23
  //180MHz@3,3V -> 5ws after table, page 64
24
  FLASH -> ACR |= FLASH_ACR_LATENCY_5WS;
25
26
  //Config realtime clock, set LSI as clocksource
27
  RCC -> CSR |= RCC_CSR_LSION;
28
29
  //Set AHB clock prescaler for main bus matrix, psc=1 for running with      
30
  //maximum speed -> 180MHz, Presc. =1
31
  RCC -> CFGR |= RCC_CFGR_HPRE_DIV1;
32
33
  //Set clock for APB1 to AHB/4 => 180MHz/4 -> 45MHz
34
  RCC -> CFGR |= RCC_CFGR_PPRE1_DIV4;
35
36
  //Set clock for APB2 to AHB/2 => 180MHz/2 -> 90MHz
37
  RCC -> CFGR |= RCC_CFGR_PPRE2_DIV2;
38
39
  //Set phase lock loop
40
  //Disable PLL for config
41
  RCC -> CR &= ~RCC_CR_PLLON;
42
  RCC -> CR &= ~RCC_CR_PLLI2SON;
43
44
  //Select HSI for pll clock source
45
  RCC -> PLLCFGR |= RCC_PLLCFGR_PLLSRC_HSI;
46
47
  //Set prescaler for pll clock input: HSI(16MHz) -> input should be 2MHz 
48
  //-> Prescaler =8
49
  RCC -> PLLCFGR |= RCC_PLLCFGR_PLLM_3;
50
51
  //Clock = fin*(N/M) -> 2MHz*(360/4) for 180MHz
52
  //Set N=360 -> 0b1 0110 1000
53
  RCC -> PLLCFGR |= RCC_PLLCFGR_PLLN_8 |= RCC_PLLCFGR_PLLN_6 |= 
54
  RCC_PLLCFGR_PLLN_5 |= RCC_PLLCFGR_PLLN_3;
55
  //Set M=4 0b100
56
  RCC -> PLLCFGR |= RCC_PLLCFGR_PLLM_2;
57
  //Set 48MHz for VCO, -> 2*(N/Q) -> Set Q=15 -> 0b1111
58
  RCC -> PLLCFGR |= RCC_PLLCFGR_PLLQ_3 |= RCC_PLLCFGR_PLLQ_2 |= 
59
  RCC_PLLCFGR_PLLQ_1 |= RCC_PLLCFGR_PLLQ_0;
60
61
  //Enable PLL-Modul
62
  RCC -> CR |= RCC_CR_PLLON;
63
64
  //Wait for PLL is stable running
65
  while(RCC -> CR & RCC_CR_PLLRDY){}
66
67
  //Switch SYSCLK to PLL_P, SW -> 10
68
  RCC -> CFGR |= RCC_CFGR_SW_1 &= ~RCC_CFGR_SW_0;
69
70
  //Wait for clock switch is ready -> SWS = 10
71
  while(RCC -> CFGR & RCC_CFGR_SWS_PLL;
72
}

von Nico W. (nico_w)


Lesenswert?

Ich hab mein Nucleo F446 schon mit 100MHz und auch mit 250MHz 
durchgetaktet. Da schließt du dich nicht so schnell aus.


Selbst mit nem Linkerskript von nem F401 nicht. Spiel damit bis du deine 
Flashzyklen durch hast. Kann aber dauern :)

von Wühlhase (Gast)


Lesenswert?

Konnte es nicht abwarten, habs mal kompiliert und (da waren doch 
tatsächlich noch ein paar kleine Syntaxfehlerteufelchen) - und siehe da, 
er hängt an dieser Stelle fest:
1
//Wait for HSI is stable running
2
while(RCC -> CR & RCC_CR_HSIRDY){}

Warum kommt der da nicht aus der Schleife raus? Anscheind wird das 
HSI-Ready-Flag nicht gesetzt...aber wieso?

von Patrick R. (ohmann)


Lesenswert?

Kleine Info am Rande - es gibt ein offizielles Excel-Sheet von ST, dort 
stellt man alle gewünschten Takte und Parameter ein und dieses generiert 
automatisch eine Datei. Aufgerufen wird diese dann über SystemInit(). 
Dadurch umgeht man die vielen möglichen Fehler, wenn man es selbst macht 
(was bei der RCC bei ARMs doch sehr schnell passiert..)

von Patrick R. (ohmann)


Lesenswert?

Wühlhase schrieb:
> Konnte es nicht abwarten, habs mal kompiliert und (da waren doch
> tatsächlich noch ein paar kleine Syntaxfehlerteufelchen) - und siehe da,
> er hängt an dieser Stelle fest:
>
>
1
> //Wait for HSI is stable running
2
> while(RCC -> CR & RCC_CR_HSIRDY){}
3
>
>
> Warum kommt der da nicht aus der Schleife raus? Anscheind wird das
> HSI-Ready-Flag nicht gesetzt...aber wieso?

Hast du den Hinweis im RefMan beachtet? Siehe 
https://stackoverflow.com/questions/31407505/pll-clock-configuration. 
Ansonsten kann das auch auf einen Verdrahtungsfehler oder Hardwarefehler 
rauslaufen, dass er die HSI nicht einschalten kann. Ist das ein eigenes 
Board?

von Andreas S. (igel1)


Lesenswert?

Patrick R. schrieb:
> Kleine Info am Rande - es gibt ein offizielles Excel-Sheet von ST, dort
> stellt man alle gewünschten Takte und Parameter ein und dieses generiert
> automatisch eine Datei. Aufgerufen wird diese dann über SystemInit().
> Dadurch umgeht man die vielen möglichen Fehler, wenn man es selbst macht
> (was bei der RCC bei ARMs doch sehr schnell passiert..

Ich kann nur Patricks Hinweis unterstreichen:
Ich nutze ebenfalls das Excel-Tool, und passe dann selber die Werte in 
meiner einmal aufgesetzten Clock-Initialisierung an.

Das Gute an dem Tool: es zeigt an, wenn Du Grenz-Taktfrequenzen über- 
oder unterschreitest und verhindert so, dass Du unsinnige Kombinationen 
als Clock-Config entwirfst.

Tool-Link:
https://my.st.com/content/my_st_com/en/products/development-tools/software-development-tools/stm32-software-development-tools/stm32-configurators-and-code-generators/stsw-stm32091.license%3d1509378915655.product%3dSTSW-STM32091.html

Viele Grüße

Igel1

von Wühlhase (Gast)


Angehängte Dateien:

Lesenswert?

Mensch, daß mit der Excel wäre klasse...allerdings unterstützt die den 
F446 anscheinend nicht. :(
Und Open Office zickt mit der Datei rum...kommt anscheinend mit den 
Makros nicht zurecht.

Schade, dabei klang das so schlüssig in dem Link. Jetzt ist wohl 
Fehlersuche angesagt...
Wenn ich die Funktion einfach kopiere beschwert sich der Compiler...und 
findet Fehler in der stm32f446xx.h?

Beitrag #5195703 wurde vom Autor gelöscht.
von Retro N. (retronerd)


Angehängte Dateien:

Lesenswert?

Warum verwendest Du nicht STM32CubeMX, so wie es ST auf der oben 
verlinkten Seite vorschlägt?

von Wühlhase (Gast)


Lesenswert?

Ich kenne CubeMX.

von A. B. (Gast)


Lesenswert?

Wühlhase schrieb:
> er hängt an dieser Stelle fest:
>
> //Wait for HSI is stable running
> while(RCC -> CR & RCC_CR_HSIRDY){}
>
> Warum kommt der da nicht aus der Schleife raus? Anscheind wird das
> HSI-Ready-Flag nicht gesetzt...aber wieso?

Naja, weil die Bedingung genau falsch herum ist: "Solange HSI ready ist 
..."

von Wühlhase (Gast)


Lesenswert?

JAAAAAAhahahaaaa...es klappt! :)

Die while-Schleife war das Problem....war wohl etwas spät geween, jetzt 
klappts. :)

Ich stelle mal die laufende Funktion rein. Falls jemand anders die mal 
braucht...
1
//für STM32F446, CPU@180MHz
2
3
//Initialize clock config
4
//Siehe: https://github.com/jkerdels/stm32edu/blob/master/src/rcc.c
5
void initClock(){
6
  //Enable HSI-clock -> 16MHz
7
  RCC -> CR |= RCC_CR_HSION;
8
9
  //Wait for HSI is stable running
10
  while((RCC -> CR & RCC_CR_HSIRDY) == 0){}
11
  
12
  //Switch SYSCLK to HSI, SW -> 00
13
  RCC -> CFGR &= ~RCC_CFGR_SW_0;
14
  RCC -> CFGR &= ~RCC_CFGR_SW_1;
15
16
  //Wait for switching SYSCLK is ready
17
  while((RCC -> CFGR & RCC_CFGR_SWS_0 == false) && (RCC -> CFGR & RCC_CFGR_SWS_1 == false)){}
18
19
  //Config waitstates for flash memory, cpu should run with nearly 180MHz@3,3V -> 5ws after table, page 64
20
  FLASH -> ACR |= FLASH_ACR_LATENCY_5WS;
21
22
  //Config realtime clock, set LSI as clocksource
23
  RCC -> CSR |= RCC_CSR_LSION;
24
25
  //Set AHB clock prescaler for main bus matrix, psc=1 for running with maximum speed -> 180MHz, Presc. =1
26
  RCC -> CFGR |= RCC_CFGR_HPRE_DIV1;
27
28
  //Set clock for APB1 to AHB/4 => 180MHz/4 -> 45MHz
29
  RCC -> CFGR |= RCC_CFGR_PPRE1_DIV4;
30
31
  //Set clock for APB2 to AHB/2 => 180MHz/2 -> 90MHz
32
  RCC -> CFGR |= RCC_CFGR_PPRE2_DIV2;
33
34
  //Set phase lock loop
35
  //Disable PLL for config
36
  RCC -> CR &= ~RCC_CR_PLLON;
37
  RCC -> CR &= ~RCC_CR_PLLI2SON;
38
39
  //Select HSI for pll clock source
40
  RCC -> PLLCFGR |= RCC_PLLCFGR_PLLSRC_HSI;
41
42
  //Set prescaler for pll clock input: HSI(16MHz) -> input should be 2MHz -> Prescaler =8
43
  RCC -> PLLCFGR |= RCC_PLLCFGR_PLLM_3;
44
45
  //Clock = fin*(N/M) -> 2MHz*(360/4) for 180MHz
46
  //Set N=360 -> 0b1 0110 1000
47
  RCC -> PLLCFGR |= RCC_PLLCFGR_PLLN_8 | RCC_PLLCFGR_PLLN_6 | RCC_PLLCFGR_PLLN_5 | RCC_PLLCFGR_PLLN_3;
48
  //Set M=4 0b100
49
  RCC -> PLLCFGR |= RCC_PLLCFGR_PLLM_2;
50
  //Set 48MHz for VCO, -> 2*(N/Q) -> Set Q=15 -> 0b1111
51
  RCC -> PLLCFGR |= RCC_PLLCFGR_PLLQ_3 | RCC_PLLCFGR_PLLQ_2 | RCC_PLLCFGR_PLLQ_1 | RCC_PLLCFGR_PLLQ_0;
52
53
  //Enable PLL-Modul
54
  RCC -> CR |= RCC_CR_PLLON;
55
56
  //Wait for PLL is stable running
57
  while(RCC -> CR & RCC_CR_PLLRDY){}
58
59
  //Switch SYSCLK to PLL_P, SW -> 10
60
  RCC -> CFGR |= RCC_CFGR_SW_1 & ~RCC_CFGR_SW_0;
61
62
  //Wait for clock switch is ready -> SWS = 10
63
  while(RCC -> CFGR & RCC_CFGR_SWS_PLL){};
64
}

von Andreas S. (igel1)


Lesenswert?

Respekt an A. B. für sein scharfes Auge!

von Wühlhase (Gast)


Lesenswert?

Ja, aber sowas von.

von Nico W. (nico_w)


Lesenswert?

Dann hoffen wir Mal dass er die anderen Stellen auch findet :)

von Andreas S. (igel1)


Lesenswert?

Nico W. schrieb:
> Dann hoffen wir Mal dass er die anderen Stellen auch findet :)

?

von Wühlhase (Gast)


Lesenswert?

Nico W. schrieb:
> Dann hoffen wir Mal dass er die anderen Stellen auch findet :)

Versteh ich nicht. Die Taktinit läuft jetzt. Und wenn ich einen Timer so 
mißbrauche daß er als Taktteiler bis herunter in den Hertzbereich dient 
(Einfaches Blinklicht) scheint das Teil zu tun was es soll.

Oder hast du noch Fehler gefunden...?

von Andreas S. (igel1)


Lesenswert?

Nico W. steht immer noch im Raum mit seinem "ich sehe was, was Du nicht 
siehst und das ist falsch ..."

Nur leider spendiert er uns keine Auflösung zu seinem Menetekel.

@Wühlhase:
Ich selber nutze zum Aufsetzen meines Clocktrees die Funktionen der 
Standard Peripheral Library (SPL) von ST, weil ich einfach annehme, dass 
die Funktionen darin inzwischen einen entsprechenden Reifegrad erreicht 
haben.

In Deinem Fall könntest Du ggf. Deine Clock-Initialisierung einmal mit 
der Funktion "static void SetSysClock(void)" aus dem File 
"system_stm32f4xx.c" vergleichen.

Beim Überfliegen schienen mir in der SPL-Funktion noch ein paar Dinge 
mehr beachtet zu sein, als es in Deiner Funktion der Fall ist. Auch 
passiert dort einiges an "Casting", was bei Dir gänzlich fehlt. Aber 
einen Detailvergleich habe ich nicht gemacht - dafür kenne ich mich 
einfach nicht hinreichend gut in der Materie aus.

Viele Grüße

Igel1

von Wühlhase (Gast)


Lesenswert?

@Andreas:
Die Standardbibliothek...als ich mich mit den STM32 das erste Mal 
beschäftigt habe war die schon abgekündigt. Ich habe deren Sinn bisher 
noch nie recht verstanden. Die Registerbezeichnungen nochmal hinter 
einem Struct versteckt-ich hab nich gesehen wo mir das Arbeit erspart 
hätte. Das Datenblatt hätte ich so oder so lesen müßen (ist ja kein 
Arduino), und die Werte die ich sonst direkt in die Register geschrieben 
hätte, werden dann halt stattdessen in eine Struktur gepackt.

Und dann ist die auch noch für jede Controllerfamilie unterschiedlich 
(dieses Manko soll ja die HAL ausbügeln). Nee...in die SPL arbeite ich 
mich lieber nicht mehr ein. Entweder HAL, wenn es sein muß, oder lieber 
direkt Register...

von Wühlhase (Gast)


Lesenswert?

Noch etwas:

Andreas S. schrieb:
> Beim Überfliegen schienen mir in der SPL-Funktion noch ein paar Dinge
> mehr beachtet zu sein, als es in Deiner Funktion der Fall ist. Auch
> passiert dort einiges an "Casting", was bei Dir gänzlich fehlt. Aber
> einen Detailvergleich habe ich nicht gemacht - dafür kenne ich mich
> einfach nicht hinreichend gut in der Materie aus.
Die HAL macht sicher auch noch einiges mehr als mein Codeschnipsel da 
oben. Ich hab mir das HAL-Pendant dazu auch noch nicht angesehen, aber 
die Funktion um ein Byte aus der SPI rauszuschieben ist etwas über 100 
Zeilen lang. Da wird dann noch geprüft ob die Hardware wirklich frei 
ist, lauter Fehlerflags geprüft, auf Freigaben gewartet, und am Ende 
alles wieder aufgeräumt und freigegeben.
Die STL ist zwar sicher nicht so überladen, ich kann mir aber vorstellen 
daß da auch die ein oder andere Sicherung drin ist die man sich bei 
direktem Registerzugriff selber baut oder wegläßt.

von Andreas S. (igel1)


Lesenswert?

Wühlhase schrieb:

> Die STL ist zwar sicher nicht so überladen, ich kann mir aber vorstellen
> daß da auch die ein oder andere Sicherung drin ist die man sich bei
> direktem Registerzugriff selber baut oder wegläßt.

Die Frage ist ja stets "Habe ich an alles gedacht, was manchmal quer 
über die 1600 Seiten Reference Manual verstreut ist oder nicht?"

Denn wenn Code in meinem Test funktioniert, so heißt das noch lange 
nicht, dass dieser Code auch in allen späteren 
Lebenslagen/Programmzuständen funktionieren wird. Und deshalb schaue ich 
mir der SPL-Funktionen gerne an.

Die besagte Funktion "static void SetSysClock(void)" aus dem File
"system_stm32f4xx.c" sollte für Dich als CMSIS-Nutzer ein offenes Buch 
sein.

Dafür brauchst Du Dich definitiv nicht in die SPL einarbeiten und hast 
trotzdem den Vorteil, Deinen Code mit dem Code von jemand anderem (der 
mehr oder weniger dasselbe implementiert hat) vergleichen zu können.

Aber das ist nur meine ganz persönliche Meinung - selbstverständlich hat 
da so jeder seine eigene Herangehensweise und ich möchte Dich zu nichts 
zwingen ...

Viele Grüße

Igel1

von Wühlhase (Gast)


Angehängte Dateien:

Lesenswert?

Andreas S. schrieb:
> Die besagte Funktion "static void SetSysClock(void)" aus dem File
> "system_stm32f4xx.c" sollte für Dich als CMSIS-Nutzer ein offenes Buch
> sein.
Hm...ich hab die Funktion mal eben gesucht, find sie im Projekt aber 
nicht...?

Andreas S. schrieb:
> Aber das ist nur meine ganz persönliche Meinung - selbstverständlich hat
> da so jeder seine eigene Herangehensweise und ich möchte Dich zu nichts
> zwingen ...
Nun, ich schätze mal daß sich in diesem Thread hier jeder mit dem STM32 
besse auskennt als ich-ich stell hier ja nicht umsonst Fragen. Ich hab 
dich da wohl falsch verstanden...

von Andreas S. (igel1)


Lesenswert?

Wühlhase schrieb:
> Andreas S. schrieb:
>> Die besagte Funktion "static void SetSysClock(void)" aus dem File
>> "system_stm32f4xx.c" sollte für Dich als CMSIS-Nutzer ein offenes Buch
>> sein.
> Hm...ich hab die Funktion mal eben gesucht, find sie im Projekt aber
> nicht...?

In dem von Dir referenzierten Projekt wird kein SPL verwendet,
deshalb gibt es dort das File "system_stm32f4xx.c" nicht.

Wenn Du Dir das Teil anschauen möchtest, mußt Du zunächst die SPL hier 
herunterladen: http://www.st.com/en/embedded-software/stsw-stm32065.html
(Letzter Link "Get Software").

Schön auspacken ...
Dann findest Du das File "system_stm32f4xx.c" hier:
STM32F4xx_DSP_StdPeriph_Lib_V1.8.0\Libraries\CMSIS\Device\ST\STM32F4xx\S 
ource\Templates

> Andreas S. schrieb:
>> Aber das ist nur meine ganz persönliche Meinung - selbstverständlich hat
>> da so jeder seine eigene Herangehensweise und ich möchte Dich zu nichts
>> zwingen ...
> Nun, ich schätze mal daß sich in diesem Thread hier jeder mit dem STM32
> besse auskennt als ich-ich stell hier ja nicht umsonst Fragen. Ich hab
> dich da wohl falsch verstanden...

Ganz und gar nicht - ich bin ebenfalls nur STM32-Anfänger.
Meine Ratschläge sind also mit Vorsicht zu geniessen.

Viele Grüße

Igel1

von Nico W. (nico_w)


Lesenswert?

Andreas S. schrieb:
> Nico W. steht immer noch im Raum mit seinem "ich sehe was, was Du nicht
> siehst und das ist falsch ..."
>
> Nur leider spendiert er uns keine Auflösung zu seinem Menetekel.

Liegt auch daran, dass ich nicht mit F5 im Forum rumstöbere und manchmal 
ein paar Tage auch was anderes zu tun habe.

Ich dachte, dass der TO nach dem Hint

A. B. schrieb:
> Naja, weil die Bedingung genau falsch herum ist: "Solange HSI ready ist
> ..."

und meiner Anmerkung, vielleicht einmal den Code genauer ansieht und den 
gleichen Fehler ein paar Zeilen später wieder erkennt.

Ist vielleicht nur Zufall, dass das Init durchläuft.

Wühlhase schrieb:
> //Wait for PLL is stable running
>   while(RCC -> CR & RCC_CR_PLLRDY){}

Weiter suche ich jetzt nicht. Aber das ist mir halt auch noch 
aufgefallen.

von Wühlhase (Gast)


Angehängte Dateien:

Lesenswert?

Ich muß mich nochmal mit Schande zurückmelden. Die Clockinit läuft 
längst nicht so wie sie soll.

Gemerkt hab ich es erst so richtig bei der UART-Benutzung. Wenn ich von 
der Baudrate auf die Taktung zurückrechne, müßte APB1 mit etwa 18MHz 
laufen statt mit 45MHz, wobei ich nicht weiß wie stark der FT232 
überabtastet und dabei Fehler vermeidet.

Ich habe außerdem einen Timer, der eine LED im Sekundentakt toggelt, 1s 
an, 1s aus. Ich zähle insgesamt 20 Perioden, und die parallel laufende 
Stoppuhr zeigt knapp 1min (statt der zu erwartenden 40s).

Ich finde nichts mehr was ich noch einzustellen vergessen haben könnte. 
Die Warteschleifen beim Warten auf HW-Konfiguration sollten auch korrekt 
laufen. Hab ich irgendetwas in der falschen Reihenfolge gemacht?
Ich hab mir auch den Code angesehen den CubeMX generiert, aber auch da 
nichts gefunden was mir aufgefallen wäre.

Hat noch jemand eine Idee? Die aktuelle Clockinit sieht so aus, das 
Projekt in den Anhang gestopft falls es jemandem hilft:
1
//Initialize clock config
2
//Siehe: https://github.com/jkerdels/stm32edu/blob/master/src/rcc.c
3
void initClock(){
4
  //Enable HSI-clock -> 16MHz
5
  RCC -> CR |= RCC_CR_HSION;
6
7
  //Wait for HSI is stable running
8
  while(RCC -> CR & !RCC_CR_HSIRDY ){}
9
  
10
  //Switch SYSCLK to HSI, SW -> 00
11
  RCC -> CFGR &= ~RCC_CFGR_SW_0;
12
  RCC -> CFGR &= ~RCC_CFGR_SW_1;
13
14
  //Wait for switching SYSCLK is ready
15
  while((RCC -> CFGR & RCC_CFGR_SWS_0 == false) && (RCC -> CFGR & RCC_CFGR_SWS_1 == false)){}
16
17
  //Config waitstates for flash memory, cpu should run with nearly 180MHz@3,3V -> 5ws after table, page 64
18
  FLASH -> ACR |= FLASH_ACR_LATENCY_6WS;
19
20
  //Config realtime clock, set LSI as clocksource
21
  RCC -> CSR |= RCC_CSR_LSION;
22
23
  //Set AHB clock prescaler for main bus matrix, psc=1 for running with maximum speed -> 180MHz, Presc. =1
24
  RCC -> CFGR &= ~RCC_CFGR_HPRE_3 & ~RCC_CFGR_HPRE_2 & ~RCC_CFGR_HPRE_1 & ~RCC_CFGR_HPRE_0;
25
26
  //Set clock for APB1 to AHB/4 => 180MHz/4 -> 45MHz
27
  RCC -> CFGR |= RCC_CFGR_PPRE1_DIV8;
28
29
  //Set clock for APB2 to AHB/2 => 180MHz/2 -> 90MHz
30
  RCC -> CFGR |= RCC_CFGR_PPRE2_DIV8;
31
32
  //Set phase lock loop
33
  //Disable PLL for config
34
  RCC -> CR &= ~RCC_CR_PLLON;
35
  RCC -> CR &= ~RCC_CR_PLLI2SON;
36
37
  //Select HSI for pll clock source
38
  RCC -> PLLCFGR |= RCC_PLLCFGR_PLLSRC_HSI;
39
40
  //Set prescaler for pll clock input: HSI(16MHz) -> input should be 1-2MHz -> Prescaler =10, clock is set to 1,6MHz
41
  RCC -> PLLCFGR |= RCC_PLLCFGR_PLLM_3 | RCC_PLLCFGR_PLLM_1;
42
43
  //Clock = fin*(N/P) -> 1,6MHz*(225/2) for 180MHz
44
  //Set N=225 -> 0b1110 0001
45
  RCC -> PLLCFGR |= RCC_PLLCFGR_PLLN_7 | RCC_PLLCFGR_PLLN_6 | RCC_PLLCFGR_PLLN_5 | RCC_PLLCFGR_PLLN_0;
46
  //Set P=2 0b00
47
  RCC -> PLLCFGR &= ~RCC_PLLCFGR_PLLP_1 & ~RCC_PLLCFGR_PLLP_0;
48
49
  //Enable PLL-Modul
50
  RCC -> CR |= RCC_CR_PLLON;
51
52
  //Wait for PLL is stable running
53
  while(RCC -> CR & !RCC_CR_PLLRDY){}
54
55
  //Switch SYSCLK to PLL_P, SW -> 10
56
  RCC -> CFGR |= RCC_CFGR_SW_1 & ~RCC_CFGR_SW_0;
57
58
  //Wait for clock switch is ready -> SWS = 10
59
  while((RCC -> CFGR & RCC_CFGR_SWS_PLL) != RCC_CFGR_SWS_PLL){};
60
}

von Nop (Gast)


Lesenswert?

Wühlhase schrieb:
> //Set clock for APB1 to AHB/4 => 180MHz/4 -> 45MHz
>   RCC -> CFGR |= RCC_CFGR_PPRE1_DIV8;
>
>   //Set clock for APB2 to AHB/2 => 180MHz/2 -> 90MHz
>   RCC -> CFGR |= RCC_CFGR_PPRE2_DIV8;

Ich hab jetzt keine Lust, mich durch die defines zu wühlen, aber 
merkwürdig sieht das schon aus.

von Nico W. (nico_w)


Lesenswert?

Wühlhase schrieb:
> //Wait for PLL is stable running
>   while(RCC -> CR & !RCC_CR_PLLRDY){}


Das sieht auch nicht richtig aus. !RCC_CR_PLLRDY ist wahrscheinlich 
nicht das worauf du testen willst. Das ist müsste normal zu 0 werden.

1
while (!(RCC->CR & RCC_CR_PLLRDY));

Beitrag #5222964 wurde von einem Moderator gelöscht.
von Wühlhase (Gast)


Lesenswert?

Guten Morgen

@Nop:
Argh...das ist ein Überbleibsel vom Testen, das ist keiensfalls der 
"normale" Code.

Allerdings ist mir dabei eines aufgefallen was mich stutzig macht, 
jedoch finde ich keine Erklärung dafür. Wenn ich beide Bustakte durch 8 
teile, statt der Teiler in den Kommentaren, ändert sich an der 
Blinkfrequenz der LED genau gar nichts.
Hatte jemand das Problem schon mal oder kennt jemand den STM32 gut genug 
um zu wissen was da schief gegangen sein könnte? Kann das mit dem 
Problem, das Nico noch entdeckt hat, zusammenhängen? (Ich hätte jetzt 
nein gesagt, aber ich weiß es halt nicht.)

@Nico:
Allmählich schäme ich mich für mein C...danke fürs Finden.

von A. B. (Gast)


Lesenswert?

Wühlhase schrieb:

>   //Switch SYSCLK to HSI, SW -> 00
>   RCC -> CFGR &= ~RCC_CFGR_SW_0;
>   RCC -> CFGR &= ~RCC_CFGR_SW_1;

Keine gute Idee, je nachdem, ob der Compiler das eventuell "in einem 
Rutsch" macht. Alle zusammengehörenden Bits sollten in nur einem 
Schreibzugriff gemeinsam verändert werden. Sonst gibt's u. U. ungültige 
"Zwischenzustände".

>   //Set AHB clock prescaler for main bus matrix, psc=1 for running with
> maximum speed -> 180MHz, Presc. =1

Die max. Abweichung der HSI-Frequenz vom Soll beträgt -8%/+4.5%! Da 
würde ich sicherheitshalber 10% unter den 180MHz bleiben. Auch wenn man 
den Prozessor sicher problemlos etwas übertakten kann, solide sieht 
anders aus.
Angesichts dieser Toleranz ist UART damit schon recht optimistisch. Na 
gut, bei konstant Zimmertemperatur geht's ja aber ...

>   RCC -> CFGR &= ~RCC_CFGR_HPRE_3 & ~RCC_CFGR_HPRE_2 & ~RCC_CFGR_HPRE_1
> & ~RCC_CFGR_HPRE_0;
>
>   //Set clock for APB1 to AHB/4 => 180MHz/4 -> 45MHz
>   RCC -> CFGR |= RCC_CFGR_PPRE1_DIV8;

Und hier werden plötzlich alle Bits gemeinsam gesetzt? Aber auch nur 
gesetzt, eventuell zu löschende (da in RCC_CFGR_PPRE1_DIV8 eventuell =0, 
eventuell vorher schon mal gesetzt) bleiben auf '1'! Wenig 
änderungsfreundlich.

>   //Set clock for APB2 to AHB/2 => 180MHz/2 -> 90MHz
>   RCC -> CFGR |= RCC_CFGR_PPRE2_DIV8;

S. o.

>   //Select HSI for pll clock source
>   RCC -> PLLCFGR |= RCC_PLLCFGR_PLLSRC_HSI;

Das macht keinen Sinn, denn das PLLSRC-Bit muss für HSI auf '0' gesetzt 
werden. Ist zwar nach Reset sowieso '0', aber ...

>   //Set prescaler for pll clock input: HSI(16MHz) -> input should be
> 1-2MHz -> Prescaler =10, clock is set to 1,6MHz
>   RCC -> PLLCFGR |= RCC_PLLCFGR_PLLM_3 | RCC_PLLCFGR_PLLM_1;

Wieder Bitfeld, wo nur die Einsen passend gesetzt, aber die Nullen nicht 
gelöscht werden ...

>   //Clock = fin*(N/P) -> 1,6MHz*(225/2) for 180MHz
>   //Set N=225 -> 0b1110 0001
>   RCC -> PLLCFGR |= RCC_PLLCFGR_PLLN_7 | RCC_PLLCFGR_PLLN_6 |
> RCC_PLLCFGR_PLLN_5 | RCC_PLLCFGR_PLLN_0;

dto.

>   //Set P=2 0b00
>   RCC -> PLLCFGR &= ~RCC_PLLCFGR_PLLP_1 & ~RCC_PLLCFGR_PLLP_0;

und hier mal umgekehrt.

Wenn es Unklarheiten bzgl. des tatsächlichen Taktes gibt, bleiben zwei 
offensichtliche Optionen:
1) Nach der Taktkonfiguration sieht man sich alle Registerwerte noch 
einmal an und kontrolliert jedes einzelne(!) Bit anhand des Ref.Man. Es 
gibt da übrigens auch noch ein paar Statusbits ...
Generell ist die Frage, was tatsächlich in den Registern steht, nicht 
das, was man glaubt, hineingeschrieben zu haben.

2) MCO1 oder MCO2 aktivieren und Oszi / Frequenzzähler an den Ausgang. 
Beim Teiler gibt's nur wenige Möglichkeiten, so dass man sich kaum 
vertun kann.

Per Schleife ein Portbit togglen und messen oder ähnliche Methoden sind 
angesichts der vielen Einflüsse (Wait-States, Prefetch, Pipeline, 
GPIO-Eigenheiten ...) kaum praktikabel.

von Wühlhase (Gast)


Lesenswert?

Oha...noch viel mehr was anders besser zu machen wäre.

Ich werde erst heute Nachmittag dazu kommen das zu ändern, aber trotzdem 
eine Frage: Steht in den Registern grundsätzlich eine 0, wenn es nicht 
überschrieben bzw. im Programm nichts anderes vorgesehen wird? 
(Statusbits die von der HW geändert werden natürlich ausgenommen.)

Oder ist das auch anders...

von Nico W. (nico_w)


Lesenswert?

Steht alles genau im RefMan.

von Stefan F. (Gast)


Lesenswert?

>>   RCC -> CFGR &= ~RCC_CFGR_SW_0;
>>   RCC -> CFGR &= ~RCC_CFGR_SW_1;

> Keine gute Idee, je nachdem, ob der Compiler das eventuell
> "in einem Rutsch" macht.

Das wird nie der Fall sein.

von U. Rectum (Gast)


Lesenswert?

Wieso ist es heutzutage verpönt, sich die Herstellerdoku wenigstens mal 
kurz anzusehen?
Ich rede hier nur vom kurzen Überfliegen und nicht vom Auswendiglernen?

Stattdessen wird irgendwo aus dem Netz abgekupfert und das auch noch von 
Quellen, die entweder selber sich nicht mit Fachwissen belasten oder 
aber wie hier von einem anderen Controller handeln.

Grundsätzlich:
Die Konfiguration ist kein Hexenwerk und dementsprechend auch nicht 
schwer. Steht alles im Datenblatt und Reference Manual. Teils sogar sehr 
detailliert. Muss man halt nur lesen.

Wer also die beiden Dokumente mal überflogen hat, merkt sofort, dass der 
Controller, der hier mit 180MHz betrieben werden soll, anders als der 
Controller aus der ungeeigneten Quelle (168MHz) konfiguriert werden 
muss. @Wühlhase: Wieso berücksichtigst Du das nicht?

Datasheet September 2016, DocID027107 Rev 6, Seite 76 (auch Reference 
Manual Seite 65):
Für 180MHz muss der Spannungsregler im Overdrive-Modus laufen. Wieso 
programmierst Du den nicht entsprechend?
Detailliert beschrieben im Reference Manual July 2017, DocID026976 Rev 
3, Seite 96.

Die HSI-Clocksource ist nach dem Reset aktiviert, die muss man gar nicht 
konfigurieren, wie Du das machst (Reference Manual, Seite 126).
Das Lesen der Reset Werte empfiehlt sich auch für andere Register. Das 
spart nämlich unnötigen Platz im Flash und Performance.


Daher mal ein Vorschlag hier (aus dem Bauch heraus, nicht getestet 
mangels Controller):
1
  #define PLLM      8
2
  #define PLLN      180
3
  #define PLLP      2
4
5
6
  // Caches, Prefetch, Waitstates
7
  FLASH->ACR |= ( FLASH_ACR_DCEN | FLASH_ACR_ICEN | FLASH_ACR_PRFTEN | FLASH_ACR_LATENCY_5WS );
8
9
  //
10
  RCC->CFGR = ( RCC_CFGR_PPRE2_DIV8 | RCC_CFGR_PPRE1_DIV8 | RCC_CFGR_HPRE_DIV1);
11
12
  // 0x2400 -> Reset-Werte der Peripherie-PLLs oder Reserviert
13
  RCC->PLLCFGR = ( 0x24000000 | RCC_PLLCFGR_PLLSRC_HSI | ((( PLLP >> 1 ) - 1 ) << 16 ) | ( PLLN << 6 ) | PLLM );

Bei letzterem nimmst Du völlig krude Werte. Wieso die 1,6MHz als 
VCO-Eingangsfrequenz, wenn das Reference Manual auf Seite 129 
unmissverständlich schreibt:
"It is recommended to select a frequency of 2 MHz to limit PLL jitter."
?
1
  RCC->CR |= ( RCC_CR_PLLON );
2
  while( !( RCC->CR & RCC_CR_PLLRDY ) )
3
    asm ("nop");
4
5
  PWR-CR |= ( PWR_CR_ODEN );
6
  while( !( PWR->CR & PWR_CSR_ODRDY ) )
7
    asm ("nop");
8
9
  RCC->CFGR |= ( RCC_CFGR_SW_PLL );
10
  while( ( RCC->CFGR & RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL )
11
    asm ("nop");


Es können noch ein paar kleinere Fehler drin sein, ausprobieren kann ich 
das jetzt nicht.
Im Groben sollte das aber so reichen.
Für eine "richtige" Anwendung solltest Du in die while-Schleifen noch 
einen Timeout einbauen, falls da was schiefgeht und dann entsprechend 
eine Notfallroutine starten.

Zum Schluss nochmals die Empfehlung:
Überflieg kurz Datasheet und Reference Manual. Sieht erstmal nach viel 
aus, aber die komplette Peripherie kannst Du ja am Anfang weglassen, 
damit ist der größte Teil schonmal raus.
Wenn Du dann so einen Controller programmierst, schau Dir im Manual 
jedes Register an und überlege, ob das für Dich Relevanz hat.
Falls ja, dann konfiguriere es entsprechend, falls es nicht eh schon den 
benötigten Wert als Reset Wert hat. Doppelt hält in diesem Falle nicht 
besser...

von Andreas S. (igel1)


Lesenswert?

Nachdem Wühlhase von Nico W. und U. Rectum vor zwei Tagen die volle 
Breitseite bekommen hat, möchte ich hier ein wenig mein Mitgefühl 
ausdrücken:

Wer sich nur hobbymäßig mit einem solchen Prozessor beschäftigt und 
dafür vielleicht 5h jeweils verteilt über die Woche investieren kann, 
der ist mit 2000-3000 Seiten Doku (Ref + Datasheet + Application Notes) 
wirklich am Limit (jedenfalls gilt das für mich).

Dem Vorwurf "Steht alles genau im RefMan." oder "Wieso ist es heutzutage 
verpönt, sich die Herstellerdoku wenigstens mal kurz anzusehen?" steht 
man da etwas sprachlos gegenüber: natürlich habt Ihr recht - aber was 
soll ein Hobbymensch dagegen machen? Job an den Nagel hängen? Frau und 
Kinder verkaufen? Hobby an den Nagel hängen? Oder sich vielleicht doch 
in diesem Forum von hilfsbereiten Geistern unter die Arme greifen 
lassen?

Ich bin für letztere Alternative ...

Und um auch etwas Inhaltliches beizutragen, hier an Wühlhase nochmals 
den Tipp: schau Dir an, wie die Clock-Initialisierung in der Funktion 
"SystemInit()" in der SPL (Standard Peripheral Library) realisiert 
wurde.

Die ist von ST selbst publiziert worden, ist millionenfach erprobt und 
sogar an Dinge wie "overclocking" ist dort gedacht worden.

Entgegen Deiner Befürchtungen brauchst Du Dich dafür nicht in die 
komplette SPL einzuarbeiten. Es sollte ausreichen, wenn Du die Datei 
"system_stm32f4xx.c" gut durcharbeitest.

Viele Grüße

Igel1

von Wühlhase (Gast)


Lesenswert?

@U Rectum:
So verpönt ist DB-Lesen nicht...im Gegenteil. Und wenn im Kapitel zur 
Taktungerzeugung auch was vom Spannungsstabilisator stehen würde, 
wenigstens ein Hinweis in normaler Schriftgröße, hätte ich das bestimmt 
schon ausprobiert.

Danke aber dennoch für den Hinweis, das sind genau die Kleinigkeiten 
weswegen ich hier frage.
Trotzdem-ein normaler Umgangston würde die deutlich besser zu Gesicht 
stehen. Mach ich denn echt den Eindruck, dieses Schreiben so komplett zu 
ignorieren?

@Andreas:
Ich werd das mit der SPL wohl nochmal überdenken und sie mir mal 
ansehen.
Danke

von Nico W. (nico_w)


Lesenswert?

Breitseite?


Das war ein Hinweis! Nur weil ich nicht in jedem Satz einen Smiley nutze 
und du das falsch interpretiert hast, heißt das noch lange nicht das 
hier scharf geschossen wird.


Btw. Ich habe mit uC im Berufsleben wenig zu tun. Bei solchen Sachen 
braucht man Zeit. Davon habe ich auch nur sehr begrenzt. Bis mein ADC 
über DMA lief, verging ein halbes Jahr. Nicht weil ich zu blöd war, 
vielleicht ein wenig, hätte ja abgucken können, vor allem weil ich wenig 
Zeit habe und mir nicht alles vorkauen lassen möchte.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Wühlhase schrieb:
> Ich werd das mit der SPL wohl nochmal überdenken und sie mir mal
> ansehen.

Ich weiss auch gar nicht, warum SPL bei dir so tabu ist, zumal es sich 
hier um Programmteile handelt, die ein einziges Mal beim Startup laufen 
- das Argument mit den angeblich so langsamen Routinen zählt hier nicht.
Noch ein Hinweis - für z.B. stabile I²S Clocks werden z.B. 2MHz als 
Raster der ersten PLL empfohlen, 1 Mhz jittert hier deutlich mehr.

von U. Rectum (Gast)


Lesenswert?

Matthias S. schrieb:
> Wühlhase schrieb:
>> Ich werd das mit der SPL wohl nochmal überdenken und sie mir mal
>> ansehen.
>
> Ich weiss auch gar nicht, warum SPL bei dir so tabu ist, zumal es sich
> hier um Programmteile handelt, die ein einziges Mal beim Startup laufen


Weil gerade die system_xxx Maximal-Bloat ist. Für das bisschen 
Register-Setzen?
Das Verständnis für die Controller wird dabei auch nicht besser.
Und nochmals: Auch wenn einen die Seitenanzahl der Doku erschreckt, wenn 
man nur die relevanten Teile (Reset, Power, Clock) durchliest, weiss man 
schnell Bescheid, wenn man zuvor schon AVR oder andere Controller 
programmiert hat.


> Noch ein Hinweis - für z.B. stabile I²S Clocks werden z.B. 2MHz als
> Raster der ersten PLL empfohlen, 1 Mhz jittert hier deutlich mehr.


Hatte ich oben bereits angemerkt:

U. Rectum schrieb:
> Bei letzterem nimmst Du völlig krude Werte. Wieso die 1,6MHz als
> VCO-Eingangsfrequenz, wenn das Reference Manual auf Seite 129
> unmissverständlich schreibt:
> "It is recommended to select a frequency of 2 MHz to limit PLL jitter."

von Wühlhase (Gast)


Lesenswert?

Ich hab mir die SPL jetzt mal angesehen...das wirft aber mehr Fragen auf 
als es mir beantwortet. Vielleicht ist das aber auch ganz gut...

Zum Beispiel: Gleich zu Beginn aktiviert die Funktion den HSE. Was 
passiert da eigentlich, wenn gar kein Quarz angeschlossen ist?
1
static void SetSysClock(void)
2
{
3
#if defined(STM32F40_41xxx) || defined(STM32F427_437xx) || defined(STM32F429_439xx) || defined(STM32F401xx) || defined(STM32F412xG) || defined(STM32F413_423xx) || defined(STM32F446xx)|| defined(STM32F469_479xx)
4
/******************************************************************************/
5
/*            PLL (clocked by HSE) used as System clock source                */
6
/******************************************************************************/
7
  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
8
  
9
  /* Enable HSE */
10
  RCC->CR |= ((uint32_t)RCC_CR_HSEON);
11
 
12
  /* Wait till HSE is ready and if Time out is reached exit */
13
  do
14
  {
15
    HSEStatus = RCC->CR & RCC_CR_HSERDY;
16
    StartUpCounter++;
17
  } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));

von Walter T. (nicolas)


Lesenswert?

Wühlhase schrieb:
> Was
> passiert da eigentlich, wenn gar kein Quarz angeschlossen ist?

Das steht zumindest bei mir in der SetSysClockToHSE() (STM32F10x):
1
  else
2
  { /* If HSE fails to start-up, the application will have wrong clock
3
         configuration. User can add here some code to deal with this error */
4
  }

von Andreas S. (igel1)


Lesenswert?

Wühlhase schrieb:
> Ich hab mir die SPL jetzt mal angesehen...das wirft aber mehr Fragen auf
> als es mir beantwortet. Vielleicht ist das aber auch ganz gut...
>
> Zum Beispiel: Gleich zu Beginn aktiviert die Funktion den HSE. Was
> passiert da eigentlich, wenn gar kein Quarz angeschlossen ist?
>

Hiermit wird der externe Oszillator eingeschaltet:
1
  /* Enable HSE */
2
  RCC->CR |= ((uint32_t)RCC_CR_HSEON);

Man könnte meinen, dass der STM32 anschließend stehenbleibt, wenn kein 
Quarz angeschlossen ist ...

Wenn da nicht die folgenden 2 kleinen Sätze im Referenz Manual RM0090, 
Revision 15 in Kapitel  "6.2.1 HSE clock", page 154 unten ständen:
1
The HSERDY flag in the RCC clock control register (RCC_CR) indicates if the high-speed external oscillator is stable or not. At startup, the clock is not released until this bit is set by hardware.

Kurzum: der Prozessor ist schlau genug, sich nicht den Ast (HSI) selbst 
abzusägen, auf dem er sitzt. Umgeschaltet wird nur, wenn:

1.) Du den Befehl erteilt hast (per HSEON bit)
2.) Der Prozessor mit dem HSE-Signal zufrieden ist (was er Dir 
netterweise anzeigt, indem er das HSERDY bit setzt)

Dazu findet man in der Registerbeschreibung von RCC_CR unter dem Bit 
HSERDY:
1
Set by hardware to indicate that the HSE oscillator is stable. [...]

Wenn also nach HSE_STARTUP_TIMEOUT while-Schleifendurchläufen (immerhin 
0x05000 an der Zahl) das HSERDY-Bit noch immer nicht gesetzt ist, so 
bleibt HSEStatus=0 und das Programm rennt weiter unter Nutzung seines 
internen Oszillators.

Die anschließende Konfiguration von PLL & Co. wird übrigens nur dann 
vorgenommen, wenn HSEStatus==1. Wenn das nicht der Fall ist, so landest 
Du in der von  Walter Tarpan in seinem letzten Posting zitierten 
Code-Sektion.


Will sagen: das ist schon alles ganz gut durchdacht und genau deshalb 
empfehle ich das Studium eben dieser SPL-Routinen.

Viele Grüße

Igel1

: Bearbeitet durch User
von Wühlhase (Gast)


Lesenswert?

Aha...ja, dann macht das Sinn, danke für die Erläuterung. :)

von Andreas S. (igel1)


Lesenswert?

Danke für das Feedback und freut mich, dass ich helfen konnte.

Viele Grüße

Igel1

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.