Forum: Mikrocontroller und Digitale Elektronik Problem bei Einlesen der Analogwerte


von Peter (Gast)


Lesenswert?

Schönen Guten Abend, mein Problem ist folgendes. Ich habe eine Schaltung 
mit zwei Analogen Eingängen wo jeweils ein CNY70 dranhängt. An den 
beiden Ausgängen jeweils eine LED.
Das Programm läuft soweit, mit einem Eingang. Nehme ich allerdings den 
zweiten Eingang dazu, reagieren beide Ausgänge identisch, obwohl ich nur 
einen Eingang manipuliere.
Wäre nett wenn ihr mir eine Tipp bzw. weiterhelfen könntet. Seh den Wald 
vor lauter Bäumen nicht mehr!

Programmiert wird ein ATmega32 über das Pollin Evaluationsboard.
1
#include <avr/io.h>
2
3
void ADC_Init(void);
4
5
uint16_t ADC_Read(uint8_t channel);
6
7
int main(void)                
8
{      
9
    unsigned int ch0, ch1;
10
    DDRC = 0xff;            // Alles Pins von Port C als Ausgang setzen
11
    
12
    ADC_Init();              // ADC initialisieren
13
        
14
    while( 1 ) {      
15
    ch0 = ADC_Read(0);          //ADC Channel 0 
16
    
17
        if(ch0 >= 512)        // Bei Eingang U=2,5V setze PC0 HIGH
18
        PORTC |= 1<<PC0;
19
        else
20
        PORTC &= ~(1<<PC0);      // Ansonsten setze PC0 LOW
21
        
22
        
23
    ch1 = ADC_Read(1);          //ADC Channel 1 
24
    
25
        if(ch1 >= 512)        // Bei Eingang U=2,5V setze PC0 HIGH
26
        PORTC |= 1<<PC1;
27
        else
28
        PORTC &= ~(1<<PC1);      // Ansonsten setze PC0 LOW
29
30
    }
31
}
32
33
34
/* ADC initialisieren */
35
void ADC_Init(void)
36
{
37
  uint16_t result;
38
  ADMUX = (1<<REFS0);            // AVcc (5V) als Referenz festlegen
39
                      
40
41
  ADCSRA = (1<<ADPS1) | (1<<ADPS0);    // Frequenzvorteiler einstellen
42
43
  ADCSRA |= (1<<ADEN);          // ADC wird aktiviert
44
45
46
                      /* Wird der ADC aktiviert, sollte ein ein "Dummy-Readout" durchgeführt werden, zum "warmlaufen" des ADC */
47
  ADCSRA |= (1<<ADSC);          // eine ADC-Wandlung durchführen
48
  while (ADCSRA & (1<<ADSC)) {}      // Warte auf Abschluss der Konvertierung
49
  
50
  unsigned int dummy = ADC;        //Und ersten Messwert wegwerfen
51
52
}
53
54
55
/* ADC Einzelmessung durchführen */
56
57
uint16_t ADC_Read(uint8_t channel)
58
{
59
  // Auswahl des Kanals, andere Bits dabei nicht beeinflussen
60
  
61
  ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);
62
  ADCSRA |= (1<<ADSC);        // Wandlung im "single conversion" Modus
63
  while (ADCSRA & (1<<ADSC)) {}    // Warten auf abgeschlosser Konvertierung
64
  return ADC;              // Auslesen und zurückgeben des ADC als ganzes Wort
65
}

Danke schonmal!

von Joachim B. (jar)


Lesenswert?

ich verstehe nur Bahnhof.....


wie wäre es mal mit einem Schaltplan.

von Easylife (Gast)


Lesenswert?

1.)
1
ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);
 maskiert insgesamt die untesten 5 bit, wenn channel 0 oder 1 ist 
veränderst du nur das untere bit.
Warum also nicht
1
 ADMUX = (ADMUX & ~(0x01)) | (channel & 0x01);
?
Das könnte der erste Fehler sein (falsches Bit in ADMUX manipuliert).
Wie Joachim B. schon sagt: Schaltplan könnte hier helfen.
Es gibt auch MUXen, bei denen die Eingänge nicht analog zur Adresse 
geschaltet werden, will sagen Adresse 0 heisst nicht zwangsläufig 
Eingang 0 usw.

2.)
Zur Sicherheit könntest du so auf die Konvertierung warten (für den Fall 
dass Bit 2 von ADSC nicht sofort nach "ADCSRA |= (1<<ADSC);" auf high 
geht):
1
while (!(ADCSRA & (1<<ADSC))) ;
2
while (ADCSRA & (1<<ADSC)) ;

von Easylife (Gast)


Lesenswert?

