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

Gast #5824555
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
}
Gast #5824589
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.
#5824592
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.
Gast #5824726
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren