>>length=buf[1];// lokale Var. soll den Wert von buf[1] annehmen, funxoniert überhaupt nicht!
6
outstr0(length);// 1. Gegencheck, Wertausgabe über serielle Schnitte
7
outstr0(buf[1]);// 2. Gegencheck, Wertausgabe über serielle Schnitte
8
...
9
}
Eingabewert *buf in die Fkt. crc16, Zahlen in dezimal:
buf[0] = 1
buf[1] = 3
buf[2] = 71
buf[3] = 194
buf[4] = 96
Der Gegencheck bringt folgendes Ergebnis :
length = 0
buf[1] = 3
obwohl in der 5. Zeile (>>) der Wert kopiert werden sollte.
Warum geht das beim 1. Mal nie, fragt
Hegy
Der Fehler liegt nicht in dem geposteten Code. Wenn du eine Zuweisung
schreibst, wird diese auch stets (semantisch) stattfinden, denn etwas so
simples wird kein C-Compiler fehlerhaft hinbekommen.
Hast du die Moeglichkeit das Programm zeilenweise zu debuggen?
Die Var. *buf, 1. Parameter der crc16()-Fkt., ist doch nur ein Zeiger
auf die Variable, von wo die Fkt. aufgerufen wurde. Im ganzen sieht das
dann so aus.
Datei main.c
1
staticvolatileunsignedcharpcbuf[MAXMSGLEN];// globaler geht's nicht
Hegy wrote:
> Ich vermute mal, irgendwo ist irgendwas falsch definiert/deklariert.
Mag sein. Aber nicht in den von dir dir geposteten Ausschnitten.
Die sind in Ordnung.
Kannst du nicht mal ein vollständiges, kompilierbares,
möglichst kurzes Beispiel zusammenstellen, welches den Fehler
immer noch enthält? Dann könnten mehr Leute das durch ihre
Compiler jagen und mal untersuchen, was da abgeht.
Bau ich gerade >mal eben schnell< zusammen, mal kukken ob der Fehler
dann auch noch da ist. Zuvor aber noch mal einen Auszug aus dem Listing,
dem .lss-File, wo der Gegencheck der beiden Var. erfolgt. Auch sieht es
m. M. nach richtig aus.
1
; hier wird die Fkt. crc16 aufgerufen
2
e3a: ldi r22, 0x02 ; 2
3
e3c: ldi r24, 0xAC ; 172
4
e3e: ldi r25, 0x02 ; 2
5
e40: call 0x131a <crc16>
6
....
7
8
0000131a <crc16>:
9
;....push push push....
10
133c: ldd r16, Y+1 ; 0x01
11
133e: mov r24, r16
12
1340: call 0x5b6 <outstr0>
13
1344: ldd r24, Y+1 ; 0x01
14
1346: call 0x5b6 <outstr0>
Wenn ich es richtig verstehe:
133c: lade direkt Reg. 16 mit dem Wert, wo Zeiger Y+1 draufzeigt
133e: kopiere (move) Inhalt von Reg. 16 nach Reg. 24
1340: Fkt. oustr0 ausführen, da wird auch das Reg. 24 gelesen &
ausgegeben
1344: lade direkt Reg. 24 mit dem Wert, woder Zeiger Y+1 draufzeigt,
also genau dasselbe wie in 133c, nur nicht den Wert in Reg. 16 laden
sondern Reg. 24.
1346: und raus damit über UART0
und 2x verschiedene Werte....komich.....sehr sehr komich....
Aber jetzt bau ich mal ein Kurzproggi, >mal eben schnell<
Mit dem Kurzproggi >mal eben schnell machen< wird so keiner. Macht mehr
Arbeit als alles andere. Also poste ich doch mal den gesamten Quellcode,
es geht ja nur um einen Funktionsaufruf und die Parameterübergabe, das
kann ich ja hier erklären, wo was zu finden ist.
Es geht nur um 3 oder 4 Zeilen in 2 Dateien, main.c und crc16.c
In main.c, Zeile 289 bis 292 steht:
1
289// Hier geht es los. Wenn der Datensatz vollständig vom PC angekommen ist
2
290// wird der CRC-Check gemacht. In pcbuf[] sind die Daten richtig enthalten
3
291ret=crc16(pcbuf,IN);// crc16(adr of beginning buffer, direction);
4
292// hier ist das Problem ENDE
Es wird also in Zeile 291 die Funktion mit richtigen Parametern
aufgerufen. Diese Parameter sind:
buf[0] bis buf[4] = 1, 3, 71, 194, 96 (alles dez.)
IN ist per #define festgelegt (2)
Die Daten von buf[] bedeuten folgendes:
buf[0] = SW-Adresse (1)
buf[1] = Anzahl der noch folgenden Bytes incl. CRC16 (3)
buf[2] = Kommando ('G')
buf[3 & 4] = CRC16-Checksumme (194, 96)
Diesen String erwarte ich natürlich auch in der crc16()-Fkt., da nur der
Zeiger auf den Datensatz übergeben wird. Und die Daten kommen auch an,
nur klemmt es beim kopieren der Daten in eine lokale Variable.
Hier die Stelle im File crc16.c, Zeile 37 bis 40
1
37// hier ist das ganz große Problem
2
38length=buf[1];// buf[1] (Datenlänge) nach length kopieren
3
39outstr0(length);// auf UART0 ausgeben, am PC kukken, ob's richtig ist...
4
40outstr0(buf[1]);// beim 1. Mal war length immer 0, buf[1] = 3, was richtig wäre
Der Rest, wie Variablendefinition usw. steht ja alles in den Header-
bzw. im den C-Dateien.
Den Datensatz habe ich ohne Fehler durch den gcc gekricht, also
funktioniert. Es gibt nur eine Warnung, wenn es eine gibt.
How2Probier aus:
ATmega162 mit 7,3728MHz, an UART0 eine Verbindung zum PC herstellen,
Baudrate 57600.
Nachdem der ATmega einen Reset hatte, folgenden String über die serielle
Schnitte zum ATmega schicken: Dezimal: 1, 3, 71, 194, 96 oder Hex: $01,
$03, $47, $C2, $60.
Innerhalb von 0,nix Sekunden gibt der ATmega 12 Bytes auf der UART0 an
den PC zurück. Eingelesen ist das:
1
000 003 001 003 071 194 096 000 255 255 050 003
Aufgedröselt nach Byte 1...12:
1
000 stammt aus Zeile 39, crc16.c, outstr0(length),
2
003 stammt aus Zeile 40, crc16.c, outstr0(buf[1]),
3
001 1. Byte des Befehlsstrings vom PC, SW-Adresse, Z. 57, crc16.c
4
003 2. Byte, Anzahl der noch kommenden Bytes, Z. 58, crc16.c
5
071 3. Byte, Befehl 'G', Z. 59, crc16.c
6
194 4. Byte, CRC16, Z. 60, crc16.c
7
096 5. Byte, CRC16, Z. 61, crc16.c
8
000 wieder das Längenbyte, immernoch 0, Zeile 62, crc16.c
9
255 da das Lägenbyte nicht ankommt, Fehler, Zeile 73/80, crc16.c
10
255 da das Lägenbyte nicht ankommt, Fehler, Zeile 74/81, crc16.c
11
050 Fehlercode an PC: EBADCRC (defs.h), Zeile 295, main.c
12
003 nochmal das Längenbyte aus dem String vom PC, Zeile 296, main.c
Wie gesagt, das Problem tritt immer nur beim 1. Mal auf. Also entweder
bei einem x-beliebigen Befehl nach Reset oder wenn ein neuer Befehl
kommt.
Hallo Hegy,
wenn ich das richtig verstehe, dann ist pcbuf[] der RX-Empfangspuffer,
daher volatile und die ISR(SIG_USART0_RECV) schreibt da bei jedem
Interrupt rein.
Wenn der gerade zwischen der Zuweisung und dem ersten outstring() kommt,
das ja naturgemäss etwas länger dauert, hat sich das Byte geändert, oder
sehe ich da was nicht ?
Gruß
Hermann-Josef
Deine Annahme ist fast richtig.
pcbuf[] ist der Empfangsspeicher für Zeichen vom PC, die über UART0
eintrudeln. Bei jedem Zeichen wird die ISR(SIG_USART0_RECV) ausgelöst.
Darin wird das gerade empfangene Zeichen in pcbuf[i] gespeichert, und i
wird um eins erhöht, es wird also mitgezählt, wieviele Zeichen da sind
und es wird verglichen, ob alle Zeichen da sind. Sind alle Zeichen da,
wird das Flag uart0rcvcmpl = 1 (uart0rcvcmpl = UART0 receive complete)
gesetzt. In main() wird zyklisch abgefragt, ob uart0rcvcmpl = 1 ist.
Erst wenn das so ist, wird die crc16-Fkt. aufgerufen, von daher kann
nichts zwischen den beiden outstr0() den Wert verändern. Es wäre dann
auch ein zufälliger Fehler, aber der Fehler ist definitiv
reproduzierbar.
Ich verstehe das immernoch nicht und habe schon soooo viel rumgeändert.
Eine Möglichkeit wäre, ich hab's gerade versucht, das File crc16.c aus
dem Makefile zu kikken und dafür die beiden darin enthaltenen Funktionen
in das File main.c mit reinzunehmen. Jetzt geht es, aber das ist keine
Problemlösung, es ist eine Behelfslösung.
Wegen der Verlagerung vermute ich jetzt, daß der Fehler nicht im Code,
sondern eher im Makefile liegt bzw. bei den Compiler-/Linkeroptionen.
Nur ne Vermutung! Vllt. ist ja doch im Code was faul.
>static volatile unsigned char pcbuf[MAXMSGLEN]; // globaler geht's nicht
Doch es geht noch globaler, wenn Du "static" weglässt! Mit "static" ist
der Buffer nur in der Compile-Einheit "main.c" sichtbar. Ich denke aber
nicht, dass dies das Problem ist, da Du den Pointer übergibst. Aber
ausprobieren schadet ja nicht.
>unsigned char crc16(volatile unsigned char *buf, unsigned char inout)
Hierbei hab ich schon eher Zweifel: Ich fürchte Du deklarierst den
Pointer als "volatile" nicht aber den Inhalt des Buffers. Versuche mal
die "volatiles" wegzulassen und schau ob's dann tut. Arrays liegen
nämmlich ohnehin stets im RAM und nicht in Registern, daher bringt das
volatile für Arrays ansich nichts...
Hast Du verschidene Optimierungsstufen ausprobiert? Geht es ohne
Optimieriung bzw mit -O0?
MfG Peter
Das Weglassen von 'static' oder 'volatile' habe ich alles schon
durchexerziert, alles keine Lösung. Manchmal ändert sich die
Programmgröße, wenn ich irgendwo an einer Variable ein 'volatile' oder
'static' zufüge (wird größer) aber in dem konkreten Fall war das nicht
so.
Mit Optimierungsstufen habe ich noch nix gemacht, Standard war bisher
immer -Os, s. Makefile.
Das hat jetzt nichts mit deinem eigentlichen Problem zu tun,
aber lass um Gottes Willen das SREG in einer ISR in Ruhe.
Du brauchst, nein, du sollst in einer ISR nicht selbst cli
aufrufen und du sollst auch nicht selbst das SREG sichern.
Das alles macht der Compiler für dich.
Weiter gehts mit Code durchsehen ....
Vielleicht sollte man den USART0_RECV Interrupt während der Bearbeitung
des pcbuf[] abschalten, ich habe immer noch ein ungutes Gefühl, dass das
noch was ankommen kann.
Noch 2 Anmerkungen zum Code:
- Kommentare der Form
1
//****
2
//****
3
//****
werden von einigen kontextsensitiven Editor (z.B. nedit interpretiert
das als C-code, es ist aber C89 bzw. C++) als Beginn eines Kommentares
gesehen, das Ende fehlt dann, der Compiler sieht das anders (= Codegröße
bleibt gleich), es ist halt nur etwas verwirrend
- ich würde z.B. EnableSerial0Receiver so definieren und einsetzen:
ist doch eigentlich egal. Der Präprozessor ersetzt jedes
EnableSerial0Receiver mit UCSR0B |= (1<<RXEN0);
Erst wenn der Präprozessor die Syntx verstanden und umgebaut hat, kommt
der Compiler zum Einsatz.
Die UART0 während der Bearbeitung von pcbuf[] abzuschalten bringt IMHO
nix, weil nur ein relativ kurzer String von weniger als 30 Byte da
reingeschossen wird. ISt der String raus, wartet die Anwendung für 0,5
Sekunden max. auf eine Antwort (ACK/NAK & Fehlercode), die ebenfalls nur
ein paar Byte lang ist. Ander beim dem Befehl, den ich oben schonmal
erwähnt hatte. Da bekomme ich 145 Bytes an gesammelten Daten aus dem
internen SRAM.
@Karl heinz
Besten Dank für deine Bemühungen. Hilft es, wenn ich mal das ELF, HEX
oder was auch immer Output mal poste? Evtl. ist meine GCC-Karre ja im
Eimer oder irgendwas drumherum. Ich benutze übrigens avr-gcc (GCC)
4.1.0.
Die *.lss-Datei ist doch das in Assembler übersetzte C-Programm. Wenn
ich mir den 7. Beitrag (oder 6. Antwort,
Beitrag "Re: lokale Variable nimmt Übegabewert nicht an") ansehe, den Teil mit
dem Assemblerlistung (Zeilen 133c bis 1346), so muß das eigentlich
funktionieren, zumindest sollte der Simulator/Debugger auch sowas
anzeigen.
Vllt. sollte ich die lokale Variable einfach mal global definieren,
könnte helfen, aber ist auch nur eine Behelfslösung.
Hallo Hegy,
bzgl. der Benutzung des Preprocessors:
Ja es ist hinsichtlich des Ergebnisses für den Compiler egal, ich habe
aber schon C-Kurse erlebt bei denen mit diesem Hilfsmittel solange
umdefiniert wurde bis es nach FORTRAN aussah, und sowas ist sicher nicht
im Sinne der Erfinder. Alles was man mit dem Preprocessor einführt,
sollte wieder nach C aussehen, um es eben für den geneigten Leser
leichter verständlich zu machen. So ist
1
intvar1;
2
3
/* viel Code dazwischen ... */
4
5
var1;
legal, erzeugt maximal eine Warnung ('statement without effect'), aber
wer würde so etwas schreiben?
OK ist vielleicht Ansichtssache, zu dem eigentlichen Problem:
Tatsache ist, dass sich eine Varable an einer Stelle ändert, wo man es
nicht erwartet. Das heißt, jemand/etwas schreibt da rein. Entweder durch
- einen Interrupt (race condition)
- durch einen Stackoverflow
- oder durch einen Schreibzugriff nach UDR0 während der Ausgabe.
Den Compiler würde ich als letztes verdächtigen.
Es schadet sicher nichts, mal testweise alle Interrupts in crc16()
auszuschalten, um den 1. Fall sicher auszuschliessen.
Da das Programm ca. 52 % des RAMs belegt, würde ich das 2. recht sicher
ausschliessen.
Zu outstr0() hätte ich noch den Vorschlag zu machen, erst dann ein Byte
nach UDR0 zu schreiben, wenn der USART das auch erlaubt, sprich UDRE0 im
UCSR0A auszuwerten (hoffe das Register heißt so).
Viel Erfolg
Hermann-Josef
Viele Möglichkeiten habe ich jetzt mal durchgechekkt. Außerdem habe ich
noch eine 3. Variable zum testen dazugenommen, welche in main.c als
globale uchar8 definiert ist und mit 55 initialisiert wird. In crc16.c
steht die Variable nochmal global als extern uchar8 drin. Um
sicherzugehen, daß die UART0 mich nicht bescheißt, sende ich noch über
UART0 einen Buchstaben vor und nach den Variablen raus, sieht alles so
aus:
Bevor was über UART0 rausgefunkt wird, wird das Flag UDRE0 gecheckt, ob
es 1 ist. Wenn, dann den Wert der einzelnen Variablen rausfunken,
interruptgesteuert. Ergebnis:
1
'a' buf[1] length lllll 'e'
2
097 003 000 000 101
Der 1., 2. und 5. Wert ist richtig. Zumindest bei der Var. lllll, also
das 4. Byte, da hätte ich 55 erwartet, aber auch hier 0.
Ich habe es auch schon mit strncpy(), auch keine Lösung.
Oder ein _delay_loop_2(5), natürlich auch nix.
Und bevor die crc16()-Fkt. aufgerufen wird, habe ich ein cli und danach
ein sei, eingebaut, hilft auch nicht.
Noch eine Erkenntnis:
wenn ich versuche, die Werte von pcbuf[] in eine andere Var. zu
kopieren, geht das nur mit dem 1. Byte beim 1. Mal. Die Funktionen
memcpy(tmpstr, pcbuf, 5) und strncpy(tmpstr, pcbuf, 5) kopieren auch nur
das 1. Byte, der Rest von tmpstr[] ist nach dem ersten lesen 0. Auch
hier habe ich wieder Dummy-ASCII-Zeichen nach jeder Zeichenausgabe von
pcbuf[] bzw. tmpstr[] ausgegeben, um sicherzustellen, daß die UART mich
nicht bescheißt.
problem seems to be fixed :))))
Ist schon suboptimal, wenn man sein eigenes Protokoll nicht kennt.....
Ursache/Lösung im File main.c, Zeile 189:
1
178ISR(SIG_USART0_RECV)
2
179{
3
183pcbuf[uart0incnt]=UDR0;
4
185if(pcbuf[0]==OWNADR)
5
186{
6
187uart0incnt++;
7
188
8
189>>>if(uart0incnt==pcbuf[1]+1)
9
193uart0rcvcmpl=1;
10
194}
11
198}
Der Zähler uart0incnt zählt die eingehenden Zeichen, die über UART0
reinkommen. Kein Zeichen da, uart0cnt = 0. In dem String, der reinkommt,
steht an 2. Stelle die Anzahl der noch folgenden Bytes inkl. CRC16 (2
Bytes). Im dem Fall von mir war immer die Rede von folgendem String: 01
03 71 194 96 (alles dez.). 1. Byte SW-Adresse, 2. Byte = 03 = 3 noch
folgende Bytes (71 194 96). wenn das 2. Byte angekommen ist, also die
ISR zum 2. Mal aufgerufen wird, wird erst der Zähler hochgezählt, steht
also richtigerweise auf zwei und nicht wie von mir angenommen auf eins.
uart0incnt steht nach dem 1. Byte schon auf eins, und wenn ich die Länge
auswerte (Zeile 189), dann steht uart0incnt auf zwei. Dieses +1 bei
pcbuf[1]+1 war die Ursache, so wie es aussieht. Deswegen kamen auch erst
die Daten entsprechend rüber, wenn ich den String 2x abgefeuert habe,
weil nach dem 2. Mal abfeuern das Flag uartrcvcmpl = 1 gesetzt wird.
Ich bau den Code mal wieder zurück, so wie es ursprünglich war und werde
das ausgiebig testen. Dann das hoffentlich finale Ergebnis.
So, feddich meinich.
Es geht!
Und dann war da noch ein Fehler in der ISR(SIG_USART0_RECV) mit der
Bytezählerei, und pcbuf[] wurde beim Start nicht initialisiert. hat der
gcc nicht mitbekommen, weil nur das erste Element von pcbuf[]
initialisiert wird und der Rest geht leer aus. Es sind also mehrere
SW-Fehler gewesen.
Danke auch an Hermann-Josef, für die Anteilnahme!