Forum: Mikrocontroller und Digitale Elektronik Temperatur an LEDs ausgeben


von miki (Gast)


Lesenswert?

Hallo

Der Temepratursensor ist an einem 10Bit A/DW angeschlossen 2^10=1024.
Die Temperatur rechne ich im Programm.

Ich will eien Bereich von 10-100° an die LEDs ausgeben. Und je nach 
Temepratur sollen die LEDs leuchten.

switch  (  ??Was kommt hier genau?? Temperaturwert? )
{
12.5°  PORTB = 0b00000001; break;
25°    PORTB = 0b00000011; break;
37.5°  PORTB = 0b00000111; break;
50°    PORTB = 0b00001111; break;
62.5°  PORTB = 0b00011111; break;
75°    PORTB = 0b00111111; break;
87.5°  PORTB = 0b01111111; break;
100°   PORTB = 0b11111111; break;
}

Das erste LED darf aber leuchten wenn die Temp zwischen 12.5 und 25 
liegt.
Wie stell ich diesen Bereich?

So wie jetzt dargestellt ist würde das LEd nur dann leuchten wenn die 
TEmp genau 12.5° ist,.. oder ?

vielen dank

von Karl H. (kbuchegg)


Lesenswert?

miki schrieb:

> switch  (  ??Was kommt hier genau?? Temperaturwert? )

Das worauf sich deine Fallunterscheidung begründet.
Sollte eigentlich logisch sein.

> {
> 12.5°  PORTB = 0b00000001; break;
> 25°    PORTB = 0b00000011; break;
> 37.5°  PORTB = 0b00000111; break;
> 50°    PORTB = 0b00001111; break;
> 62.5°  PORTB = 0b00011111; break;
> 75°    PORTB = 0b00111111; break;
> 87.5°  PORTB = 0b01111111; break;
> 100°   PORTB = 0b11111111; break;
> }
>
> Das erste LED darf aber leuchten wenn die Temp zwischen 12.5 und 25
> liegt.
> Wie stell ich diesen Bereich?

Mit einem switch ... gar nicht

> So wie jetzt dargestellt ist würde das LEd nur dann leuchten wenn die
> TEmp genau 12.5° ist,.. oder ?

Genau.
Und deshalb benutzt du hier das falsche Werkzeug.

Übrigens:
Du musst die Temperatur ja gar nicht ausrechnen.
Du kannst ja auch zurückrechnen:
Wenn meine Entscheidungstemperatur 12.5 Grad beträgt, dann muss der ADC 
dazu den Wert x liefern. Ist meine Entscheidungstempertatur 25 Grad, 
dann bedeutet das, das der ADC y liefern würde. Liefert daher der ADC 
einen Wert, der zwischen x und y liegt, dann liegt auch die Temperatur 
zwischen 12.5 Grad und 25 Grad und daher muss dann LED xyz leuchten.

Du rechnest also nicht im Programm die Temperatur aus sondern schnappst 
dir deinen Taschenrechner und rechnest dir die zu den Temperaturgrenzen 
gehörenden ADC Werte aus und benutzt die als Entscheidungskriterium 
welche LED leuchten sollen. Das ändert allerdings nichts daran, dass ein 
switch immer noch das falsche Werkzeug ist. Es erspart dir nur im 
Programm einiges an Herumrechnerei.

Was anderes wäre es natürlich, wenn du die Temperatur zb auf einem LCD 
im Klartext ausgeben musst. Dann musst du sie ausrechnen lassen, weil du 
ja den Zahlenwert benötigst. Aber in deiner jetzigen Anwendung brauchst 
du den Zahlenwert ja gar nicht.

von kosi (Gast)


Lesenswert?

Hallo!

Kannst du es nicht über IF-Abfragen realisieren?

IF Temp<=12,5°, THEN LED1, ELSEIF Temp>12,5°,Temp<25° THEN 
LED1+LED2.....

Es fällt mir aber gerade ein, dass du eine gewisse Hysterese 
berücksichtigen müsstest, für den Fall dass der Wert zB genau um 12,5° 
schwankt. Hättest Du keine Hysterese, würde die LED entsprechend deiner 
Wandlungsrate flimmern.

von Karl H. (kbuchegg)


Lesenswert?

> 12.5°  PORTB = 0b00000001; break;
> 25°    PORTB = 0b00000011; break;
> 37.5°  PORTB = 0b00000111; break;
> 50°    PORTB = 0b00001111; break;
> 62.5°  PORTB = 0b00011111; break;
> 75°    PORTB = 0b00111111; break;
> 87.5°  PORTB = 0b01111111; break;
> 100°   PORTB = 0b11111111; break;

Kannst du eine Formel finden, nach der sich die Anzahl der 
einzuschaltenden LED aus dem Zahlenwert der Temperatur errechnen lässt 
:-)

Ist ganz einfach!

Trickreicher ist es, einen Ausdruck zu finden, der aus dieser Anzahl 
eine Binärzahl macht, die genau diese Anzahl an 1 Bits gesetzt hat.

    Mask = ( 1 << Anzahl ) - 1;

Linksschieben mit einer nicht konstanten Anzahl an Bits ist aber auf 
einem AVR nicht so der Brüller. Da du aber nur wenige Werte hast, würde 
ich hier ein Array bevorzugen

  uint8_t Masks[8] = { 0b00000001,
                       0b00000011,
                       0b00000111,
                       0b00001111,
                       0b00011111,
                       0b00111111,
                       0b01111111,
                       0b11111111 };

  PORTB = Masks[Anzahl];

