Forum: Compiler & IDEs Codexplosion bei _delay_ms mit ADC Wert


von Michael O. (michi26206)


Lesenswert?

Hallo,

ich möchte mit einem Attiny25 eine Spannung, welche durch ein Poti 
einstellbar ist, messen und diese dann mit der funktion _delay_ms(); als 
Wartezeit verwenden.

Funktionieren würde das ganze. Allerdings wenn ich den ADC Wert als 
Parameter an _delay_ms(); übergeben explodiert mein Code. Sprich er 
springt von ca. 250 Bytes auf ca. 4000 Bytes. Da ich aber wie oben 
geschreiben einen Attiny25 verwende hab ich jetzt ein Problem ;)

Code-Snippet:
1
uint16_t Pause;
2
Pause = 0x0000;
3
4
uint16_t ADC_messen(uint8_t channel)
5
{
6
  ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);
7
  ADCSRA = (1<<ADSC);            // messen
8
  while (ADCSRA & (1<<ADSC) );    // auf AVR warten
9
  ADC_Hilfe = (ADCL<<8);  //ADCL um 8 Bit verschieben
10
  ADC_Hilfe = ADC_Hilfe + ADCH;  //ADCH hinzufügen
11
  return ADC_Hilfe;                    // ADC Wert zurückgeben
12
}
13
14
Pause = ADC_messen(0x01)
15
_delay_ms(Pause);

Kann mir jemand sagen, ob es geht den Code zu verkleinern. Um mir dann 
auch nen Tipp geben kann wie ich das anstellen muss. Die beiden Timmer 
sind leider für PWM belegt.

Gruß

von Hc Z. (mizch)


Lesenswert?

1
for (uint16_t i = Pause; i > 0; i--)
2
    delay_ms(1);
Grund: Aus der Dukumentation von delay.h:
1
In order for these functions to work as intended, compiler optimizations
2
 must be enabled, and the delay time must be an expression that is a known
3
constant at compile-time. If these requirements are not met, the resulting
4
 delay will be much longer (and basically unpredictable), and applications 
5
that otherwise do not use floating-point calculations will experience severe 
6
code bloat by the floating-point library routines linked into the
7
 application.

von X- R. (x-rocka)


Lesenswert?

sieht doch kurz und knackig aus.
nimmt _delay_ms() auch 16 bit integer?
vielleicht selber eine delay funktion bauen mit NOPs und zähler?

Abgesehen davon:
ADC_Hilfe ist woanders deklariert?
ADCH solltest du zuerst lesen und shiften, dann ADCL addieren.

von Hc Z. (mizch)


Lesenswert?

1
  ADC_Hilfe = (ADCL<<8);  //ADCL um 8 Bit verschieben
2
  ADC_Hilfe = ADC_Hilfe + ADCH;  //ADCH hinzufügen
sieht mir eher nach einer Vertauschung des oberen mit dem unteren Byte 
aus.   Ich glaube nicht, dass das so beabsichtigt ist.

von Roland P. (pram)


Lesenswert?

Der GCC kann IMHO auch 16Bit-Register lesen.

