Hallo Zusammen,
ich arbeite seit ein paar Monaten mit einem STM32. Genauer gesagt einem
STM32103RD. Es soll eine schnelle Regelung mit ein paar weiteren "netten
Funktionen" entstehen. Der µC ist extern mit einem 8Mhz Quarz getaktet.
AZur Entwicklung wird Atollic TrueStudio verwendet.
In der Software ist der Systemtakt auf 72Mhz eingestellt.
Nur kommt mir der µC aber sehr langsam vor. Daher habe ich ein kurzes
Programm geschrieben, in dem ein IO-Pin gesetzt und gleich wieder
gelöscht wird, um zu sehen wie lange das braucht. Danach wird der Pin
wieder gesetzt, ein Integer fünf mal hochgezählt und dann der Pin wieder
gelöscht.
GPIO_SetBits(GPIOB, GPIO_Pin_14);
GPIO_ResetBits(GPIOB, GPIO_Pin_14);
GPIO_SetBits(GPIOB, GPIO_Pin_14);
i++;
i++;
i++;
i++;
i++;
GPIO_ResetBits(GPIOB, GPIO_Pin_14);
Wenn ich das mit dem Osci "vermesse" ergibt sich folgendes:
Pin setzen und löschen: ca. 540ns
Pin setzen, 5x addieren, Pin löschen: 1,4µs
Die Zeit für die 5 Additionen sollte sich wie folgt ergeben:
1,4µs - 540ns = 860ns
Das durch 5 sollte die Zeit für eine Addition ergeben:
860ns / 5 = 172ns
Und davon der Kehrwert:
5,8Mhz
Ein bisschen wenig für einen 32-bit Controller mit 72Mhz!
Bei einem PIC weiß ich, dass ein Maschinenzyklus vier Taktzyklen
entspricht und eine Addition einen Maschinenzyklus benötigt. Wie das bei
derm STM32 ist, weiß ich nicht. Habe schon im Ref.-Manual gesucht.
Mal angenommen, der STM32 würde auch 4 Taktzyklen für die Addition
benötigen, dann wäre er mit knapp 24Mhz getaktet. Fehlt aber immernoch
Faktor 3.
Die Clock-Konfiguration sieht folgendermaßen aus:
void RCC_Configuration(void)
{
/* PCLK2 = HCLK/2 */
RCC_PCLK2Config(RCC_HCLK_Div1);
/* ADCCLK = PCLK2/4 */
RCC_ADCCLKConfig(RCC_PCLK2_Div4);
/* Enable peripheral clocks
--------------------------------------------------*/
/* GPIOA, GPIOB, GPIOC and ADC1 clock enable */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB |
RCC_APB2Periph_GPIOC | RCC_APB2Periph_ADC1 | RCC_APB2Periph_ADC2|
RCC_APB2Periph_ADC3 | RCC_APB2Periph_AFIO| RCC_APB2Periph_USART1,
ENABLE);
}
In der Datei system_stm32f10x.c kann man die Taktfrequenz und somit den
Multiplier einstellen. Wenn ich das von 72Mhz auf 24Mhz verstelle wird
alles auch um Faktor 3 langsamer. Aber generell kommt mir der Controller
zu langsam vor.
Habe ich hier einen Denkfehler oder ist der wirklich zu langsam?
Falls zu langsam: Wie kriege ich den schneller???
Grüße
Rainer
Da stimmt was nicht. Ich habe so im Kopf das Cortex-M3s eine Addition
pro Taktzyklus machen (kann man bei ARM nachgucken nicht bei ST). Ich
würde aber den Test eher mit einer millionen Additionen machen, ist
einfach ein bessser Mittelwert und die Zeit für den IO-Bus usw. kannst
du dann auch vernachlässigen. Ich schätze irgenetwas mit der PLL
Konfiguration läuft schief.
In der Setbits Routine sind noch einige zusätzliche funktionen drin, die
die Parameter prüfen.
Wenn du statt der Setbits direkt die Register nimmst, wird es deutlich
schneller. Das sollte dann glaub ich so aussehen:
Schon mal einen Blick in den Kompileroutput geworfen? Evt ist da
unnoetiger Code drinnen.
Auf welcher Ausgabegeschwindigkeit wurden die GPIOx_CRH MODEy Bits
initialisiert? Vielleicht steht da 2 MHz oder 10 MHz.
Du hast die Möglichkeit den Prozessortakt an den MCO Pin ausgeben zu
lassen.
Üblicherweise ist das PA8, damit kannst du gut überprüfen ob er so rennt
wie er soll. Der Takt wurde möglicherweise an mehreren Stellen im code
definiert, fande das am Anfang auch recht stickig.
Am besten gehts über den Startup code...
Ping Konfiguration für PA.8 als MCO Pin
Uwe Bonnes schrieb:> Schon mal einen Blick in den Kompileroutput geworfen? Evt ist da> unnoetiger Code drinnen.
Yep. Ist es. Fast der gesamte Code in GPIO_SetBits ist gewissermassen
überflüssig. Wer es eilig hat darf diese Funktionen nicht verwenden,
sondern sollte direkt an die Portregister gehen..
Hast du da eventuell externes Flash an einem schmalen Bus (8 oder 16
bit) dran, und auch noch mit langsamen Bausteinen?
Das nur als Hinweis, denn ich weiß nicht, ob dies beim STM32 möglich
ist. So gut kenne ich den nicht. Außer, daß es ein ARM Cortex ist.
Bei den LPC2000 (ARM7) konnte man den externen Bus verschiedenartig
gestalten und konfigurieren, das ergab auch große Unterschiede in der
Programmlaufzeit. Unabhängig von System- und CPU-Takt.
Hubu schrieb:> Außerdem kann man die Treiber für die Ausgänge konfigurieren, es gibt> glaube ich Modi für 50 und 2 Mhz, hier sollte man auch noch mal gucken.
Das beeinflußt nur die Treiberstärke der Portpins (und damit die
Anstiegszeiten). Das ist von ST unglücklich formuliert worden.
Rainer schrieb:> /* PCLK2 = HCLK/2 */> RCC_PCLK2Config(RCC_HCLK_Div1);
Hier passt der Kommentar nicht zum Code.
Duke
Hi!
Also ersteinmal Danke für die schnellen Antworten.
Die Ausgangstreiber sind auf 50Mhz gestellt:
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
Das man die Ausgänge schneller schalten kann, indem man direkt auf die
Register schreibt ist sicherlich richtig. Dadurch wird der gesamte Chip
aber auch nicht schneller :-)
Habe eben einmal die Idee von "vaid" ausprobiert und den PA.8 als
Ausgang für den Systemtakt konfiguriert. Passiert leider garnichts.
Zuvor war der Pin als "GPIO_Mode_Out_PP" konfiguriert um eine LED zu
schalten. Die LED ist zwar nach wie vor angeschlossen, aber das sollte
der Sache doch keinen Abbruch tun, oder?
Generell muss ich sagen, habe ich Probleme mit dem STM32. Diese riesigen
vorgefertigten Lib's und Konfigurationen finde ich absolut
undurchsichtig. Da blickt doch kein Mensch durch!
Welche Funktionen muss ich denn alle in meinem Code aufrufen, um die
wichtigesten Clocks zu konfigurieren? Ich habe auch schon einmal
versucht die "SetSysClockTo72(void);" usw. alle manuell am Anfang noch
einmal aufzurufen, aber das macht keinen Unterschied.
Rainer schrieb:> Dadurch wird der gesamte Chip aber auch nicht schneller :-)
Deine Frequenzberechnung oben ist komplett unbrauchbar. So wie du das
machst kann man keine Chipfrequenz ableiten und definitiv nicht daraus
schliessen, dass da etwa nur 5,8MHz am Werk wären.
Beispielsweise wird die mancher Compiler deine Additionen sämtlich
wegstreichen und als +5 gesammelt woanders unterbringen, sofern sich für
den Wert von i überhaupt jemand interessiert.
> Generell muss ich sagen, habe ich Probleme mit dem STM32. Diese riesigen> vorgefertigten Lib's und Konfigurationen finde ich absolut> undurchsichtig. Da blickt doch kein Mensch durch!
Ich finde die FwLib eine eher begrenzte Hilfe. Die Chipreferenz zu lesen
bleibt einem sowieso nicht erspart, aber mit der FwLib muss man
zusätzlich auch noch mit der Doku der Lib kämpfen - und überlegen,
welche Funktion vom Chip welcher Parametrisierung der Lib entspricht und
umgekehrt.
Ich halte die Parametrisierung über structs für ausgesprochen
umständlich.
Die Lib ist in einzelnen Fällen als Vorlage nützlich, um zu sehen wie es
dort gemacht wurde.
> Deine Frequenzberechnung oben ist komplett unbrauchbar. So wie du das> machst kann man keine Chipfrequenz ableiten und definitiv nicht daraus> schliessen, dass da etwa nur 5,8MHz am Werk wären.
Wie würdest du vorgehen? Bzw. warum ist das deiner Meinung so sinnlos?
Wenn wir einmal davon ausgehen, dass der Chip in der Zwischenzeit nichts
anderes macht und auch keine Interrupts dazwischenfunken sollte das doch
so grob stimmen.
Rainer schrieb:> Wie würdest du vorgehen?
Die Frage ist zwar nicht an mich gerichtet, aber ich machte das auch
schon mal so, daß ich einfach ein Stück Code schrieb, und dessen
Laufzeit mit dem Timer gemessen habe. Notfalls auch mit dem Oszi. Für
eine Einschätzung reicht das.
Dann kann man die Assemblerbefehle zählen, und die Zeit durch die Anzahl
Befehle dividieren, um eine mittlere Zeit für einen einzelnen Befehl
heraus zu bekommen. Das geht ganz einfach und gut.
Und wenn das stimmt, ist was anderes im Argen. Programmiertechnik,
Softwaregestaltung.
Und wenn die 72MHz dennoch nicht reichen sollten, es gibt bereits einen
STM32F20x mit 120MHz.
Der ist nicht 100% Pinkompatibel, ich meine 2 Pins sind anders definiert
und muss man im Detail anschauen.
Rainer schrieb:> Wie würdest du vorgehen? Bzw. warum ist das deiner Meinung so sinnlos?
Weil du dafür nicht einfach C Statements zusammenzählen kannst. Du musst
runter aufs Assembler-Listing und darin die Befehle mit ihrer jeweiligen
Laufzeit rechnen.
Dazu kommen weitere Abweichungen zwischen den Varianten mit und ohne
Addition, die sich durch unterschiedliches Code-Alignment ergeben
können. So kann es einen Unterschied ergeben, an welche Stelle einer
Flash-Line der Rücksprung aus der Funktion erfolgt.
Mit welcher Compiler Optimierungsstufe wird der Code generiert (ich hab
jetzt nicht alles hier gelesen)?
Mit 1 oder 2 oder 3 wird der Code auch schon mal schneller.
(nicht vergessen, alles neu übersetzen!)
Rainer schrieb:> Habe eben einmal die Idee von "vaid" ausprobiert und den PA.8 als> Ausgang für den Systemtakt konfiguriert. Passiert leider garnichts.> Zuvor war der Pin als "GPIO_Mode_Out_PP" konfiguriert um eine LED zu> schalten. Die LED ist zwar nach wie vor angeschlossen, aber das sollte> der Sache doch keinen Abbruch tun, oder?
Keine Ahnung ehrlich gesagt :-)
Rainer schrieb:> Generell muss ich sagen, habe ich Probleme mit dem STM32. Diese riesigen> vorgefertigten Lib's und Konfigurationen finde ich absolut> undurchsichtig. Da blickt doch kein Mensch durch!>> Welche Funktionen muss ich denn alle in meinem Code aufrufen, um die> wichtigesten Clocks zu konfigurieren? Ich habe auch schon einmal> versucht die "SetSysClockTo72(void);" usw. alle manuell am Anfang noch> einmal aufzurufen, aber das macht keinen Unterschied.
Ich arbeite mit Eclipse. Da kann man dann in der Projektstruktur bis in
die hinterste Ecke der Standardlib vordringen und sich die Funktionen
etc. anzeigen lassen. Damit wird das ganze dann zumindest ein wenig
besser. Auch kann man dank der guten Namenswahl sehen welche
Möglichkeiten die STDlib bietet und diese dann nutzen oder eben selbst
umsetzen.
Bisher musste ich feststellen, Knackpunkt ist eine gescheite Makefile
(die man versteht) und vernünftiger Startupcode. Dieser stellt im
Idealfall schon die Controllerclock so ein wie für das Projekt
gewünscht.
Bezüglich der MCO Clock schau am besten in die _rcc.c. Wenn die läuft
kannst du immerhin ausschließen, dass der Controller nicht im richtigen
Takt läuft.
Hilfreiche Funktionen können auch RCC_GetClocksFreq usw. sein.
Also der STM32 läuft scheinbar doch mit 72Mhz! Ich habe Über den Pin PA8
nun den Systemtakt ausgeben lassen und da kommen genau 72Mhz heraus.
Dass das vorher nicht funktioniert hat lag an meiner Dummheit. Ich habe
das Osci an den Pin PB8 gehalten und mich gewundert, weshalb da nichts
passiert. Mein Fehler, sorry!
@A.K.
Du hast natürlich recht, dass ich nicht einfach von den C-Statements auf
Maschinenbefehle und deren Laufzeit usw. zurückschließen kann. Ich habe
eine Weile PIC's in Assembler programmiert und da ist das noch eine
"alte Gewohnheit" einfach mal zu denken, dass eine Addition
"soundsoviele" Taktzyklen benötigt.
Ich finde es aber erschreckend, dass ein so schneller Prozessor so lange
für so simple Operationen braucht.
"Markus Müller" hat wahrscheinlich recht, dass ich mich einmal mit den
Einstellungen des Compilers auseinander setzen sollte. Das habe ich
bisher nicht getan.
@Markus Müller
> Und wenn die 72MHz dennoch nicht reichen sollten, es gibt bereits einen> STM32F20x mit 120MHz.
Das versuche ich zu vermeiden. Ich kriege da bei den neuen PC-Programmen
schon immer zuviel. Frei nach dem Motto: Das Programm ist sauschlecht
programmiert und voller Bugs. Aber macht ja nichts: Da soll sich der
Anwender einfach schon wieder einen neuen PC kaufen g
PS: IO setzen mit GPIOB->BSRR = GPIO_Pin_14; ist doppelt so schnell wie
mit GPIO_SetBits(GPIOB, GPIO_Pin_14); :) Danke für den Hinweis.
Rainer schrieb:> Ich finde es aber erschreckend, dass ein so schneller Prozessor so lange> für so simple Operationen braucht.
Du bist immer noch auf dem Holzweg, weil du immer noch nicht weisst, was
da für ein Code dahinter steht. Wenn du ohne Optimierung übersetzt hast,
dann läuft das für jedes i++ möglicherweise ungefähr so ab, wenn i
global ist:
Lade Adresse von "i" nach R0 aus dem Flash, 5 Takte
Lade [R0] nach R1, 2 Takte
Addiere 1 auf R1, 1 Takt
Speichere R1 nach [R0], 2 Takte
= 10 Takte oder so in der Grössenordnung (die Takte pro Befehl sind grad
aus dem Ärmel, also leg mich nicht drauf fest).
Wenn du das dann optimiert übersetzt, dann wird daraus:
Addiere 1 auf das Register in dem "i" liegt, 1 Takt.
Oder auch:
garnix, weil der Compiler das als Unfug erkennt und wegoptimiert.
Machst du das auf einem PIC, dann wird daraus
Addiere 1 auf Speicherstelle "i", 1 Befehlszyklus
wenn i 8 Bit breit und die richtige Bank aktiv ist - aber auch nur dann.
@A. K.
Ich habe dich schon verstanden. Ich hätte mich vielleicht anders
ausdrücken sollen: Es ist erschreckend, wie fatal sich schlecher / nicht
optimierter Code auf die Performance auswirken kann.
Wie gesagt: Ich habe mich bisher nicht mit den Einstellungen des
Compilers auseinander gesetzt. Da der µC mit der vollen Taktfrequenz
läuft, kann der langsame Ablauf nur an nicht optimiertem oder auch
umständlichem Code liegen.
War gerade am Spielen, äääh.. Testen eines F207VC boards. Hab mal den
obigen Code geladen und angepasst, denn wenn man die Variable i nicht
benutzt, dann Optimiert einem der Compiler das gleich ganz raus. Deshalb
hab ich mal folgendes verwendet:
1
uint32_ti=0;
2
3
while(i<0x87654321)
4
{
5
GPIOA->BSRRL=GPIO_Pin_7;// Pin A7 high
6
GPIOA->BSRRH=GPIO_Pin_7;// Pin A7 low
7
GPIOA->BSRRL=GPIO_Pin_7;// Pin A7 high
8
i++;
9
i++;
10
i++;
11
i++;
12
i++;
13
GPIOA->BSRRH=GPIO_Pin_7;// Pin A7 low
14
}
Der Compiler macht dann leicht optimiert dieses hier draus, vor allem
daran zu erkennen dass aus den paar i++ ein i+= 5 gebaut wurde:
Wie auch an den Adressen zu erkennen ist, läuft der Code im Flash.
Nun Logicport an den Pin gehängt, Ergebnis hängt bei.
Was man nun erkennen kann:
* Es ist möglich die I/Os in 8,33ns umzuschalten.
* Der Sprung an den Anfang der Schleife dauert vergleichsweise lange
(hier schwächelt der Flash Beschleuniger)
* Die gesamte Schleife braucht 144ns.
Das entspräche dann im Schnitt 1 Waitstate. ST verspricht eigentlich 0
Waitstate performance. Naja. Gut, Wenn man ein Programm ohne Sprünge
hat? (ist halt Marketingmännisch gerundet).
Egal wie, langsam würde ich das nun aber gleicht garnicht nennen. Vor
allem für einen 6EUR controller... wer in der Preisregion schnelleres
hat, bitte gerne... (Kinetis?)
Ach ja: Die F103 dürften bei 72MHz nicht ganz halb so schnell sein.
> ST verspricht eigentlich 0 Waitstate performance. Naja. Gut, Wenn man ein> Programm ohne Sprünge hat?
Ja, nur dann. Aber das ist realitätsfern.
Der "Beschleuniger" kann nur die ersten 4 Bef's auffangen, danach läufts
wieder ausm Flash. Und der geht nur mit 32 MHz.
(war schon öfters Thema)
Hannes S. schrieb:> 0x8000e4a: 0x4932 LDR.N R1, ??Data65 ; GPIOA_BSRR> * Der Sprung an den Anfang der Schleife dauert vergleichsweise lange> (hier schwächelt der Flash Beschleuniger)
Ich weiss grad nicht wie breit die Einträge vom Flash-Cache sind, aber
*e4a ist möglicherweise etwas ungünstig. Könnte mit Zieladresse *e4c
oder *e50 besser aussehen.
Hannes S. schrieb:> Ach ja: Die F103 dürften bei 72MHz nicht ganz halb so schnell sein.
Wobei die meisten davon die GPIOs auf den APBs haben, nicht wie neuere
auf dem schnelleren AHB. Könnte bremsen
A. K. schrieb:> Ich weiss grad nicht wie breit die Einträge vom Flash-Cache sind, aber>> *e4a ist möglicherweise etwas ungünstig. Könnte mit Zieladresse *e4c>> oder *e50 besser aussehen.
In der Tat, e4c ist 1 Takt besser. Man lernt nie aus. Damit wären es
dann 134ns. Und die Waitstates hätten schon eine 0 vor dem Komma...
Und ja, richtig: I/O bei den 103ern braucht 2 clocks, also 28ns bei
72MHz.
Hannes S. schrieb:> * Die gesamte Schleife braucht 144ns.
Wobei der Code besser optimiert sein könnte. Die beiden LDR.N wären
ausserhalb der Schleife besser untergebracht.