Hallo. Hab ein Programm geschrieben,dass per Timer-Interrupt ca. aller 1Sekunde die Daten eines Sensors über I2C einliest und diese dann mittels UART an den PC überträgt (Ausgabe auf HyperTerminal).Wenn der Interrupt nach dieser Sekunde ausgelöst wird,zeigt es mir dies durch toggeln einer LED. Ich benutze einen Atmega16,STK500,JTAGICE mkII Nun zum Problem: Bei Nutzung von sprintf innerhalb der Programmschleife gibt mir HyperTerminal verlässliche Werte aus.Das Problem dabei ist aber,dass meine while(1)-Schleife verlassen wird und somit z.B. der UART oder Timer jedesmal neu initialisiert wird. Nutze ich innerhalb der Programmschleife keine UART-Funktion (somit auch keine Ausgabe auf HyperTerminal möglich),dann verlässt das Programm zwar die while(1)-Schleife nicht,demzufolge wird auch nicht ständig neu initialisier(UART,Timer),dafür kommen dann aber über die I2c-Schnittstelle komische Werte vom Sensor an!(teilweise liest er die Datenregister mit "0" aus). Woran kann das liegen? Anbei mein Programm-Code.
>> char buffer1[10],buffer2[10],buffer3[10]; // array for output-values
Ist das nicht ein bisschen wenig???
EDIT:
warum eigentlich 3 Puffer, wenn du sowieso alles nacheinander machst?
Nimm besser 1 Puffer mit ausreichender Länge:
1 | char buffer[30]; // array for output-values |
2 | :
|
3 | sprintf(buffer,"testx: %i\n\t",testx); // output test variable |
4 | uart_puts(buffer); // and send it to uart |
Du hast natürlich recht.Ist viel sinnvoller mit einem Puffer. Trotzdem bleibt noch das besagte Problem bestehen! ;-)
> Ist viel sinnvoller mit einem Puffer.
Und der ist jetzt groß genug?
Ja ist er.Hab ihn jetz bei buffer[50]. In der uart_init() hab das TXCIE-Bit im UCSRB-Register wieder gelöscht und siehe da: die while(1)-Schleife wird nicht mehr verlassen und die LED blinkt demzufolge in meinem eingestellten Intervall. Allerdings spinnen die Sensorwerte immer noch,da sie wie schon vorher beschrieben teilweise leere Datenregister ausgeben. Da ich den Sensor aber auch schon an einem Fujitsu-Board dran hatte und dies dort nicht der Fall war,muss es einen bestimmten Grund dafür geben?!?
Wo ist eigentlich deine Transmit-ISR? Du hast zwar den Interrupt aktiviert aber anscheinend keine ISR dafür -> die Runtime Lib sorgst in so einem Fall dafür, dass der µC resettet wird.
Wozu soll das
1 | ISR(TIMER1_OVF_vect) // 16 bit timer ISR |
2 | {
|
3 | TIMSK &= (1 << TOIE1); // clear timer overflow interrupt bit |
4 | inter++; // increment variable for 1second interrupt |
5 | }
|
gut sein? Du deaktivierst innerhalb der Overflow ISR den Overflow. Ich sehe aber innerhalb deiner Schleife nicht, wie dieser Overflow jemals wieder aktiviert werden könnte. Du künstelst mir da insgesamt ein wenig zu sehr mit den Interrupt Bits rum. Im Regelfall funktioniert das so: Die Interrupts werden in der Initialisierungsphase des Programms auf die gewünschten Werte eingestellt und während der Bearbeitung muss man sie nicht mehr angreifen, weil alles seinen Weg geht. Es gibt natürlich Ausnahmen, das ist klar (Umkonfigurieren von fallender Flanke auf steigende Flanke, UART mit Ausgabepuffer in Interruptbetrieb, etc) aber bei dir scheint nichts davon der Fall zu sein.
Warum bürdest du deinem µC hier
1 | void uart_puts(const char *name ) // send string over uart |
2 | {
|
3 | int c = 1; |
4 | |
5 | while (c) |
6 | {
|
einen Zwischenschritt über 16 Bit Arithmetik auf?
1 | void uart_puts(const char *name ) // send string over uart |
2 | {
|
3 | int c = 1; |
4 | |
5 | while (c) |
6 | {
|
7 | c = *name++; |
8 | |
9 | if (c == 10) |
10 | send_uart(13); |
11 | send_uart(c); |
12 | }
|
13 | }
|
Das ist insgesamt unklug. Bei Strings wird also für einen Line Feed noch ein Carriage Return vorangestellt. Das ist ja grundsätzlich so ok. Aber wäre es nicht besser diese Funktionalität in die send_uart zu verfrachten? Dann würde nämlich dieser Automatismus auch dann funktionieren, wenn jemand einen einzelnen Line Feed ausgeben muss. (Und verwende doch bitte die C-Schreibweise für Sonderzeichen. Dann muss man nicht ständig eine ASCII Tabelle daneben liegen haben um zu wissen von welchem Zeichen hier die Rede ist) Das geht natürlich nur dann, wenn du nur textuelle Übertragung machst. Sobald binäre Übertragung im Spiel ist, soll die send_uart nur das machen was auch drauf steht: In Byte verschicken. Ev. wäre es besser da noch eine 3. Funktion ins Spiel zu bringen * send_uart Basisfunktion. Verschickt ein Byte so wie Gott das gewollt hat * uart_putc Verschickt ein ASCII Zeichen. Macht die \n -> \r\n Erweiterung * uart_puts Verschickt einen String via uart_putc
1 | void uart_puts(const char *name ) // send string through uart |
2 | {
|
3 | while( *name ) |
4 | send_uart( *name++ ); |
5 | }
|
6 | |
7 | void send_uart(unsigned char data) |
8 | {
|
9 | if( data == '\n' ) // preceed New Line with a Carriage Return |
10 | send_uart( '\r' ); |
11 | |
12 | while(!(UCSRA & (1 << UDRE))) |
13 | {
|
14 | } // wait for transmit buffer empty |
15 | |
16 | UDR = data; // 1Byte into UART data register |
17 | }
|
> TIMSK &= (1 << TOIE1);
Recht hast du.Das ist Unsinn. :)
Auch das mit den uart-Funktionen leuchtet ein.Danke für deine Hilfe
bisher.
Trotzdem bleibt der Fehler weiterhin bestehen,dass die Sensorwerte
irrsinnig sind.
:(
Dann würde ich an deiner Stelle hier ansetzen
1 | PORTB ^= (1 << LED1); // LED1 an PB1 an-/ausschalten |
2 | startmagnetismus(); // readout the sensor |
3 | |
4 | testx = sign12(puzzle_x); // get 12bit from x-axis |
5 | testy = sign12(puzzle_y); // get 12bit from y-axis |
6 | testz = sign12(puzzle_z); // get 12bit from z-axis |
7 | |
8 | arcx = (double) testx; // typecast x-avlue to calculate atan2 |
9 | arcy = (double) testy; // typecast y-value .. |
10 | kurs = atan2(arcx,arcy)/PI*180; // calculate heading by atan2 (so result is also possible if |
11 | // x = 0 or y = 0)
|
und die Werte für testx, testy und testz mit bekannten Werten fix besetzen und erst mal nachsehen ob die Ausgabekette bis zum Hyperterminal den Fehler verursacht. WEnn gesichert ist, dass das fehlerfrei funktioniert (auch die sign12 Funktion), kann der Fehler nur noch irgendwo in startmagnetismus() stecken :-) Sukzessive eine Fehlerquelle nach der anderen ausschliessen. Angefangen wird immer bei den Ausgabefunktionen. Die müssen als erstes getestet werden, denn sie sind deine einzige Möglichkeit Einblick in die Porgrammfunktion zu gelangen. Ich würde mir an deiner Stelle auch eine Funktion
1 | void uart_puti( int number ) |
2 | {
|
3 | char buffer[20]; |
4 | itoa( number, buffer, 20 ); |
5 | uart_puts( buffer ); |
6 | uart_puts( "\n" ); |
7 | }
|
8 | |
9 | void uart_debug( const char* text, int number ) |
10 | {
|
11 | uart_puts( text ); |
12 | uart_puts( " " ); |
13 | uart_puti( number ); |
14 | uart_puts( "\n" ); |
15 | }
|
schreiben. Mit dieser Hilfsfunktion ist es dann zb einfach, in den Programmcode Zwischenausgaben einzufügen. zb
1 | PORTB ^= (1 << LED1); // LED1 an PB1 an-/ausschalten |
2 | startmagnetismus(); // readout the sensor |
3 | |
4 | uart_debug( "Vor sign12 X:", testx ); |
5 | uart_debug( "Vor sign12 Y:", testy ); |
6 | uart_debug( "Vor sign12 Z:", testz ); |
7 | |
8 | testx = sign12(puzzle_x); // get 12bit from x-axis |
9 | testy = sign12(puzzle_y); // get 12bit from y-axis |
10 | testz = sign12(puzzle_z); // get 12bit from z-axis |
11 | |
12 | uart_debug( "Nach sign12 X:", testx ); |
13 | uart_debug( "Nach sign12 Y:", testy ); |
14 | uart_debug( "Nach sign12 Z:", testz ); |
15 | |
16 | arcx = (double) testx; // typecast x-avlue to calculate atan2 |
17 | arcy = (double) testy; // typecast y-value .. |
18 | kurs = atan2(arcx,arcy)/PI*180; // calculate heading by atan2 (so result is also possible if |
19 | // x = 0 or y = 0)
|
wenn du keine Hilfsmittel zum debuggen hast, dann musst du dir eben selbst welche machen! Ziel ist es, auf der UART Texte und Zahlen ausgeben zu können, so dass du mit dieser Mitschrift mitverfolgen kannst, was im Programm passiert, welche Werte woher kommen etc. Dazu muss die UART Ausgabe unter allen Umständen funktionieren und deshalb wird die als erstes getestet und fehlerfrei gemacht. Systematisch vorgehen! Vergiss, dass es auf einem anderen Board schon funktioniert hat. Das interessiert hier nicht. Du bist jetzt auf diesem Board und da fängst du bei 0 an. Nimm nichts als funktionierend an und nimm eine Komponente nach der anderen in Betrieb. In der Reihenfolge so dass du auf eine jeweils getestete Komponente aufbauen kannst. Nicht raten, sondern Fakten schaffen. Im Programm Zwischenergebnisse ausgeben lassen, so dass du aus den Zwischenergebnissen Rückschlüsse ziehen kannst. Dann hört das Raten auf was das Problem sein könnte und du fängst an, am Problem zu arbeiten. Ergänze das Programm mit Ausgaben zwischendrinn, so dass dir das Programm selber hilft, Fehler im Programm zu finden. Wenn dann alles läuft, kann man diese Ausgaben immer noch entfernen. Aber besser jetzt 3 Minuten für ein paar Hilfsfunktionen investieren als 3 Stunden Rätselraten.
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.