Hallo, ich will das EA W202-XLG Display mit einem STM32F4 Discovery zum Laufen bekommen. Ich verwende den 4-Bit Modus mit den Funktionen, wie im Datenblatt auf Seite 5 beschrieben. Meinen Code habe ich angehängt und hoffe, dass ihr mir weiterhelfen könnt. Ich kann einfach keinen Fehler finden!!! Ich habe davor ein Programm für das Display geschrieben, in welchem ich keine Funktionen verwende - damit hat es geklappt. Nun habe ich diesen Code geschrieben und es werden mir nur ganz kurz irgendwelche Zeichen angezeigt, aber am Ende ist das Display leer. Ich habe nun extra an jede Datenleitung eine LED gehängt und mit dem Debugger die einzelnen Zustände kontrolliert. Dies war alles wie beim alten Programm und müsste daher ja funktionieren.
Ich denke nicht, dass du Arbeitsweise und Sinn des BSRR richtig erfasst hast. Es sollte zu denken geben, wenn man den Inhalt eines Register lesen will, dass dies überhaupt nicht zulässt. Pin(s) auf 1: BSRRL = mask; Pin(s) auf 0: BSRRH = mask; Pin(s) 0..3 auf Wert setzen, ohne an den anderen Pins was zu ändern: BSRR = 0b1111<<16 | Wert & 0b1111;
:
Bearbeitet durch User
Diese Delay-Loops funktionieren? Compiler haben mitunter sehr eigene Ansichten zu Code, der ihnen völlig sinnlos vorkommt.
A. K. schrieb: > Compiler haben mitunter sehr eigene Ansichten zu Code, der ihnen völlig > sinnlos vorkommt. Compiler kontrollieren, ob sich nach aussen durch den Code irgendwas ändert. Wenn nicht, dann können sie ihn wegoptimieren. Leere Schleifen sind das erste, was dem Optimizer zum "Opfer" fällt. Denn sie ändern nichts, machen aber alles unnötig langsam, und das Ergebnis der Schleife kann im Voraus berechnet werden... jogl schrieb: > Habe den neuen Code vergessen :D Seit wann enthalten *.txt Files Code?
:
Bearbeitet durch Moderator
A. K. schrieb: > Diese Delay-Loops funktionieren? Compiler haben mitunter sehr eigene > Ansichten zu Code, der ihnen völlig sinnlos vorkommt. Haben bislang immer funktioniert.
Aber kann jemand einen Fehler in meinem neuen Programm erkennen oder einen Unterschied zum alten?
jogl schrieb: > Nun habe ich diesen > Code geschrieben und es werden mir nur ganz kurz irgendwelche Zeichen > angezeigt, aber am Ende ist das Display leer. Geh doch mal mit dem Debugger schrittweise durch. Achte dabei auch bei jedem Schritt auf den Zustand aller GPIO-Pins, ob sie so gesetzt sind wie Du sie haben wolltest, etc. Außerdem ist Deine Verwendung der BSRRH und BSRRL Register ziemlich befremdlich. Darüber hinaus solltest Du den Pins sinnvolle Namen geben anstatt einfach nur 4 oder 5. Und Deine Verzögerungsschleifen optimiert der Compiler weg, je nach Mondphase entweder sofort oder erst morgen wenn Du was an den Compilerflags änderst oder einfach so in der nächsten Version. Das musst Du anders machen.
:
Bearbeitet durch User
jogl schrieb: > Aber kann jemand einen Fehler in meinem neuen Programm erkennen oder > einen Unterschied zum alten? Dein Umgang mit BSRR ist schlicht und einfach falsch. Und: In einer Situation recht behalten zu willen, in der man schon ahnt, dass man auf dem Holzweg ist, ist eine zweifelhafte Strategie. Auch wenn es der Widerspruchsreflex so befiehlt.
:
Bearbeitet durch User
Bernd K. schrieb: > Geh doch mal mit dem Debugger schrittweise durch. Achte dabei auch bei > jedem Schritt auf den Zustand aller GPIO-Pins, ob sie so gesetzt sind > wie Du sie haben wolltest, etc. Haben die Zustände der GPIO-Pins über LEDs getestet und es waren alle so wie ich sie haben wollte, beziehungsweise wie beim alten Programm. Bernd K. schrieb: > Außerdem ist Deine Verwendung der BSRRH und BSRRL Register ziemlich > befremdlich. > > Darüber hinaus solltest Du den Pins sinnvolle Namen geben anstatt > einfach nur 4 oder 5. Mhh, was ist daran denn sooooo falsch? Könntet ihr mir bitte zeigen, wie ich beispielsweise Pin 3 von Port D auf High und dann wieder auf Low setzt? Am besten, wenn dieser davor noch als "DB6" definiert wurde. Bernd K. schrieb: > Und Deine Verzögerungsschleifen optimiert der Compiler weg, je nach > Mondphase entweder sofort oder erst morgen wenn Du was an den > Compilerflags änderst oder einfach so in der nächsten Version. Das musst > Du anders machen. Habe nun noch eine weitere Variable in den Schleifen hochzählen lassen, damit diese auf jeden Fall nicht wegoptimiert werden. Aber immer noch nichts auf dem Display zu sehen.
>Habe nun noch eine weitere Variable in den Schleifen hochzählen lassen, >damit diese auf jeden Fall nicht wegoptimiert werden. Die haut der Compiler trotzdem in die Tonne, es sei denn die Variable ist volatile.
jogl schrieb: >> Außerdem ist Deine Verwendung der BSRRH und BSRRL Register ziemlich >> befremdlich. >> >> Darüber hinaus solltest Du den Pins sinnvolle Namen geben anstatt >> einfach nur 4 oder 5. > > Mhh, was ist daran denn sooooo falsch? Könntet ihr mir bitte zeigen, wie > ich beispielsweise Pin 3 von Port D auf High und dann wieder auf Low > setzt? Am besten, wenn dieser davor noch als "DB6" definiert wurde. Dort schreibt man Einsen rein für die Bits die gesetzt (oder gelöscht) werden sollen oder nullen für alle bits die GLEICH bleiben sollen. Also kein verodern mit |= sondern das verodern macht die Hardware wenn Du einfach die Maske mit den Bits mit = reinschreibst. z.B. so (hier ist mal ein STM32F401 Hello-World Blinky Beispiel) das illustriert eigentlich alle meine Punkte in einem vollständigen Beispiel, das hatte ich zur Hand ohne jetzt erst was speziell dafür schreiben zu müssen:
1 | #include <stm32f401xe.h> |
2 | |
3 | |
4 | /*
|
5 | * The green LED is connected to port A5,
|
6 | * -> see schematic of NUCLEO-F401RE board
|
7 | */
|
8 | #define LED_GPIO GPIOA
|
9 | #define LED_PIN 5
|
10 | |
11 | /**
|
12 | * Quick 'n' dirty delay
|
13 | *
|
14 | * @param time the larger it is the longer it will block
|
15 | */
|
16 | static void delay(unsigned time) { |
17 | for (unsigned i=0; i<time; i++) |
18 | for (volatile unsigned j=0; j<20000; j++); |
19 | }
|
20 | |
21 | |
22 | /**
|
23 | * Hello world blinky program
|
24 | *
|
25 | * @return never
|
26 | */
|
27 | int main(void) { |
28 | |
29 | |
30 | /*
|
31 | * Turn on the GPIOA unit,
|
32 | * -> see section 6.3.9 in the manual
|
33 | */
|
34 | RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; |
35 | |
36 | |
37 | /*
|
38 | * Set LED-Pin as output
|
39 | * Note: For simplicity this assumes the pin was configured
|
40 | * as input before, as it is when it comes out of reset.
|
41 | * -> see section 8.4.1 in the manual
|
42 | */
|
43 | LED_GPIO->MODER |= (0b01 << (LED_PIN << 1)); |
44 | |
45 | |
46 | while(1) { |
47 | |
48 | /*
|
49 | * LED on
|
50 | * -> see section 8.4.7 in the manual
|
51 | */
|
52 | LED_GPIO->BSRRL = (1 << LED_PIN); |
53 | |
54 | delay(200); |
55 | |
56 | /*
|
57 | * LED off
|
58 | */
|
59 | LED_GPIO->BSRRH = (1 << LED_PIN); |
60 | |
61 | delay(200); |
62 | }
|
63 | }
|
Erstens ich gebe meinen Ports und Pins logische Namen, dann liest sich der Code leichter, es ist besser zu sehen welchen Pin ich da beackere, besonders wenns später mehr werden. So kann ich auch später noch das Platinenlayout ändern und nen anderen Pin nehmen und muss nur noch an einer einzigen Stelle Anpassungen vornehmen, nicht mehr kreuz und Quer über das Programm verstreut. Zweitens beachte wie ich die BSRR Register verwende. Ich verodere da nichts, ich schreibe einfach eine 1 an das Bit das gesetzt oder gelöscht werden soll und nullen an alle anderen. Lies bitte nochmal im Referenz Manual nach über das Thema, da stehts auch geschrieben, das gleiche Konzept findet man auch bei vielen anderen Controllern. > Bernd K. schrieb: >> Und Deine Verzögerungsschleifen optimiert der Compiler weg, je nach >> Mondphase entweder sofort oder erst morgen wenn Du was an den >> Compilerflags änderst oder einfach so in der nächsten Version. Das musst >> Du anders machen. > > Habe nun noch eine weitere Variable in den Schleifen hochzählen lassen, > damit diese auf jeden Fall nicht wegoptimiert werden. Aber immer noch > nichts auf dem Display zu sehen. Beachte wie ich die innere Laufvariable als volatile deklariert habe. Es ginge auch mit nur einer Schleife aber die Variable muss volatile sein. Volatile bedeutet für den Compiler soviel wie: "Schreiben und Lesen dieser Variable wechselwirkt mit der Außenwelt, das muss ich also in genau der Reihenfolge und Anzahl durchführen wie es da steht sonst ändert sich die Außenwirkung und das darf ich nicht, ich darf nur Sachen wegoptimieren die nicht von außen oder nach außen wirken." Deshalb sind auch alle Register für GPIO und andere Peripheriegeräte in den Headern immer als volatile deklariert, damit der Compiler weiß daß das nicht nur unsichtbares Schreiben in irgendein internes Register ist sondern jeder Zugriff eine gewollte Außenwirkung darstellt. Volatile Zugriffe müssen immer durchgeführt werden und immer exakt in der Reihenfolge wie sie programmiert sind. Also mach Laufvariablen für solche aus Sicht des Compilers sinnlose Schleifen (die nichts berechnen was später noch verwendet würde) volatile, dann kann nichts passieren.
Zwei Beispiele:
1 | int zeitvertreib(void) { |
2 | const int rot = 42; |
3 | const int gruen = 43; |
4 | const int blau = 44; |
5 | |
6 | int x = rot + gruen; |
7 | |
8 | return blau; |
9 | }
|
Das bedeutet: Geh da rein, dort findest Du drei Zahlen, addiere die ersten zwei (im Kopf) und vergiss das alles sofort wieder, dann nimm die blaue Zahl und komm mit der blauen Zahl wieder raus. Jeder der nicht komplett auf den Kopf gefallen ist wird reingehen, die blaue Zahl nehmen und wieder rauskommen. Pfeif auf die Berechnung, kann ja per Definition keiner kontrollieren, das Ergebnis soll ja vergessen werden, die einzig nachprüfbare Aufgabe (blaue Zahl holen) wurde ja ordnungsgemäß erfüllt, der Rest ist Optimierung des Arbeitsablaufs.
1 | int zeitvertreib(void) { |
2 | const int rot = 42; |
3 | const int gruen = 43; |
4 | const int blau = 44; |
5 | |
6 | volatile int x = rot + gruen; |
7 | |
8 | return blau; |
9 | }
|
Das bedeutet: Geh da rein, dort findest Du drei Zahlen, addiere die ersten zwei (und zwar schriftlich, und zwar am Fenster so daß ich es von außen sehen kann) dann vergiss das Ergebnis, vernichte den Zettel, nimm die blaue Zahl und komm damit wieder raus. Jetzt kann derjenige nicht mehr bescheißen, auch wenn die Addition immer noch sinnlos ist, er muß sie am Fenster durchführen so daß der Chef ihn von außen beobachten kann. So funktioniert Optimierung und so funktioniert volatile.
:
Bearbeitet durch User
jogl schrieb: > Könntet ihr mir bitte zeigen, wie > ich beispielsweise Pin 3 von Port D auf High und dann wieder auf Low > setzt? Am besten, wenn dieser davor noch als "DB6" definiert wurde. Beitrag "Re: STM32: Ich finde einfach keinen Fehler in meinem Code"
Das sind aber alles keine "schönen" delay Funktionen. Ich hätte gerne eine Funktion, mit der das Programm eine genaue Zeit wartet. Sowas hier: delay(1000); //wartet 1s Kann mir hier bitte jemand weiterhelfen, konnte keine gute Erklärung finden. Welchen Takt habe ich denn überhaupt beim STM32F4?
welchen compiler nehmt ihr denn? gcc linux?
keil µvision, das ist doch aber nicht vom compiler abhängig?
jogl schrieb: > Kann mir hier bitte jemand weiterhelfen, konnte keine gute Erklärung > finden. Welchen Takt habe ich denn überhaupt beim STM32F4? In der HAL Library gibt es eine delay Funktion (HAL_Delay). Das basiert aber auch nur darauf, die system clock interrupts zu zählen (SysTick_Handler).
Torsten R. schrieb: > Das basiert > aber auch nur darauf, die system clock interrupts zu zählen > (SysTick_Handler). Davon habe ich schon etwas gelesen, weiß aber nicht genau wie ich den SysTick_Handler benutze bzw. was die Systicks überhaupt sind.
jogl schrieb: > Davon habe ich schon etwas gelesen, weiß aber nicht genau wie ich den > SysTick_Handler benutze bzw. was die Systicks überhaupt sind. SysTick_Handler ist eine interrupt handler, der periodisch aufgerufen wird und in der Regel verwendet wird, um eine globale Variable hoch zu zählen. Durch das Beobachten der globalen Variable, kann man dann ein Delay implementieren. Guck es Dir einfach mal in den Sourcen der HA-Library an und lese einfach mal das "Reset and clock control" Kapitel im Reference Manual zu Deinem Controller. ggf. findest Du auch, dass einige Einstellungen bereits im Startup Code von ST gemacht 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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.