Ausserdem setzt
1
ADCSRA |= (1<<ADSC);
das Bit einmalig high, und da bleibt es dann auch.
Ich nehme an, du solltest es irgendwo auch wieder auf low setzen (nach 
der Wandlung?)

von spess53 (Gast)


Lesenswert?

Hi


>...dass Bit 2 von ADSC nicht sofort nach "ADCSRA |= (1<<ADSC);" auf high >geht.

ADSC ist Bit6 von ADCSRA.

>Ausserdem setzt
>ADCSRA |= (1<<ADSC);
>das Bit einmalig high, und da bleibt es dann auch.

Nein. ADSC bleibt nur bis zum Abschluss der AD-Wandlung auf H.

MfG Spess

von Peter (Gast)


Angehängte Dateien:

Lesenswert?

Danke schon mal für die Antworten.

Dachte eigentlich es wäre verständlich erklärt.


@ easylife
1
ADMUX = (ADMUX & ~(0x01)) | (channel & 0x01);

Das ändert nichts.

1
while (!(ADCSRA & (1<<ADSC))) ;
2
while (ADCSRA & (1<<ADSC)) ;

Auf die Konvertierung warte ich ja im ADC_Read, der jedes mal 
durchlaufen wird.

Wie gesagt, beide Ausgänge werden unabhängig welcher Sensor manipuliert 
wird, gleichzeitig gesetzt.

Im Anhang ist eine Zeichnung der Sensoren. Das es drei sind kann man 
erstmal vernachlässigen.

von spess53 (Gast)


Lesenswert?

Hi

>ADCSRA = (1<<ADPS1) | (1<<ADPS0);

Mit welcher Frequenz läuft dein ATMega?

MfG Spess

von Peter (Gast)


Lesenswert?

SUT_CKSEL ist auf 1MHz eingestellt

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Peter schrieb:
> Das Programm läuft soweit, mit einem Eingang. Nehme ich allerdings den
> zweiten Eingang dazu, reagieren beide Ausgänge identisch, obwohl ich nur
> einen Eingang manipuliere.
> Wäre nett wenn ihr mir eine Tipp bzw. weiterhelfen könntet. Seh den Wald
> vor lauter Bäumen nicht mehr!

 Das Programm ist soweit in Ordnung.
 Das einzige, was zu einem solchen Verhalten führen könnte, wären die
 bits3-4 in ADMUX, so dass MEGA praktisch eine Differenzmessung der
 beiden Kanäle macht. Sehe ich zwar nicht in gepostetem Code, aber ich
 würde es trotzdem so machen:

  ADMUX = (ADMUX & 0xE0) | (channel & 0x07);

 Ansonnsten bleibt nur eine Verbindung zwischen beiden Sensorausgängen.

von Ralf G. (ralg)


Lesenswert?

In den Datenblättern(*) wird empfohlen, die Kanalumschaltung nach 
beendeter Messung zu machen. Da das hier nicht möglich ist, würde ich 
mal eine Dummy-Messung einschieben.

(*) Datenblatt -> Analog to Digital Converter -> Changing Channel or 
Reference Selection -> ADC Input Channels

von Peter (Gast)


Lesenswert?

Danke für die Tipps!

Wenn ich den ADC in der Schleife initialisiere dann funktioniert es.
1
int main(void)                
2
{      
3
    unsigned int ch0, ch1;
4
    DDRC = 0xff;            // Alles Pins von Port C als Ausgang setzen
5
    
6
        
7
    while( 1 ) { 
8
9
    ADC_Init();              // ADC initialisieren
10
     
11
    ch0 = ADC_Read(0);          //ADC Channel 0 
12
    
13
        if(ch0 >= 512)        // Bei Eingang U=2,5V setze PC0 HIGH
14
        PORTC |= 1<<PC0;
15
        else
16
        PORTC &= ~(1<<PC0);      // Ansonsten setze PC0 LOW
17
        
18
        
19
    ch1 = ADC_Read(1);          //ADC Channel 1 
20
    
21
        if(ch1 >= 512)        // Bei Eingang U=2,5V setze PC0 HIGH
22
        PORTC |= 1<<PC1;
23
        else
24
        PORTC &= ~(1<<PC1);      // Ansonsten setze PC0 LOW
25
26
    }

Verstanden habe ich aber nicht!

von Karl H. (kbuchegg)


Lesenswert?

Peter schrieb:

> Verstanden habe ich aber nicht!

Wie hast du denn die CNY angeschlossen?

Es könnte sein, dass der Anschluss zu hochohmig ist, so dass sich die 
Sample und Hold Stufe nach dem Umschalten des Analogmultiplexers nicht 
schnell genug auf die neue Spannung umladen kann, ehe dann die Wandlung 
beginnt.

D.h. der springende Punkt ist an dieser Stelle, dass zwischen
1
  ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);

