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.
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.
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
unsignedintadc_read(unsignedcharchannel){
2
unsignedintreadit;
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
returnreadit;
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.
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.
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: