Hallo Kollegen,
ich habe in der main zwei variablen:
uint8_t timer0 = 0;
uint8_t timer1 = 0;
dort beschreibe ich in einer state machine diese variablen mit
uart-charactern und baue mir daraus einen uint16:
case ST_timer0:
timer0 = uart_getc();
next_state = ST_timer1;
break;
case ST_timer1:
timer1 = uart_getc();
next_state = ST_checksum;
break;
case ST_checksum:
if (uart_checksum_recv()) { /* checksum successful */
timer_count = orig_timer_count = (((uint16_t)timer1)<<8) | timer0;
timer_init();
sei();
leider geht der timer1 bei der shift operation verloren (timer1<<8
ergibt 1). wenn ich den source neu übersetze ohne die shift operation
kommt in timer1 und in timer0 der richtige wert an. so. wenn ich vor die
variablen volatile schreibe dann tut es. aber hier steht dass man
volatile nur braucht wenn ich per ISR und main gleichzeitig auf einer
resource operiere. das mache ich aber nicht. den uart bediene ich durch
pollen.
die funktion uart_getc() sieht so aus:
char uart_getc()
{
loop_until_bit_is_set( UCSR0A, RXC0 );
const char ch = UDR0;
uart_checksum_update(ch);
return ch;
}
der ganze source liegt unter:
http://github.com/ndim/freemcan/tree/master/firmware/
ich übersetze mit:
avr-gcc -c -mmcu=atmega644 -I. -fshort-enums -gstabs -I../include -Os
-Wall -Wextra -Wstrict-prototypes -std=gnu99 main.c -o main.o
hat jemand eine idee???
Viele Grüße & Danke im Voraus
Käfer schrieb:> hat jemand eine idee???
Nope.
Keine Erklärung
Hast du dir schon mal den Assembler Code rund um diese Stelle angesehen?
Hast du versuchsweise die beiden Variablen aus dem main() raus zu
globalen Vairablen gemacht? Wenns dann geht, wäre das ein Hinweis
darauf, dass unter Umständen irgendwo ein Bufferoverflow die beiden
Variablen auf dem Stack niedergebügelt hat.
Wie stellst du fest, dass da was verloren geht?
>Hast du versuchsweise die beiden Variablen aus dem main() raus zu>globalen Vairablen gemacht? Wenns dann geht, wäre das ein Hinweis>darauf, dass unter Umständen irgendwo ein Bufferoverflow die beiden>Variablen auf dem Stack niedergebügelt hat.
Ich würde einfach mal die Optimierung ausschalten.
Wahrscheinlich jagd da wieder jemand Geistern mit dem Debugger
hinterher;)
holger schrieb:>> const char ch = UDR0;>> Was soll denn der Quatsch mit dem const?
Wieso Quatsch? ch wird im weiteren nicht verändert, also ist nix dagegen
einzuwenden.
als globale static funktionierts auch.
den stackbedarf haben wir schon einmal gemessen und überprüfen dies
während dem make
CHECK: MAX_BSS_END=0x1100 >= BSS_END=0xd3e
CHECK: STACK_START=0x10ff >= 0xd6e = (BSS_END=0xd3e +
MIN_STACK_SIZE=0x30)
sollte eigentlich i.O. sein.
ramschmierer - hmm.
compiler optimierung ausschalten hilft aber auch.
das blöde ist ich habe keinen debugger. d.h. ich muss mich darauf
verlassen ob die anwendung das tut was sie soll. ob im timer_count das
richtige drinsteht oder nicht merkt man sofort bei der anwenung
siehe oben. optimierung ausschalten löst auch das problem.
was mach ich denn nun? ist der avr-gcc denn bekannt dafür dass er nicht
so gut mit optimierungen umgehen kann?
>siehe oben. optimierung ausschalten löst auch das problem.>was mach ich denn nun? ist der avr-gcc denn bekannt dafür dass er nicht>so gut mit optimierungen umgehen kann?
Welche Version hast du?
Gibt da gerade einen Bug
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=45052
avr-gcc-4.4.2-2.fc12.x86_64. der bug sollte nicht das problem sein da
mein gcc das volatile ja berücksichtigt.
das blöde ist halt dass es egal ist was ich tue, sobald nur irgend was
sich im assemblerfile ändert geht es plötzlich (ob es jetzt
-fshort-enums oder -Os ist). das schliesst einen ramschmierer halt nicht
aus.
die idee das assembler file zu interpretieren ist nicht so doof, nur bin
ich zu doof dafür ;-)
muss mal drüber schlafen.
Käfer schrieb:> leider geht der timer1 bei der shift operation verloren
Wie stellst du das fest? Stimmt das Ergebnis in timer_count und
orig_timer_count nicht oder ändert sich timer1?
Außerdem, mein Bauchgefühl sagt mir grad, dass solche Doppelzuweisungen
nicht eindeutig definiert sind (sicher bin ich mir da aber nicht):
timer_count = orig_timer_count = (((uint16_t)timer1)<<8) | timer0;
trenn das mal irgendwie so:
orig_timer_count = (((uint16_t)timer1)<<8) | timer0;
timer_count = orig_timer_count;
das auftrennen der doppelzuweisung auf zwei zeilen habe ich schon
versucht. geht auch nicht.
orig_timer_count = (((uint16_t)timer1)<<8) | timer0;
timer_count = orig_timer_count;
-> die ANWENDUNG VERHÄLT SICH SO als ob immer 000000001 bin im oberen
byte von timer_count stehen würde (egal was übers uart kommt), das
untere byte entspricht timer0
muss mal gerade überlegen:
/*snip*/
timer_count = orig_timer_count = timer1;
-> es steht das gesendete byte von timer1 in timer_count (über uart muss
timer1 also richtig kommen)
/*snip*/
timer_count = orig_timer_count = timer0;
-> es steht das gesendete byte von timer0 in timer_count
/*snip*/
timer_count = orig_timer_count = (((uint16_t)timer1)<<8)
-> ich glaube das hat auch nicht getan -> shift operation?
/*snip*/
was aber komischer weise funktioniert ist anstatt mit dem uart_getch
harte zahlen auf timer1 und 0 im code zu schreiben. -> das wiederlegt
die these von der kaputten shiftoperation (du hast recht, ich habe mich
missverständlich ausgedrückt ;-). deswegen bin ich auch auf das volatile
gekommen.
/*snip*/
timer1 und 0 als 16 bit (anstatt auf 8 bit variablen) und dafür beim
shiften nicht mehr auf 16 bit zu casten geht auch nicht.
/*snip*/
so muss mal überlegen - achja ein typecast auf uint8_t beim schreiben
auf timer1 und 0 mit getchar hab ich auch versucht. das ändert auch
nichts (die assembler files sind sogar identisch).
harrr
Käfer schrieb:> das auftrennen der doppelzuweisung auf zwei zeilen habe ich schon> versucht. geht auch nicht.
Ist egal.
Das ist eindeutig und sauber definiert.
Deine Analysen sind interessant. Aber im Grunde gilt: Was du da an
dieser Stelle im Code hast ist alles sauber.
Ich glaub du hast ganz andere Probleme. Atomarer Zugriff zum Beispiel.
timer_count wird in einem Interrupt runtergezählt
timer_count--;
if (timer_count == 0) {
Nur so nebenbei: Ist ein Unterlauf von timer_count erlaubt?
timer_count = orig_timer_count;
Kann ohne atomaren Zugriff in die Hose gehen!
Was ist wenn dabei der Timer Interrupt zuschlägt?
atomarer zugriff ist ein gutes stichwort aber auch da haben wir sehr
viel gehirnschmalz reingesteckt:
der rechner kommt aus einem kalt oder warmstart. holt sich timer0, dann
timer1 geht dann in in den zustand ST_checksum und dort steht das
statement timer_count = ... usw.
erst dann wird der timer initialisert und erst dann kommt der sei().
d.h. zu diesem zeitpunkt sind alle interrupts noch gesperrt. der zustand
kann erst wieder durch softreset oder kaltstart angesprungen werden.
wenn der timer counter überläuft ist das egal da das timer_flag im
interrupt service selber geschrieben wird. das ist die einzige
steuervariable des systems. ist der timer abgelaufen (das flag gesetzt)
ist alles vorbei und man kommt nur noch durch einen reset heraus. das
timer_flag ist ein uint8 - die entsprechenden if - statements müssten
alle atomar sein.
im code gibt es nur eine einzige stelle die nicht atomar ist (das ist
aber absicht) aber die wird nirgends angesprungen, ausser wenn nicht
ganz ausdrücklich vom user angefordert und auch da werden nur dann daten
ans uart verschickt und nicht an den systemvariablen rumgeschraubt.
also ich bin relativ ratlos gerade.
wenn ich den code zurückbaue verschwindet der fehler auch.
habe vor dem sei() mal folgendes eingefügt:
loop_until_bit_is_set(UCSR0A, UDRE0);
UDR0 = (char)(timer_count>>8);
loop_until_bit_is_set(UCSR0A, UDRE0);
UDR0 = (char)(timer0);
loop_until_bit_is_set(UCSR0A, UDRE0);
UDR0 = (char)(timer_count&0xff);
loop_until_bit_is_set(UCSR0A, UDRE0);
UDR0 = (char)(timer1);
Sending 'm' command to device (param=10=0x000a)
Received 0x0006=6 bytes of layer 1 data
0000 01 0a 0a 00 46 4d ....FM
->01 oberes byte timer_count (falsch)
->0a timer0 (richtig)
->0a unteres byte timer_count (richtig)
->00 timer1 (richtig)
bestätigt damit obige vermutungen
die punkte mit dem avrgcc selberbau werde ich mal im hinterkopf
behalten. vorerst mache ich ohne optimierungen mal weiter. das löst
temporär ja auch das problem. ein kleines bisschen schlechter
beigeschmack bleibt halt.
in diesem sinne - danke nochmal für das zahlreiche feedback