und
1
  ADCSRA |= (1<<ADSC);

einfach nur ein bischen Zeit vergehen muss, damit die Sample&Hold Stufe 
der jeweils durch den Multiplexer zugeführten Spannung folgen kann.

von Ralf G. (ralg)


Lesenswert?

Karl Heinz schrieb:
> einfach nur ein bischen Zeit vergehen muss, damit die Sample&Hold Stufe
> der jeweils durch den Multiplexer zugeführten Spannung folgen kann.

... und dazu gibt's den Trick mit zwei Wandlungen nacheinander. Da ist 
die Zeit mit Sicherheit eingehalten. (Vermutlich mindestens der öfter an 
anderen Stellen im Datenblatt erwähnte 'one ADC clock cycle'.)

von Karl H. (kbuchegg)


Lesenswert?

Ralf G. schrieb:
> Karl Heinz schrieb:
>> einfach nur ein bischen Zeit vergehen muss, damit die Sample&Hold Stufe
>> der jeweils durch den Multiplexer zugeführten Spannung folgen kann.
>
> ... und dazu gibt's den Trick mit zwei Wandlungen nacheinander. Da ist
> die Zeit mit Sicherheit eingehalten.

Exakt.
Die Zeit ist von 2 Faktoren abhängig:
Von der Taktfrequenz mit der der µC arbeitet
Von der Impedanz der Aussenbeschaltung an den Messeingängen.

Ohne die beiden Parameter zu kennen, ist es daher schwierig eine Aussage 
darüber zu machen. Man kann natürlich ganz einfach auf Nummer sicher 
gehen und lang genug warten. Ob man dazu ein delay benutzt, oder ob man 
die Init Sequenz noch mal durchlaufen lässt oder ob man eine Dummy 
Messung einstreut ist eigentlich 2-rangig. Oft kann man auch diese 
Systematik benutzen
1
  Multiplexer auf 0 einstellen
2
  while( 1 ) {
3
4
    Messung für 0 machen
5
    Multiplexer auf 1 umstellen
6
    Messwert für 0 verarbeiten
7
8
    Messung für 1 machen
9
    Multiplexer auf 0 umstellen
10
    Messwert für 1 verarbeiten
11
  }

jetzt liefert der Punkt 'Messwert ... verarbeiten' die notwendige 
Zeitverzögerung.

Das funktionert wunderbar. Nur hat man dann eben nicht eine einzige 
Rundum-Sorglos Funktion zur Benutzung des ADC.

von Peter (Gast)


Lesenswert?

Habe diese zwei Möglichkeiten getestet. Egal ob ich in der Main eine 
Zeit einbaue, in der Init eine Zeit einbaue, oder mehrer Dummy Readouts 
machen, dass Programm läuft dann nicht. Es bemerkt dann anscheinend 
keine Wertänderung bzw bekommt es nicht verarbeitet.

Die CNY habe ich wie oben im Schaltplan angeschlossen. Da stehen auch 
die Widerstandswerte.

von Karl H. (kbuchegg)


Lesenswert?

Peter schrieb:

> Die CNY habe ich wie oben im Schaltplan angeschlossen. Da stehen auch
> die Widerstandswerte.

Ah. Sorry. Nicht gesehen.

na ja. Was willst du mit 47k? Das ist viel zu hoch! Ein Wert so um die 
10k wäre gut.

Auf der anderen Seite: Wozu eigentlich der ADC? Willst du wirklich 
wissen, wie stark die Lichtschranke abgeschattet ist oder reicht dir 
nicht einfach die Aussage: Lichtstrahl intakt / Lichtstrahl 
unterbrochen.
Für letzteres brauchst du keinen ADC. Schalte den CNY einfach wie einen 
Taster an einen Eingang (Pullup nicht vergessen, wobei du auch den 
internen benutzen kannst) und gut ists.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz schrieb:

> Für letzteres brauchst du keinen ADC. Schalte den CNY einfach wie einen
> Taster an einen Eingang (Pullup nicht vergessen, wobei du auch den
> internen benutzen kannst) und gut ists.


Wobei du in diesem Fall natürlich auch bei den 47k externen Pullup 
Widerstand bleiben kannst. Die ca 10k sind nur beim ADC relevant.
Aber die eigentliche Frage lautet: warum wertest du die Lichtschranke 
mit dem ADC aus? Zumindest im Moment gibt es dafür im Programm keinen 
Grund. Ausser natürlich dass es komplizierter ist.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

1
#include <avr/io.h>
2
3
int main(void)                
4
{      
5
    DDRC = 0xff;            // Alles Pins von Port C als Ausgang setzen
6
    
7
    while( 1 ) {      
8
9
      if( PINA & ( 1 << PA0 ) )
10
        PORTC |= 1<<PC0;
11
      else
12
        PORTC &= ~(1<<PC0);
13
14
      if( PINA & ( 1 << PA1 ) )
15
        PORTC |= 1<<PC1;
16
      else
17
        PORTC &= ~(1<<PC1);      // Ansonsten setze PC0 LOW
18
    }
19
}

