zwei werte in if-abfrage vergleichen, trotz unterschiedlichen werten wird if-abfrage nicht ausgelöst

Gast #5824555
Hallo,
ich habe an adc0 (atmega328p) einen Poti und möchte mir gerne den adc 
über uart ausgeben lassen. Aber nur, wenn sich dieser ändert. Dazu lese 
ich diesen im Timerinterrupt (jede 1ms) ein und speichere den Wert in 
adcval. In meiner While-schlife vergleiche ich den eingelesenen Wert mit 
den vorigen wert, welcher in adcvalOld steht. Wenn diese unterschiedlich 
sind, soll mir der wert über uart gesendet werden und der aktuelle wert 
wird in adcvalOld gespeichert, damit beim nächsten aufruf der wert nicht 
erneut gesendet wird. Allerdings wird mir der Wert nie gesendet. Nehme 
ich die Abfrage raus, bekomme ich den wert dauernd gesendet. Dieser 
ändert sich auch, wenn ich am poti drehe. Ich habe aber keine erklärung, 
warum das so ist. Der wert muss ja unterschiedlich sein. Daher weiß ich 
nicht, warum nichts gesendet wird. Habt ihr einen rat und könnt was 
sehen, woran es hapern könnte?
1
static char sendString[4];
2
static uint16_t adcval = 0; // adc
3
static uint16_t adcvalOld = 0; // adc
4

5
int main(void)
6
{
7
  timer0_init(&TCCR0A,  ~(1 << WGM00) | (1 << WGM01),
8
          &TCCR0B,  ((1 << CS00) | (1 << CS01)) & ~(1 << CS02),
9
          &OCR0A,   (250 - 1),
10
          &TIMSK0,  (1 << OCIE0A),
11
          &TCNT0, 0);
12

13

14
  uart_init(&UBRR0H, &UBRR0L, MYUBRR,/* Set baud rate */
15
      &UCSR0B, ((1<<RXCIE0)|(1<<RXEN0)|(1<<TXEN0)), /* Enable receiver and transmitter */
16
      &UCSR0C, ((1<<UCSZ00)|(1<<UCSZ01)));  /* Set frame format: 8data, 1stop bit */
17

18
  ADC_Init();
19

20
  sei();
21
  while(1)
22
  {
23
    if(adcval != adcvalOld)
24
    {
25
      itoa(adcval, sendString, 10);
26
      uart_sendString(sendString);
27
      uart_transmit('\n');
28
      adcvalOld = adcval;
29
    }
30

31
  }
32
}
33

34
ISR (TIMER0_COMPA_vect)
35
{
36
  static uint16_t timerInterruptTicks = 0;  // Anzahl der Aufrufe der ISR
37

38
  timerInterruptTicks++;
39
  adcval = ADC_Read(0);
40

41
  // Eine Sekune vorbei
42
  if(timerInterruptTicks>=1000)
43
  {
44
    timerInterruptTicks = 0;
45
  }
46
}
47

48
void ADC_Init(void)
49
{
50
  // die Versorgungsspannung AVcc als Referenz wählen:
51
  ADMUX = (1<<REFS0);
52
  // oder interne Referenzspannung als Referenz für den ADC wählen:
53
  // ADMUX = (1<<REFS1) | (1<<REFS0);
54

55
  // Bit ADFR ("free running") in ADCSRA steht beim Einschalten
56
  // schon auf 0, also single conversion
57
  ADCSRA = (1<<ADPS1) | (1<<ADPS0);     // Frequenzvorteiler
58
  ADCSRA |= (1<<ADEN);                  // ADC aktivieren
59

60
  /* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man liest
61
     also einen Wert und verwirft diesen, um den ADC "warmlaufen zu lassen" */
62

63
  ADCSRA |= (1<<ADSC);                  // eine ADC-Wandlung
64
  while (ADCSRA & (1<<ADSC) ) {         // auf Abschluss der Konvertierung warten
65
  }
66
  /* ADCW muss einmal gelesen werden, sonst wird Ergebnis der nächsten
67
     Wandlung nicht übernommen. */
68
  (void) ADCW;
69
}
70

71
/* ADC Einzelmessung */
72
uint16_t ADC_Read( uint8_t channel )
73
{
74
  // Kanal waehlen, ohne andere Bits zu beeinflußen
75
  ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);
76
  ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
77
  while (ADCSRA & (1<<ADSC) ) {   // auf Abschluss der Konvertierung warten
78
  }
79
  return ADCW;                    // ADC auslesen und zurückgeben
80
}
Gast
Die beiden Variablen musst Du "volatile" deklarieren, weil sie in der 
ISR verändert werden.
sonst optimiert Dein Compiler die weg und ersetzt sie einfach mit einer 
statischen 0.
Gast
Das mit dem Volatile hat gestimmt. Das hat das Problem behoben.
aber mit statisch ist die variable doch auch global und in allen 
funktionen bekannt oder nicht?
Gast
Martin B. schrieb:
> Das einzige, was mir auffällt, ist dass adcval nicht als volatile
> deklariert ist.

Es könnte auch auffallen, sass sich ADC Werte eigentlich immer bei jeder 
Abfrage um +/-1 ändern, und daher der ganze Ansatz, nur Dinge tun zu 
wollen wenn man am Poti dreht, Schwachsinn ist. Man müsste wenigstens 
spezifizieren wie weit man innerhalb welcher Zeit dreht.
Johannes schrieb:
> Das mit dem Volatile hat gestimmt. Das hat das Problem behoben.
> aber mit statisch ist die variable doch auch global und in allen
> funktionen bekannt oder nicht?

Ja, bekannt ist es, aber das reicht nicht. Der Optimierer könnte auf die 
Idee kommen adcval vor der while(1) Schleife in ein Register zu laden, 
um die Schleife zu beschleunigen. Die if Abfage in der Schleife bekommt 
dann nicht mit, dass sich adcval geändert hat. Das siehst Du im 
Assembleroutput.
Gast
> Es könnte auch auffallen, sass sich ADC Werte eigentlich immer bei jeder
> Abfrage um +/-1 ändern, und daher der ganze Ansatz, nur Dinge tun zu
> wollen wenn man am Poti dreht, Schwachsinn ist.

Das sind die Grundlagen, die man sich als Anfänger erarbeiten muß *). 
Auf das Wackeln des ADC-Wertes ist wohl am Anfang jeder hereingefallen.
1
VoltageRead1 = analogRead (1);
2
if ((VoltageRead1 - lastVoltage1) > 1 || (lastVoltage1 - VoltageRead1) > 1) {
3
  // pruefen auf <> 1 weil ungleich nicht klappt, vermutlich Rauschen.
4
  Serial.print("Port A1 = ");
5
  Serial.println(VoltageRead1);
6
  lastVoltage1 = VoltageRead1;
7
  }

In der Hauptschleife steuere ich noch eine LED an, ob das Poti rechts 
oder links der Mitte steht, nicht ohne Grund mit zwei unterschiedlichen 
Abfragewerten:
1
if (analogRead (1) > 520) {
2
  digitalWrite(9, HIGH);
3
  }
4
if (analogRead (1) < 500) {
5
  digitalWrite(9, LOW);
6
  }

@Johannes: Das ist keine Kritik, eher im Gegenteil - erstmal einfache 
Funktionen durchspielen, bevor Dich ein reales Projet zur Verzweifelung 
treibt.

Johannes schrieb:
> einen Poti
Das Poti, also unbestimmt ein Poti.

Antwort schreiben

Eine eigene Frage? Dieser Thread ist über 7 Jahre alt. Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.

Neuen Thread starten

Eine Lösung, Ergänzung oder Rückmeldung zu diesem Thread? Dann antworte hier.

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.