Forum: Mikrocontroller und Digitale Elektronik zwei werte in if-abfrage vergleichen, trotz unterschiedlichen werten wird if-abfrage nicht ausgelöst


von Johannes (Gast)


Lesenswert?

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
}

von Müchi (Gast)


Lesenswert?

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.

von Müchi (Gast)


Lesenswert?

bzw...

zumindest die adcval muss volatile sein.

von Martin B. (ratazong)


Lesenswert?

Das einzige, was mir auffällt, ist dass adcval nicht als volatile 
deklariert ist. Das sollte er aber sicherheitshalber, damit der 
Optimierer nichts wegoptimiert.

Hast Du Dir mal den Assembleroutput angeguckt?

von Johannes (Gast)


Lesenswert?

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?

von MaWin (Gast)


Lesenswert?

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.

von Martin B. (ratazong)


Lesenswert?

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.

von Manfred (Gast)


Lesenswert?

> 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.

von Heiko L. (zer0)


Lesenswert?

Was auch noch zu beachten wäre, ist, dass dort 16-bit Werte verglichen 
werden, und dieser Vergleich auf 8-bit Micros nicht atomar ist. Kann 
also auch zu falschen Ergebnissen führen.

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.