Forum: Mikrocontroller und Digitale Elektronik Falsche Berechnungen mit ATtiny26


von Sandro L. (sandro_l)


Angehängte Dateien:

Lesenswert?

Hallo,
Ich habe folgendes Programm auf einen ATtiny26 geflasht:
1
#include <avr/io.h>
2
3
#include <avr/interrupt.h>
4
#include <avr/sleep.h>
5
6
volatile uint16_t counter;
7
volatile uint32_t adcValue;
8
9
void showNumber(uint16_t value) {
10
    // Code zur Anzeige auf 7-Segment-Anzeigen ...
11
    // Verwendet Timer1 (zählt auch die "counter" Variable hoch)
12
}
13
14
void startAnalogConversion() {
15
    // Prepare ADC noise cancelling mode
16
    set_sleep_mode(SLEEP_MODE_ADC);
17
    sleep_mode();
18
    // Loop if the interrupt that woke the cpu was something other than the ADC finishing the reading
19
    while((ADCSR & (1<<ADSC)) != 0) {
20
        sleep_mode();
21
    }
22
    // Vordefinierter Wert zu Testzwecken
23
    adcValue = 200;
24
    
25
    showNumber(adcValue);             // funktioniert:       Zeigt 200 an.
26
    showNumber((adcValue * 50) / 55); // funktioniert:       Zeigt 181 an.
27
    showNumber((adcValue * 96) / 100);// funktioniert nicht: Zeigt 102 an.
28
}
29
30
void setup() {
31
    // Analog-Digital-Converter (ADC)
32
    ADMUX = (1<<REFS1) | (1<<REFS0); // select internal reference source (2.56V) and input channel ADC0
33
    ADCSR = (1<<ADEN)  | (1<<ADIE) | (1<<ADPS2);   // enables ADC with prescaler 16 and activates interrupt
34
35
    counter = 0;
36
37
    sei();
38
39
    startAnalogConversion();
40
}
41
42
int main(void)
43
{
44
    setup();
45
    
46
    for(;;) {
47
        if (counter >= 500) {
48
            counter = 0;
49
            startAnalogConversion();
50
        }
51
    }
52
    return 0;   /* never reached */
53
}
54
55
ISR(ADC_vect)
56
{
57
    adcValue = ADC;
58
}

In der Funktion "startAnalogConversion" möchte ich den Wert von 
"adcValue" * 0.96 rechnen. Diese Berechnung ( (adcValue * 96) / 100 ) 
funktioniert jedoch nicht so wie erwartet. Wenn adcValue den Wert 200 
hat, sollte das Resultat 192 sein. Es kommt jedoch immer 102 als 
Ergebnis raus. Im Anhang habe ich das verwendete Makefile angehängt.

Was könnte der Grund sein, dass diese Berechnung nicht funktioniert?

Beste Grüsse,
Sandro

von Nobody (Gast)


Lesenswert?

Der Fehler stecht da, wo du ihn nie vermuten würdest!
Darum hast du den Teil ja auch abgeschnitten.

Ein Bug in: showNumber();
ohne Gewähr

von Timmo H. (masterfx)


Lesenswert?

Und wie sieht der Inhalt von showNumber aus?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Möglicherweise überschreibt die ISR den adcValue von 200.

von (prx) A. K. (prx)


Lesenswert?

Bei Soll=192 und Ist=102 käme evtl. auch ein Fehler in der 7-Segment 
Ausgabe in Frage. Was kommt denn bei showNumber(192) raus?

: Bearbeitet durch User
von Nobody (Gast)


Lesenswert?

Das Auslesen von adcValue muss sowieso ATOMIC erfolgen.
Aber das Problem würde dann nur sporadisch auftreten, wäre also eher 
schwer reproduzierbar.

von Sandro L. (sandro_l)


Lesenswert?

A. K. schrieb:
> Bei Soll=192 und Ist=102 käme evtl. auch ein Fehler in der 7-Segment
> Ausgabe in Frage. Was kommt denn bei showNumber(192) raus?

bei showNumber(192) wird auch 102 ausgegeben. Muss wohl doch an der 
Ansteuerung der Anzeige liegen..

In showNumber extrahiere ich die einzelnen Ziffern, welche danach per 
Timer ISR an die 7-Segment-Anzeigen ausgegeben werden:
1
void showNumber(uint16_t value) {
2
    digits[0] = value / 100;
3
    value -= digits[0] * 100;
4
    digits[1] = value / 10;
5
    digits[2] = value - (digits[1] * 10);
6
}

Ich habe den Fehler gefunden. Da habe ich doch glatt etwas ganz banales 
übersehen. Ich habe in der Timer ISR geprüft, ob die Ziffer < 9 ist 
(müsste Ziffer < 10 sein). Falls nicht, wird "0" angezeigt.

Besten Dank für die vielen Tipps.

von Sandro L. (sandro_l)


Lesenswert?

Nobody schrieb:
> Das Auslesen von adcValue muss sowieso ATOMIC erfolgen.
> Aber das Problem würde dann nur sporadisch auftreten, wäre also eher
> schwer reproduzierbar.

Um adcValue korrekt auszulesen, muss ich folgende Schritte einbauen:
1. Die globalen Interrupts deaktivieren
2. adcValue auslesen
3. Die globalen Interrupts wieder aktivieren

Habe ich das so richtig verstanden?

: Bearbeitet durch User
von Nobody (Gast)


Lesenswert?

Ja!
Da gibts fertige Macros für...

von Nobody (Gast)


Lesenswert?


von Karl M. (Gast)


Lesenswert?

Hallo,
hast Du hier an eine Cast auf uin32_t gedacht ?
(adcValue * 96)
sonst gibt einen 16Bit Überlauf.

von Karl M. (Gast)


Lesenswert?

Ach ja,

es ist natürlich so, dass der ATtiny26 mit dem avr gcc Paket richtig 
rechnet !

Was bleibt, ist dann der Mensch.

von Peter D. (peda)


Lesenswert?

Karl M. schrieb:
> es ist natürlich so, dass der ATtiny26 mit dem avr gcc Paket richtig
> rechnet !

Der ATtiny26 hat in der Tat einen Hardwarebug, der z.B. ltoa() 
fehlerhaft ausgeben läßt.
Man sollte besser den ATtiny261 nehmen.

von Ingo L. (corrtexx)


Lesenswert?

Karl M. schrieb:
> hast Du hier an eine Cast auf uin32_t gedacht ?
Aber nicht wenn adcValue vom Typ uint32_t ist.
Versuch mal:
1
void showNumber(uint16_t value) {
2
    digits[0] = value / 100;
3
    digits[1] = (value / 10) % 10;
4
    digits[2] = value % 10;
5
}

: Bearbeitet durch User
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.