ist doch viel simpler und für eine simple ja/nein Entscheidung über den 
Zustand des Lichtstrahls vollkommen ausreichend.

von johann (Gast)


Lesenswert?

Hallo, ich versuch mich mal...
Was mir auffiel ist deine Art den ADC auszulesen.
Du musst normalerweise den ADC in 2 Schritten auslesen:
uint8_t adc_low = ADCL;
uint8_t  adc_high = ADCH;
uint16_t adc_value = (adc_high << 8 | adc_low);

Wenn Du ADCL liest, wird das ADC Datenregister so lange nciht geupdated, 
bis ADCH gelesen wird...daher klappt das, wenn du in der Schleife immer 
wieder neu initialisierst.

Kein Garantie, ob das die Lösung ist.

von Karl H. (kbuchegg)


Lesenswert?

johann schrieb:
> Hallo, ich versuch mich mal...
> Was mir auffiel ist deine Art den ADC auszulesen.
> Du musst normalerweise den ADC in 2 Schritten auslesen:
> uint8_t adc_low = ADCL;
> uint8_t  adc_high = ADCH;
> uint16_t adc_value = (adc_high << 8 | adc_low);

Das kann der gcc alles ganz von alleine. Du schreibst einfach nur
1
   uint16_t adc_value = ADC;
und der gcc dröselt das in 2 Zugriffe auf ADCH und ADCL auf und setzt 
die auch korrekt zusammen. Es gibt keinen Grund, sich um derartige 
"Kleinigkeiten" selbst kümmern zu müssen (zumindest nicht beim ADC).

: Bearbeitet durch User
von Peter (Gast)


Lesenswert?

Karl Heinz schrieb:
> ist doch viel simpler und für eine simple ja/nein Entscheidung über den
> Zustand des Lichtstrahls vollkommen ausreichend.

Sehr gute Idee Karl-Heinz, das wäre natürlich auch eine Möglichkeit. Das 
Programm soll später eine Graycode Scheibe auslesen. Da ich momentan 
noch nicht weiß wie es später mit den Lichtverhältnissen aussieht, habe 
ich mich für die analoge Variante entschieden. Dann kann ich 
nachträglich die Werte anpassen.

Ich werde es mal mit 10k provbieren.

von Ralf G. (ralg)


Lesenswert?

Karl Heinz schrieb:
> Ausser natürlich dass es komplizierter ist.

Vielleicht LED ein-/ ausschalten zur Umgebungslichterkennung...

von Karl H. (kbuchegg)


Lesenswert?

Peter schrieb:

> noch nicht weiß wie es später mit den Lichtverhältnissen aussieht

Das ist leicht. Die LED im CNY leuchtet auf den Phototransistor.
Ausser wenn du da aus 3cm Abstand mit 20 Bauscheinwerfern draufhältst, 
sind die Lichtverhältnisse praktisch konstant immer gleich.

Und wenn doch nicht, dann macht man eben eine kleine Blende drummherum. 
Das ist bei Lichtschranken sowieso sinnvoll, aktiv erst mal alles 
Fremdlicht so gut es geht wegzubringen. Ist ja bei Licht an sich 
meistens nicht weiter schwer.

: Bearbeitet durch User
von Ralf G. (ralg)


Lesenswert?

Karl Heinz schrieb:
> Die LED im CNY leuchtet auf den Phototransistor.

Ist das nicht eine Reflexlichtschranke?

von Karl H. (kbuchegg)


Lesenswert?

Ralf G. schrieb:
> Karl Heinz schrieb:
>> Die LED im CNY leuchtet auf den Phototransistor.
>
> Ist das nicht eine Reflexlichtschranke?

Ja. Hatte das mit einer Gabellichtschranke verwechselt.
Ändert aber auch nicht viel. Macht man eben eine Blende rund um den 
Lichtweg. Oder gibt es einen Grund, warum die CNY 20cm von der SCheibe 
entfernt sein muss?

von Ralf G. (ralg)


Lesenswert?

Karl Heinz schrieb:
> 20cm von der SCheibe entfernt

... das wird, glaube ich, nix.

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz schrieb:

> Ändert aber auch nicht viel. Macht man eben eine Blende rund um den
> Lichtweg. Oder gibt es einen Grund, warum die CNY 20cm von der SCheibe
> entfernt sein muss?


Genau das wird zb beim Asuro für die Odomentrie vorgeschlagen
http://www.asurowiki.de/pmwiki/pmwiki.php/Main/Odometrie

