www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Analogsignal auf AVR


Autor: Fox (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: 6646 (Gast)
Datum:

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

Autor: mb (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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)

Autor: Fox (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
#include <avr/io.h>
#define F_CPU 1000000UL     /* Quarz mit 1Mhz  */
#include <util/delay.h>

int x = 0;         
 
int main (void) {           
 
   DDRB = 0b00110111;             // Pin 3 = Eingang
   PORTB = 0b00101111;             // Pin 4 = LOW



  while(x==0){

  if ( !(PINB & (1<<PINB3)) ){


PORTB |= (1<<PB4);
x = 1;
_delay_ms(200); 

}
}
  while(x==1){

  if ( !(PINB & (1<<PINB3)) ){

PORTB &= ~(1<<PB4);  
x = 0;
_delay_ms(200); 
}
}
                          
    return main();               
}

Autor: Michael H* (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert

Autor: Fox (Gast)
Datum:

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

Habe noch etwas cooles gefunden, leider ohne Anleitung:

Youtube-Video "MAX1247 + ATTiny2313 = USB-ADC"

Autor: Fox (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hat jemand ein Beispiel für den ATTiny13/45?

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

Gruss Fox

Autor: Axel Düsendieb (axel_jeromin) Benutzerseite
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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

Autor: Michael Wilhelm (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Fox (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke werde ich mal probieren.

Autor: Ulrich P. (uprinz)
Datum:

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

Gruß, Astralix

Autor: Fox (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Axel Düsendieb (axel_jeromin) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
vier mal so etwas:


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



"value" kann Werte zwischen 0 und 1023 annehmen.

Autor: Fox (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So und jetzt noch Festlegen der Ports:

Das ist ja für IO etc.
 #include <avr/io.h>
#define F_CPU 9600000UL     /* Quarz mit 9.6Mhz */
#include <util/delay.h>
         
 
int main (void) {           
 
   DDRB = 0b00110111;             // Pin 3 = Eingang
   PORTB = 0b00001000;             // Pin 4 = LOW

int value;

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

else PORTB &=~_BV(1);



                          
    return main();               
}
 

Autor: Fox (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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???
  #include <avr/io.h>
#define F_CPU 9600000UL     /* Quarz mit 9.6Mhz */
#include <util/delay.h>
         
 
int main (void) {           
 
   DDRB = 0b00111111;             // Pin 3 = Eingang
   PORTB = 0b00001000;             // Pin 4 = LOW


ADMUX=0x02;    // PortB4 = ADC2 = Analog Eingang
ADCSRB=0x00;
ADCSRA=0x84;  // ADEN =1 / Prescaler = 16





int value;

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

else PORTB &=~_BV(1);



                          
    return main();               
}

Autor: Fox (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo: Wie muss ich den Analog-Eingang korrekt definieren?

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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wer hat dir denn den Müll gezeigt?
int main (void) {

   ...

   return main();
}

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?
int main() {

  .... hier die Initialisierungen des Programms, die Hardware
  .... wird entsprechend den Anforderungen konfiguriert

  while( 1 ) {      // die klassische Hauptschleife. Aus ihr kommt
                    // der Prozessor bis zum nächsten Reset bzw.
                    // Stromabschalten nicht mehr heraus

    ....   Programmlogik, Hardware abfragen, Ports lesen, Ports auf
    ....   neue Ausgabewerte setzen, etc
  }
}

Richtig. Gar nichts ist damit falsch.

Autor: Fox (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Fox (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

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




int main (void) {           
 
     DDRB = 0b11101111;             // Pin 4 = Eingang
     PORTB = 0b00010000;             // Pin 4 = High

  ADC = 0b11010000;
  ADMUX = 0b01010000;



while(1) {               


int value;

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

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



}
                          
    return 0;               
}


Autor: Fox (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Versuch ist mit ATTiny13/45 !

Autor: Kai G. (runtimeterror)
Datum:

Bewertung
0 lesenswert
nicht 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 ;) :
#include <avr/io.h>
#define F_CPU 9600000UL     /* Quarz mit 9.6Mhz */
#include <util/delay.h>

int main (void)
{
    int value; // Was ist das für eine Variable? Wer befüllt die?

    DDRB = 0b11101111;             // Pin 4 = Eingang
    PORTB = 0b00010000;             // Pin 4 = High

    // Hier wäre ein Kommentar angebracht, was du damit
    // eigentlich bezwecken willst:
    ADC = 0b11010000;
    ADMUX = 0b01010000;

    while(1)
    {
        // Wo wird value verändert?
        if (value > 200)
        {
            // Was soll diese Zeile tun?
            PORTB |= ((1 << PB0) | (1 << PB1) | (1 << PB2) | (1 << PB3));
        }
        else
        {
            // Was soll diese Zeile tun?
            PORTB &= ~((1 << PB0) | (1 << PB1) | (1 << PB2) | (1 << PB3));
        }
    }

    return 0;               
}

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht 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!

Autor: Fox (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht 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!

Autor: Fox (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Fox
beratungsresitent? :-)

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

Autor: Fox (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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
  ADMUX = 2; // Kanalnummer

Das hattest du weiter oben aber schon mal richtig.

Jetzt fehlt noch die Initialisierung des ADC über das ADCSRA-Register:
  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.
  while(1) {
    int value;

Erst einmal muss der ADC gestartet werden, dies geschieht durch setzen
des ADSC-Bits:
    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:
    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:
    if (value>200)
      PORTB |= ( (1<<PB0) | (1<<PB1) | (1<<PB2) | (1<<PB3) );
    else
      PORTB &= ~( (1<<PB0) | (1<<PB1) | (1<<PB2) | (1<<PB3) );
  }

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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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

int main()
{
  DDRB = 0b11101111;

  while( 1 ) {
    if( ReadChannel( 2 ) > 200 )
      PORTB |= ( 1 << PB0 ) | ( 1 << PB1 ) | ( 1 << PB2 ) | ( 1 << PB3 );
    else
      PORTB &= ~( ( 1 << PB0 ) | ( 1 << PB1 ) | ( 1 << PB2 ) | ( 1 << PB3 ) );
  }
}

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.

Autor: Fox (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.
#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;
value = ADC;
ADCSRA |= ADSC;                       // ADC starten

while(ADCSRA & ADSC){                    // ADC wird gestartet
PORTB |= (1<<PB0);
}



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

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



}
                          
    return 0;               
}

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>value = ADC;
>ADCSRA |= ADSC;                       // ADC starten

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

Autor: Fox (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
lol!!!

Ok habs geändert, klappt trotzdem noch nicht.
int value;

ADCSRA |= ADSC;                       // ADC starten
while(ADCSRA & ADSC){                    // ADC wird gestartet
PORTB |= (1<<PB0);
}

value = ADC;

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Fox wrote:
> lol!!!
>
> Ok habs geändert, klappt trotzdem noch nicht.
>
>
> int value;
> 
> ADCSRA |= ADSC;                       // ADC starten
> while(ADCSRA & ADSC){                    // ADC wird gestartet
> PORTB |= (1<<PB0);
> }
> 
> value = ADC;
> 

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

Sieh dir die Unterschiede genau an!

Autor: Michael U. (amiga)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
 ADCSRA |= (1<<ADSC); 

 while (ADCSRA & (1<<ADSC))
{

}

Autor: Mr K. (mrk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ich habe noch nie in C programmiert (bin noch mit Assembler beschäftigt) 
aber ich kann dir sagen wo dein Fehler liegt
....
ADCSRA |= ADSC;                     // ADC starten

while(ADCSRA & ADSC){                    // ADC wird gestartet
PORTB |= (1<<PB0);
}

value = ADC;
...

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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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
>
>
> ....
> ADCSRA |= ADSC;                     // ADC starten
> 
> while(ADCSRA & ADSC){                    // ADC wird gestartet
> PORTB |= (1<<PB0);
> }
> 
> value = ADC;
> ...
> 
>

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.

Autor: Fox (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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]

Autor: Mr K. (mrk)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.
  PORTB = 1 << PB0;        // PB0 ist immer ein. Egal bei welcher Spannung

  if( value > 400 )        // wenn größer als 400, also zb auch bei 900
    PORTB |= ( 1 << PB1 ); // dann brennt PB1 auf jeden Fall
  else
    PORTB &= ~( 1 << PB1 );

  if( value > 800 )
    PORTB |= ( 1 << PB2 );
  else
    PORTB &= ( 1 << PB2 );

  if( value > 1000 )
    PORTB |= ( 1 << PB3 );
  else
    PORTB &= ( 1 << PB3 );

Autor: Mr K. (mrk)
Datum:

Bewertung
0 lesenswert
nicht 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?
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) );

Autor: Fox (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.
#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;



while (value<400){
  PORTB |= ( (1<<PB0) );
  ADCSRA |= (1<<ADSC);                          // eine ADC-Wandlung 
  while ( ADCSRA & (1<<ADSC) ) {                // auf Abschluss der Konvertierung warten 
  value = ADCW;
  }
}


while (value>400 & value<800){  
  PORTB |= ( (1<<PB0) | (1<<PB1)  );
  ADCSRA |= (1<<ADSC);                          // eine ADC-Wandlung 
  while ( ADCSRA & (1<<ADSC) ) {                // auf Abschluss der Konvertierung warten 
  value = ADCW;
  }
}


while (value>800 & value<1000){  
  PORTB |= ( (1<<PB0) | (1<<PB1) | (1<<PB2) );
  ADCSRA |= (1<<ADSC);                          // eine ADC-Wandlung 
  while ( ADCSRA & (1<<ADSC) ) {                // auf Abschluss der Konvertierung warten 
  value = ADCW;
  }
}


while (value>1000){  
   PORTB |= ( (1<<PB0) | (1<<PB1)  | (1<<PB2) | (1<<PB3) );
  ADCSRA |= (1<<ADSC);                          // eine ADC-Wandlung 
  while ( ADCSRA & (1<<ADSC) ) {                // auf Abschluss der Konvertierung warten 
  value = ADCW;
  }
}


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

}
                          
    return 0;               
}

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mr K. wrote:

>
> 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) );
> 

Das würde ganauso flimmern.
Ein Wert von 900 für value würde hier
  if (value>400 & value<800)  PORTB |= ( (1<<PB0) | (1<<PB1)  );
  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
  if (value>800 & value<1000)  PORTB |= ( (1<<PB0) | (1<<PB1) | (1<<PB2) );
  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"

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mr K. wrote:
>
> if (value>400 & value<800) //...
> 
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
if (value>400 && value<800) //...

Autor: Mr K. (mrk)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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 :-)

Autor: Fox (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Fox (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Noch eine Frage:

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

Autor: Michael H* (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Kai G. (runtimeterror)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Michael H* (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.