Hallo,
nachdem ich mich die letzte Stunde mit dem Suchen nach Lösungen
herumgeschlagen habe, wende ich mich an euch.
Vor mir befindet sich ein STM32F4 (Taktfrequenz 168Mhz) und ein Display
4*20 Zeichen mit einem SSD1803 Cotroller. Derzeit habe ich das Display
erfolgreich angesteuert mit einer delay Funktion. Da ich unmöglich
während der Datenverarbeitung 39µS warten kann, würde ich gerne auf
Timer umstellen.
Funktion derzeit:
Datenbyte an die IOs legen
EnablePin auf 1
1µS delay
EnablePin auf 0
39µS delay --> danach wieder von vorne mit dem nächsten
Byte/Zeichen/Befehl
40µS entsprechen immerhin 6720 Takte sinnlos warten.
Die delays würde ich gerne durch Timer ersetzen. Timer7 wird so
initalisiert, dass er alle 50µS einen Interrupt erzeugt.
Die Frage, die sich mir stellt ist:
Wie bekomme ich die Daten gescheit in den Interrupt?
sprintf( buffer, "%9.7f ", doubleVariable); und buffer als Globale
Variable halte ich für eine schlechte Idee, zumal es bei der sprintf
Funktion mit volatile eine Warnung geben wird.
Viele Grüße
riterkuni schrieb:> Funktion derzeit:> Datenbyte an die IOs legen> EnablePin auf 1> 1µS delay> EnablePin auf 0> 39µS delay --> danach wieder von vorne mit dem nächsten> Byte/Zeichen/Befehl
Da wird doch anscheinend nur das Datenbyte übergeben, sollte also mit
einem volatile uint8_t (oder besser mit einen Puffer, s.u.) zu lösen
sein. Das m.E. schwierigere an der Sache ist das Vermeiden von Blocking,
wenn die ISR noch nicht abgearbeitet ist. Ein Flag könnte hier hilfreich
sein, mit dem die ISR signalisiert, das sie gearbeitet hat. Dein stdout
muss das Flag setzen und abfragen, ob es noch gesetzt ist, dann blockt
es entweder, oder, besser, schiebt das Datenbyte in einen Fifo, der dann
im Hintergrund vom Timer abgearbeitet wird.
Nachtrag:
Wie bekomme ich die 1µS für den Enable Impuls noch weg?
Idee dazu war: (Display reagiert auf eine fallende Flanke am EnablePin)
EnablePin auf 1
Datenbyte an die IOs legen
EnablePin auf 0
39µS delay --> danach wieder von vorne mit dem nächsten
Byte/Zeichen/Befehl
Bei der Methode gibt es viele Fehler bei der Übertragung. Scheinbar
benötigt der LCD Controller Zeit zwischen Anlegen des Bytes und
Enablepin fallende Flanke. Konnte im Datenblatt nichts dazu finden.
riterkuni schrieb:> Wie bekomme ich die 1µS für den Enable Impuls noch weg?
Du baust dir eine Statemachine. Ein Flag sagt der Timer Routine, das sie
das E generieren soll, sonst ist Datenbyte Zeit. Wenn du die Wahl hast,
wäre aber auch evtl. ein SPI Display einfach simpler...
Matthias Sch. schrieb:> Da wird doch anscheinend nur das Datenbyte übergeben, sollte also mit> einem volatile uint8_t (oder besser mit einen Puffer, s.u.) zu lösen> sein. Das m.E. schwierigere an der Sache ist das Vermeiden von Blocking,> wenn die ISR noch nicht abgearbeitet ist. Ein Flag könnte hier hilfreich> sein, mit dem die ISR signalisiert, das sie gearbeitet hat. Dein stdout> muss das Flag setzen und abfragen, ob es noch gesetzt ist, dann blockt> es entweder, oder, besser, schiebt das Datenbyte in einen Fifo, der dann> im Hintergrund vom Timer abgearbeitet wird.
Genau so ist es geplant. Der µC erledigt seine Berechnungen. Ergebnis
geht in einen Buffer (FIFO), und bei jedem Interrupt überträgt die ISR
ein Zeichen.
Müsste ich nicht aber, wenn ich mit volatile uint8_t immer nur ein
Zeichen als volatile setze, andauernd im Hauptprogramm überprüfen ob die
ISR aktiv war? Hört sich auch wieder sehr CPU lastig an :S
Wenn du sowieso schon einen Timer einsetzt, würde ich auch mal in die
folgende Richtung überlegen.
Im Prozessor reservierst du dir ein 4*20 char Array, welches deinen
'Bildschirmspeicher' darstellt.
Dein Progrtamm kümmert sich nicht darum, wie die tatsächlichen Inhalte
von diesem Bildschirmspeicher zum LCD kommen. Wenn es etwas auszugeben
gilt, dann schreibt es das einfach in diesen Speicher.
Im Timer Interrupt wird laufend bei jedem Aufruf jeweils 1 Byte von
diesem Bildschirmspeicher zum tatsächlichen LCD übertragen. Auf die Art
wird 'nach und nach' jede Ausgabe in den Bildschirmspeicher dann auch
tatsächlich auf dem PCD sichtbar. Wobei wir bei "nach und nach" über
Verzögerungen reden, die für einen Menschen praktisch nicht wahrnehmbar
sind. Bei jedem Timer Interrupt Aufruf könntest du zb jeweils
abwechselnd die Sache mit dem Enable Pin und dem Character regeln.
1
InterruptRoutine
2
3
NrAufruf++;
4
if(NrAufruf==3)
5
NrAufruf=0;
6
7
if(NrAufruf==0){
8
Enableauf1
9
nächstencharausgeben
10
}
11
elseif(NrAufruf==1){
12
Enableauf0
13
}
14
}
mit den Zeiten müsste man ein bischen spielen, so dass in der Interrupt
Routine sowohl die 1µS (bzw. ein Vielfaches davon) und die 39µs
unterzubringen sind.
AUf die Art brauchst du keine komplizierte FIFO und auch der
Rechenzeitbedarf in der Interrupt Routine ist mehr als überschaubar.
Wenn das komplette LCD pro Sekunde 5 mal ausgegeben wird, dann langt das
dicke. Schneller kann kein Mensch sich verändernde Inhalt am LCD
erfassen.
Karl Heinz schrieb:> Im Prozessor reservierst du dir ein 4*20 char Array, welches deinen> 'Bildschirmspeicher' darstellt.> Dein Progrtamm kümmert sich nicht darum, wie die tatsächlichen Inhalte> von diesem Bildschirmspeicher zum LCD kommen. Wenn es etwas auszugeben> gilt, dann schreibt es das einfach in diesen Speicher.>
Am Ende der Berechnungen habe ich int und double. Wie bekomme ich die
denn zu einem charArray, welches ich aus der ISR erreichen kann?
sprintf macht mir leider Probleme in Kombination mit volatile. Gibt die
Warnung: passing argument 1 of 'sprintf' discards 'volatile' qualifier
from pointer target type [enabled by default]
> mit den Zeiten müsste man ein bischen spielen, so dass in der Interrupt> Routine sowohl die 1µS (bzw. ein Vielfaches davon) und die 39µs> unterzubringen sind.>> AUf die Art brauchst du keine komplizierte FIFO und auch der> Rechenzeitbedarf in der Interrupt Routine ist mehr als überschaubar.
Die Zeiten sind relativ unkritisch. Selbst wenn ich 50µS Interrups
nehme, also mit dem Enable Interrupt 2*50µS=100µS, werden 250Hz
erreicht.
riterkuni schrieb:> Am Ende der Berechnungen habe ich int und double. Wie bekomme ich die> denn zu einem charArray, welches ich aus der ISR erreichen kann?
Wie kriegst du sie denn jetzt rein?
Nochmal: der ISR ist das piep.schnurz-egal was die Character in diesem
Buffer bedeuten oder wo sie herkommen. Die gibt die Character einfach
nur aus. Und zwar laufend und reihum.
Wenn du einen int ausgeben willst, dann benutzt du zb itoa und anstatt
den so erhaltenen String auf die LCD Funktionen zu geben, kopierst du
den String in diesen Bildschirmspeicher an die der Ausgabeposition
entsprechenden Stelle.
Für dein Programm (mit Ausnahme der ISR) IST dieser Bildschirmspeicher
die Ausgabefläche. Wer auch immer irgendwas auszugeben hat, kopiert den
entsprechenden Character (oder den String) in diesen Bildschirmspeicher
und kümmert sich nicht weiter drum, wie die Zeichen dann von dort aufs
LCD kommen. Das ist der Job des Timerinterrupts.
> sprintf macht mir leider Probleme in Kombination mit volatile.> Gibt die Warnung: passing argument 1 of 'sprintf' discards> 'volatile' qualifier from pointer target type [enabled by default]
An dieser Stelle denke ich, ist es zulässig von der Annahme auszugehen,
dass sprintf die Character auch wirklich schreiben wird und nicht der
Optimizer alles wegoptimieren wird. Also: volatile einfach wegcasten.
Karl Heinz schrieb:> Wie kriegst du sie denn jetzt rein?
Gar nicht, da liegt ja mein Problem. Wie gesagt, ich habe das ganze
derzeit provisorisch mit eine delay Funktion gelöst. Soll jetzt aber auf
TimerInterrups umgestellt werden.
So sah es bis jetzt aus:
1
sprintf(buffer,"%9.7f ",doublevariable);
2
lcd_ausgabe(buffer);
1
voidlcd_ausgabe(constchar*data){
2
while(*data!='\0')
3
LCD_Data(*data++);//Erkennt ob Formatierungszeichen
4
}
1
voidLCD_Data(uint8_tlcddata){
2
//Hier nur die Struktur.
3
GPIO_ResetBits();//Reset Daten- und Steuerleitungen
Karl Heinz schrieb:> Für dein Programm (mit Ausnahme der ISR) IST dieser Bildschirmspeicher> die Ausgabefläche. Wer auch immer irgendwas auszugeben hat, kopiert den> entsprechenden Character (oder den String) in diesen Bildschirmspeicher> und kümmert sich nicht weiter drum, wie die Zeichen dann von dort aufs> LCD kommen. Das ist der Job des Timerinterrupts.
Das Verstehe ich, so habe ich es mir auch vorgestellt. Damit das
Hauptprogramm halt nichts mehr mit dem ganzen zutun hat.
riterkuni schrieb:> Gar nicht, da liegt ja mein Problem.
du musst jetzt doch nur deine Funktion "lcd_ausgabe" anpassen, so dass
sie nicht mehr LCD_Data aufruft, sondern die Zeichen in den Puffer
schreibt.
Und sicher hast du auch sowas wie lcd_setcursor, das musst du entweder
auch anpassen, so dass es den Puffer-index entsprechend setzt, oder du
baust das mit in die "lcd_ausgabe" mit ein:
Nun klappt es.
Habe folgendes geändert:
sprintf wird jetzt explizit als (char*) gecastet, damit auch keine
Warnung mehr.
Es gibt jetzt ein Array fürs ganze Display.
Nachdem die Daten im Array sind wird ein "Busy" Flag gesetzt und der
Timer aktiviert.
Enable und Datenleitungen werden Interruptgesteuert. Es sind 2x 45µS
Interrups pro Zeichen nötig
Wenn das Array auf das LCD übertragen wurde, wird das "Busy" Flag
zurückgesetzt und der Timer deaktiviert.
Die Interrups brauchen zusammen knapp 180 Takte, damit kann ich gut
leben.
Vielen Dank