Die haben auch die Phototransistoren an den ADC geklemmt um mit dem 
Schwellwert auf UMgebungslicht reagieren zu können.
Taugt nix.

Abhilfe: eine simple Abdeckung, um LED und Transistor vor Fremdlicht zu 
schützen und gut ists.

von johann (Gast)


Lesenswert?

Hallo Karl Heinz,

Du hast Recht! Da hab ich wieder was gelernt.
Danke dafür und Sorry, dass ich nicht weiterhelfen konnte...
evtl beim nächsten Mal.
Gruß
Johann

von Peter (Gast)


Lesenswert?

Habe mich dann doch für die binäre Variante von Karl Heinz entschieden.

Karl Heinz schrieb:
> Auf der anderen Seite: Wozu eigentlich der ADC? Willst du wirklich
> wissen, wie stark die Lichtschranke abgeschattet ist oder reicht dir
> nicht einfach die Aussage: Lichtstrahl intakt / Lichtstrahl
> unterbrochen.
> Für letzteres brauchst du keinen ADC. Schalte den CNY einfach wie einen
> Taster an einen Eingang (Pullup nicht vergessen, wobei du auch den
> internen benutzen kannst) und gut ists.


Meine Frage ist nun wie ich sinnvoll den aktuellen Status der drei CNY 
abfrage.
Bei entsprechendem Graycode soll ein String gesendet werden. Momentan 
gebe ich ein Signal auf einen entsprechenden Ausgang zum testen.

Hier meine Codeschnipsel
1
#define SENSORS        PINA
2
#define GRAYBIT0      (1<<PA0)
3
#define GRAYBIT1      (1<<PA1)
4
#define GRAYBIT2      (1<<PA2)

1
    if( !(SENSORS & GRAYBIT0) && !(SENSORS & GRAYBIT1) && !(SENSORS & GRAYBIT2) )
2
    PORTC |= 1<<PC0;                  
3
    else
4
    PORTC &= ~(1<<PC0);  
5
6
    if( !(SENSORS & GRAYBIT0) && !(SENSORS & GRAYBIT1) && (SENSORS & GRAYBIT2) )
7
    PORTC |= 1<<PC1                  
8
    else
9
    PORTC &= ~(1<<PC1);

von Karl H. (kbuchegg)


Lesenswert?

Peter schrieb:

> Meine Frage ist nun wie ich sinnvoll den aktuellen Status der drei CNY
> abfrage.
> Bei entsprechendem Graycode soll ein String gesendet werden. Momentan
> gebe ich ein Signal auf einen entsprechenden Ausgang zum testen.

Hast du denn schon probiert, ob deine Lichtschranken zu sauberen 
Schaltvorgängen führen? Das wär das erste was ich testen würde, da alles 
weitere darauf basiert.

Es ist nicht gut, wenn man von zu vielen unbelegten Annahmen ausgeht. 
Und die erste Annahme ist nun mal: Ich krieg von meinen Sensoren genau 
das, was ich erwarte.

3 LED am Port C (dieslben Pins wie die 3 Eingänge) und dann
1
  while( 1 ) {
2
    PORTC &= ~( 0x07 );
3
    PORTC |= ( SENSORS & 0x07 );
4
  }

und dann siehst du mal nach, ob die LED beim langsamen(!) drehen deines 
Encoders entsprechend der schwarz/weiss Färbung an bzw. aus gehen.

Ehe ich diesen Test nicht erfolgreich hinter mich gebracht habe, würde 
ich erst mal überhaupt nichts weiteres tun bzw. programmieren. Denn das 
kann dann nur 'nicht funktionieren', wenn schon die Lichtschranken nicht 
richtig durchkommen. Also überzeuge ich mich davon, dass die 
Lichtschranken richtig durchkommen und dass meine baulichen 
ENtstörmassnahmen tatsächlich greifen.

von Peter (Gast)


Lesenswert?

Karl Heinz schrieb:
> Hast du denn schon probiert, ob deine Lichtschranken zu sauberen
> Schaltvorgängen führen?

Nein, aber das habe ich soeben getestet. Danke für die Info. Von 
sauberen Schalten ist auszugehen. Problem ist, was ich gesehen habe, das 
natürlich nicht alle drei Bits ihren Status gleichzeitig ändern sondern 
zwischen verschienden Graycode Segmenten in einem undefinierten Zustand 
sind. Liegt wohl daran das die Scheibe nicht sauber gemalt ist.

von Karl H. (kbuchegg)


Lesenswert?

Im übrigen. Das ist der 3 Bit Graycode
1
000
2
001
3
011
4
010
5
110
6
111
7
101
8
100