Jetzt musst du nur noch den mathematischen Ausdruck finden, wie du aus 
dem Temperaturwert (oder dem ADC Wert) die Anzahl ausrechnen kannst.

von miki (Gast)


Lesenswert?

Ich habe auch später vor an einem LCD die Temeprturmessungen auszugeben.
Da ich aber schrittweise vorwärts gehe, weil ich eben noch ein Anfänger 
bin, wollte ich zuerst die Werte an den LEDs ausgeben. Deshalb auch die 
Temperatur in Grad.

von miki (Gast)


Lesenswert?

Array ? Soweit bin ich leider nicht.
Was macht den ein Array? :(

Geht das nicht einfach mit if else Funktion?

von Karl H. (kbuchegg)


Lesenswert?

miki schrieb:
> Array ? Soweit bin ich leider nicht.
> Was macht den ein Array? :(
>
> Geht das nicht einfach mit if else Funktion?

OK. Vergiss das Array.
Benutzte if-else_if-else
1
   if( Temperatur < 12.5 )
2
     PORTB = 0b00000001;
3
4
   else if( Temperatur < 25.0 )
5
     PORTB = 0b00000011;
6
7
   else if( Temperatur < 37.5 )
8
     PORTB = 0b00000111;
9
10
   ....

von miki (Gast)


Lesenswert?

merci vielmals,..

Wenn ich es mit if else schaffe werd ich versuchen das ganze auch mit 
array zu realisieren :D

von miki (Gast)


Angehängte Dateien:

Lesenswert?

So sieht mein prog jetzt aus,.. -> in anhang

Kann den jemand anschauen und mir die Fehler erklären wo ich falsch 
gemacht habe. Zwar zeigt der Compiler keine fehler aber das muss ja 
nichts heissen.

von Peter D. (peda)


Lesenswert?

Karl heinz Buchegger schrieb:
> Mit einem switch ... gar nicht

Natürlich geht das mit einem switch, ist auch codesparend.
Man muß bloß vorher nach int umwandeln, da switch nicht mit float geht:
1
  switch((uint8_t)(temp * 2)){
2
    case   0 ...  24: PORTB = 0; break;
3
    case  25 ...  49: PORTB = 0b00000001; break;
4
    case  50 ...  74: PORTB = 0b00000011; break;
5
    case  75 ...  99: PORTB = 0b00000111; break;
6
    case 100 ... 124: PORTB = 0b00001111; break;
7
    case 125 ... 149: PORTB = 0b00011111; break;
8
    case 150 ... 174: PORTB = 0b00111111; break;
9
    case 175 ... 199: PORTB = 0b01111111; break;
10
    default:          PORTB = 0b11111111; break;
11
  }


Peter

von Karl H. (kbuchegg)


Lesenswert?

Peter Dannegger schrieb:
> Karl heinz Buchegger schrieb:
>> Mit einem switch ... gar nicht
>
> Natürlich geht das mit einem switch, ist auch codesparend.

Ich finde es nicht besonders gut, einem Neuling gcc-Erweiterungen 
vorzusetzen. Meiner ANsicht nach sollte er besser zuerst C so lernen, 
wie die Sprache definiert ist und erst dann die Nicht-Standard (und auch 
nicht übliche) Erweiterungen seines Compiler benutzen.

von jl (Gast)


Lesenswert?

das schlimmste was du machen konntest: Gleitkommazahlen verwenden, da 
braucht der Controller ewig. Noch merkst du es nicht da dein Programm 
klein ist.

Nimm dir eine feste Skalierung z.B 1 Inc = 0.1° und arbeite mit ganzen 
Zahlen wie integer.


JL

von Falk B. (falk)


Lesenswert?

Das Ganze nennt man Festkommaarithmetik

MFG
Falk

von Karl H. (kbuchegg)


Lesenswert?

miki schrieb:
> So sieht mein prog jetzt aus,.. -> in anhang
>
> Kann den jemand anschauen und mir die Fehler erklären wo ich falsch
> gemacht habe. Zwar zeigt der Compiler keine fehler aber das muss ja
> nichts heissen.

Wie wahr :-)

Dein Problem ist zur Zeit, dass du zwar irgendetwas berechnest, aber 
keine Möglichkeit hast, das Berechnete auf seinen Zahlenwert zu 
kontrollieren.

Fang langsam an.
Ändere deine Aussenbeschaltung so, dass du anstelle des 
Temperaturfühlers  ein Poti hast, mit dem du am ADC Eingang die Spannung 
einstellen kannst.
Dann ändere dein Programm so um, dass du zunächst den ADC Wert (ein 
Viertel davon) an den 8 Leds ausgibt. Da du keine andere Möglichkeit zur 
Ausgabe hast, musst du sehen, wie du daraus für dich das Maximum zur 
Programmkontrolle herausholen kannst.

Wenn du am Poti drehst, muss sich das LED Muster ändern. Und zwar so, 
dass die LEDs als Binärzahl den Wert des ADC (ein Viertel davon) 
anzeigen. Und erst dann fängst du an, mit den ADC Werten zu rechnen.

(Einfacher wäre es, wenn du zuerst das LCD in Betrieb nehmen könntest. 
Dann könntest du nämlich die Zahlenwerte lesbar ausgeben lassen und 
würdest dir insgesamt leichter tun)

von miki (Gast)


Angehängte Dateien:

Lesenswert?

So ,.. jetzt habe ich die 5V Spannung in 8Bit aufgeteilt.
Noch nichts mit Temperatur, nur die 5V an Leds ausgeben.

5V -> 256
4V -> 204.8
3V -> 153.6
2V -> 102.4
1V -> 51.2

Geht aber immer noch net. Ich habe an ADC0 (MUX0) eine Spannung von 0-5V 
angehängt und eine REFSpannung von 5V.

Hab ich etwas bei der Initialisierung vergessen ?

von miki (Gast)


Lesenswert?

Die switsch und arry funktion würde ich später anschaune. Zuerst will 
ich aber mit if else zu Stande bringen.

mfg

von Karl H. (kbuchegg)


Lesenswert?

Auch wenn du den ADC auf Autotrigger stellst, musst du die erste Messung 
händisch starten!

Warum benutzt du eigentlich nicht einfach die ADC Routine aus dem 
Tutorial? Die funktioniert und du kannst damit erst mal von einer 
funktionierenden Funktion ausgehen, die du dann immer noch an deine 
Bedürfnisse anpassen kannst.

Mit dieser Funktion wäre dein erstes Programm so einfach wie
1
...
2
...
3
// hier die ReadChannel Funktion aus dem Tutorial
4
...
5
6
int main()
7
{
8
  DDRB = 0xFF;
9
10
  while( 1 ) {
11
    PORTB = ReadChannel(0) / 4;
12
  }
13
}

Und schon kannst du loslegen und deine Hardware testen :-)
Wenn du die Messspannung veränderst muss sich das Leuchtmuster 
nachvollziehbar verändern.

