Forum: Mikrocontroller und Digitale Elektronik 200kOhm Poti an Mega8 auslesen


von NathanIon (Gast)


Lesenswert?

Hi!

Ausgangslage:

-Mega8 Controller
-200kOhm Poti an PC4 (Wiper/Schieber an ADC, ein Ende an GND, andere 
Ende an 5V+)

Problem:

Trotz Tutorial-Studium Verständnisprobleme - da Anfänger. Das Poti soll 
im Controller eine Zahl zwischen 0 und 350 regeln. Die am Poti 
eingestellte Spannung muss also in eine Zahl gewandelt werden. Und ich 
weiß nicht, welche Teile der vielen Beispiele im Internet für meine 
Anwendung gebraucht werden.

Die Abfrage muss permanent laufen, darum darf der ADC glaube ich nicht 
abgeschaltet werden nach der Abfrage.

Als Grudgerüst habe ich das hier stehen:
1
uint16_t ReadChannel(uint8_t mux)
2
{
3
uint8_t i;
4
uint16_t result;
5
ADCSRA = (1<<ADEN) | (1<<ADPS1) | (1<<ADPS0); 
6
7
ADMUX = mux;
8
ADMUX |= (1<<REFS1) | (1<<REFS0);
9
ADCSRA |= (1<<ADSC);
10
while ( ADCSRA & (1<<ADSC) ) {
11
;
12
}
13
result = ADCW; 
14
15
result = 0;
16
for( i=0; i<4; i++ )
17
{
18
ADCSRA |= (1<<ADSC);
19
while ( ADCSRA & (1<<ADSC) ) {
20
; 
21
}
22
result += ADCW; 
23
}
24
ADCSRA &= ~(1<<ADEN);
25
result /= 4;
26
return result;
27
}

Wo muss ich den Eingangsport angeben, also PC4??? Und wie muss die 
Umrechnung aussehen, damit ich auf eine Zahl zwischen 0 und 350 komme???

Danke!!!

von Uhu U. (uhu)


Lesenswert?

Die Umrechnungsfunktion ist eine Geradenfunktion der Bauart

   y = ax + b

Die Gerade schneidet den Nullpunkt, also ist (0, 0) auf der Geraden und 
das b in der Geradengleichung ist 0.

Damit ist bereits ein Punkt bekannt und du brauchst nur noch einen 
zweiten Punkt, um die Zweipunkteform der Geradengleichung aufzustellen.

Vom zweiten Punkt ist ist bekannt, daß y 350 ist. Du brauchst also nur 
noch den x-Wert - in diesem Fall den Ausgabewert des ADC - und du kannst 
die Geradengleichung aufstellen.

http://de.wikipedia.org/wiki/Geradengleichung

von NathanIon (Gast)


Lesenswert?

Plausibel...danke!

Weißt Du auch, wie ich zu dem Punkt komme, an dem man die Formel im Code 
berechnen kann?

Taster abfragen habe ich schon geschafft. Poti noch nicht, da ich im 
Beispielcode keine Anpassungsmöglichkeiten finde. Poti ist korrekt an 
PC4 verschaltet.

von kiara (Gast)


Lesenswert?

Die Funktion gibt dir einen Wert zwischen 0 und 1023 zurück, was du 
ändern solltes ist:
1
//falsch
2
ADMUX |= (1<<REFS1) | (1<<REFS0);
3
// richtig
4
ADMUX |= (1<<REFS0);
Sonst hast du die internen 2,ungradV als Referenzspannung und keine 5 
Volt.
1
uint16_t wert;
2
wert = ReadChannel(4); // PC4 als Channel ausgewählt
3
wert = wert / (1023 / 350); // 1023/350 ist 2,922... Problem!!
Wenn du deinen Wert durch 3 teilst, dann bekommst du 0-341, durch 2 
0-511...

Ziemlich nahe würdest du hiermit an deine 350 kommen ( 0-355 ):
1
uint16_t wert;
2
wert = ReadChannel(4);
3
wert *= 8; 
4
wert /= 23;
Bzw.: du teilst result am ende von ReadChannel() nicht durch 4, sondern 
nimmst result mal 2, spart zeit :-)

von Peter D. (peda)


Lesenswert?

Nochmehr Zeit spart, garnicht zu dividieren:
1
wert = (wert * 44) >>7;
2
if( wert > 350 ) wert = 350;

Peter

von NathanIon (Gast)


Lesenswert?

@kiara

Die Referenzspannung habe ich so gesetzt wie Du es geschrieben hast. Mit 
den anderen Teilen komm ich aber nicht klar weil ich nicht weiß, wo ich 
diese Zeilen positionieren soll.

Und bei

wert = ReadChannel(4); // PC4 als Channel ausgewählt

wird ja nur die Nummer gewählt und nicht die Bank oder? Weil das könnte 
ja auch PD4 sein...

