Forum: Mikrocontroller und Digitale Elektronik Atmega8-16 ADC von Poti


von Michael N. (garril)


Lesenswert?

Hallo,

für mein aktuelles Programm möchte ich über ein Potentiometer die 
Helligkeit von LEDs einstellen.
Die LEDs lassen sich mit Werten von 0 bis 100 per Software PWM ansteuern 
(0=ganz aus, 50=ca. mittelhell, 100=maximal hell).

Die eine Seite des Potis hängt an 5V die andere an GND. Der veränderbare 
Kontakt des Potis geht dann an PC0 am Atmega8-16.

Hier mein Programm:
1
#include <avr/io.h>
2
#include <util/delay.h>
3
#define an(port,b) (port) |= (1<<(b))
4
#define aus(port,b) (port) &= ~(1<<(b))
5
//Beispielaufruf: an(PORTC,PB1);
6
7
int main () {
8
  // Helligkeit in %
9
  int pb1_soll=50;
10
  int pb2_soll=100;
11
  int counter=0;
12
13
  // Ausgänge schalten
14
  DDRB|=(1<<PB1);
15
  DDRB|=(1<<PB2);
16
  DDRB|=(1<<PB3);
17
18
   an(PORTB,PB1);
19
  an(PORTB,PB2);
20
21
  //ADC einlesen
22
  int value;
23
  ADMUX=0x00;
24
  ADCSRA=0x80;
25
26
  while (1) {
27
28
    //ADC Potis auslesen
29
    value=0;
30
    ADCSRA |= (1<<ADSC);
31
    while (ADCSRA & (1<<ADSC) ) {}
32
    value=ADCW;
33
//Hier tritt das Problem auf. value ist immer 1023!
34
    if (value<1024) {
35
          pb2_soll=1;
36
//Schalte LED auf 1% Helligkeit
37
    }
38
      else {
39
          pb2_soll=50;
40
//Schalte LED auf 50% Helligkeit
41
    }
42
43
    counter++;
44
    if (counter>=pb1_soll) {
45
      aus(PORTB,PB1);
46
    }
47
    if (counter>=pb2_soll) {
48
      aus(PORTB,PB2);
49
    }
50
      if (counter>=100) {
51
      counter=0;
52
      an(PORTB,PB1);
53
      an(PORTB,PB2);
54
    }
55
  }
56
}

Ich möchte eigentlich nur die Variablen in Abhängigkeit des Potis 
ändern.
In Value steht aber immer 1023 (schätze mal das hängt mit 
Dualzahl->Dezimalzahl zusammen).

Wie komm ich an den tatsächlichen Wert?

Habe auch eben nochmal mit dem Multimeter nachgemessen, Spannung ändert 
sich wie gewünscht

von hp-freund (Gast)


Lesenswert?

Wirf mal ein Blick auf die ADC Initialisierung:

http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Nutzung_des_ADC

von Michael N. (garril)


Lesenswert?

Hätte ich das noch nicht angeschaut, hätte ich mich garnicht hier 
gemeldet.

Trotzdem vielen Dank für den Hinweis, ich hab mir das jetzt nochmal 
durchgeschaut (hatte vorhin schon viele verschiedene Programmversionen 
probiert von denen einige auf dem Code des verlinkten Artikel 
basierten).

Habe es jetzt tatsächlich hinbekommen. War garnicht so schwer (brauchte 
anscheinend nur mal eine Pause, programmiere hier schon seit Mittag...)

Danke erstmal soweit, ich denke jetzt bekomme ichs hin^^

edit:ganz am Ende (wenn es fast ganz hell ist) macht die Helligkeit 
plötzlich einen Sprung auf ganz hell. Woran liegt das?
Wenn das Poti fast auf maximal ist, ist die LED eigentlich noch zu 
dunkel. Und dann springt sie plötzlich auf ganz hell
1
#include <avr/io.h>
2
#include <util/delay.h>
3
#define an(port,b) (port) |= (1<<(b))
4
#define aus(port,b) (port) &= ~(1<<(b))
5
//Beispielaufruf: an(PORTC,PB1);
6
7
int main () {
8
  // Helligkeit in %
9
  int pb1_soll = 100;
10
  int pb2_soll = 100;
11
  int counter=0;
12
13
  // Ausgänge schalten
14
  DDRB|=(1<<PB1);
15
  DDRB|=(1<<PB2);
16
  DDRB|=(1<<PB3);
17
18
  //ADC-Variable initialisieren
19
  uint16_t value;
20
  uint16_t result;
21
22
  //1=Poti für PB1, ...
23
  int akt_poti=1;
24
  int anzahl_potis=2;
25
  //Poti an PC0 initialisieren
26
  ADMUX = (0<<REFS1) | (1<<REFS0);  // ADC Ref auf Avcc, PC0 gewählt, normale Formatierung
27
  ADCSRA= (1<<ADEN) | (1<<ADSC) | (1<<ADFR) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);
