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
staticcharsendString[4];
2
staticuint16_tadcval=0;// adc
3
staticuint16_tadcvalOld=0;// adc
4
5
intmain(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
staticuint16_ttimerInterruptTicks=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
voidADC_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_tADC_Read(uint8_tchannel)
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
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.
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?
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?
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.
> 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.
// 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 PotiDas Poti, also unbestimmt ein Poti.
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.