Mach einfach ein "return ADC" dann kannst dir das Bitgeshifte sparen.
(ADC entspricht ADCL | ADCH << 8

Außerdem sehe ich gerade, dass du ADCL shiftest. Verwechslung oder 
Absicht?

Gruß
Roland

von Hubert G. (hubertg)


Lesenswert?

Wenn du unbedingt ein delay verwenden musst dann schau dir mal das
util/delay_basic.h  an.
Steht in der gcc.lib.referenz.

von Detlev T. (detlevt)


Lesenswert?

Michael O. schrieb:
> Kann mir jemand sagen, ob es geht den Code zu verkleinern. Um mir dann
> auch nen Tipp geben kann wie ich das anstellen muss.
Baue eine Schleife mit _delay_ms(1.0) drin, die Pause oft durchlaufen 
wird.
1
while(Pause--) _delay_ms(1.0);

Gruß, DetlevT

von Michael O. (michi26206)


Lesenswert?

danke @ all

auf die For Schleife hätte ich auch kommen können.

Das mit ADCH und ADCL war ein Fehler von mir, da ich ncht aufgepasst 
habe.

Den im Datenbaltt steht ja:
>When ADCL is read, the ADC Data Register is not updated until ADCH is read. 
>Consequently, if
>the result is left adjusted and no more than 8-bit precision is required,
>it is sufficient to read
>ADCH. Otherwise, ADCL must be read first, then ADCH.

Allerdings hab ich nicht auf die Bitwertigkeit geschaut.

ADC_Hilfe war auch deklariert hab ich nur im Snippet vergessen.

Gruß

von Max (Gast)


Lesenswert?

Ich möchte noch auf richtige Datentypen hinweisen (den Code selber habe 
ich nicht nachgeschaut). Man MUSS dir Typen für Konstanten auch als 
unit16 machen mit dem Suffix "U". Nach ANSI ist jede Konstante immer 
"signed int"-Datatype und ohnen Suffix wir schreiben einen 
signed-Operand in die unsigned-Variable, was datentypmismatch bedeutet. 
Weiter, "bitwise and shift operations" nur für "unsigned" definiert 
sind. Und leztens, "Integer promotion" Regel achten, d.h. nach ANSI ist 
immer jede Opration auf mind. Integer-Breite durchgeführt und klenere 
Typen werden auf "signed int" im dem Fall hochcastet. Beim AVR kann zum 
Abweichung kommen, aber ANSI spricht klar und MISRA schreibt das vor.
1
uint16_t Pause;
2
Pause = 0x0000U;
3
4
uint16_t ADC_messen(uint8_t channel)
5
{
6
  ADMUX = (ADMUX & ~(0x1FU)) | (channel & 0x1FU);
7
  ADCSRA = (1U<<ADSC);            // messen
8
  while (ADCSRA & (1U<<ADSC) );    // auf AVR warten
9
  ADC_Hilfe = ((uint16_t)ADCL)<<8);  //ADCL um 8 Bit verschieben
10
  ADC_Hilfe = (uint16_t)ADC_Hilfe + (uint16_t)ADCH;  //ADCH hinzufügen
11
  return ADC_Hilfe;                    // ADC Wert zurückgeben
12
}
13
14
Pause = ADC_messen(0x01U)
15
_delay_ms(Pause);

von Michael O. (michi26206)


Lesenswert?

Ich stell gerade fest, dass die ADC Messung gar nicht funktioniert. 
Gemessen werden soll mit ADC1 bzw. PB2

Code-Snippet:
1
uint16_t Pause;
2
Pause = 0x0000;
3
4
ADMUX = (1<<ADLAR);  //"runden"
5
ADCSRA = (1<<ADATE);
6
ADCSRA = (1<<ADEN);  //ADC ein
7
8
uint16_t ADC_messen(uint8_t channel)
9
{
10
  ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);
11
  ADCSRA = (1<<ADSC);            // messen
12
  while (ADCSRA & (1<<ADSC) );    // auf AVR warten
13
  return ADCH;                    // ADC Wert zurückgeben
14
}
15
16
Pause = ADC_messen(1);

Hat jemand nen Fehler entdeckt?

Gruß

von Stefan E. (sternst)


Lesenswert?

Michael O. schrieb:
> Hat jemand nen Fehler entdeckt?

1
ADCSRA = (1<<ADATE);
2
ADCSRA = (1<<ADEN);  //ADC ein
Die zweite Zeile setzt ADATE wieder auf Null (aber warum setzt du ADATE 
überhaupt?).

1
  ADCSRA = (1<<ADSC);            // messen
Und beim Starten der Messung den ADC gleichzeitig abzuschalten ist 
sicher auch keine gute Idee.

von Michael O. (michi26206)


Lesenswert?

ups....

da hat wohl wern nen | vergessen

danke dir

gruß

von Simon K. (simon) Benutzerseite


Lesenswert?

Michael O. schrieb:
>
1
>   return ADCH;                    // ADC Wert zurückgeben
2
>

->
1
return ADC;

von Hc Z. (mizch)


Lesenswert?

> Nach ANSI ist jede Konstante immer
> "signed int"-Datatype und ohnen Suffix wir schreiben einen
> signed-Operand in die unsigned-Variable, was datentypmismatch bedeutet.

In ANSI C gibt es kein Ding namens "datentypenmismatch", sondern 
"promotion rules".  Es ist also vielmehr genau definiert, zu welchem 
Typen erweitert wird, wenn 2 verschiedene Typen aufeinander treffen.

> Weiter, "bitwise and shift operations" nur für "unsigned" definiert
> sind.

Nein, auch für signed Integers ist Shift definiert, solange sie nicht 
negativ sind:
1
4. The result of E1 << E2 is E1 left-shifted E2 bit positions: vacated
2
bits are filled with zeros. ... If E1 has a signed type and a
3
nonnegative value, and E1x2^32 is representable in the result type,
4
then that is the resulting value; otherwise, the behavior is undefined.

> aber ANSI spricht klar und MISRA schreibt das vor.

Ich weiß nicht, was MISRA schreibt, aber ANSI sagt für den gezeigten 
Code klar aus, dass die Korrektur überflüssige Mühe ist.

von Roland P. (pram)


Lesenswert?

lies dir mal das Datenblatt zum ADC durch. Ich glaube mich zu erinnern, 
dass man beim Umschalten von ADMUX den ersten Wert unter bestimmten 
Umständen verwerfen musss, weil er falsch ist.

Gruß
Roland

von Max (Gast)


Lesenswert?

4. The result of E1 << E2 is E1 left-shifted E2 bit positions: vacated
bits are filled with zeros. ... If E1 has a signed type and a
nonnegative value, and E1x2^32 is representable in the result type,
then that is the resulting value; otherwise, the behavior is undefined.

Das ist aber klare Problembeschreibung! Wenn ich 0xFF (also signed) 
8-mal nach links shifte, E1x2^16 (ich habe 16-Bit integer, deswegen nur 
16), E1 nicht mehr "representable" ist. 0xff00 (signed) bedeute -32512 
anstatt 65280 unsigned. Die ASNI-Zeile oben beschreibt nur die Werte, 
die maximal 0x7f haben. Für die Werte 0x80 bis 0xff ist "the behavior is 
undefined". Wer sicherheitsrelevente Systeme programiert, kennt diese 
Problematik und verwendet explizit Casting.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Du hast mit deiner Erklärung Recht, aber das "2x2^32" bzw. "2x2^16"
da im Text ist falsch: im Standard steht "2 x 2^E2".  E2 ist hier
gleich 8.  Ändert nur nichts dran, dass 0xff00 nicht als signed int
darstellbar ist.

Allerdings trifft das auf das ADC-Ergebnis nicht zu: die ganzen
IO-Register sind vom Typ uint8_t, damit wird der Teil "If E1 has
an unsigned type, [...]" wirksam, für den das Ergebnis definiert
ist.

von DERLEVELER (Gast)


Lesenswert?

1
ADC_Hilfe = ADC_Hilfe + ADCH;  //ADCH hinzufügen

würde ich lieber so schreiben:
1
ADC_Hilfe += ADCH;  //ADCH hinzufügen

von Simon K. (simon) Benutzerseite


Lesenswert?

DERLEVELER schrieb:
>
1
ADC_Hilfe = ADC_Hilfe + ADCH;  //ADCH hinzufügen
2
>
>
> würde ich lieber so schreiben:
>
1
ADC_Hilfe += ADCH;  //ADCH hinzufügen

Nein, ADCH und ADCL sollte man nicht direkt auslesen, sondern ADC 
benutzen, damit diese in der richtigen Reihenfolge ausgelesen werden.
Einfach mal lesen, was schon geschrieben wurde, kann nicht schaden!

von Falk B. (falk)


Lesenswert?

@  Simon K. (simon) Benutzerseite

>Nein, ADCH und ADCL sollte man nicht direkt auslesen, sondern ADC
>benutzen, damit diese in der richtigen Reihenfolge ausgelesen werden.

Ich würde lieber ADCW nutzen ;-)

MFG
Falk

von (prx) A. K. (prx)


Lesenswert?

Falk Brunner schrieb:

> Ich würde lieber ADCW nutzen ;-)

Kein Unterschied.

von Falk B. (falk)


Lesenswert?

@  A. K. (prx)

>> Ich würde lieber ADCW nutzen ;-)

>Kein Unterschied.

Hmm, stimmt, im C. Aber im ASM geht das nicht, weil ADC ein Befehl ist. 
Ist auch so mit Fallunterscheidung in den Include Files drin ;-)

MfG
Falk

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Falk Brunner schrieb:
> Aber im ASM geht das nicht, weil ADC ein Befehl ist.

Könnte schon, da der C-Präprozessor ja nur ADCW erseten würde,
während adcw immer noch für den Befehl benutzbar ist.

Aber: wer sich für Assembler entschieden hat, kann sowieso keine
16-bit-IO-Register automatisch lesen oder schreiben lassen. ;-)

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
Noch kein Account? Hier anmelden.