28
29
  while (1) {    
30
31
    counter++;
32
    if (counter>=pb1_soll/2) {
33
      aus(PORTB,PB1);
34
    }
35
    if (counter>=pb2_soll/2) {
36
      aus(PORTB,PB2);
37
    }
38
      if (counter>=50) {
39
40
      //ADC aktuelles Poti auslesen und neues Poti initialisieren
41
      if (akt_poti==1) {
42
        value=ADCW;  //Wert in Variable schreiben
43
        pb1_soll=value/10;
44
        akt_poti++;
45
        //Nächstes Poti beobachten
46
        ADMUX = (0<<REFS1) | (1<<REFS0) | (0<<MUX2) | (0<<MUX1) | (1<<MUX0);  // ADC Ref auf Avcc, PC1 gewählt, normale Formatierung
47
        ADCSRA= (1<<ADEN) | (0<<ADFR) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);
48
        while (ADCSRA & (1<<ADSC) ) {}  // auf Abschluss der Konvertierung warten
49
        result = ADCW;
50
        ADCSRA|= (1<<ADSC);
51
        while (ADCSRA & (1<<ADSC) ) {}  // auf Abschluss der Konvertierung warten
52
      }
53
      if (akt_poti==2) {
54
        value=ADCW;  //Wert in Variable schreiben
55
        pb2_soll=value/10;
56
        akt_poti++;
57
        //Nächstes Poti beobachten
58
        ADMUX = (0<<REFS1) | (1<<REFS0);  // ADC Ref auf Avcc, PC0 gewählt, normale Formatierung
59
        ADCSRA= (1<<ADEN) | (0<<ADFR) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);
60
        while (ADCSRA & (1<<ADSC) ) {}  // auf Abschluss der Konvertierung warten
61
        result = ADCW;
62
        ADCSRA|= (1<<ADSC);
63
        while (ADCSRA & (1<<ADSC) ) {}  // auf Abschluss der Konvertierung warten
64
      }
65
      if (akt_poti>anzahl_potis) {
66
        akt_poti=1;
67
      }
68
      if (pb1_soll>1) {
69
        an(PORTB,PB1);
70
      }
71
      if (pb2_soll>1) {
72
        an(PORTB,PB2);
73
      }
74
    counter=0;
75
    }
76
  }
77
}

von Neuling (Gast)


Lesenswert?

Ich grabe jetzt mal dieses alte Thema wieder aus, denn mir stellt sich 
das gleiche Problem/ Frage. Wenn ich die Spannung messen will von einem 
Poti, dann steigt diese nicht linear an sondern progressiv. Wieso und 
was kann man da machen?

Meine Idee ist es,dass wenn das Poti in der mitte steht auch die hälfte 
der Spannung gemessen wird. Hänge ich das Poti vom MC ab, so ist das der 
Fall.

Schaltplan und Code kann ich gerne auch beilegen wenn nötig.

von InFo (Gast)


Lesenswert?

Poti mit linearem Spannungsverlauf benutzen.
Ausgleichsfunktion implementieren.

von guest (Gast)


Lesenswert?

Neuling schrieb:
> Ich grabe jetzt mal dieses alte Thema wieder aus, denn mir stellt sich
> das gleiche Problem/ Frage. Wenn ich die Spannung messen will von einem
> Poti, dann steigt diese nicht linear an sondern progressiv. Wieso und
> was kann man da machen?
>
> Meine Idee ist es,dass wenn das Poti in der mitte steht auch die hälfte
> der Spannung gemessen wird. Hänge ich das Poti vom MC ab, so ist das der
> Fall.

Da Du nichts zu Deinem Poti schreibst kann man nur raten daß es das 
falsche ist.

1) Potis arbeiten nicht notwendigerweise linear.
2) Der Eingang eines ADC hat einen endlichen Widerstand. Und was 
passiert wenn man Widerstände parrallel schaltet solltest Du wissen.

von Patrick J. (ho-bit-hun-ter)


Lesenswert?

Hi

Wenn es hier auch um den Sprung 'am Ende' geht - dort könnte, banal 
gesprochen, einfach die Widerstandsbahn 'am Ende' sein, also dort, wo 
Diese mit dem Anschlußpin irgendwie verbunden ist - Da muß die Bahn ja 
irgendwie anders aussehen als Da, wo kein Pin angeschlossen ist - reiner 
Schuß ins Blaue, hatte noch kein Poti offen.

MfG

PS: nur die letzten 4 Beiträge sind aktuell, davor ist seit 6 Jahren 
eher ruhig ;)

von Neuling (Gast)


Lesenswert?

Wow, dachte nicht das so schnell so viele Antworten kommen :D
Danke euch allen.

So, ich habe in der Zwischenzeit auch etwas weitergeforscht und habe die 
schaltung mal etwas abgeändert. Bisher habe ich einen selber 
programmierten Binär/Dezimalwandler benutzt und nachdem ich diesen mal 
ausgelassen habe und direkt aus dem ADCH Register gelesen habe war der 
Verlauf korrekt.

Poti ist ein Lineares 10kOhm. Spannungsabgriff in der mitte.

Nun geht's an die Fehlersuche im Code :D

von Neuling (Gast)


Lesenswert?

So, der Fehler ist gefunden... aber noch nicht geklärt. Weshalb ergibt 
diese if Abfrage nicht 1? Sollte doch so sein, nicht? Bei 3-5 ergibt es 
nicht 1 sonst schon.

Ich ging jetzt davon aus das man den Integer "11111111" wie ein Register 
verwenden kann.
1
int main()
2
{
3
  if (11111111 & (1<<3))
4
  {
5
    printf("lol");    //printf ist hier nur zum testen eingebaut
6
  }
7
}

von Neuling (Gast)


Lesenswert?

So, ist nun auch geklärt, hätte nicht unbedingt fragen müssen :O

Danke trotzdem an alle.

Meine Erkenntniss: Bitoperator "&" arbeitet mit der Binärzahl, d.H. der 
Operator "&" hat meine Zahl "11111111" in die Binärzahl umgewandelt und 
mit dieser gearbeitet. Korrekt wäre gewesen wenn ich 255 eingeschrieben 
hätte (ergibt Binärzahl 11111111) oder einfach als Binärzahl definiert 
hätte (0b vorne rangeschrieben)

So da das nun auch geklärt ist wird es nun Zeit zu schlafen (endlich :D)

von Neuling (Gast)


Lesenswert?

Abschlussbericht:

Der Fehler lag doch woanders... ich habe zu Beginn in Register DDRB Pin 
1-3 folgendermassen als Ausgang gesetzt:
1
DDRB |= (111<<1);
Richtig wäre gewesen:
1
DDRB |= (1<<1) | (1<<2) | (1<<3);

Wusste gar nicht das das so nicht funktioniert und sogar Einfluss auf 
den ADC hat.... naja, wieder mal was dazugelernt. Mein Programm 
funktioniert jetzt (jeay, nach 4h Fehlersuche hat es doch noch geklappt 
:D)

von jz23 (Gast)


Lesenswert?

Neuling schrieb:
> DDRB |= (111<<1);

Richtig wäre 0b111<<1. Woher soll der Compiler sonst wissen, dass du 
nicht Hundertelf meinst?

von Patrick J. (ho-bit-hun-ter)


Lesenswert?

Hi

Neuling schrieb:
> int main()
> {
>   if (11111111 & (1<<3))
>   {
>     printf("lol");    //printf ist hier nur zum testen eingebaut
>   }
> }

Denke, Das wird vom Compiler direkt wegoptimiert, da sowohl die Zahl 
(11111111, 0xA98AC7, 0b101010011000101011000111) wie auch 1<<3 (=0b1000 
oder 0x08 oder eben 8) beim Compilieren bekannt sind und die Ver-UND-ung 
NULL ergibt - bar gesparter Speicherplatz.

1<<(0,1,2,6,7,9,11,15,16,19,21,23) würde dagegen das 'lol' zur Ausgabe 
bringen - aber ebenfalls ohne die IF-Abfrage, da Diese, wie zuvor, ja 
bereits beim Compilieren bekannt ist.

Etwas weniger abweisend würde sich in dem Code-Beispiel ein 'foo' 
machen, das 'LOL' lässt erkennen, daß Du noch keine Ahnung hast, was Du 
da machst und Das wohl auch noch lustig findest.
'foo' oder 'bar' sind hier die üblichen Begriffe, wenn man 'irgendwas' 
meint.



MfG

: Bearbeitet durch User
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.