Ich würde jetzt auch erst mal überprüfen, ob diese Folge von Codes sich 
auch tatsächlich einstellt, wenn ich den Encoder langsam(!) drehe.

Jetzt ist aber so ein Gray Code keine fortlaufende Binärzahl. Das soll 
er auch gar nicht sein, denn der Sinn eines Gray Codes besteht ja gerade 
darin, dass sich immer nur 1 Bit ändert, damit man Fehlererkennung 
betreiben kann.

Aber mal angenommen, man fasst den Gray Code einfach als Binärzahl auf. 
Welche Zahlen ergeben sich dann, wenn man gleichzeitig die Zeilen 
fortlaufen zählt?
1
           Binärzahl   Code-Nummer
2
  0 0 0        0          0
3
  0 0 1        1          1
4
  0 1 1        3          2
5
  0 1 0        2          3
6
  1 1 0        6          4
7
  1 1 1        7          5
8
  1 0 1        5          6
9
  1 0 0        4          7

Das jetzt in eine fortlaufende Nummerierung, also den Übergang von der 
Spalte 'Binärzahl' in die Spalte 'Code-Nummer' zu schaffen, ist aber 
nicht so schwer. Wann immer es kein offensichtliches arithmetisches 
Schema besteht, oder man keines findet, oder einfach nur zu faul zum 
Suchen ist, benutzt man einfach ein Array als Umsetztabelle. Mit der 
Binärzahl, also der Zusammensetzung der 3 Bit, geht man als Index in ein 
Array und aus dem Array kriegt man dann die Zahl, die man haben will.
1
uint8_t grayCode[8] = { 0, 1, 3, 2, 7, 6, 4, 5 };
2
3
...
4
5
  uint8_t code;
6
  uint8_t number;
7
8
  while( 1 ) {
9
    code = SENSORS & 0x07;    // Pins A0 bis A3
10
    number = grayCode[ code ];
11
12
    ....
13
  }

Wenn die EIngänge alle passen, die Lichtschranken also korrekt arbeiten, 
dann müsste sich dann eigentlich in number beim langsamen(!) drehen, die 
auf- bzw. absteigende Nummerierung 0 bis 7 einstellen.
Das wird natürlich sofort getestet und nachgesehen ob das auch wirklich 
so ist.

Tja. Wies dann weiter geht musst du entscheiden. Ich weiss ja nicht, wo 
die Reise hingehen soll.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Peter schrieb:

> was ich gesehen habe, das
> natürlich nicht alle drei Bits ihren Status gleichzeitig ändern sondern
> zwischen verschienden Graycode Segmenten in einem undefinierten Zustand
> sind. Liegt wohl daran das die Scheibe nicht sauber gemalt ist.

Ähm.
Bist du sicher, dass du einen Gray COde hast?
Denn genau das ist der Sinn eines Gray Codes, das eben nicht ALLE 
Signale gleichzeitig ihren Pegel ändern können, sondern immer nur 1. 
D.h. du kannst in diesem Sinne überhaupt keinen ungültigen 
Zwischenzustand haben, der nur darauf beruht, dass mehrere Bits 
gleichzeitig bzw. extrems kurz hintereinander ihren Pegel wechseln, wie 
es beim händischen malen der Code-Scheibe praktisch unvermeidbar ist.

http://de.wikipedia.org/wiki/Gray-Code

Was maximal sein kann, das ist, dass die sich ergebende Codenummer von 
einer Zahl zur nächsten mal kurz pendelt, das sich also die Folge ... 4, 
4, 4, 5, 4, 5, 5, 5, 5, ... ergibt, wenn du von 4 auf 5 weiterdrehst. 
Aber es kann nicht sein, dass sich da kurzfristig eine andere Zahl 
dazwischenschummelt ... 4, 4, 7, 5, 4, 1, 5, 5, 5, ....

Das ist durch das Schema der Änderungen (und damit dem auf die Scheibe 
aufzumalenden Muster) ausgeschlossen. Es sei denn natürlich, es gibt 
eine Störung. Aber abgesehen davon, ist der Code so gestaltet, dass sich 
von einer Zeile zur nächsten immer nur 1 Bit verändert und nie mehrere.

: Bearbeitet durch User
von Peter (Gast)


Angehängte Dateien:

Lesenswert?

Ok, ich arbeite mit der falschen Scheibe. Bei der Scheibe die ich 
momentan benutze ist es auch möglich das sich zwei oder sogar drei Bits 
gleichzeitig ändern. Wahnsinn was sich im Netz alles als Graycode 
Scheibe schimpft. Naja wäre ich ein wenig aufmerksamer gewesen, hätte 
ich es auch selbst festgestellt. Weil wie du ja schon sagtest, der 
eigentliche Sinn des Graycodes geht damit verloren.

Werde die andere Scheibe mir mal ausdrucken und es nochmal testen.