von miki (Gast)


Lesenswert?

Wo ist die Tutoiral,..
Ich sollte nachlesen was der ReadChannel überhaupt macht.

mfg

von Karl H. (kbuchegg)


Lesenswert?

miki schrieb:
> Wo ist die Tutoiral,..

Diese Web-Site.
Linker Rand, gaaaaaanz nach oben.
Dort steht "AVR"
draufklicken
daraufhin erscheinen 2 neue Menüpunkte. -> AVR-GCC-Tutorial

Unter anderem ist für dich der Punkt 11.2.1 interessant

von miki (Gast)


Lesenswert?

int main (void){

//--Init---


DDRB = 0xFF;      // PortB0 als Ausgang
----------------------------------------------------------
uint16_t ReadChannel(uint8_t mux)
{
  uint8_t i;
  uint16_t result;

  ADMUX = mux;                      // Kanal waehlen
  ADMUX |= (1<<REFS1) | (1<<REFS0); // interne Referenzspannung nutzen

  ADCSRA = (1<<ADEN) | (1<<ADPS1) | (1<<ADPS0);    // Frequenzvorteiler
                               // setzen auf 8 (1) und ADC aktivieren 
(1)

  /* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man 
liest
     also einen Wert und verwirft diesen, um den ADC "warmlaufen zu 
lassen" */
  ADCSRA |= (1<<ADSC);              // eine ADC-Wandlung
  while ( ADCSRA & (1<<ADSC) ) {
     ;     // auf Abschluss der Konvertierung warten
  }
  result = ADCW;  // ADCW muss einmal gelesen werden,
                  // sonst wird Ergebnis der nächsten Wandlung
                  // nicht übernommen.

  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden 
Wandlungen */
  result = 0;
  for( i=0; i<4; i++ )
  {
    ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
    while ( ADCSRA & (1<<ADSC) ) {
      ;   // auf Abschluss der Konvertierung warten
    }
    result += ADCW;        // Wandlungsergebnisse aufaddieren
  }
  ADCSRA &= ~(1<<ADEN);             // ADC deaktivieren (2)

  result /= 4;                     // Summe durch vier teilen = arithm. 
Mittelwert


So bis hier versteh ich den code,.. Ich könnte ja 1 zu 1 übernehmen.
Ab hier müsste ich aber die Summe = result an den PORTB ausgeben.

von Karl H. (kbuchegg)


Lesenswert?

miki schrieb:

> So bis hier versteh ich den code,.. Ich könnte ja 1 zu 1 übernehmen.
> Ab hier müsste ich aber die Summe = result an den PORTB ausgeben.

Welchen Teil der Vorgabe, die ich dir weiter oben gegeben habe, hast du 
nicht verstanden?

Alternativ (*) wäre der Erwerb eines C-Buches in Erwägung zu ziehen um 
zu lernen, wie die Sache mit Funktionen in C wirklich funktioniert.


(*) Was red ich denn da. Definitiv IST der Erwerb eines C-Buches nicht 
nur in Erwägung zu ziehen sondern durchzuziehen.


Hier nochmal die Vorgabe
1
...
2
...
3
// hier die ReadChannel Funktion aus dem Tutorial
4
...
5
6
int main()
7
{
8
  DDRB = 0xFF;
9
10
  while( 1 ) {
11
    PORTB = ReadChannel(0) / 4;
12
  }
13
}

Du schreibst jetzt noch
1
#include <avr/io.h>
darüber

und an der Stelle mit den Punkten und dem Kommentar kopierst du die 
Funktion ReadChannel aus dem Tutorial ein.
Fertig. Das Ganze dauert keine 2 Minuten.

von miki (Gast)


Lesenswert?

Ja, was ich nicht verstehe, wo kommt den das ganze

int main()
{
  DDRB = 0xFF;

  while( 1 ) {
    PORTB = ReadChannel(0) / 4;
  }
}


Ich mein am beispiel von -> Tutoiral, kommt man zu dem

result /= 4;

Dort hat man den digitalen Wert, jetzt müsste man den noch wo 
darstellen, sei es auf dem Display oder in meinem Fall den LEDs.

von miki (Gast)


Angehängte Dateien:

Lesenswert?

Habe ja genau so gemacht,.. am PORTB macht sich aber nix

von Karl H. (kbuchegg)


Lesenswert?

miki schrieb:
> Habe ja genau so gemacht,.. am PORTB macht sich aber nix

Dann hast du ein Hardware-Problem

Was hast du als Beschaltung am ADC-Eingang?
Wie ist ARef beschaltet? Wie ist AVcc beschaltet?
Die LED hast du schon getestet?

Und zu guter letzt: Über welchen Prozessor reden wir eigentlich?

von miki (Gast)


Lesenswert?

Atmega64

Ich habe als - ARef eine 5V Spannung an Pin AREF zu GND geschaltet und
             - Eine 5V Spannung an ADC0 zu GND

von Karl H. (kbuchegg)


Lesenswert?

miki schrieb:
> Atmega64
>
> Ich habe als - ARef eine 5V Spannung an Pin AREF zu GND geschaltet und

Autsch.
Mach die gleich mal wieder weg.
ARef beschaltest du mit einem 100nF Kondensator zu GND.

Der Mega erzeugt sich seine eigene Referenzspannung und gibt sie am Pin 
ARef raus. Legst du selbst eine an, dann arbeiten zwei Spannungserzeuger 
gegeneinander.

>              - Eine 5V Spannung an ADC0 zu GND

Und wie variierst du die?
Solange deine Spannung immer 5V ist, wirst du an den LED kaum eine 
Veränderung sehen :-) Das muss schon eine veränderbare Spannung sein 
damit sich im LED-Muster auch was ändert.

von miki (Gast)


Lesenswert?

Ok ,.. mach ich

Ne die 5V Spannung an ADC0 zu GND stell ich mit nem Poti ein, 0-5V. :D

von Sajuuk (Gast)


Lesenswert?

@miki

Dein Programm ist noch auf interne Reference geschaltet.

>ADMUX |= (1<<REFS1) | (1<<REFS0); // interne Referenzspannung nutzen

diese Zeile sollte so aussehen:

ADMUX |= (0<<REFS1) | (1<<REFS0); // externe Referenzspannung nutzen

Ausserdem solltest du die Spannung an ADC0 mittels Poti einstellbar 
machen.
Wenn du konstant 5V an ADC0 anliegen hast wirst du immer den Maximalen 
Wert
messen d.h. alle LED´s sollten Leuchten.

Und was genau meinst du mit:

>Ich habe als - ARef eine 5V Spannung an Pin AREF zu GND geschaltet und
>             - Eine 5V Spannung an ADC0 zu GND

Das klingt irgendwie nach Kurzschluss.

von Karl H. (kbuchegg)


Lesenswert?

miki schrieb:
> Ok ,.. mach ich
>
> Ne die 5V Spannung an ADC0 zu GND stell ich mit nem Poti ein, 0-5V. :D

Und hast du das auch mit einem Voltmeter kontrolliert?

von Sajuuk (Gast)


Lesenswert?

Ach so hab gerade mist erzählt. Wenn du ne externe Referenz anlegst 
musst du die interne abschalten:

ADMUX |= (0<<REFS1) | (0<<REFS0); // interne Referenzspannung aus

von Karl H. (kbuchegg)


Lesenswert?

Nochmal zum Thema Referenzspannung (damit da jetzt nichts 
durcheinanderkommt)

Es gibt grundsätzlich 2 Möglichkeiten
* interne Referenzspannung
* externe Referenzspannung

intern bedeutet: Der Mega erzeugt sich die Referenzspannung selber und 
gibt sie am Pin ARef aus
extern bedeutet: Dem Mega wird von aussen, über den Pin ARef die 
Referenzspannung vorgegeben.

Die extern Methode benutzt man dann, wenn von den Referenzspannugen, die 
der Mega selbst erzeugen kann, keine passt. Der Mega hat eine ganze 
Reihe von Referenzspannungen vorrätig, mindestens die 
Versorgungsspannung ist immer dabei.

Benötigt man daher die Versorgungsspannung als Referenz, gibt es keinen 
Grund, von aussen auch noch ARef mit einer Spannung zu beschalten. 
Insbesondere deswegen, da es für den Mega tödlich sein kann, wenn per 
Program eine interne Referenzspannung vorgegeben wird und von aussen ein 
Netzteil gegen den Referensspannungsgenerierer arbeitet. In so einem 
Fall kann der Mega nur verlieren.

von miki (Gast)


Lesenswert?

Also wenn ich jetzt die exteren Referenzspannung nutze bräuchte ich den 
100n Kondensator nicht oder ?

--> ADMUX |= (0<<REFS1) | (1<<REFS0); // externe Referenzspannung nutzen
_____________________

Damit meine ich ich habe eine Konstante 5V Ref Spannung an Pin AREF zu 
GND  und die einstellbare Spannung 0-5V an ADC0 zu GND geschaltet habe.

______________________

Ja ich habe auch mit dem Multim gemessen die Spannung verstellt sich 
zwischen 0 und 5.005V :)

