Forum: Mikrocontroller und Digitale Elektronik Analogsignal auf AVR


von Fox (Gast)


Lesenswert?

Hallo Zusammen

Sehr gerne würde ich ein Analogsignal z.B. 0V-5V mit einem 
Mikrokontroller auswerten.

1. Mit welchem Kontroller kann ich das machen? ATMEGA8, ATTINY45 ?
2. Wie kann ich das Signal an den PC schicken und dort auswerten?

Ich habe bereits einen Sensor für Methanol über ein USB-Interface 
angeschlossen und eine Umgebung programmiert. Ist es möglich, z.B. mit 
RS232 oder über USB das Signal auszuwerten? Bis jetzt weiss ich nicht so 
viel über AVR-Controller, aber ich mache Fortschritte!

Danke für die Antwort

Gruss Fox

von 6646 (Gast)


Lesenswert?

1. Alle AVR mit ADC koennen das. Das sind die meisten.
2. Mit einem UART, seriell.

von mb (Gast)


Lesenswert?

Für diese anwendung gibt es einen ganzen haufen möglichkeiten bei den 
avr's

anforderungen: du brauchst einen AVR mit internem ADC (ATTINY2313 hat 
z.B. KEINEN DC), die meisten allerdings verfügen über einen.
du kannst das ganze entweder über rs232, oder usb (rs232->usb wandler, 
da die AVR's über keine interne USB-Anbindung verfügen)

von Fox (Gast)


Lesenswert?

Das tönt ja super!

Wie kann ich den AnalogDigital ansprechen mit C?

Weiss jemand eine Schaltung und Code für die Auswertung auf RS232?
Habe noch ein MAX232 oder so etwas rumliegen. Habe ich mal vor Jahren 
gekauft und nie gebraucht.

Bis jetzt kenne ich nur so grundlegende Elemente wie:
1
#include <avr/io.h>
2
#define F_CPU 1000000UL     /* Quarz mit 1Mhz  */
3
#include <util/delay.h>
4
5
int x = 0;         
6
 
7
int main (void) {           
8
 
9
   DDRB = 0b00110111;             // Pin 3 = Eingang
10
   PORTB = 0b00101111;             // Pin 4 = LOW
11
12
13
14
  while(x==0){
15
16
  if ( !(PINB & (1<<PINB3)) ){
17
18
19
PORTB |= (1<<PB4);
20
x = 1;
21
_delay_ms(200); 
22
23
}
24
}
25
  while(x==1){
26
27
  if ( !(PINB & (1<<PINB3)) ){
28
29
PORTB &= ~(1<<PB4);  
30
x = 0;
31
_delay_ms(200); 
32
}
33
}
34
                          
35
    return main();               
36
}

von Michael H* (Gast)


Lesenswert?


von Fox (Gast)


Lesenswert?

Ok da werde ich nicht ganz schlau, ich habe mit dem Code mühe.

Habe noch etwas cooles gefunden, leider ohne Anleitung:

http://www.youtube.com/watch?v=6a3C3btTSds

von Fox (Gast)


Lesenswert?

Hat jemand ein Beispiel für den ATTiny13/45?

Um den Analogeingang anzusprechen?
Kann man auch ein Analogsignal ausgeben?

Gruss Fox

von Axel D. (axel_jeromin) Benutzerseite


Angehängte Dateien:

Lesenswert?

Hallo Fox,
hier ist ein Beispiel für die Auswertung des ADC2 an einem Tiny 45.

Sammelt immer 30 Werte und bildet den Mittelwert.


Axel

von Michael Wilhelm (Gast)


Lesenswert?

@ Axel,

mach das in deinem Code mit 32 Werten, die Divisionsroutine wird 
schneller Wert = Wert >> 5,

aber nur wenn das RAM reicht. Ansonsten finde ich die Schreibweise:

Register = (1 << Bitname)

wesentlich aussagefähiger. Wer weiß nach einiger zeit noch was z. B.:
Register = 0x84

eigentlich bewirkt.

Außerdem wird dir dann hier auch leichter geholfen, weil nicht jeder die 
Maske überstülpen und nachrechnen muss.

MW

von Fox (Gast)


Lesenswert?

Danke werde ich mal probieren.

von Ulrich P. (uprinz)


Lesenswert?

Falls das mit USB geschehen soll, AT90USB64/128 haben auch einen ADC an 
Bord.

Gruß, Astralix

von Fox (Gast)


Lesenswert?

Ok ich formuliere die Frage mal anders:

Ich würde gerne, ein Signal von 0-5V mit dem AVR verarbeiten. Am 
einfachsten, um das Ganze zu lernen, wäre es sinvoll, einen Eingang 
gleich wieder auf einen Ausgang zu schalten.

z.B. Eingang PB3 0-5V auf Ausgang PB0 0-5V. (falls so möglich)

oder: Eingang PB3 0-5V dabei wird ein eingeschalten 1V PB0, 2V PB1, 3V 
PB2, 4V PB4

Wie muss ich das mit C realisierien?

von Axel D. (axel_jeromin) Benutzerseite


Lesenswert?

vier mal so etwas:


if (value>787)  PORTB |= _BV(1); else PORTB &=~_BV(1);



"value" kann Werte zwischen 0 und 1023 annehmen.

von Fox (Gast)


Lesenswert?

So und jetzt noch Festlegen der Ports:

Das ist ja für IO etc.
1
 #include <avr/io.h>
2
#define F_CPU 9600000UL     /* Quarz mit 9.6Mhz */
3
#include <util/delay.h>
4
         
5
 
6
int main (void) {           
7
 
8
   DDRB = 0b00110111;             // Pin 3 = Eingang
9
   PORTB = 0b00001000;             // Pin 4 = LOW
10
11
int value;
12
13
if (value>787)  PORTB |= _BV(1);
14
15
else PORTB &=~_BV(1);
16
17
18
19
                          
20
    return main();               
21
}

von Fox (Gast)


Lesenswert?

So hab mal ein wenig probiert aber ohne Erfolg. Es fehlt noch eine 
Kleinigkeit: Irgendwie muss ich die Ports korrekt zuordnen. PB3 = 
Eingang Analog --> Wie???
1
  #include <avr/io.h>
2
#define F_CPU 9600000UL     /* Quarz mit 9.6Mhz */
3
#include <util/delay.h>
4
         
5
 
6
int main (void) {           
7
 
8
   DDRB = 0b00111111;             // Pin 3 = Eingang
9
   PORTB = 0b00001000;             // Pin 4 = LOW
10
11
12
ADMUX=0x02;    // PortB4 = ADC2 = Analog Eingang
13
ADCSRB=0x00;
14
ADCSRA=0x84;  // ADEN =1 / Prescaler = 16
15
16
17
18
19
20
int value;
21
22
if (value>787)  PORTB |= _BV(1);
23
24
else PORTB &=~_BV(1);
25
26
27
28
                          
29
    return main();               
30
}

von Fox (Gast)


Lesenswert?

Hallo: Wie muss ich den Analog-Eingang korrekt definieren?

Ist das korrekt? Kann ich direkt 5V auf den Eingang geben?
1
ADMUX=0x02;    // PortB4 = ADC2 = Analog Eingang
2
ADCSRB=0x00;
3
ADCSRA=0x84;  // ADEN =1 / Prescaler = 16

von Karl H. (kbuchegg)


Lesenswert?

Bitte, bitte, bitte

Wir haben hier ein Tutorial geschrieben, dass dir die wichtigsten
Dinge zeigt. Tu dir selbst einen Gefallen und schau da mal
rein. In deinem Pgm ist soviel falsch, dass man gar nicht weiß
wo man mit der Korrektur anfangen soll.

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

von Karl H. (kbuchegg)


Lesenswert?

Wer hat dir denn den Müll gezeigt?
1
int main (void) {
2
3
   ...
4
5
   return main();
6
}

Das läuft in 0 Komma Nix auf einen Stack-Overflow hinaus. Das
blöde dabei ist nur, daß nach einem Stack Overflow so ziemlich
alles oder nichts passieren kann.

Was ist falsch an der guten alten Mainloop, so wie sie Millionen
Programmierer weltweit benutzen?
1
int main() {
2
3
  .... hier die Initialisierungen des Programms, die Hardware
4
  .... wird entsprechend den Anforderungen konfiguriert
5
6
  while( 1 ) {      // die klassische Hauptschleife. Aus ihr kommt
7
                    // der Prozessor bis zum nächsten Reset bzw.
8
                    // Stromabschalten nicht mehr heraus
9
10
    ....   Programmlogik, Hardware abfragen, Ports lesen, Ports auf
11
    ....   neue Ausgabewerte setzen, etc
12
  }
13
}

Richtig. Gar nichts ist damit falsch.

von Fox (Gast)


Lesenswert?

Ich arbeite mich mal durch das ganze Tutorial. Danach werde ich mich 
wieder melden. Zum Teil fehlen mir die Beispiele, es hat zwar viele 
Erklährungen im Tutorial, jedoch kann ich z.B. mit den nichts anfange, 
da ich nicht weiss was man do aktivieren muss etc.

  ADMUX = mux;                      // Kanal waehlen

ja was für ein Kanal etc.

von Karl H. (kbuchegg)


Lesenswert?

Fox wrote:
> Ich arbeite mich mal durch das ganze Tutorial. Danach werde ich mich
> wieder melden. Zum Teil fehlen mir die Beispiele, es hat zwar viele
> Erklährungen im Tutorial, jedoch kann ich z.B. mit den nichts anfange,
> da ich nicht weiss was man do aktivieren muss etc.
>
>   ADMUX = mux;                      // Kanal waehlen
>
> ja was für ein Kanal etc.


Ich vergass:
Das Tutorial alleine ist zuwenig. Du brauchst auch noch das
Datenblatt zu deinem Prozessor. Dieses kriegst du bei www.atmel.com

von Fox (Gast)


Lesenswert?

So habe mich mal in den letzten Tagen hinter das Tutorial gemacht. Komme 
nicht mehr weiter, ich brauche Hilfe. Mit dem unten stehenden Programm, 
möchte ich ein Potentiometer an PinB4 (ADC2) anhängen, welches mir 0-5V 
gibt. Danach möchte ich z.B. ab 2V alle LEDs anfangen zu leuchten.

So schwer ist dass doch nicht? könnt ihr mir helfen bei diesem, für 
euch, trivialen Problem?

Gruss Fox
1
#include <avr/io.h>
2
#define F_CPU 9600000UL     /* Quarz mit 9.6Mhz */
3
#include <util/delay.h>
4
5
6
7
8
int main (void) {           
9
 
10
     DDRB = 0b11101111;             // Pin 4 = Eingang
11
     PORTB = 0b00010000;             // Pin 4 = High
12
13
  ADC = 0b11010000;
14
  ADMUX = 0b01010000;
15
16
17
18
while(1) {               
19
20
21
int value;
22
23
if (value>200)  PORTB |= ( (1<<PB0) | (1<<PB1) | (1<<PB2) | (1<<PB3) );
24
25
else PORTB &= ~( (1<<PB0) | (1<<PB1) | (1<<PB2) | (1<<PB3) );
26
27
28
29
}
30
                          
31
    return 0;               
32
}

von Fox (Gast)


Lesenswert?

Versuch ist mit ATTiny13/45 !

von Kai G. (runtimeterror)


Lesenswert?

Ich habe jetzt gerade die ADC-Initialisierung nicht im Kopf, aber wie 
genau hast du das Poti angeschlossen?

Eine Fehlerbeschreibung wie "geht nicht" kann zwischen "alle LEDs 
bleiben aus" und "LEDs samt Board brennen lichterloh" alles bedeuten.

Funktionieren die LEDs prinzipiell?

Der Kommentar hier ist irreführend:
statt:
  PORTB = 0b00010000; // Pin 4 = High
besser:
  PORTB |= 1 << PB4; // Pin 4 - internen Pull-Up aktivieren

Das wäre treffender, da du den Pin zuvor ja als Eingang konfiguriert 
hast.

Hier nochmal einen Vorschlag für ein lesbares Quelltext-Layout mit 
einigen Fragezeichen als Hausaufgabe ;) :
1
#include <avr/io.h>
2
#define F_CPU 9600000UL     /* Quarz mit 9.6Mhz */
3
#include <util/delay.h>
4
5
int main (void)
6
{
7
    int value; // Was ist das für eine Variable? Wer befüllt die?
8
9
    DDRB = 0b11101111;             // Pin 4 = Eingang
10
    PORTB = 0b00010000;             // Pin 4 = High
11
12
    // Hier wäre ein Kommentar angebracht, was du damit
13
    // eigentlich bezwecken willst:
14
    ADC = 0b11010000;
15
    ADMUX = 0b01010000;
16
17
    while(1)
18
    {
19
        // Wo wird value verändert?
20
        if (value > 200)
21
        {
22
            // Was soll diese Zeile tun?
23
            PORTB |= ((1 << PB0) | (1 << PB1) | (1 << PB2) | (1 << PB3));
24
        }
25
        else
26
        {
27
            // Was soll diese Zeile tun?
28
            PORTB &= ~((1 << PB0) | (1 << PB1) | (1 << PB2) | (1 << PB3));
29
        }
30
    }
31
32
    return 0;               
33
}