So sieht es jetzt aus:
1
uint16_t ReadChannel(uint8_t mux)
2
{
3
uint8_t i;
4
uint16_t result;
5
ADCSRA = (1<<ADEN) | (1<<ADPS1) | (1<<ADPS0); 
6
ADMUX = mux; 
7
ADMUX |= (1<<REFS0);
8
9
ADCSRA |= (1<<ADSC); 
10
while ( ADCSRA & (1<<ADSC) ) {
11
;
12
}
13
result = ADCW;
14
result = 0;
15
for( i=0; i<4; i++ )
16
{
17
ADCSRA |= (1<<ADSC);
18
while ( ADCSRA & (1<<ADSC) ) {
19
;
20
}
21
result += ADCW;
22
}
23
ADCSRA &= ~(1<<ADEN);
24
result *= 2;
25
return result;
26
}

@Peter, kiara und alle:
Mein Ziel ist eine Zahl die in einer Variable gespeichert wird damit ich 
damit später weiterlernen/-arbeiten kann. Der Code ist aber für mich 
nicht ganz nachvollziehbar. Kann mir jemand aus obigen Informationen es 
so zusammenkopieren, damit es am Ende stimmt - also als gesamtes?

von Uhu U. (uhu)


Lesenswert?

> Mein Ziel ist eine Zahl die in einer Variable gespeichert wird damit ich
> damit später weiterlernen/-arbeiten kann. Der Code ist aber für mich
> nicht ganz nachvollziehbar. Kann mir jemand aus obigen Informationen es
> so zusammenkopieren, damit es am Ende stimmt - also als gesamtes?


Selten so gelacht...

Wie wärs denn, wenn du dir einfach mal die Beschreibung des ADC 
vornimmst und nachsiehst, was die einzelnen Bits in den Controlregistern 
bedeuten?

Das macht zwar ein bischen Arbeit, führt aber doch erstaunlich schnell 
zu netten Aha-Effekten.

von NathanIon (Gast)


Lesenswert?

Das habe ich gemacht. Es tauchen aber keine Sachen auf, die ich wieder 
erkenne. Den Syntax von C verstehe ich, aber bei Potis gibt es mehr als 
high oder low und nach 4 Stunden Google-Suche darf ich doch vorsichtig 
fragen, woran es scheitert?!

ReadChannel(4); - meiner hat aber mehrere Bänke...

Ich mach das nunmal nicht beruflich und ich bin nur auf Probleme mit 
Code-Wirrwar gestoßen. Ein kleines und kompaktes Beispiel, in dem am 
Ende eine regelbare Zahl rauskommt, gabs bei meinen Suchbegriffen nicht. 
Entweder etwas, was nicht funktionierte oder nur eine allgemeine 
Erkärung der Register. Oder undokumentierter Code, mit dem ich als 
Einsteiger nichts anfangen kann.

von kiara (Gast)


Lesenswert?

wert = ReadChannel(4) bedeutet, dass ADC4 als Ananlogeingang ausgewählt 
wird und das ist der Pin PC4 (Datenblatt). Wenn du die Funktion so 
verwendest wie oben darfst du halt nicht vergessen, dass sie den 8fachen 
Analog-wert zurückgibt...
Da ist der code von peda besser, !! result muss hier am Ende von 
ReadChannel durch 4 geteilt werden!!

Also ich würds dann so machen:
1
uint16_t ReadChannel(uint8_t mux)
2
{
3
uint8_t i;
4
uint16_t result;
5
ADCSRA = (1<<ADEN) | (1<<ADPS1) | (1<<ADPS0); 
6
ADMUX = mux; 
7
ADMUX |= (1<<REFS0);
8
9
ADCSRA |= (1<<ADSC); 
10
while ( ADCSRA & (1<<ADSC) ) {
11
;
12
}
13
result = ADCW;
14
result = 0;
15
for( i=0; i<4; i++ )
16
{
17
ADCSRA |= (1<<ADSC);
18
while ( ADCSRA & (1<<ADSC) ) {
19
;
20
}
21
result += ADCW;
22
}
23
ADCSRA &= ~(1<<ADEN);
24
result /= 4;
25
return result;
26
}
27
28
// aufruf (in main() oder sonstwo..
29
30
uint16_t wert;
31
wert = ReadChannel(4);
32
wert = (wert*44) >> 7;
33
if (wert > 350) 
34
{
35
wert = 350;
36
}

von NathanIon (Gast)


Lesenswert?

@kiara

So muss es also aussehen. Ich teste es so schnell wie möglich. Mit dem 
ADC war es dann ein Missverständnis, ich habe nämlich soeine kostenlose 
Eagle-Version installiert und dort weicht die Portbeschreibung vom 
Datenblatt ab.

Danke für die Hilfe und ich melde mich mit dem Ergebnis!

von Durchblicker (Gast)


Lesenswert?

Ist ist auch nicht peinlich, wenn man

uint16_t wert = (uint16_t)((ReadChannel(4) * 22422UL) >> 16);

rechnet, also einen long für erhöhte Genauigkeit konsultiert.

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.