von Sajuuk (Gast)


Lesenswert?

Ja dann bräuchtest du den Kondensator nicht unbedingt.

>--> ADMUX |= (0<<REFS1) | (1<<REFS0); // externe Referenzspannung nutzen

So wäre es in dem Fall richtig

ADMUX |= (0<<REFS1) | (0<<REFS0); // externe Referenzspannung nutzen

aber oben steht beschrieben warum dies nicht die beste Variante wäre.

von miki (Gast)


Lesenswert?

Wenn ich jetzt die externe Spannung benutze,.. beim einschalten des uC 
springt die Spannung am Speisegerät an 5V. Die Spannung an Speisegerät 
(meine REfspannung) hab ich aber nicht gegeben.

von Gast (Gast)


Lesenswert?

>ADMUX |= (0<<REFS1) | (0<<REFS0); // externe Referenzspannung nutzen
nöö

warum willst du ne 0 wieoft auch immer rumschubsen?

du meinst wahrscheinlich das

ADMUX &= ~((1<<REFS1) | (1<<REFS0)); // externe Referenzspannung nutzen

von Naja (Gast)


Lesenswert?

>Die Spannung an Speisegerät (meine REfspannung) hab ich aber nicht gegeben.

Du liest die Spannung doch von irgendeinem Messinstrument ab. Damit ist 
sie "gegeben". Diese Spannung musst Du dann in Deinem Programm bei der 
Umrechnung berücksichtigen.

von Karl H. (kbuchegg)


Lesenswert?

miki schrieb:
> Wenn ich jetzt die externe Spannung benutze,.. beim einschalten des uC
> springt die Spannung am Speisegerät an 5V. Die Spannung an Speisegerät
> (meine REfspannung) hab ich aber nicht gegeben.

Das macht nichts.
Der ADC vergleicht ja nur deine zu messende Spannung mit dieser 
Referenzspannung. Der Zahlenwert spiegelt das Verhältnis wieder. Du 
kennst deine Referenzspannung, daher kannst du aus dem Zahlenwert auf 
die tatsächliche Spannung zurückrechnen.

Was ist nun?
Ändern sich deine LED, wenn du am Poti drehst?

von Sajuuk (Gast)


Lesenswert?

@Gast

Ja richtig! Ich Dödel.

Normalerweise mache ich es so:

ADMUX = Binary(00000000);
//             |||+++++---------- MUX Analog Channel and Gain Selector
//             ||+--------------- ADLAR ADC Left Adjust
//             ++---------------- REFS Reference Selection (external
//                                Reference)

von miki (Gast)


Lesenswert?

Wär schön

Ich hab genau so gemacht wie sie es gesgat haben.

ARefSpannung

ADMUX &= ~((1<<REFS1) | (1<<REFS0)); // externe Referenzspannung nutzen

--> An Pin AREF zu GND geschaltet

Und die Potispannung 0-5V an ADC0 zu GND.


Das Prog kompaliert und auf uC gedownloadet .
______________________________________________________________________

von Karl H. (kbuchegg)


Lesenswert?

Und, ändern sich die LED wenn du am Poti drehst?

(Wie ist das Poti angeschlossen?
Hoffentlich so

      ARef (oder Vcc in deinem Fall
       o------------------
                         |
                        +-+
                        | |
                      <--------------------o ADC0
                        | |
                        +-+
                         |
       o------------------
       GND

von miki (Gast)


Lesenswert?

Nein, die Sannung ändert aber am PORTB ist nichts zu sehen.

Genau, es istt ein Spannungsteiler wo ich am Ausgang mit dem Poti von 0 
- bis 5V einstellen kann.

Und die ARef speise ich mit nem separaten Speisegerät direkt 5V an.

von miki (Gast)


Angehängte Dateien:

Lesenswert?

Im  Anhang

- Hardware
- Software

mfg

von ASDF (Gast)


Lesenswert?

Der Mittelwert wird schon in der Funktion ReadChannel gebildet, daher 
muss man im Hauptprogramm nicht durch 4 teilen.

von ASDF (Gast)


Lesenswert?

Also gut. Hab mir gerade nochmal alles durchgelesen. Doch durch 4 
teilen. :)

von Sajuuk (Gast)


Lesenswert?

Hab mir nochmal dein Programm angesehen.
Da ist diese zeile enthalten.

>ADMUX |= (1<<MUX0);                      // Kanal waehlen

Das bedeutet du hast Kanal 1 gewählt und nicht Kanal 0.

Am besten du schmeißt das

>  ADMUX |= (1<<MUX0);                      // Kanal waehlen
>  ADMUX |= (1<<REFS1) | (1<<REFS0); // interne Referenzspannung nutzen

raus und schreibst einfach

ADMUX = 0x00;

das sollten in deinem fall die richtigen Einstellungen sein.

Schöner ist es so da es übersichtlicher ist:

ADMUX = Binary(00000000);
//             |||+++++---------- MUX Analog Channel (Channal 0)
//             ||+--------------- ADLAR ADC Left Adjust
//             ++---------------- REFS Reference Selection (external
//                                Reference)

von miki (Gast)


Lesenswert?

Ok,werd morgen mal ausprobieren. Wieso sollte ich aber

>  ADMUX |= (1<<REFS1) | (1<<REFS0); // interne Referenzspannung nutzen

rausschmeissen? ,. Ich brauche ja die Referenzspannung.

Im Prog steht übrigens so:

ADMUX &= ~((1<<REFS1) | (1<<REFS0));  // interne (externe meinte ich) 
Referenzspannung nutzen

danke .:)

MFG

von Peter D. (peda)


Lesenswert?

Sajuuk schrieb:
> ADMUX = 0x00;

Nö, laß Dich nicht beirren, nimm die Bitnamen aus dem Datenblatt.

Ich schätze, 99% finden das besser verstehbar und machen das auch so.

Es gibt auch die Möglichkeit, immer alle Bits anzugeben, auch die auf 0 
gesetzten:
1
REG_X = 1<<BIT_X | 0<<BIT_Y | 0<<BIT_Z;
Das ist dann Geschmackssache.


Peter

von Sajuuk (Gast)


Lesenswert?

ADMUX ist ein Register es besitzt 8 Bit.
Hier sehr schön zu sehen, jede 0 steht für 1 bit das du wahlweise auf 0 
oder 1 setzen kannst. Rechts das LSB und links das MSB.

ADMUX = Binary(00000000);
//             |||+++++---------- MUX Analog Channel (Channal 0)
//             ||+--------------- ADLAR ADC Left Adjust
//             ++---------------- REFS Reference Selection (external
//                                Reference)

Du müsstest im ADMUX Register alle Bits auf 0 setzen.

Das hier...
ADMUX &= ~((1<<REFS1) | (1<<REFS0));  // interne (externe meinte ich)
...bedeutet einfach nur das du bit 7 und 6 auf 0 setzt.

Du kannst das ADMUX Register aber auch setzte in dem du einfach den 
entsprechenden Hex wert rein schreibst. In deinem Fall 0x00.

Soll heißen...

ADMUX = 0x00:
oder
ADMUX = Binary(00000000);
//             |||+++++---------- MUX Analog Channel (Channal 0)
//             ||+--------------- ADLAR ADC Left Adjust
//             ++---------------- REFS Reference Selection (external
//                                Reference)

...ersetzten die anderen beiden Befehle.

Wenn du Kanal 1 verwenden möchtest müsstest du in ADMUX = 0x01 schreiben
oder
ADMUX = Binary(00000001);
//             |||+++++---------- MUX Analog Channel (Channal 1)
//             ||+--------------- ADLAR ADC Left Adjust
//             ++---------------- REFS Reference Selection (external
//                                Reference)

von Sajuuk (Gast)


Lesenswert?

@Peter dannegger

Ich wollte damit auch nicht sagen das das schön ist.
Ich persönlich bevorzuge es auch leicht verständlich.
Wollte ihm nur mal die Möglichkeiten die es gibt aufzeigen.

Wie schon gesagt ich bevorzuge diese Variante da man sich sicher sein 
kann wie jedes einzelne bit steht ausserdem ist es schön anschaulich.

ADMUX = Binary(00000000);
//             |||+++++---------- MUX Analog Channel (Channal 0)
//             ||+--------------- ADLAR ADC Left Adjust
//             ++---------------- REFS Reference Selection (external
//                                Reference)



@miki
Du kannst es natürlich auch so schreiben.

ADMUX &= ~((1<<MUX0)|(1<<MUX1)|(1<<MUX2)|(1<<MUX3)|(1<<MUX4));  // Kanal 
0 gesetzt
ADMUX &= ~((1<<REFS1) | (1<<REFS0));  // interne (externe meinte ich)

von Karl H. (kbuchegg)


Lesenswert?

Sajuuk schrieb:

> Wie schon gesagt ich bevorzuge diese Variante da man sich sicher sein
> kann wie jedes einzelne bit steht ausserdem ist es schön anschaulich.

Es ist trotzdem nicht zweckdienlich.
Die Anzahl der Threads hier im Forum, bei denen sich der Fragesteller in 
der Bitcodierung verhaut hat, sind Legion. Wenn schon zu nichts anderem, 
so sind die Bitnamen wenigstens dazu gut, die Fehlersuche etwas zu 
vereinfachen, weil man das Bitgedöns nicht auch noch in Einzelbits 
aufdröseln muss ehe man im Datenblatt nach der zugeordneten Bedeutung 
sucht. Eine Fehlerquelle weniger.

> @miki
> Du kannst es natürlich auch so schreiben.

Wenn schon, dann so
1
  ADMUX &= ~((1<<MUX0)|(1<<MUX1)|(1<<MUX2)|(1<<MUX3)|(1<<MUX4));
2
  ADMUX |= mux;
3
4
  ADMUX &= ~((1<<REFS1) | (1<<REFS0));  // externe Referenz

Schliesslich ist ja einer der Knackpunkte in der ReadChanel Funktion aus 
dem Tutorial, das sie die Nummer des ADC Kanals mitbekommt. Also sollte 
man diese Nummer auch verwenden.

Erhebt sich nur noch die Frage, warum du das überhaupt geändert hast.

von miki (Gast)


Lesenswert?

Ok,.. ich habe jetzt so ausprobiert.

ADMUX = 0x00; (Binery erkennt er nicht)

geht aber immer noch net.

Ich habe mir die Überlegung gemacht, da ich ein SMD uC intern benutze 
(auf dem AVR Board), könnte es sein das andere Eigenschaften auf die 
benutzte Ports zugreifen. Ich werd mir sicher jetzt einz mit dem DIL 
Gehäuse kaufen. So kann ich extern arbeiten.

Was würdet ihr mir für einen empfehlen?

Ich arbeite mit dem - AVR Starter Kit
                    - JTAG ICE  mk-ll

______________________________________________________

Trotzdem wollte ich es wissen, ob ich das Prog. simulieren konnte, 
unabhängig von uC. Ich würde gern sehen was ich eigentlich in ADCW habe.

Und, schreibt die ReadChannel Funktion tatsächlich auf dem PORTB?

int main()
{
  DDRB = 0xFF;

  while( 1 ) {
    PORTB = ReadChannel(0) / 4;
  }
}

von Karl H. (kbuchegg)


Lesenswert?

miki schrieb:

> Und, schreibt die ReadChannel Funktion tatsächlich auf dem PORTB?
>
> int main()
> {
>   DDRB = 0xFF;
>
>   while( 1 ) {
>     PORTB = ReadChannel(0) / 4;
>   }
> }

Die ReadChanel nicht. Aber das Ergebnis der ReadChanel Funktion (der 
gelesene Wert) wird durch 4 geteilt (damit die 1024 auf 255 runtergeholt 
werden) und an PORTB ausgegeben.

Ich denke nicht, dass es am µC liegt.
Aber ich finde dann jetzt auch keinen Fehler mehr. Laut deiner 
Beschreibung ist die Hardware richtig, wenn du die Funktion 1:1 aus dem 
Tut reinkopiert hast sollte die auch keine Probleme machen. Allerdings 
ist die für einen Mega8 geschrieben. Man könnte jetzt höchstens noch das 
Datenblatt durchforsten ob es da am ADC Unterschiede gibt.

Blöde Frage: Die LED hast du getestet?
(Ich weiß, ich weiß. Aber bei der Fehlersuche muss man grundsätzlich 
alle Möglichkeiten in Betracht ziehen)

von Sajuuk (Gast)


Lesenswert?

Ach so jetzt sehe ich erst das der Kanal ja der
uint16_t ReadChannel(uint8_t mux) übergeben wird.

@Miki

Ich hab mir jetzt mal den Originalcode aus dem Tutorial angeschaut.
Karl heinz Buchegger hat recht.
Wenn du anstelle von dem hier...

ADMUX = (1<<MUX0);

...einfach...

ADMUX |= mux;

...schreiben würdest sollte dein Code Funktionieren.

Ich denke mal du hast die Funktion "uint16_t ReadChannel(uint8_t mux)" 
nicht richtig verstanden.

Also mal im Detail.

Das "unit16_t" bedeutet das die Funktion dir einen 16bit Wert zurück 
gibt.
In deinem Fall wird dieser Rückgabewert nochmal durch 4 geteilt und auf 
PortB geschrieben.
"ReadChannel" ist einfach nur der Name der Funktion.
und der Wert in der Klammer bedeutet das der Funktion ein 8bit Wert 
übergeben wird nämlich die Variable mux - mit dieser Variable wählst du 
den Kanal.

Bist du dir sicher das deine Led´s an PortB angeschlossen sind?
Du solltest vielleicht mal versuchen PortB = 0x01; oder so.
Wenn dann auch nix leuchtet liegt es nicht an der Software. Es sei den 
du hast PortB nicht auf Ausgang gesetzt.

von Sajuuk (Gast)


Lesenswert?

Ok ich seh grad PORTB wird auf Ausgang gesetzt daran sollte es nicht 
scheitern.
Aber versuch mal ne Led zum leuchten zu bringen in dem du PORTB von Hand 
einen Wert zuweist.
Also PORTB = 0x01; oder so.

von Karl H. (kbuchegg)


Lesenswert?

Sajuuk schrieb:

> Aber versuch mal ne Led zum leuchten zu bringen in dem du PORTB von Hand
> einen Wert zuweist.
> Also PORTB = 0x01; oder so.

Das versuch ich schon seit Stunden aus ihm rauszukitzeln, ob er diesen 
Test gemacht hat :-)

von miki (Gast)


Lesenswert?

Natürlich hab ich diesen Test gemacht,..
Die LEDS sind im AVR Starter Kit integriert. Ich habe zuerst natürlich 
mit kleinere Progs angefangen wie:

-->

/* Laesst eine LED an PB0 und PB7 im wechsel endlos blinken
 *
 */

#include <avr/io.h>
#include <util/delay.h>    // _delay_ms()

// wartet ms Millisekunden
void delay_ms(uint16_t ms)
{
  for(uint16_t t=0; t<=ms; t++)
    _delay_ms(1);
}

int main()
{
  DDRB = 0xFF;        // PORTB als Ausgang

    while (1)
  {
    PORTB = 0b00000001;    // LED an PB0 ein
    _delay_ms (250);
    PORTB = 0x80;      // LED an PB7 ein
    _delay_ms (250);
  }
  return 0;

______________________________________________________________________ 
__

Das ging alles tiptop.

von Sajuuk (Gast)


Lesenswert?

Na schön.

Du benutzt doch sicher das AVR-Studio zum debuggen?
Auf der linken Seite kannst du dir die Register und Statusflags des
AD-Wandlers anschauen. Da gibt es einen Punkt ADC Data Register hier 
wird dein Messwert gespeichert.

Setzt dir als mal nen Breakpiont auf.

PORTB = ReadChannel(0) / 4;

Dann lass dein Programm ein bis zweimal drüber laufen und schau nach
ob etwas im ADC DATA Register drin steht.

von miki (Gast)


Lesenswert?

Unter ADC finde sich die ADCH 0x05(0x25) und ADCL 0x04(0x24) Registers.

Wenn ich jetzt das Prog einfach mal so auf dem uc downloade (ohne die 
Spannungen) leuchten alle LEDs am PORTB,..

von Karl H. (kbuchegg)


Lesenswert?

miki schrieb:

> Wenn ich jetzt das Prog einfach mal so auf dem uc downloade (ohne die
> Spannungen) leuchten alle LEDs am PORTB,..

Ist schon mal was.
Und wenn du dann den ADC Eingang mit deinem Poti verbindest, gehen alle 
LED aus?


(Das Problem ist, dass dir der Simulator da nicht viel hilft.
Klar kann man nachsehen, ob was in den Registern steht bzw. selber was 
reinschreiben. Aber das hilft nicht viel um abzuklären ob die 
Konfiguration stimmt. Was anderes wäre es, wenn der Simulator bei 
korrekter Konfiguration in den Registern irgendwelche Zufallswerte 
reinschreiben würde. Aber m.W. tut er das nicht)

von miki (Gast)


Lesenswert?

Eben net,.. Wenn ich den ADC Eingang verbinde und die Spannung 
generiere, leuchten immer noch die LEDs am PORTB.

Wie wärs wenn ich die ARef intern speise. Vllt liegts an dem. Es ist 
einfach kommisch dass, wenn ich das AVR Starter Kit Board anschalte am 
Speisegerät der REferenzspannung eine 5V Spannung bekomme obwohl die 
Spannung am Speisegerät nich eingeschaltet ist.

von miki (Gast)


Lesenswert?

Die LEDs leuchten weil in Hauptprog DDRB=0xFF; gesetzt haben.
Die ReadChannel Funktion wird gar net ausgeführt.

int main()
{
  DDRB = 0xFF;

  while( 1 ) {
    PORTB = ReadChannel(0) / 4;
  }
}

von Karl H. (kbuchegg)


Lesenswert?

?
Zeig nochmal das vollständige Programm

von Karl H. (kbuchegg)


Lesenswert?

miki schrieb:
> Die LEDs leuchten weil in Hauptprog DDRB=0xFF; gesetzt haben.

Das ist Unsinn.
Nur dadurch, dass du einen Port auf Ausgang setzt, werden die Pins nicht 
high.

> Die ReadChannel Funktion wird gar net ausgeführt.

Natürlich wird die ausgeführt.
Das Programm hat gar keine andere Wahl.

von miki (Gast)


Angehängte Dateien:

Lesenswert?

Die 5V Spanung an ARef zu GND
Und die Potispanung an ADC0 zu GND

von Adrian M. (maagadrian)


Lesenswert?

Hallo,
kannst du mal den aktuellen Code hochladen, dann kann ich auch noch 
meinen Senf dazugeben... oder so.

Mfg, Adrian

von miki (Gast)


Lesenswert?

Ja, ist schon richtig,.. aber wenn ich jetzt DDRB=0xFF; schreibe, 
leuchten die LEDs net.

von miki (Gast)


Lesenswert?

Maag, bisch morn pünktlich i de schuel :D

von Sajuuk (Gast)


Angehängte Dateien:

Lesenswert?

Also ich sehe keinen Fehler im Programm ausser das diese Zeile 
überflüssig ist.

>ADMUX &= ~((1<<MUX0)|(1<<MUX1)|(1<<MUX2)|(1<<MUX3)|(1<<MUX4));

Entweder du hast einen Hardwarefehler drin oder du hast den Kanal 0 
schon kaputt gespielt.

von Adrian M. (maagadrian)


Angehängte Dateien:

Lesenswert?

Ok... war zu langsam im schreiben, ist ja egal, danke.
Also, ich bin weder Freak noch Profi, aber wäre es nicht verständlicher, 
wenn du gewisse Register nur einmal setzen würdest, z.B. so wie ichs im 
Anhang gemacht habe. Das Programm funktioniert evtl. nicht, ich habs nur 
umgeschrieben, damit man sieht, was ich meine.

Gruss, Adrian

von Sajuuk (Gast)


Lesenswert?

Ups das Bild sollte da nicht mit rein Sorry.

von Adrian M. (maagadrian)


Lesenswert?

öpe scho...

von miki (Gast)


Lesenswert?

Ich werd mir jetzt ein ATmega8 in DIL Gehäuse besorgen und probiers 
nochmal.
Ich melde mich dann wenns funktioniert :-)

Danke vielmals an euch allen.
Muss ich jetzt jedem ein Bier spendieren? :)

von miki (Gast)


Lesenswert?

ATmega16-16AU
oder
ATmega8-16AU

Welecher wär geeigneter (basiert auf die Beispielen in AVR-GCC 
Tutorial),..

von Sajuuk (Gast)


Lesenswert?

Der ATMega 8 reicht völlig aus.

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.