Hi, ich habe vielleicht ein komisches Problem: Ändere ich die beschriftung meiner werte, kommen plötzlich falsche adc werte raus. Woran liegt das? RS232 Log: a 228 b 3 a 371 a 229 b 3 a 371 a 229 b 3 a 371 a 229 b 2 a 371 adc 228 b -2 a 24063 adc 14656 b 192 a 23744 adc 14656 b 192 a 23744 adc 14656 b 192 a 23744 Codeausschnitt: char buffer[20]; send=adc_read_and_null(ADC_highmosfet); sprintf( buffer, "adc %i ", send); usart_puts( buffer); send=adc_read_and_null(0); sprintf( buffer, "b %i ", send); usart_puts( buffer); send=adc_read_and_null(ADC_supplyvoltageref); sprintf( buffer, "a %i ", send); usart_puts( buffer);
Nachtrag: Entspricht der 3. String dem ersten oder 2. geht es, sonst kommt müll.. a 14656 b 192 c 23744 a 14656 b 192 c 23744 a 255 b -2 b 371 a 229 b 3 b 371 Hoffe ihr könnt mir helfen, schonmal besten Dank im Vorraus...
Wieviel Platz hast du noch im µC? sprintf( buffer, "adc %i ", send); sprintf( buffer, "b %i ", send); sprintf( buffer, "a %i ", send); Braucht 8+6+6 Bytes im ROM und nochmal im RAM für die Formatstrings. sprintf( buffer, "a %i ", send); sprintf( buffer, "b %i ", send); sprintf( buffer, "b %i ", send); Braucht 6+6 Bytes im ROM und nochmal im RAM für die Formatstrings.
Im Rom ist noch mehr als genug Platz >5kByte... @Peter: was brauchst du denn an files?
Michael H. schrieb: > Im Rom ist noch mehr als genug Platz >5kByte... > > @Peter: was brauchst du denn an files? Dein Buffer ist zu klein !!! Zähl mal die Zeichen nach.
Klaus Skibowski schrieb: > Michael H. schrieb: >> Im Rom ist noch mehr als genug Platz >5kByte... >> >> @Peter: was brauchst du denn an files? > > Dein Buffer ist zu klein !!! Zähl mal die Zeichen nach. sorry, muss mich korrieren, ich habe den Code nicht vollständig gelesen. Du schickst den String ja nicht am Stück...
Michael H. schrieb:
> Entspricht der 3. String dem ersten oder 2. geht es, sonst kommt müll..
Zwei gleiche Strings werden vom Compiler als einer abgebildet.
Riecht alles danach, als wäre dein SRAM voll und wird vom Stack
zerschossen.
meint ihr den Stack von uart_puts? der beträgt 64 bytes, sollte eigentlich reichen... Auf dem Ram ist auchnoch genug Platz vorhanden (800 Bytes)...
Nachtrag: An sprintf liegts selbst nicht, folgendes funktioniert: code: char buffer[60]; sprintf( buffer, "s:%i hv%i o%i", send3, send2, OCR2); usart_puts( buffer); sprintf( buffer, "a%i b%i c%i d%i", 123, 456, 789, 12); usart_puts( buffer); log: restart s:22464 hv216 o0a123 b456 c789 d12 s:22528 hv13888 o14a123 b456 c789 d12 s:22464 hv13888 o14a123 b456 c789 d12 s:22528 hv13888 o14a123 b456 c789 d12 s:22464 hv13888 o14a123 b456 c789 d12 nur irgendwie verreists mir die adc werte. Danke für eure Hilfe
Dann zeig jetzt endlich mal das komplette Programm und nicht immer nur nichtssagende Ausschnitte.
Soweit so gut. Und wo sind jetzt besagte sprintf? Edit: Bis jetzt habe ich eigentlich nur gesehen, dass du Zugriffe auf _adcdata nicht gegen Interrupts abgesichert hast. Da _adcdata ein int ist, musst du das tun, da die Operation nicht atomar ausgeführt werden kann. zb
1 | unsigned int adc_read(unsigned char channel){ |
2 | unsigned int readit; |
3 | do{ |
4 | cli(); |
5 | readit=_adcdata[channel]; |
6 | sei(); |
7 | } while(readit==ADC_value_nulled); |
8 | |
9 | cli(); |
10 | readit = _adcdata[channel]; |
11 | sei(); |
12 | return readit; |
13 | }
|
Wenn man es ganz exakt machen will, müsste man anstelle des sei() eigentlich immer den vorhergehenden Zustand wiederherstellen. Schliesslich sagt ja niemand, dass Interrupts generell aktiviert sind, wenn adc_read aufgerufen wird. Aber in deinem Fall kann man wohl davon ausgehen, dass das immer so sein wird. Nitpicking: Namen, die mit einem _ beginnen, sind für den Compiler bzw. die Runtime Library reserviert.
cli() und sei() tun's nicht (genauer: tun's oft, müssen aber nicht), denn der Compiler ist frei, sie umzugruppieren. Besser sind die ATOMIC_BLOCK-Macros der AVR-libc aus atomic.h, die sind für solche Fälle gedacht.
Hazeh Zimmerer schrieb: > cli() und sei() tun's nicht (genauer: tun's oft, müssen aber nicht), > denn der Compiler ist frei, sie umzugruppieren. Ich stimme dir zwar zu, dass man die atomic-Makros nehmen soll, aber cli() expandiert zu _asm__ __volatile_, das sollte der Compiler nicht umgruppieren.
Hazeh Zimmerer schrieb: > Besser sind die > ATOMIC_BLOCK-Macros der AVR-libc aus atomic.h, die sind für solche Fälle > gedacht. :-) Old habit. An die ATOMIC Makros muss ich mich erst noch gewöhnen
Hallo Jörg, > cli() expandiert zu asm__ __volatile, das sollte der > Compiler nicht umgruppieren. ohne mich jetzt sooo besonders auszukennen: Um nicht umgruppiert zu werden, müssten sei() und cli() dem Compiler mitteilen, dass ihre Seiteneffekte beliebigen Speicher und beliebige Register modifizieren können (was aus Gründen der Optimierung aber sonst unerwünscht ist, aber oben im Bezug auf den ADC-Wert der Fall ist). So jedenfalls habe ich es aus einer längeren und sehr interessanten Diskussion vor ca. 2 Monaten in comp.embedded (mitlesend) geschlossen. Der Compiler darf solange umgruppieren, wie das Ergebnis inklusive aller Seiteneffekte dasselbe bleibt, so mein Stand.
Ja, könnte passen. Es fehlt halt zunehmend im C-Standard ein volatile, das man auf einen Block Code anwenden kann, sowas wie:
1 | cli(); |
2 | volatile { readit = _adcdata[channel]; } |
3 | sei(); |
Ja. Die Optimierungen moderner Compiler, die dem Einen ein Segen sind, werden sonst leicht zum Fluch.
Das Atomarmachen hat leider nichts geholfen. Anbei mal eine main.c mit sprintf. Hoffentlich habt ihr noch andere ideen.
Ausser, dass %i der falsche Formatspecifier für einen unsigned int ist, sehe ich im geposteten Códe nichts mehr. Aber das ist hier nicht das Problem. Ich denke, dass irgendwo der Stack durcheinanderkommt.
Lösung gefunden. In meiner ADC Datei kommt beim ADMUX (Analog Multiplexer) scheinbar was durcheinander. Ich habe das jetzt ersetzt und wunderweis, es geht! folgendes char temp; temp=ADMUX; temp&=~((1<<MUX3)+(1<<MUX2)+(1<<MUX1)+(1<<MUX0)); temp|=_adc_current_pos; ADMUX=temp;; durch: ADMUX=ADMUX_CONFIG+_adc_current_pos; ersetzt und es geht! Hat jemand zufällig eine Erklärung dafür?
Hmm Ersetze mal char temp; durch unsigned char temp; (WEnn du mit Bytes arbeitest, sei immer explizit! char benutzt du nur dann, wenn du es auch tatsächlich mit Charactern im Sinne von Textverarbeitung zu tun hast. Du weißt nie (ausser du siehst in der Doku nach) ob der Compiler char als signed oder unsigned ansiehst. Je nachdem kann da einiges passieren.
Oder halt gleich sich an C99-Stil gewöhnen und uint8_t / int8_t aus <stdint.h> benutzen, wenn man gar keine druckbaren Zeichen sondern kleine Integer-Zahlen meint. Auch uint_least8_t und uint_fast8_t sind einen Moment des Nachdenkens wert, falls der Code ggf. auf größere Prozessoren portabel sein soll.
Und das hier sollte man auch lassen:
1 | _adc_current_pos; |
Unterstriche vor dem Variablen-/Funktionsnamen sind doch den Libs und Headern vorbehalten!?
7.1.3 Reserved identifiers ... -- All identifiers that begin with an underscore are always reserved for use as identifiers with file scope in both the ordinary and tag name spaces.
Ich bin kein Freund von uint8_t da mein Prog das nicht hervorhebt. Jetzt müsst ihr mich aber mal ein bisschen Aufklären: was ist der unterschied zwischen uint_least8_t und uint_fast8_t? Bzw. was heißt das? Übersetzt mir das Englische jetzt noch jemand bitte ins Deutsche? Ich komm trotz meinen 8 Jahren Englisch nicht dahinter was gemeint ist...
Michael H. schrieb: > Ich bin kein Freund von uint8_t da mein Prog das nicht hervorhebt. Dann kannst du dir immer noch einen anderen Editor suchen oder aber denjenigen, der ihn gebaut hat, darauf hinweisen, dass es an der Zeit wäre, 10 Jahre nach der Verabschiedung des aktuellen C-Standards sowas mit zu unterstützen. > Jetzt müsst ihr mich aber mal ein bisschen Aufklären: was ist der > unterschied zwischen uint_least8_t und uint_fast8_t? uint8_t verlangt eine vorzeichenlose Ganzzahl, die exakt 8 bit breit ist. Wenn die zu Grunde liegende Maschine eine derartige Zahl nicht darstellen kann (und der Compiler sie auch nicht emulieren kann oder will), dann gibt es diesen Typ nicht, und entsprechender Code lässt sich nicht compilieren. Eine mögliche Folge davon ist, dass eine derartige Zahl im Zugriff u. U. weniger effektiv ist, als eine größere Zahl auf einer bestimmten Maschine wäre. Damit ist dieser Datentyp prädestiniert für Informationen, bei denen es wirklich auf die genaue Bitanzahl ankommt, bspw. in irgendwelchen Kommunikations- protokollen (Netzwerk, SCSI etc.) oder beim Zugriff auf bestimmte Hardwarekomponenten. uint_least8_t verlangt eine vorzeichenlose Ganzzahl, die mindestens 8 bit breit ist. Falls die zu Grunde liegende Maschine eine solche Zahl nicht darstellen kann, kann sie aber auch einen größeren Daten- typ substituieren. Das Augenmerk liegt aber auf dem Einsparen von Speicherplatz, wenn die Maschine das also implementieren kann (auch wenn es auf Kosten der Effektivität geht), dann ist er auch 8 bit breit. uint_fast8_t verlangt eine vorzeichenlose Ganzzahl, die mindestens 8 bit breit ist. Falls die zu Grunde liegende Maschine jedoch mit einer größeren Bitanzahl schnelleren Code erzeugt (weil bspw. auf einer 32-bit-Maschine Zahlen von 32 bits schneller zu bearbeiten sind als deren 8-bit-Teilwerte), dann soll dieser Typ substituiert werden. Augenmerk ist also die Geschwindigkeit, auch wenn es auf Kosten des Speicherbedarfs möglicher Variablen geht. Damit wäre an vielen Stellen, wo sich in der AVR-Community derzeit uint8_t eingebürgert hat, eigentlich uint_fast8_t zu bevorzugen, wenn der Code ggf. auch auf andere (größere) Architekturen portierbar sein soll. Die ganzen Datentypen gibt's dann auch noch ohne dem führenden u für vorzeichenbehaftete Zahlen.
Vielen Dank Jörg für deinen ausführliche Erklärung. Ich nutze zur Zeit geany und frage gleich mal in der Newsgroup nach, was man da machen kann... >7.1.3 Reserved identifiers >... >-- All identifiers that begin with an underscore are always reserved > for use as identifiers with file scope in both the ordinary and > tag name spaces. Kann mir das bitte noch jemand übersetzten, ich komme nicht dahinter was gemeint ist. Update: geany unterstützt es seit der neuen Version, ich hatte es nur mit der alten probiert... Nochmal vielen Dank für deine Erklärungen!
Michael H. schrieb: >>7.1.3 Reserved identifiers >>... >>-- All identifiers that begin with an underscore are always reserved >> for use as identifiers with file scope in both the ordinary and >> tag name spaces. > Kann mir das bitte noch jemand übersetzten, ich komme nicht dahinter was > gemeint ist. Die Details, an welchen Stellen man einen führenden Unterstrich auch als Autor eines normalen Anwenderprogramms schreiben darf und an welchen nicht, sind ein wenig, naja, nicht sofort offensichtlich. Solange du dich mit den Details nicht befassen willst, kannst du also oberste Regel nehmen: schreib keine Bezeichner, die mit einem Unterstrich beginnen. Die vielleicht prominenteste Ausnahme davon wäre diese hier:
1 | struct _foo { |
2 | ...
|
3 | };
|
Dort ist er immer zulässig, sofern es ein Unterstrich ist (nicht zwei) und sofern danach ein Kleinbuchstabe folgt. Ansonsten sind Bezeichner mit einem führenden Unterstich ,reserviert', d. h. für die Benutzung durch den Compiler oder die Systembibliotheken vorbehalten. Natürlich darfst du sie dann trotzdem benutzen, aber halt genau dann, wenn dir die Dokumentation des Compilers oder der Bibliothek sowas sagt. Beispiele dafür sind:
1 | char x[] __attribute__((progmem)) = "Hi"; |
2 | /* Dokumentation von GCC beschreibt das */
|
3 | #define __STDIO_FDEVOPEN_COMPAT_12 /* avr-libc-Dokumentation beschreibt, |
4 | was dabei passiert */
|
5 | PORTE = _BV(1); /* ebenfalls Bestandteil der avr-libc */ |
Vielen Dank, ich hab's kapiert! Danke auch für die ausführliche Erklärung.
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.