Erstmal recht herzlichen dank! Werde das mit dem Array jetzt mal testen.

von Karl H. (kbuchegg)


Lesenswert?

Peter schrieb:

> Werde die andere Scheibe mir mal ausdrucken und es nochmal testen.

Die sieht besser aus.
Wenn ich das richtig sehe, dann ist bei der Scheibe Bit 0 die äussere 
Spur und Bit 2 die innerste.

Die andere Scheibe ist kein Gray-Code. Da ist einfach nur eine binäre 
Codierung drauf. Mit genau den Problemen, die du schilderst. Wenn dir 
die jemand als 'Gray-Code' verkauft hat, dann hat er nicht verstanden, 
was ein Gray Code ist. Vielleicht hat er auch gedacht, es reicht, wenn 
er sie in Grau ausdruckt :-)

: Bearbeitet durch User
von Peter (Gast)


Lesenswert?

So das funktioniert schonmal super!!! Bekomme im HyperTerminal immer den 
richtigen Code angezeit. Top!

Das kuriose ist jetzt, das wenn ich die util/delay.h libary einbinde, 
das da nur noch Müll rauskommt.

Benutze die uart.c library von Peter Fleury, ist da jemand dieser Fehler 
bekannt?

von Karl H. (kbuchegg)


Lesenswert?

Peter schrieb:

> Das kuriose ist jetzt, das wenn ich die util/delay.h libary einbinde,
> das da nur noch Müll rauskommt.

Zeigen.
In einem üblichen C Programm für den AVR braucht man so gut wie nie 
explizite Delays. Ganz selten kommt sowas vor, wie zb bei der 
Implementierung eines One-Wire Protokolls, wo die Zeiten so kurz sind, 
dass sich eine Lösung mit Timer nicht lohnt. Aber abgesehen von solchen 
Sonderfällen, sind delays meist die Ursache von Problemen und nicht die 
Lösung.

> Benutze die uart.c library von Peter Fleury, ist da jemand dieser Fehler
> bekannt?

Nope. Die Fleury UART Lösung funktioniert erstklassig. Die ist auch 
nicht abhängig von irgendwelchen Delays, sondern Interrupt gesteuert.

: Bearbeitet durch User
von Eric B. (beric)


Lesenswert?

Peter schrieb:
> Das kuriose ist jetzt, das wenn ich die util/delay.h libary einbinde,
> das da nur noch Müll rauskommt.

seufz Was heisst denn "Library einbinden"?
/util/delay.h/ is eine Header-Datei, keine Library.
Wenn du die Header-Datei mit /#include "util/delay.h"/ inkludierst und 
sonst nichts an deine Code änderst, sollte sich erst mal nichts im 
Programmverhalten ändern.
Also, was hast du noch geändert?

von Peter (Gast)


Lesenswert?

ok. Ich habe überlegt das ich eventuell nur alle 1-2sek den aktuellen 
Code verschicke. Hier der aktuelle Quelltext der funktioniert. Binde ich 
die Delay Bib ein, funktionierts nicht mehr.

1
/* define CPU frequency 16Mhz */
2
#ifndef F_CPU
3
#define F_CPU 16000000UL
4
#endif
5
6
/* include Bibliotheken */
7
#include <stdlib.h>
8
#include <avr/io.h>
9
#include <avr/interrupt.h>
10
#include <avr/pgmspace.h>
11
12
#include "uart.h"
13
14
15
/* define 19200 baud */
16
#define UART_BAUD_RATE      19200      
17
18
/* define In- and Output */
19
#define SENSORS        PINA
20
#define GRAYBIT0      (1<<PA0)
21
#define GRAYBIT1      (1<<PA1)
22
#define GRAYBIT2      (1<<PA2)
23
24
25
/* MAIN Programm */
26
int main(void)
27
{
28
    unsigned int c;
29
    char buffer[7];
30
 
31
    uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); 
32
33
    sei();
34
35
  while (1)
36
     { 
37
    uint8_t grayCode[8] = { 0, 1, 3, 2, 7, 6, 4, 5 };  // Array für den GrayCode anlegen
38
                                       
39
40
    char wind[7];
41
    uint8_t code;
42
    uint8_t number;
43
44
     
45
       code = SENSORS & 0x07;              // Pins A0 bis A3
46
       number = grayCode[ code ];        
47
       itoa( number, wind, 10);            // Integer in String konvertieren
48
       uart_puts(wind);                // Transferiere zu UART
49
       
50
     }
51
       
52
    for(;;)
53
    {
54
        /*
55
         * Get received character from ringbuffer
56
         * uart_getc() returns in the lower byte the received character and 
57
         * in the higher byte (bitmask) the last receive error
58
         * UART_NO_DATA is returned when no data is available.
59
         *
60
         */
61
        c = uart_getc();
62
        if ( c & UART_NO_DATA )
63
        {
64
            /* 
65
             * no data available from UART 
66
             */
67
        }
68
        else
69
        {
70
            /*
71
             * new data available from UART
72
             * check for Frame or Overrun error
73
             */
74
           if ( c & UART_FRAME_ERROR )
75
           {
76
                /* Framing Error detected, i.e no stop bit detected */
77
                uart_puts_P("UART Frame Error: ");
78
            }
79
            if ( c & UART_OVERRUN_ERROR )
80
            {
81
                /* 
82
                 * Overrun, a character already present in the UART UDR register was 
83
                 * not read by the interrupt handler before the next character arrived,
84
                 * one or more received characters have been dropped
85
                 */
86
                uart_puts_P("UART Overrun Error: ");
87
            }
88
            if ( c & UART_BUFFER_OVERFLOW )
89
            {
90
                /* 
91
                 * We are not reading the receive buffer fast enough,
92
                 * one or more received character have been dropped 
93
                 */
94
                uart_puts_P("Buffer overflow error: ");
95
             }
96
            /* 
97
             * send received character back
98
             */
99
            uart_putc( (unsigned char)c );
100
        }
101
    }
102
    
103
}

von Dietrich L. (dietrichl)


Lesenswert?

Peter schrieb:
> Binde ich
> die Delay Bib ein, funktionierts nicht mehr.

Und wie ist der Quelltext dann?

von Peter (Gast)


Lesenswert?

Sorry, es funktioniert. Aber wo ist denn der Unterschied zwischen
1
#include <util/delay.h>

und
1
#include "util/delay.h"

von Karl H. (kbuchegg)


Lesenswert?

Peter schrieb:
> Sorry, es funktioniert.

Jetzt muss ich nachfragen.
Was, genau, verstehst du unter 'funktioniert nicht'.
Der allgemeine Sprachgebrauch ist der, dass 'funktioniert nicht' ein 
Laufzeitproblem darstellt. Sprich: das Programm macht nicht das, was du 
erwartest.

WEnn du ein Problem mit einer Fehlermeldung des Compilers hast, dann ist 
der Terminus nicht 'funktioniert nicht', sondern 'compiliert nicht' oder 
'gibt einen Fehler beim compilieren'.

> Aber wo ist denn der Unterschied zwischen
>
1
> #include <util/delay.h>
2
>
>
> und
>
1
> #include "util/delay.h"
2
>

bei <> sucht der Compiler zuerst auf den ihm bekannten 
Systemverzeichnissen. Bei "" wird das angegegene File zuallerst mal auf 
deinem Projektverzeichnis gesucht.
Der Compiler benutzt normalerweise allerdings mehrere Pfade, um Header 
Files zu finden. D.h. wird ein Header File nicht auf dem 
Projektverzeichnis gefunden, kann es schon sein, dass er dann auch die 
Systemverzeichnisse danach durchsucht.
Das ganze hängt auch davon ab, welche Include Pfade dem COmpiler 
mitgegeben werden, sprich welche Include-Pfade in der IDE eingetragen 
worden sind.

Solange du also kein eigenes delay.h auf dem Projektverzeichnis hast, 
sollte das eigentlich keinen UNterschied machen.

von Peter (Gast)


Angehängte Dateien:

Lesenswert?

Karl Heinz schrieb:
> Was, genau, verstehst du unter 'funktioniert nicht'.
> Der allgemeine Sprachgebrauch ist der, dass 'funktioniert nicht' ein
> Laufzeitproblem darstellt. Sprich: das Programm macht nicht das, was du
> erwartest.

genau so meinte ich es auch. Compiliert hat das Programm immer ohne zu 
meckern. Wollte es nochmal nachstellen, aber jetzt macht es ihm keinen 
unterschied. Fakt ist, das wenn ich mit
1
#include <util/delay.h>
kompiliert habe, im Hyperterminal nur
1
□□□□□□□□
 rauskam.

Jetzt, nachdem ich die Delay so eingebunden habe
1
#include "util/delay.h"
funktioniert es wieder perfekt. (Siehe Screenshot, ein Durchlauf der 
Scheibe) Nun macht es auch keinen unterschied wie ich sie einbinde.

Naja jetzt weiß ich wenigstens mal wo der Unterschied zwischen "" und <> 
ist. :)
Trotzdem etwas kurios. Kann es manchmal auch einfach helfen den ATmega 
einfach mal zu löschen, trotz dessen das man ihn vor jeder 
Programmierung löscht? Macht das einen Unterschied?

Nochmal vielen lieben Dank für die ganzen Infos. Ihr habt mir bis jetzt 
wirklich sehr weitergeholfen!

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.