uint32_t division atmega328p

OP (Firma: GDV-IT) #6858525
Lesenswert?
• ▲
▼
Hallo Forum,

ich habe hier ein kleines Verständnisproblem zur 32-Bit Division.
Ab und zu, in vieleicht 5% der Fälle, kommt es zu fehlerhaften 
Berechnungen wenn ich 2 UINT32_T Zahlen miteinander dividiere. Hier mein 
Auszug aus Code und Log, vieleicht fällt jemanden was dazu ein. 
Dankeschön.

CODE:

declaration:
1
// ADC
2
static uint32_t AdcSamples[] = {0,0,0,0,0,0};
3
static uint32_t AdcSum[] = {0,0,0,0,0,0};
4
static bool CalculationInProgress = false;

interrupt:
1
ISR(TIMER0_OVF_vect) {
2
        // dont count on calculation
3
        if(!CalculationInProgress) {
4
                // read ADC for each sensor input
5
                // and sumarize ADC values
6
                // PINCx = ADCx
7
                for(int t = 0; t < 6; t++) {
8
                        if(SwitchLayout[t] == 3) {
9
                                AdcSamples[t]++;
10
                                AdcSum[t] = AdcSum[t] + (uint32_t) ReadADC(t);
11
                        }
12
                }
13
        }
14
}

main:
1
                        // every minute (64x60)
2
                        if (AdcSamples[t] >= (64*60)) {
3

4
                                CalculationInProgress = true;
5

6
                                uint32_t roundedVal = AdcSum[t] / AdcSamples[t];
7
                                sendGenericUint32Message(9634, AdcSum[t]);
8
                                sendGenericUint32Message(9635, AdcSamples[t]);
9
                                sendGenericUint32Message(9636, roundedVal);
10

11
                                AdcSamples[t] = 0;
12
                                AdcSum[t] = 0;
13

14
                                CalculationInProgress = false;
15

16
                      }

ERKLÄRUNG:
Ein Timer führt alle paar millisekunden die ISR durch und summiert Werte 
u.
Anzahl Zählungen um am ende einen Mittelwert bilden zu können.

RICHTIG:

9634 with len 4  0x00 0x19 0x8f 0x02 
9635 with len 4  0x00 0x00 0x0f 0x00 
9636 with len 4  0x00 0x00 0x01 0xb4 

1675010 / 3840 = 436,2005 => 436

FALSCH:

9634 with len 4  0x00 0x19 0x90 0x4c 
9635 with len 4  0x00 0x00 0x0f 0x00 
9636 with len 4  0x00 0x00 0x01 0x99 

1675340 / 3840 = 436,2864 => 436
aber berechnet wird 409
#6858530
Lesenswert?
• ▲
▼
Christian G. schrieb:
> Hier mein
> Auszug aus Code und Log, vieleicht fällt jemanden was dazu ein.

Wenn bei einer Division ein unerwartetes Ergebnis herauskommt, ist es in 
90% der Fälle so, dass nicht die Zahlen durcheinander verrechnet werden, 
von denen man es glaubt. In den restlichen 10% ist das Ergebnis, das man 
sich ansieht, nicht das Ergebnis der genannten Operation.

Sprich: Guck, ob wirklich im Array in dem Moment das drin steht, was 
erwartet wird.

Franz schrieb:
> "CalculationInProgress" sollte "volatile" sein.

Und mindestens AdcSamples[] oder AdcSum[], ansonsten finden die 
Operationen auf den Arrays nicht unbedingt zwischen den beiden 
Operationen auf das bool statt.

Ansonsten ist der Code äquivalent zu:
1
                                CalculationInProgress = true;
2
                                CalculationInProgress = false;
3
                                uint32_t roundedVal = AdcSum[t] / AdcSamples[t];
4
                                sendGenericUint32Message(9634, AdcSum[t]);
5
                                sendGenericUint32Message(9635, AdcSamples[t]);
6
                                sendGenericUint32Message(9636, roundedVal);
7
                                AdcSamples[t] = 0;
8
                                AdcSum[t] = 0;
OP (Firma: GDV-IT) #6858536
Lesenswert?
• ▲
▼
War mir garnicht bewusst, ich werde es mal so umsetzen und beobachten.

@mitlesa : ich war davon ausgegangen,dass es keine Rolle spielt, da die 
Zahlen für die Division ja die "richtigen" sind
1
uint16_t ReadADC(uint8_t ADCchannel) {
2
  //select ADC channel with safety mask
3
  ADMUX = (ADMUX & 0xF0) | (ADCchannel & 0x0F);
4
  //single conversion mode
5
  ADCSRA |= (1<<ADSC);
6
  // wait until ADC conversion is complete
7
  while( ADCSRA & (1<<ADSC) );
8
  return ADC;
9
}

und natürlich werde ich das auslagern in die main :)
Gast #6858732
Lesenswert?
• ▲
▼
mitlesa schrieb:
> Christian G. schrieb:
>> AdcSum[t] = AdcSum[t] + (uint32_t) ReadADC(t);
>
> ReadADC () ist hier nicht zu sehen, könnte bisschen länger
> dauern als das was man in der ISR tun sollte.

vor Allem, da das ja 6x ausgeführt wird.

Christian G. schrieb:
> while( ADCSRA & (1<<ADSC) );

Funktionsaufrufe können lange dauern, vor Allem, wenn viel auf den Stack 
muss. Außerdem sind solche Schleifen im Interrupt ein no-go.

Schaue dir mal den "ADC conversion complete" interrupt an!

Verkleinere mal die Datentypen, wo es möglich ist. Ist uint32_t wirklich 
nötig???

Außerdem sorgt "CalculationInProgress" dafür, dass sicherlich nicht 
jeder timer interrupt den ADC bedient. Gegebenenfalls kann das auch zu 
Problemen führen.
OP (Firma: GDV-IT) #6858758
Lesenswert?
• ▲
▼
Vielen Dank für eure Mühe,

CalculationInProgress hatte ich nur testhalber eingebaut um genau das zu 
erreichen was volatile scheinbar macht. Dass die ISR nicht in den Werten 
herumschreibt. Bis jetzt scheint es zu laufen, mir waren die Eigenheiten 
von volatile in C nicht bekannt. Ich und C werden eh keine Freunde mehr 
;)

uint32_t ist leider aufgrund der mess- und kalkulationsgenauigkeit 
nötig.

Ich beobachte mal obs noch ausreißer gibt, bis dahin hab ich erstmal 
viel anderen Code zu fixen ...

Antwort schreiben

Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt. Bitte hier nur auf die ursprüngliche Frage antworten, für neue Fragen einen neuen Beitrag erstellen.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.