Hallo,
im Forum habe ich mich schon länger umgeschaut ob eine passende Lösung
zu meinem sprintf-Problem vorliegt. Leider vergeblich.
Mein Programm funktioniert wie unten angegeben einwandfrei. Der Timer
liefert ständig einen neuen Wert auf dem Display. Falls ich in der
sprintf-Funktion int verwende.
1
#define STRINGBUF_LEN 21
2
charStringBuf[STRINGBUF_LEN];
3
intadcVal[3];
4
5
...
6
adcVal[0]=ADC_GetValue();
7
sprintf(StringBuf,"%d",adcVal[0]);
8
GLCD_DrawString(0,2*24,(char*)StringBuf);
9
...
Ich möchte aber den ADC-Wert in Volt umrechnen. Was so aussehen soll:
1
#define STRINGBUF_LEN 21
2
charStringBuf[STRINGBUF_LEN];
3
intadcVal[3];
4
floatx;
5
6
...
7
adcVal[0]=ADC_GetValue();
8
x=(adcVal[0]*5)/1024;
9
sprintf(StringBuf,"%f",x);
10
GLCD_DrawString(0,2*24,(char*)StringBuf);
11
...
Wenn ich den berechneten Wert einer float-Variable zuweise und
anschließend ausgeben möchte, hört der Timer auf neue Werte zu liefern
und ich bekomme kein float-Ergebnis ausgegeben.
Kann mir jemand hier weiterhelfen?
Max H. schrieb:> Der Compiler macht so eine Ganzzahl Division, du musst mindestens einen> Operanden nach Float casten.
erst mal danke für deine Antwort. Ich bekomme jetzt zwar ein
float-Ergebnis ausgegeben aber der Timer funktioniert immer noch nicht,
beim integer habe ich immer wieder einen neuen Wert geliefert bekommen
und jetzt leider nicht, das müsste doch damit zusammenhängen oder?
Selbst wenn ich den Wert x nicht der sprintf-Funktion übergebe, so tut
der Timer nichts mehr. Dann müsste doch das Problem nicht an der
sprintf-Funktion liegen. Liegt das Problem dann am Compiler oder
eventuell an der Stackgröße? Hat jemand damit eine Ahnung, wie ich
dieses Problem beheben kann.
Was kommt denn bei
x = adcVal[0] / 512.0;
sprintf(StringBuf, "%f", x);
raus?
Wäre doch schön, wenn was (auch falsch) rauskäme - und man
dann nur noch den richtigen Faktor suchen muss...
Ollie schrieb:> Wäre doch schön, wenn was (auch falsch) rauskäme - und man> dann nur noch den richtigen Faktor suchen muss...
Es kommt was raus. Ich dachte das Problem liegt bei der
sprintf-Funktion. Aber so ist es ja nicht auch wenn ich in sprintf kein
float benutze (sondern ein int) aber überhaupt im Programm mit einer
float variable rechne, so hört der Timer auf zu arbeiten.
Detlef Kunz schrieb:> x = (adcVal[0] * 0.5f) / 1024.0f;
Ja das habe ich korrigiert. Ich bekomme für x = 0.495605 raus. Das
passt, nur der Timer müsste jetzt noch arbeiten, wie beim int.
Kann dein sprintf auch mit Float umgehen?
Bist du dir sicher, dass der Puffer groß genug ist (dafür gäbe es dann
snprintf)?
Wie oft feuert der Timer, und wieviel Zeit verbringt deine CPU im
Leerlauf (in der Hauptschleife)?
Versuchst du, aus der ISR heraus das Display zu beschreiben?
Was ist das überhaupt für ein Controller, und wie schnell taktest du
ihn?
S. R. schrieb:> Kann dein sprintf auch mit Float umgehen?
Ja, ich bekomme für x (ist als float deklariert) eine float-Zahl
(0.495623).
S. R. schrieb:> Bist du dir sicher, dass der Puffer groß genug ist (dafür gäbe es dann> snprintf)?
Das mit dem Puffer weiß ich leider nicht. Ich habe etwas gelesen das man
in der startup-Datei den Stack-Size ändern muss, hat bei mir aber nicht
geklappt.
S. R. schrieb:> Wie oft feuert der Timer, und wieviel Zeit verbringt deine CPU im> Leerlauf (in der Hauptschleife)?
Also ich habe den Timer auf 10 Sekunden gestellt. Das heißt, das
eigentlich nach 10 Sekunden mein Program neu starten müsste bzw. ich
alle 10 Sekunden einen neuen Wert vom AD-Wandler bekommen müsste. Aber
ich bekomme jede Sekunde einen neuen Wert geliefert.
S. R. schrieb:> Was ist das überhaupt für ein Controller, und wie schnell taktest du> ihn?
Ich verwende einen MCB4300 Evaluation Board von Keil. Mit einem LPC4357
Chip.
Ich bin etwas neu in dieser Sache, deswegen verstehe ich nicht jede
Frage, was meinst du genau mit Takten, spielt das eine Rolle für den
Timer?
So sollte der Programmcode aussehen und eigentlich funktionieren.
Florian schrieb:> mach mal die Variablen, die im Interupt verändert werden "volatile". Da> hatte ich schon öfters Probleme.
Hab ich gemacht, aber das Programm scheint eine Allergie gegen floats zu
haben, sobald ich ein float in main oder in der Timer_Callback Funktion
verwende so hört der Timer auf, aber die float Zahl wird ausgegeben.
Walter schrieb:> das Programm läuft genau einmal durch main, und dann??> Da fehlt wohl> while (1);
Ich bin das Program mit dem debugger durchgegangen und zwar läuft das
Programm einmal durch main anschließend in:
Walter schrieb:> um danach wieder Reset zu machen??> Da fehlt ein while ( 1 );
Das hilft alles leider nicht. Der Timer läuft nicht wenn ich ein float
benutze.
Björn M. schrieb:> Walter schrieb:>> um danach wieder Reset zu machen??>> Da fehlt ein while ( 1 );>> Das hilft alles leider nicht. Der Timer läuft nicht wenn ich ein float> benutze.
Timer := Thread? Oder ISR?
Und hast Du in der verwendeten C-Library geschaut, ob sprintf
Multithread fähig ist (bzw. von einer ISR gerufen werden darf)? Manchmal
muss man Compiler Flags und/oder spezielle Library Varianten nehmen um
z.B. sprintf mit "zusätzlichen Hook Funktionen" ins entsprechend
Multitasking einzubinden.
Vielleicht ist es besser, Du überarbeitest Dein Programm so, dass im
Timer (ISR) nur Deine eigenen Datenstrukturen und eigene Funktionen
gerufen werden (bzw. nur Funktionen, die Multithread/ISRfähig sind).
Achim K. schrieb:> Und hast Du in der verwendeten C-Library geschaut, ob sprintf> Multithread fähig ist (bzw. von einer ISR gerufen werden darf)?
Also ich habe mal in der stdio.h nachgeschaut und folgendes gefunden:
* is equivalent to sprintf, but does not support floating-point formats.
5
* You can use instead of sprintf to improve code size.
6
* Returns: as sprintf.
7
*/
also unterstützt mein sprintf kein floating-point format. es muss ja
dafür eine alternative geben?
Ich verwende die Funktionen aus diesem Link:
https://www.keil.com/pack/doc/CMSIS/RTOS/html/group___c_m_s_i_s___r_t_o_s___timer_mgmt.html
osTimerCreate und osTimerStart. Mein Programm springt nach dem Durchlauf
von main() in die Funktion Timer_Callback() und holt sich hier den
AD-Wert und danach ist aus, falls ich ein float benutze.
Björn M. schrieb:>> Kann dein sprintf auch mit Float umgehen?> Ja, ich bekomme für x (ist als float deklariert) eine float-Zahl> (0.495623).
Gut.
>> Bist du dir sicher, dass der Puffer groß genug ist (dafür gäbe es dann>> snprintf)?> Das mit dem Puffer weiß ich leider nicht.
Mir ging es nur darum, dass die Zahlendarstellung nicht größer ist als
der Puffer, in den du sie reinschreiben willst. Deswegen sollte man
statt sprintf() auch immer snprintf() benutzen.
> Ich habe etwas gelesen das man in der startup-Datei den Stack-Size> ändern muss, hat bei mir aber nicht geklappt.
Du kannst in solchen RTOSen die Stackgröße pro Task festlegen. Die
Stacks selbst können unterschiedlich alloziiert werden, im Extremfall
auch einfach als globales Array oder so.
In der Startup-Datei wird meist nur der Systemstack (Kernelstack)
betrachtet, um überhaupt C-Code ausführen zu können. Das RTOS sollte den
sowieso beim Start durch einen eigenen ersetzen.
>> Wie oft feuert der Timer, und wieviel Zeit verbringt deine CPU im>> Leerlauf (in der Hauptschleife)?> Also ich habe den Timer auf 10 Sekunden gestellt. [...]
Mir ging es darum, ob vielleicht die ISR durch das Float so langsam
wird, dass deine Hauptschleife nicht mehr hinterherkommt. Aber das
scheint nicht das Problem zu sein.
>> Was ist das überhaupt für ein Controller, und wie schnell taktest du>> ihn?> Ich verwende einen MCB4300 Evaluation Board von Keil. Mit einem LPC4357> Chip. Ich bin etwas neu in dieser Sache, deswegen verstehe ich nicht> jede Frage, was meinst du genau mit Takten, spielt das eine Rolle für> den Timer?
Für den Timer nicht. Aber wenn du beispielsweise versuchst, einen Timer
alle 10µs auszulösen, aber die CPU selbst nur mit ein paar kHz läuft,
dann kann das nicht gut gehen (weil dann die meisten Interrupts
ignoriert werden, und die Hauptschleife selbst nicht mehr ausgeführt
wird).
Das wollte ich ausschließen. Aber mit 10 Sekunden Zykluszeit und
mehreren MHz Taktfrequenz ist das kein Problem.
> So sollte der Programmcode aussehen und eigentlich funktionieren.> [...]
(a) C-Code bitte in in [c]-Tags einschließen.
(b) Du hättest ja mal vorher verraten können, dass du mit einem OS
arbeitest. ;-)
(c) Hardware (ADC) initialisiert man am besten nur einmal, nach dem
Einschalten. In deinem Fall vermutlich, bevor du das OS startest.
(d) Deine Idle-Loop fehlt. Deine main()-Funktion rennt bis zum Ende und
dann stürzt der Controller ab (bzw. startet neu). Es gibt kein
übergeordnetes Betriebssystem, welches ein endendes Programm abfangen
kann. Das könnte übrigens auch der Grund sein, weswegen deine 10
Sekunden nicht stimmen: 1 Sekunde für die Initialisierung, ein
Durchlauf, ein Reset.
(e) Du initialisierst das LCD, nachdem du das OS gestartet hast. In
der Zwischenzeit kann ein Timer feuern und das LCD benutzen, während es
noch nicht initialisiert ist (Race Condition).
(f) Ich weiß nicht, ob sprintf() und Freunde in der Newlib reentrant
sind (also gleichzeitig aktiv sein können). Solange du es nirgendwo
anders verwendest, sollte das kein Problem sein. Aber innerhalb der ISR
solltest du es trotzdem nicht benutzen, und auch LCD-Zugriffe vermeiden.
Dafür ist die Hauptschleife da.
S. R. schrieb:> Hardware (ADC) initialisiert man am besten nur einmal, nach dem> Einschalten. In deinem Fall vermutlich, bevor du das OS startest.
Okey also initialisere ich gleich am Anfang von main() das LCD und ADC,
so dass ich in der Timer_Callback-Funktion nur den Wert vom ADC hole und
in main() ausgebe.
S. R. schrieb:> (d) Deine Idle-Loop fehlt. Deine main()-Funktion rennt bis zum Ende und> dann stürzt der Controller ab (bzw. startet neu).
Meinst du mit Idle-Loop eine Endlosschleife while(1); ?
S. R. schrieb:> Es gibt kein> übergeordnetes Betriebssystem, welches ein endendes Programm abfangen> kann.
Das verstehe ich nicht ganz, oder ist damit wieder das mit Idle-Loop
gemeint?
Björn M. schrieb:> Okey also initialisere ich gleich am Anfang von main() das LCD und ADC,> so dass ich in der Timer_Callback-Funktion nur den Wert vom ADC hole und> in main() ausgebe.
Exakt. Wenn du allerdings so fragst, ist vermutlich schon das OS
übertrieben und du hättest vielleicht drauf verzichten sollten. ;-)
>> (d) Deine Idle-Loop fehlt. Deine main()-Funktion rennt bis zum Ende und>> dann stürzt der Controller ab (bzw. startet neu).>> Meinst du mit Idle-Loop eine Endlosschleife while(1); ?
Wenn dein Design alles in Interrupthandlern erledigt, dann ja. Und du
kannst dann in dieser Schleife auch die CPU schlafen legen - sie wird
dann vom nächsten Interrupt aufgeweckt und verbraucht im Durchschnitt
weniger.
Wenn du deine Aktionen (Wert auf LCD ausgeben usw) in main() machst,
dann hast du zwar eine Endlosschleife, aber die ist nicht leer. Denn
dort werden dann nacheinander die Ergebnisse aus den Interrupthandlern
verarbeitet. Und wenn die Schleife einmal durchgelaufen ist, dann hältst
du die CPU auch bis zum nächsten Interrupt an. Dieser Ansatz ist aus
meiner Sicht zu empfehlen.
>> Es gibt kein übergeordnetes Betriebssystem, welches ein endendes>> Programm abfangen kann.>> Das verstehe ich nicht ganz, oder ist damit wieder das mit Idle-Loop> gemeint?
Wenn du in einem PC-Programm dein main() mit einem return beendest, dann
wird das Betriebssystem benachrichtigt, dass das Programm fertig ist.
Dieses Betriebssystem fehlt aber auf einem Controller, deswegen darf
main() niemals enden. Passiert das trotzdem, holt sich die CPU ihre
Rücksprungadresse vom Stack und springt da hin. Die Rücksprungadresse
war aber nie gültig. Wenn du Glück hast, hat dein Startup-Code da die
Adresse vom Reset-Handler eingetragen, dann startet dein Programm
einfach neu. Alternativ führt die CPU so lange halbzufällige Bytes aus,
bis sie entweder neu startet, eine bekannte Funktion trifft und/oder
abstürzt.
Gruß