Hallo zusammen,
ich sitze hier seit mehr als einer Stunde mit dem typischen Brett vorm
Kopf. Ich habe einen STM32F446 auf dem entsprechenden Nucleo-Board. Was
ich machen will, ist noch nicht wirklich kompliziert: Einfach einen Pin
wackeln.
Mein Problem: Bei Durchsteppen mit dem Debugger will sich das
MODER-Register partout nicht beschreiben lassen. Folglich wackelt auch
Pin PC1 nicht. PA5 läßt sich dagegen wackeln, da schon im Reset-Zustand
Output.
Wer sieht das, was ich nicht sehe?
Viele Grüße
W.T.
Walter T. schrieb:> Wer sieht das, was ich nicht sehe?
Du verwendest die StdPeriphLib nicht - ähm oder nur zum Teil?
Wieso schreibst du dir selbst obskure Funktionen wie io_setOutput?
Das wär das erste, was ich ändern würde.
Achtung: Die folgenden Beispiele gelten für STM32F103.
Ich möchte Dir nützliche CMSIS Funktionen zeigen, die sicher auch bei
deinem µC zur Verfügung stehen.
Zum Einen kann man I/O Pins so einstellen:
GPIOA->BSRR=GPIO_BSRR_BS5; // PA5=High
GPIOA->BSRR=GPIO_BSRR_BR5; // PA5=Low
Zweitens gibt es für das Ändern einzelner Bits die Hilfsfunktion:
SET_BIT(RCC->APB2ENR, RCC_APB2ENR_IOPAEN);
CLEAR_BIT(RCC->APB2ENR, RCC_APB2ENR_IOPAEN);
Wobei die CMSIS für alle Register und Bitmasken entsprechende Konstanten
definiert. Weiterhin gibt es noch eine Hilfsfunktion, um mehrere Bits
gleichzeitig zu ändern. Sie ist für Register gedacht, wie
zusammenhängende Blöcke von Bits irgendeine Bedeutung haben:
MODIFY_REG(AFIO->MAPR, AFIO_MAPR_SWJ_CFG, AFIO_MAPR_SWJ_CFG_DISABLE);
Der erste Parameter gibt das zu ändernde Register an. Der zweite
Parameter ist eine Bitmaske die angibt, welche Bit-Gruppe du ändern
willst. In diesem Fall sind es die Bits, die das SWJ Interface
konfigurieren. Der dritte Parameter gibt an, welchen Wert die maskierten
Bits bekommen sollen. Auch dafür gibt es in der CMSIS vordefinierte
Konstanten die man ggf. zusammen addiert (oder ver-odert).
Der folgende Befehl ändert gleich zwei Bitgruppen auf einmal. Er
konfiguriert PA5 als Ausgang:
MODIFY_REG(GPIOA->CRL, GPIO_CRL_CNF5 + GPIO_CRL_MODE5,
GPIO_CRL_MODE5_0);
Schau Dir an, was es da sonst noch so gibt:
https://github.com/ARMmbed/cmsis-core-stm32f4/blob/master/cmsis-core-stm32f4/stm32f4xx.h
Ich denke, so macht man das normalerweise. Vergiss die StdPeriphLib, die
ist schon lange veraltet. Nimm lieber CMSIS Funktionen, denn die ändern
sich nicht so schnell und sind außerdem auf allen ARM Controllern
gleich. Durch Verwendung von CMSIS erzeugst besser lesbaren Code, da er
näher am ARM Standard ist.
Um zu deiner eigentlichen Frage zu kommen: Ich sehe keinen Fehler,
sorry. Ich wünschte, ich hätte dieses Board um es mal selbst
auszuprobieren.
Walter T. schrieb:> Kopf. Ich habe einen STM32F446 auf dem entsprechenden Nucleo-Board. Was
Derer gibt's zwei: 64 oder 144?
> Mein Problem: Bei Durchsteppen mit dem Debugger will sich das> MODER-Register partout nicht beschreiben lassen. Folglich wackelt auch
Was heißt das? Stimmt beim Auslesen des Registers der Inhalt nicht mit
dem überein, was hineingeschrieben wurde?
> Pin PC1 nicht. PA5 läßt sich dagegen wackeln, da schon im Reset-Zustand> Output.
Oder ist nur gemeint, dass PC1 seinen Zustand scheinbar nicht ändert?
Und auch ist unklar, ob PC1 auf Input bleibt, oder schon auf Output
geschaltet wird aber sein Zustand unverändert bleibt. 10k Pullup
wechselweise an VCC bzw. GND, dann sieht man, was los ist.
Oben sehe ich außerdem nur etwas von PC1, PA5 kommt da gar nicht vor
bzw. eben gerade nicht in exakt gleicher Weise wie PC1. Da werden
sozusagen Äpfel mit Birnen verglichen.
Tipp: Die Register alle auch auslesen und mit den erwarteten Werten
vergleichen. Am einfachsten geht das mit wohl mit OpenOCD. Die Register
nach Reset wie oben im Code manuell beschreiben, auslesen und nachsehen,
was an den Pins passiert ...
Es bleibt auch noch, dass der Compiler etwas wegoptimiert (was den
Debugger u. U. total in die Irre führt), deswegen auch mal mit "-O0"
probieren und den Assembler-Code ansehen.
Mampf F. schrieb:> Du verwendest die StdPeriphLib nicht
Damit habe ich den gleichen Effekt (-kein Wunder: die GPIO_Init() macht
ja genau das Gleiche): Im Debugger wird immer MODER=0 ausgelesen, und
der Pin wackelt nicht.
So sieht es mit der StdPeriphLib aus (kleine Änderung: auch noch PC 13
dazu):
A. B. schrieb:> PA5 kommt da gar nicht vor> bzw. eben gerade nicht in exakt gleicher Weise wie PC1. Da werden> sozusagen Äpfel mit Birnen verglichen.
Ja, ich hatte die Set-Befehle für PA5 mit PC1 ersetzt.
A. B. schrieb:> Es bleibt auch noch, dass der Compiler etwas wegoptimiert
Debug-Build extra ohne Optimierung - obwohl ein __IO-Registerzugriff
ohnehin nicht wegoptimiert werden dürfte.
Bei keinem der Pins ist GPIO eine alternative Funktion.
A. B. schrieb:> Derer gibt's zwei: 64 und
Und genau das ist es. Das sollte für das Problem aber keinen Unterschied
machen.
Das Datenblatt sagt: Alle GPIOs hängen auch wirklich am AHB1.
Der Startup-Code ist unverändert der, der von EMBlitz erzeugt wird.
OK, ich sehe: In der StdPeriphLibrary von 2016 sind im Vergleich zu der,
die EmBlitz hineinpackt (September 2011), etliche Sachen für den
STM32F446 hinzugekommen. Da werde ich mal ein manuelles Update
vornehmen.
Walter T. schrieb:> RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);> RCC_APB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);> RCC_APB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);> RCC_APB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);> RCC_APB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
Kuck mal ... APB und AHB passen nicht.
Scheiß Fehler, der mich auch schon zum Wahnsinn getrieben hat! :)
Die entscheidende Fragen sind aber leider unbeantwortet, nämlich:
Werden die Register tatsächlich korrekt beschrieben? Man sollte da
Glauben von Tatsachen trennen ...
Also im Zweifelsfall JEDES Register nach dem Schreiben überprüfen.
Und wie schon erwähnt am Pin mit den Widerständen prüfen, ob zumindest
auf Ausgang geschaltet wird.
Es ist schlichtweg unrealistisch, dass sich die Register nicht
beschreiben lassen (wenn sie nicht gerade gelockt, der Takt nicht
eingeschaltet oder der Chip hinüber ist), wie in der Überschrift
behauptet wird!
Weitere Idee: Kurzes Delay nach dem Einschalten der GPIO-Clocks. Es kann
durchaus sein, dass die Register ein paar Takte "Anlaufzeit" benötigen.
Mampf F. schrieb:> Scheiß Fehler, der mich auch schon zum Wahnsinn getrieben hat! :)
Und da gibt es immer noch Leute, die behaupten, daß durch extensives
Ersetzen von (1<<xyz) durch ominöse Identifer aus CMSIS der Code besser
und lesbarer würde.
mit leisem Grinsen zum Tag der großen Vereinheitlichung
W.S.
W.S. schrieb:> Mampf F. schrieb:>> Scheiß Fehler, der mich auch schon zum Wahnsinn getrieben hat! :)>> Und da gibt es immer noch Leute, die behaupten, daß durch extensives> Ersetzen von (1<<xyz) durch ominöse Identifer aus CMSIS der Code besser> und lesbarer würde.
Ja oder noch geiler - hat mich einen Tag gekostet [1], den Fehler zu
finden:
GPIO_PinAFConfig funktioniert nicht mit zB GPIO_Pin_4 sondern mit
GPIO_PinSource_4
Ersteres ist 1<<4, zweiteres einfach nur 4. Aber vmtl hätte ich dann
trotzdem 1<<4 als Parameter verwendet xD
[1]: Beitrag "[STM32F4] I2S macht nichts PLL-Clock-Problem?!"
Tja, manchmal frag ich mich, warum denn die Leute sich selbst das Leben
so schwer machen und jegliche Art klaren lesbaren Codes vermeiden wie
der Teufel das Weihwasser. Ich kann's mir nur so erklären, daß die
allermeisten keine entsprechende fachliche Ausbildung im Kreuz haben und
eben genau deshalb sklavisch jeden Firlefanz nachmachen, den man ihnen
irgendwann mal vorgemacht hat - ohne in der Lage zu sein, das Ganze nach
Sinn zu hinterfragen.
Und bei Folgendem bin ich mir sicher, daß der gute Walter in 3 Monaten
nicht mehr weiß, wozu er das überhaupt geschrieben hat:
Hier kriegt "out" aber ne ganz dolle Null drübergeordert. Und 0x0001U
ist natürlich viel genauer als 1, gelle?
Da fällt mir eine Anekdote über Heinrich Schlusnus ein: In irgendeiner
Bühnenszene gibt's nen Zweikampf, der andere fällt regiegemäß getroffen
zu Boden, liegt aber dabei eher unbequem. Also räkelt er sich so hin,
daß er bequemer daliegt. Das Publikum grinst schon.. aber Schlusnus
rettet die Situation per Stegreif: "Was denn Kerl, du röchelst noch?
Soll ich dich noch töter töten?"
W.S.
W.S. schrieb:> Und 0x0001U> ist natürlich viel genauer als 1, gelle?
Das 'U' kann mich keinen Cent gekostet.
Wenn Du hier:
Walter T. schrieb:> out |= word & 0x8000U ? 0xC0000000U : 0;
auf den ersten Blick weißt, ob Dir irgendwelche integer propagation
rules mit ihrem Vorzeichen unter irgendeiner Compilerversion irgendeinen
Fehler an einem selten genutzten Pin 15 erzeugen können - Glückwunsch.
Ich bin nicht vom Fach. Habe aber dafür vor Jahren 10-Finger-tippen
gelernt. Ein Buchstabe kostet mich weniger Zeit, als über irgendwelche
propagation rules zu sinnieren.
Ja, die Funktion ist quick & dirty. Selbst der Compiler mag sie so
wenig, daß er sie direkt zu einem konstanten Wert wegoptimiert.
W.S. schrieb:> Hier kriegt "out" aber ne ganz dolle Null drübergeordert.Falls das nicht zu einem konstanten Wert optimiert wird, würde der
Compiler wohl die ARM-Assembler-Befehle conditional verwenden, dann
würde da vermutlich keine 0 drübergeodert werden.
Aber ansonsten muss ich W.S. doch etwas Recht geben ...
Ich für mich schreibe meinen Code für ARM-µCs mittlerweile plain stupid
einfach runter ohne mit mit Attributen, Inline, ... oder irgendwas
Anderem zu verkünsteln - außer ich brauche sie wirklich (wie packed
struct gelegentlich) oder wenn etwas im TCM landen muss.
Die Dinger sind mittlerweile so schnell und die Compiler so gut -
jegliche Optimierung, die man sich selbst überlegt ist da fast sinnlos,
außer sie reduzieren die Komplexität irgendwelcher Algorithmen von
O(n^2) auf O(n*log(n)) oder so, aber das kommt eher selten vor und
selbst dann kann man noch abschätzen, ob O(n^2) nicht gut genug ist.
Es heißt immer: Ein gutes Pferd springt nur so hoch es muss :)
Dazu gehört dann natürlich auch, dass man keine Library-Funktionen per
Hand (schlechter) nachbaut.
Vlt sind viele hier 8Bit µC-geschädigt und denken immer noch in der
Schiene, Peripherie direkt ansprechen, in Assembler programmieren, jedes
Bit optimieren usw ...
Mampf F. schrieb:> Dazu gehört dann natürlich auch, dass man keine Library-Funktionen per> Hand (schlechter) nachbaut.
Das ist doch klar. Wenn man eine eigene Funktion baut, sollte sie für
irgendeinen Zweck besser sein als das, was man sowieso schon hat.
In meinem Fall ist das das schnelle Umschalten zwischen Input und
Output.
Und daß ich vom Umzug vom STM32F103 auf den STM32F446 und später wieder
zurück möglichst wenig ändern muß.