von Johannes M. (johnny-m)


Lesenswert?

Auweia. Ich frage mich, ob das Tutorial tatsächlich dermaßen schlecht 
verständlich ist, dass man nach tagelangem Studium desselben so was 
schreibt...

Erstens ist es kontraproduktiv bzw. tödlich, an einem Analog-Eingang den 
Pull-Up einzuschalten. Zweitens muss man auch irgendwo auf das Ende der 
Wandlung warten und dann die Ergebnisregister auslesen! Und wie das geht 
steht alles im Tutorial, inklusive Beispielcode!

von Fox (Gast)


Lesenswert?

Hallo!

Ja die LEDs kann ich anschalten und abschalten. Diese habe ich sogar 
über einem Transistor gesteuert. Alles bestens.

Den Poti habe ich bei GND und +5V angehägt. Der Variable Pin geht an den 
Eingang ADC2. Dort hatte ich vorher einen Schalter und konnte auf 
Knofdruck die LEDs anschalten und wieder abschalten.

Das mit der Variable value war für mich auch unklar, diese habe ich von 
Axel übernommen. Am Besten wäre hier eine Direktzuweisung. Wie PORTB |= 
( (1<<PB0) oder so. Kenne aber den Befehl nicht.

Stimmen die Zusweisungen für ADC und ADMUX braucht ich noch etwas mehr?

von Johannes M. (johnny-m)


Lesenswert?

Fox wrote:
> Das mit der Variable value war für mich auch unklar, diese habe ich von
> Axel übernommen. Am Besten wäre hier eine Direktzuweisung. Wie PORTB |=
> ( (1<<PB0) oder so. Kenne aber den Befehl nicht.
"Befehle" gibt es in C nicht, nur Anweisungen und Operatoren. Und wie 
das mit den Bitzugriffen funktioniert ist ebenfalls im 
AVR-GCC-Tutorial, das Du ja angeblich durchgearbeitet hast, 
erläutert!

von Fox (Gast)


Lesenswert?

Wie wäre es einfach mal mit einer Lösung?!?

Du kannst nicht erwarten, dass ich bei allem draus komme, für mich ist 
das neu. Ein kleines einfaches Beispiel wäre hier sinnvoller als die 
vielen Verweise auf das Tutorial. Ich kann es auch noch 10x durchlesen 
und komme nicht weiter.

von Gast (Gast)


Lesenswert?

@Fox
beratungsresitent? :-)

1. wer "befuellt" value?
2. ...

von Fox (Gast)


Lesenswert?

value = übernommen von Axel wie schon geschrieben, wäre es besser eine 
Direktanweisung, von welcher ich nicht weiss wie sie aussieht.




DDRB muss gesetzt werden ob Eingang oder Ausgang. Dabei von links nach
rechts PB0 bis PB6. 1= Ausgang; 0=Eingang.
PORTB definiert ob der Eingang an den interne Pullup geschalten wird
oder nicht.
!(PINB & (1<<PB4)) = Abfragen von Taster High or Low
Pin zu High: PORTB |= (1<<PB4);
Pin zu LowL: PORTB &= ~(1<<PB4);

So mit dem kann man nämlich arbeiten.

Jetzt wie sieht das für den ADC aus? Eventuell könnte mir jemand ein
kleines Beispielprogramm senden? Ein Analoger Eingang schaltet ein
Ausgang ab einem gewissen Bitwert. Ist das so viel verlangt?

von yalu (Gast)


Lesenswert?

Ich versuch's auch mal und beziehe mich auf deinen Versauch vom 22.4.
um 17:29:

>     DDRB = 0b11101111;             // Pin 4 = Eingang

Bis zu dieser Zeile ist noch alles ok.

>  PORTB = 0b00010000;             // Pin 4 = High

Diese Zeile aktiviert den internen Pullup-Widerstand von PB4. Das
möchtest du sicher nicht, weil er den Spannungswert des
(wahrscheinlich als Spannungsteiler) geschalteten Potentiometers
verfälscht. Lass die Zeile einfach weg.

>  ADC = 0b11010000;

Das ADC-Register ist read-only. Da steht hinterher der gemessene Wert
drin. Lass die Zeile ebenfalls weg.

>  ADMUX = 0b01010000;

ADMUX enthält in den oberen 4 Bits u.a. die Auswahl der
Referenzspannung, in den unteren 4 Bits den A/D-Kanal. Du willst als
Referenzspannung VCC (5V) und den Kanal 2 (ADC2, PB4). Das ergibt

  ADMUX = 0b00000010;

oder einfach
1
  ADMUX = 2; // Kanalnummer

Das hattest du weiter oben aber schon mal richtig.

Jetzt fehlt noch die Initialisierung des ADC über das ADCSRA-Register:
1
  ADCSRA = 1<<ADEN | 1<<ADPS2 | 1<<ADPS1 | 1<<ADPS0;

ADEN aktiviert den ADC. Über die ADPSx-Bits wird die ADC-Taktfrequenz
gesetzt, in diesem Fall auf 1/128 der Controller-Taktfrequenz (für den
Anfang lieber etwas zu langsam als zu schnell).

> while(1) {
> int value;
> if (value>200)  PORTB |= ( (1<<PB0) | (1<<PB1) | (1<<PB2) | (1<<PB3) );

Wo wird value beschrieben? Nirgends, deswegen steht Müll in der
Variable.

Das mit der Endlosschleife ist aber schon mal eine gute Idee, da du ja
periodisch den Spannungswert einlesen möchtest.
1
  while(1) {
2
    int value;

Erst einmal muss der ADC gestartet werden, dies geschieht durch setzen
des ADSC-Bits:
1
    ADCSRA |= ADSC;

Jetzt dauert es eine Weile bis der ADC den Spannungswert umgesetzt
hat. Wenn er fertig ist, setzt er das ADSC-Bit wieder zurück. So lange
lassen wir den Prozessor einfach im Kreis drehen:
1
    while(ADCSRA & ADSC);

Nach Verlassen der Schleife kann das Ergebnis gelesen werden:

    value = ADC;

Jetzt (und erst jetzt) kannst du value auswerten, bspw. mit der
if-Abfrage in deinem Programm:
1
    if (value>200)
2
      PORTB |= ( (1<<PB0) | (1<<PB1) | (1<<PB2) | (1<<PB3) );
3
    else
4
      PORTB &= ~( (1<<PB0) | (1<<PB1) | (1<<PB2) | (1<<PB3) );
5
  }

Ich habe das oben Geschriebene nicht getestet, deswegen funktioniert
es möglicherweise nicht sofort. Zumindest zeigt es die grundsätzlichen
Schritte, die man für den Betrieb des ADC braucht.

von Karl H. (kbuchegg)


Lesenswert?

Fox wrote:
> value = übernommen von Axel wie schon geschrieben, wäre es besser eine
> Direktanweisung, von welcher ich nicht weiss wie sie aussieht.

Was ist so schwer daran das so zu schreiben
1
int main()
2
{
3
  DDRB = 0b11101111;
4
5
  while( 1 ) {
6
    if( ReadChannel( 2 ) > 200 )
7
      PORTB |= ( 1 << PB0 ) | ( 1 << PB1 ) | ( 1 << PB2 ) | ( 1 << PB3 );
8
    else
9
      PORTB &= ~( ( 1 << PB0 ) | ( 1 << PB1 ) | ( 1 << PB2 ) | ( 1 << PB3 ) );
10
  }
11
}

und die ReadChannel Funktion übernimmst du erst mal aus
dem Tutorial so wie sie dort steht. Mglw. gibt es dann dort
noch eine Fehlermeldung weil die Register oder Steuerbits anders
heissen. Das ist aber nichts was sich nicht mit dem Datenblatt
des Tiny aus der Welt schaffen ließe.

von Fox (Gast)


Lesenswert?

So klappt noch nicht ganz.

Habe herausgefunden, dass der Controller im while(ADCSRA & ADSC) hängen 
bleibt. Wenn ich das ausschalten, geht es auch noch nicht. Irgend etwas 
stimmt noch nicht.
1
#include <avr/io.h>
2
#define F_CPU 9600000UL     /* Quarz mit 9.6Mhz */
3
#include <util/delay.h>
4
5
6
/* Dieses Programm wandelt das analoge Signal auf PB4 (ADC2) in ein digitales um. Bei einem Bitwert z.B. ab 200 werden die Ausgänge
7
  PB0 bis PB3 aktiviert */
8
9
int main (void) {           
10
 
11
     DDRB = 0b11101111;                           // Pin 4 = Eingang
12
13
  ADMUX = 2;                          // auch ADMUX = 2; (Kanalnummer) oder ADMUX = 0b00000010;
14
  ADCSRA = 1<<ADEN | 1<<ADPS2 | 1<<ADPS1 | 1<<ADPS0;    // Initialisierung des ADC über das ADCSRA-Register
15
                      
16
17
18
19
while(1) {               
20
21
int value;
22
value = ADC;
23
ADCSRA |= ADSC;                       // ADC starten
24
25
while(ADCSRA & ADSC){                    // ADC wird gestartet
26
PORTB |= (1<<PB0);
27
}
28
29
30
31
if (value>200)  PORTB |= ( (1<<PB0) | (1<<PB1) | (1<<PB2) | (1<<PB3) );
32
33
else PORTB &= ~( (1<<PB0) | (1<<PB1) | (1<<PB2) | (1<<PB3) );
34
35
36
37
}
38
                          
39
    return 0;               
40
}

von holger (Gast)


Lesenswert?

>value = ADC;
>ADCSRA |= ADSC;                       // ADC starten

Klasse Idee ! 'value' lesen bevor der ADC gestartet wurde ;)

von Fox (Gast)


Lesenswert?

lol!!!

Ok habs geändert, klappt trotzdem noch nicht.
1
int value;
2
3
ADCSRA |= ADSC;                       // ADC starten
4
while(ADCSRA & ADSC){                    // ADC wird gestartet
5
PORTB |= (1<<PB0);
6
}
7
8
value = ADC;

von Karl H. (kbuchegg)


Lesenswert?

yalu wrote:
>
> Nach Verlassen der Schleife kann das Ergebnis gelesen werden:
>
>     value = ADC;

Man mag mich steinigen, aber:
Müsste das nicht

    value = ADCW;

heissen.
Wenn ich mich recht erinnere, ist das die große Ausnahme
unter den 16 Bit Registern, weil es eine Assembler Instruktion
namens ADC gibt.

von Karl H. (kbuchegg)


Lesenswert?

Fox wrote:
> lol!!!
>
> Ok habs geändert, klappt trotzdem noch nicht.
>
>
1
> int value;
2
> 
3
> ADCSRA |= ADSC;                       // ADC starten
4
> while(ADCSRA & ADSC){                    // ADC wird gestartet
5
> PORTB |= (1<<PB0);
6
> }
7
> 
8
> value = ADC;
9
>

Aus dem Tutorial (und um Variablennamen nachgebessert)
1
  ADCSRA |= (1<<ADSC);              // eine ADC-Wandlung 
2
  while ( ADCSRA & (1<<ADSC) ) {
3
     ;     // auf Abschluss der Konvertierung warten 
4
  }
5
  value = ADCW;

Sieh dir die Unterschiede genau an!

von Michael U. (amiga)


Lesenswert?

Hallo,

ohne auf den Rest einzugehen: Du hast offenbar ein Problem, Dir die 
Abläufe vorzustellen.
Spiele einfach AVR und gehe Dein Programm schrittweise durch.

Dabei sollte Dir z.B. auffallen, daß die Folge

value = ADC;
ADCSRA |= ADSC;                       // ADC starten

einfach Unsinn ist.

Du liest ERST den Wert aus dem ADC und startest DANACH erst die 
Wandlung.

Falls Du Auto fährst: fährst Du auch ERST los und startest DANACH den 
Motor?

Gruß aus Berlin
Michael

von Gast (Gast)


Lesenswert?

1
 ADCSRA |= (1<<ADSC); 
2
3
 while (ADCSRA & (1<<ADSC))
4
{
5
6
}

von Mr K. (mrk)


Lesenswert?

ich habe noch nie in C programmiert (bin noch mit Assembler beschäftigt) 
aber ich kann dir sagen wo dein Fehler liegt
1
....
2
ADCSRA |= ADSC;                     // ADC starten
3
4
while(ADCSRA & ADSC){                    // ADC wird gestartet
5
PORTB |= (1<<PB0);
6
}
7
8
value = ADC;
9
...

value kannst du erst dann zuweisen wenn der ADC mit der Wandlung fertig 
ist. d.h. erst wenn du aus der while(ADCSRA & ADSC){... raus bist.

Grundsätzlich funktioniert adc so:
1.ADC konfigurieren
2.ADC starten
3. warten bis der ADC mit der Wandlung fertig ist
4. wert auslesen

von Karl H. (kbuchegg)


Lesenswert?

Mr K. wrote:
> ich habe noch nie in C programmiert (bin noch mit Assembler beschäftigt)
> aber ich kann dir sagen wo dein Fehler liegt
>
>
1
> ....
2
> ADCSRA |= ADSC;                     // ADC starten
3
> 
4
> while(ADCSRA & ADSC){                    // ADC wird gestartet
5
> PORTB |= (1<<PB0);
6
> }
7
> 
8
> value = ADC;
9
> ...
10
>
>

Nur der Vollständigkeit halber (Fox scheint den einfachen Weg zu
gehen und alles zu übernehmen was hier so gepostet wird ohne
darüber nachzudenken ob das so stimmen kann):
Bits werden in einem Register anders gesetzt. ADSC ist der
Name eines Bits. Folgerichtig muss es zb. heissen

  ADCSRA |= ( 1 << ADSC );                // ADC starten

Steht aber auch alles in Form einer getesteten Funktion im
Tutorial, welches Fox angeblich ja studiert hat.

von Fox (Gast)


Lesenswert?

Danke für die Hilfe. Ich weiss ich bin am ausprobieren durch die 
Verzweiflung, ich beschäftige mich jetzt mit diesem einfachen Problem 
seit Tagen.

Es klappt jetzt so weit gut. Habe das Programm noch erweitert, dass es 
stufenweise die LEDs einschaltet. Das Problem ist allerdings, dass bei 
den ersten 3 IF die LEDs nur schwach leuchten. Die 4 Leuchtet dafür 
voll.

Also bis value 1000 = LEDs flackern, ab 1000 LED leuchten hell

Habe ich nur ein IF dann leuchten die LEDs hell.

[c]

#include <avr/io.h>
#define F_CPU 9600000UL     /* Quarz mit 9.6Mhz */
#include <util/delay.h>


/* Dieses Programm wandelt das analoge Signal auf PB4 (ADC2) in ein 
digitales um. Bei einem Bitwert z.B. ab 200 werden die Ausgänge
  PB0 bis PB3 aktiviert */

int main (void) {

     DDRB = 0b11101111;                           // Pin 4 = Eingang


  ADMUX = 2;                          // auch ADMUX = 2; (Kanalnummer) 
oder ADMUX = 0b00000010;
  ADCSRA = 1<<ADEN | 1<<ADPS2 | 1<<ADPS1 | 1<<ADPS0;    // 
Initialisierung des ADC über das ADCSRA-Register




while(1) {

int value;

  ADCSRA |= (1<<ADSC);              // eine ADC-Wandlung
  while ( ADCSRA & (1<<ADSC) ) {

     ;     // auf Abschluss der Konvertierung warten
  }

  value = ADCW;



if (value<400)  PORTB |= ( (1<<PB0) );

else PORTB &= ~( (1<<PB0) | (1<<PB1) | (1<<PB2) | (1<<PB3) );


if (value>400 & value<800)  PORTB |= ( (1<<PB0) | (1<<PB1)  );

else PORTB &= ~( (1<<PB0) | (1<<PB1) | (1<<PB2) | (1<<PB3) );


if (value>800 & value<1000)  PORTB |= ( (1<<PB0) | (1<<PB1) | (1<<PB2) 
);

else PORTB &= ~( (1<<PB0) | (1<<PB1) | (1<<PB2) | (1<<PB3) );


if (value>1000)  PORTB |= ( (1<<PB0) | (1<<PB1)  | (1<<PB3) | (1<<PB4) 
);

else PORTB &= ~( (1<<PB0) | (1<<PB1) | (1<<PB2) | (1<<PB3) );


}

    return 0;
}

[c]

von Mr K. (mrk)


Lesenswert?

Hab mir nicht alles geschaut weil als C-Laie hätte ich sowieso nicht 
alle Fehler finden können aber solche Fehler wie sie Fox ständig am 
fliesenden Band produziert zu finden braucht man noch nicht mal 
irgendeine Programmiersprache zu können.

@Fox: Selber denken/lernen/verstehen wird man dir hier nicht abnehmen 
können. Hab auch gerade vor zwei tagen mit ADC angefangen aber dank des 
tutorial hier + Datenblätter + Forum Beiträge + google konnte ich viel 
lernen und verstehen.

von Karl H. (kbuchegg)


Lesenswert?

Fox wrote:

> Es klappt jetzt so weit gut. Habe das Programm noch erweitert, dass es
> stufenweise die LEDs einschaltet. Das Problem ist allerdings, dass bei
> den ersten 3 IF die LEDs nur schwach leuchten. Die 4 Leuchtet dafür
> voll.

Dann denk dir einen Wert aus, zb 900 und geh mal durch deine
if in Gedanken durch. Achte darauf ob der if oder das else genommen
wird und überlege, welche Konsequenzen sich daraus für deinen
Port ergeben.
Achte insbesondere darauf, wann und wo ein Portbit ausgeschaltet
wird nur um im nächsten if dann wieder eingeschaltet zu werden
(deine Led blinkt dann sehr schnell)

Insebsondere kannst du dir ja mal überlegen, warum eigentlich
der if, der den Wert gegen 1000 abprüft sich am Pin 0 zu schaffen
macht. Pin 0 ist doch bei allen Werten die größer als 200 sind
sowieso immer eingeschaltet.
1
  PORTB = 1 << PB0;        // PB0 ist immer ein. Egal bei welcher Spannung
2
3
  if( value > 400 )        // wenn größer als 400, also zb auch bei 900
4
    PORTB |= ( 1 << PB1 ); // dann brennt PB1 auf jeden Fall
5
  else
6
    PORTB &= ~( 1 << PB1 );
7
8
  if( value > 800 )
9
    PORTB |= ( 1 << PB2 );
10
  else
11
    PORTB &= ( 1 << PB2 );
12
13
  if( value > 1000 )
14
    PORTB |= ( 1 << PB3 );
15
  else
16
    PORTB &= ( 1 << PB3 );

von Mr K. (mrk)


Lesenswert?

korigiert mich wenn ich falsch liege aber bei 400 würde die else der 
ersten if angenommen also alle LED an. ich denke das ist nicht gewollt 
oder?
1
if (value<400)  PORTB |= ( (1<<PB0) );
2
3
else PORTB &= ~( (1<<PB0) | (1<<PB1) | (1<<PB2) | (1<<PB3) );
4
5
6
if (value>400 & value<800)  PORTB |= ( (1<<PB0) | (1<<PB1)  );
7
8
else PORTB &= ~( (1<<PB0) | (1<<PB1) | (1<<PB2) | (1<<PB3) );
9
10
11
if (value>800 & value<1000)  PORTB |= ( (1<<PB0) | (1<<PB1) | (1<<PB2)
12
);
13
14
else PORTB &= ~( (1<<PB0) | (1<<PB1) | (1<<PB2) | (1<<PB3) );
15
16
17
if (value>1000)  PORTB |= ( (1<<PB0) | (1<<PB1)  | (1<<PB3) | (1<<PB4)
18
);
19
20
else PORTB &= ~( (1<<PB0) | (1<<PB1) | (1<<PB2) | (1<<PB3) );

von Fox (Gast)


Lesenswert?

So Leute, ich entschuldige mich für die Stürmerei und Ungeduld von mir. 
Trozdem habe ich sehr viel gelernt. Mein Programm konnte ich jetzt 
selber fertig stellen. Ich kann es jetzt nach belieben anpassen, 
verändern etc.

Jetzt funktioniert es so wie es sollte! Ich Danke euch nochmals ganz 
herzlich für die Hilfe.

Hier mein Programm! Für alle die, die mal Probleme mit ADC haben.  Hier 
ist ein Code der funktioniert.
1
#include <avr/io.h>
2
#define F_CPU 9600000UL     /* Quarz mit 9.6Mhz */
3
#include <util/delay.h>
4
5
6
/* Dieses Programm wandelt das analoge Signal auf PB4 (ADC2) in ein digitales um. Bei einem Bitwert z.B. ab 200 werden die Ausgänge
7
  PB0 bis PB3 aktiviert */
8
9
int main (void) {           
10
 
11
     DDRB = 0b11101111;                           // Pin 4 = Eingang
12
13
14
  ADMUX = 2;                          // auch ADMUX = 2; (Kanalnummer) oder ADMUX = 0b00000010;
15
  ADCSRA = 1<<ADEN | 1<<ADPS2 | 1<<ADPS1 | 1<<ADPS0;    // Initialisierung des ADC über das ADCSRA-Register
16
                      
17
18
19
20
while(1) {               
21
22
int value;
23
24
  ADCSRA |= (1<<ADSC);                          // eine ADC-Wandlung 
25
  while ( ADCSRA & (1<<ADSC) ) {                // auf Abschluss der Konvertierung warten 
26
  }
27
28
  value = ADCW;
29
30
31
32
while (value<400){
33
  PORTB |= ( (1<<PB0) );
34
  ADCSRA |= (1<<ADSC);                          // eine ADC-Wandlung 
35
  while ( ADCSRA & (1<<ADSC) ) {                // auf Abschluss der Konvertierung warten 
36
  value = ADCW;
37
  }
38
}
39
40
41
while (value>400 & value<800){  
42
  PORTB |= ( (1<<PB0) | (1<<PB1)  );
43
  ADCSRA |= (1<<ADSC);                          // eine ADC-Wandlung 
44
  while ( ADCSRA & (1<<ADSC) ) {                // auf Abschluss der Konvertierung warten 
45
  value = ADCW;
46
  }
47
}
48
49
50
while (value>800 & value<1000){  
51
  PORTB |= ( (1<<PB0) | (1<<PB1) | (1<<PB2) );
52
  ADCSRA |= (1<<ADSC);                          // eine ADC-Wandlung 
53
  while ( ADCSRA & (1<<ADSC) ) {                // auf Abschluss der Konvertierung warten 
54
  value = ADCW;
55
  }
56
}
57
58
59
while (value>1000){  
60
   PORTB |= ( (1<<PB0) | (1<<PB1)  | (1<<PB2) | (1<<PB3) );
61
  ADCSRA |= (1<<ADSC);                          // eine ADC-Wandlung 
62
  while ( ADCSRA & (1<<ADSC) ) {                // auf Abschluss der Konvertierung warten 
63
  value = ADCW;
64
  }
65
}
66
67
68
PORTB &= ~( (1<<PB0) | (1<<PB1) | (1<<PB2) | (1<<PB3) );
69
70
}
71
                          
72
    return 0;               
73
}

von Karl H. (kbuchegg)


Lesenswert?

Mr K. wrote:

>
1
> if (value<400)  PORTB |= ( (1<<PB0) );
2
> 
3
> else PORTB &= ~( (1<<PB0) | (1<<PB1) | (1<<PB2) | (1<<PB3) );
4
> 
5
> 
6
> if (value>400 & value<800)  PORTB |= ( (1<<PB0) | (1<<PB1)  );
7
> 
8
> else PORTB &= ~( (1<<PB0) | (1<<PB1) | (1<<PB2) | (1<<PB3) );
9
> 
10
> 
11
> if (value>800 & value<1000)  PORTB |= ( (1<<PB0) | (1<<PB1) | (1<<PB2)
12
> );
13
> 
14
> else PORTB &= ~( (1<<PB0) | (1<<PB1) | (1<<PB2) | (1<<PB3) );
15
> 
16
> 
17
> if (value>1000)  PORTB |= ( (1<<PB0) | (1<<PB1)  | (1<<PB3) | (1<<PB4)
18
> );
19
> 
20
> else PORTB &= ~( (1<<PB0) | (1<<PB1) | (1<<PB2) | (1<<PB3) );
21
>

Das würde ganauso flimmern.
Ein Wert von 900 für value würde hier
1
  if (value>400 & value<800)  PORTB |= ( (1<<PB0) | (1<<PB1)  );
2
  else PORTB &= ~( (1<<PB0) | (1<<PB1) | (1<<PB2) | (1<<PB3) );

dafür sorgen, dass erst mal alle LED ausgeschaltet werden, denn
900 ist ja nicht größer als 400 und kleiner als 800. Also wird
hier der else genommen und der schaltet alles aus.

Nur damit kurz darauf
1
  if (value>800 & value<1000)  PORTB |= ( (1<<PB0) | (1<<PB1) | (1<<PB2) );
2
  else PORTB &= ~( (1<<PB0) | (1<<PB1) | (1<<PB2) | (1<<PB3) );
hier die LEDs wieder eingeschaltet werden.

Die Leds werden also ständig ein / aus geschaltet, was natürlich
dunkler erscheint als wie wenn die LED ständig durchleuchtet.

Siehe den Beitrag weiter oben von mir, wie man sowas richtig
macht. Der Schlüssel liegt darin, dass man ab einem bestimmten
Wert immer nur eine einzige Led zu oder abschaltet. 900 ist
größer als 400, also ist die PB1 einzuschalten. 900 ist aber auch
größer als 800, also ist auch die PB2 noch dazuzuschalten. Aber
900 ist nicht größer als 1000, also ist die PB3 abzuschalten.
Daraus ergibt sich ganz von alleine, dass bei einem Wert von 900
die PB1 und die PB2 zu leuchten haben aber nicht PB3.

Code von hier:
Beitrag "Re: Analogsignal auf AVR"

von Johannes M. (johnny-m)


Lesenswert?

Mr K. wrote:
>
1
> if (value>400 & value<800) //...
2
>
Für solche Vergleiche gibt es die logischen Operatoren. Die bitweisen 
funktionieren hier zwar auch, ist aber schlechter Stil und kann schnell 
zu Fehlern führen. Besser
1
if (value>400 && value<800) //...

von Mr K. (mrk)


Lesenswert?

warum werde ich zitiert? die Code habe ich selber von Fox zitiert um auf 
etwas hinzuweisen.

ich hätte die if-abfrage auch anders gestaltet.

von Karl H. (kbuchegg)


Lesenswert?

Mr K. wrote:
> warum werde ich zitiert?

Oh Verzeihung.
Ich dachte, das wäre eine Antwort auf seine Frage warum einige
Led dunkler leuchten als andere.

Um ehrlich zu sein, hab ich mir den Code nicht näher angesehen
(weder sein Original, noch deine Modifikation). In dem Moment
in dem da auf einen Bereich abgefragt wurde, war schon alles
klar, dass es hier irgendwelche Probleme geben würde.

> ich hätte die if-abfrage auch anders gestaltet.
Wahrscheinlich nicht nur du und ich :-)

von Fox (Gast)


Lesenswert?

Darf ich noch etwas fragen? Das werde ich aber mir selber erarbeiten und 
nur im äussersten Notfall nachfragen:

Kann ich auch ein Analoges Signal ausgeben?

von Fox (Gast)


Lesenswert?

Noch eine Frage:

Wenn ich den ATTiny z.B. mit 3V betreibe, kann ich eine Spannung 
anhängen an ADC, welche 5V hat?

von Michael H* (Gast)


Lesenswert?

Fox wrote:
> Kann ich auch ein Analoges Signal ausgeben?
dazu brauchst du einen DAC.

> Wenn ich den ATTiny z.B. mit 3V betreibe, kann ich eine Spannung
> anhängen an ADC, welche 5V hat?
nein, spannungsteilen. deine zu messende spannung darf die 
referenzspannung nicht übersteigen.

von Kai G. (runtimeterror)


Lesenswert?

>> Kann ich auch ein Analoges Signal ausgeben?
>dazu brauchst du einen DAC.

Im einfachsten Fall sowas hier:
http://de.wikipedia.org/wiki/R2R

>> Wenn ich den ATTiny z.B. mit 3V betreibe, kann ich eine Spannung
>> anhängen an ADC, welche 5V hat?
>nein, spannungsteilen. deine zu messende spannung darf die
>referenzspannung nicht übersteigen.

Ich glaube, die darf überschritten werden aber nicht >Vcc... steht aber 
irgendwo im Forum ausgiebig diskutiert und zur Not wie immer im 
Datenblatt.

von Michael H* (Gast)


Lesenswert?

Kai Giebeler wrote:
>>nein, spannungsteilen. deine zu messende spannung darf die
>>referenzspannung nicht übersteigen.
>
> Ich glaube, die darf überschritten werden aber nicht >Vcc... steht aber
> irgendwo im Forum ausgiebig diskutiert und zur Not wie immer im
> Datenblatt.
man kann die versorgungsspannung als referenzspannung wählen - daher, 
ja. wie weit man über die versorgungsspannung hinaus anlegen darf, steht 
wohl im datenblatt irgendwo bei den dc-characteristics.

von yalu (Gast)


Lesenswert?

Ist jetzt zwar schon hunderte von Beiträgen her, trotzdem noch eine
kleine Anmerkung:

Karl heinz Buchegger schrieb:

>>     value = ADC;
>
> Man mag mich steinigen, aber:
> Müsste das nicht
>
>     value = ADCW;
>
> heissen.

Der offizielle Name des Doppelregisters laut Datenblatt ist ADC. Wie
von Karl heinz bereits erwähnt, führt dies in Assemblerprogrammen
(aber nur diesen) zum Konflikt mit dem gleichlautenden
Prozessorbefehl, weswegen der ADCW als Workaround eingeführt wurde.

Die Headerfiles in der AVR-Libc sind so gestrickt, dass für
Assemblerprogramme nur ADCW, für C-Programme sowohl ADC als auch ADCW
als Synonyme definiert werden. In C-Programmen würde ich die
offizielle Variante ADC vorziehen. Dies vermeidet nebenbei auch Fragen
der Art

  "Wo kann ich die ganzen Registerdefinitionen nachlesen die nicht im
  DB stehen. z.B. das ADCH und ADCL = ADCW ist."

wie vor kurzem hier im Forum gestellt.

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.