Hallo Zusammen
Ich bin kein µC Profi und arbeite mich gerade durch Beispielprogramme
sowie das Tutorial.
Zur Zeit experimentiere ich gerade mit einem atmega 8 auf der STK500
Platine
Zu tun: gewandelten AD-Wert per UART auf dem Hyperterminal
darzustellen.
Zu der Hardware:
1. ATMega8 auf dem STK500 Platine
2. extene Quarz 4 Mhz
3. int AREF also AREF-Jumper gesetzt.
4. Analog eingang direckt an PINC.0 verbunden.
Schon ausprobieren:
uart (senden und empfangen funktionieren)
Zu der Software:
Ich habe aus ADC-Beispielen eine eigene Code zusammengebastelt.
Code:
1
#ifndef F_CPU
2
#warning "F_CPU was not defined yet, now make up with 4000000"
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD-1000) //Error per thousand
11
12
13
#if ((BAUD_ERROR>10)||(BAUD_ERROR<-10))
14
#error Systematic error in the baud rate more than 1% and thus too high!
15
#endif
16
17
18
#include<avr/io.h>
19
#include<stdlib.h>
20
#include<inttypes.h>
21
#include<util/delay.h>
22
23
voiduart_Init()
24
{
25
26
UBRRH=(unsignedchar)(UBRR_VAL>>8);
27
UBRRL=(unsignedchar)UBRR_VAL;
28
UCSRB|=(1<<RXEN)|(1<<TXEN);
29
UCSRC=(1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1);
30
31
}//end uart_Init()
32
33
voiduart_Putc(unsignedcharc)
34
{
35
while(!(UCSRA&(1<<UDRE)));
36
UDR=c;
37
_delay_ms(10);
38
}//end uart_Putc()
39
40
voiduart_Puts(char*string)
41
{
42
while(*string!='\0')
43
{
44
uart_Putc(*string);
45
string++;
46
}
47
}//end uart_Puts()
48
49
voidADC_Init(uint8_tMux_channel)
50
{
51
DDRC=0x00;//Define as Input
52
PORTC=0xFF;//Enables Pull-Up on PC0
53
54
ADCSRA=(1<<ADEN);//Enables the ADC
55
ADCSRA|=(1<<ADPS2)|(1<<ADPS0);//Set ADC with Prescaler 32
56
57
ADMUX=Mux_channel;//Select pin ADC0 using MUX
58
_delay_us(40);
59
ADMUX|=(1<<REFS0)|(1<<REFS1);//Int. REF Voltage Reference
60
ADMUX|=(0<<ADLAR);//The result is right adjusted
61
}
62
63
64
uint16_tADC_Read(uint8_tMux_channel)
65
{
66
uint8_ti;
67
uint16_tresult=0;
68
69
//interrupt
70
TCNT0=5;//Zählregister setzen
71
TIMSK|=(1<<TOIE0);
72
TCCR0|=(1<<CS01);
73
TCCR0|=(1<<CS00);//CPU Takt wird verwendet --> 4MHz
74
TIFR|=(1<<TOV0);//startet interrupt bei jedem überlauf
75
76
77
ADC_Init(Mux_channel);
78
79
ADCSRA|=(1<<ADSC);//Start conversion
80
while(ADCSRA&(1<<ADSC));//wait until converstion completed
81
result=ADCW;
82
result=0;
83
84
85
//Mittwert von vier messwerte
86
for(i=0;i<4;i++)
87
{
88
ADCSRA|=(1<<ADSC);//A single conversion
89
while(ADCSRA&(1<<ADSC));
90
result+=ADCW;
91
}
92
93
ADCSRA&=(0<<ADEN);// ADC reanables
94
result/=4;
95
96
returnresult;
97
}//End int ADC_Read()
98
99
//Interrupt Service Routine für AD Wandlung
100
ISR(TIMER0_OVF_vect)
101
{
102
ADIteiler++;//clocks interrupt counter value on each ms
103
if(ADIteiler>=60)//Counter cycle for all ms 60
104
{
105
ADIteiler=0;//counter 0 set
106
107
uint16_tresult=ADC_Read(0);//Read the Analog volt. on PinC 0
108
uart_Puts("\r\n\n");
109
utoa(result,s,10);
110
uart_Puts(s);
111
uart_Putc('$');
112
}
113
}//End ISR(TIMER0_OVF_vect)
114
115
116
intmain()
117
{
118
uart_Init();
119
120
uart_Puts("\r\n");
121
uart_Puts("Messung fängt an:");
122
uart_Puts("\r\n");
123
124
sei();//Enable Globale Interrupts
125
126
for(;;);
127
128
return(0);
129
}
Problem:
ADC-Werte sind falsch. Für den selben Analog Eingang kriege ich
Unterschiedliche Digital Wert.
beispiel: für 2.04v ausgabe: 1023 oder 255 oder 3 oder 166 oder 875...
Ist eventuell ein Fehler in meinen Code? fehlt mir eine elektronische
montage? Kann jemand mirhelfen?
Danke im Voraus.
Gruss carine
probier es doch erstmal ohen deine Interrupt Service Routine für AD
Wandlung
sondern stoss über _delay_ms jede sek ein wandlung in der main an...
Was sucht das _delay_ms in der putc?
carine schrieb:> Ist eventuell ein Fehler in meinen Code? fehlt mir eine elektronische> montage? Kann jemand mirhelfen?
Möglich.
Der Code ist so verworren, das man den erst einmal gründlich auseinander
nehmen muss
Was hältst du davon, erst einmal die ADC-Routine aus dem Tutorial, so
wie sie ist, zu benutzen. Und zwar ohne den ganzen Timer Klimbim
1
intmain()
2
{
3
uint16_tresult;
4
...
5
6
while(1){
7
uint16_tresult=ReadChannel(0);
8
9
uart_Puts("\r\n\n");
10
utoa(result,s,10);
11
uart_Puts(s);
12
uart_Putc('$');
13
}
14
}
Die ReadChannel Funktion ist die Funktion aus dem Tutorial
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Aktivieren_des_ADC
Wenn du danach immer noch seltsame Werte hast, könnte es ein Hardware
Problem sein.
Wenn die Werte plausible aussehen, kannst du jetzt immer noch anfangen,
die ReadChannel Funktion auseinanderzunehmen und umzugestalten. Tauchen
dann plötzlich seltsame Werte auf, kann es nur noch mit deiner zuletztz
durchgeführten Änderung zu tun haben.
Karl heinz Buchegger schrieb:> Der Code ist so verworren
In der Tat. Bei dem geposteten Code kommt z.B. überhaupt nie eine
ADC-Wandlung zustande. Der einzige ADC_Read Aufruf steckt im
Timer-Overflow-Interrupt, aber der Timer wird überhaupt erst in ADC_Read
in Gang gebracht. Es ist somit schon völlig unerklärlich, wie der OP
überhaupt zu diesem Ergebnis kommt:
> ADC-Werte sind falsch. Für den selben Analog Eingang kriege ich> Unterschiedliche Digital Wert.> beispiel: für 2.04v ausgabe: 1023 oder 255 oder 3 oder 166 oder 875...
Da dürfte dann ja wohl mal wieder einiges an relevantem Code fehlen.
vielen Dank an alle für die viele Tipps,
leider könnte ich gestern wegen Internet unterbrechung nichts mehr
machen.
jetzt habe ich eins zu eins den Tutorial Code copier, aber diesmal
bekommen ich für irgend eine Spannung eingang 1023.
Wo liegt jetzt mein Fehler?
Danke für eure Hilfe im voraus
>bekommen ich für irgend eine Spannung eingang 1023.
Klingt irgendwie nach eingeschaltetem Pull-Up oder einem anderen
Hardware-Fehler.
(Fehlerfrei) compilierbarer Code wäre übrigens sehr hilfreich.
Analog Strom vom Laser ----- direckt an PINC.0
(Ich habe kein Poti benutzt weil der Strom im bereich 0v bis 2,005v
liegt)
AREF------100n condensator (weil int AREF wird benutzt)
ist das richtig?
danke
carine schrieb:> bitte hilfe!!!> ich glaube, dass mein Fehler an der Schaltung liegt!!!!!
Tja, nur wenn du sie uns nicht ordnetlich zeigst, woher sollen wir
wissen, WIE deine Schaltung aussieht?
Analog Strom vom Laser ----- direckt an PINC.0
(Ich habe kein Poti benutzt weil der Strom im bereich 0v bis 2,005v
liegt)
lies mal bitte noch mal den Unterschied von Strom und Spannung nach...
Was kommt von deinem Sensor? ein Strom oder eine Spannung? Wenn eine
Spannung was zeigt das Multimeter an?
>> PORTC = 0xFF; //Enables Pull-Up on PC0> Analog Strom vom Laser ----- direckt an PINC.0
Warum schaltest du die Pull-ups an PC0 an?
> AREF------100n condensator (weil int AREF wird benutzt)
Wo ist der Kondensator noch angeschlossen?
Schaltplan wäre gut!
Wie ist das STK500 gejumpert, speziell der VTARGET und der AREF Jumper?
Bei "interne Referenzspannung" im Programm würde ich den AREF Jumper
entfernen.
http://www.atmel.com/dyn/resources/prod_documents/doc1925.pdf> ADMUX |= (1<<REFS1) | (1<<REFS0); // interne Referenzspannung nutzen
Internal 2.56V Voltage Reference with external capacitor at AREF pin
> (Ich habe kein Poti benutzt weil der Strom im bereich 0v bis 2,005v liegt)
Bist du 100% sicher, dass die zu messende Spannung nicht über Internal
2.56V Voltage Reference liegt. Wurde das schon im Betrieb nachgemessen?
Wie wurde das gemessen? Besteht eine Chance kurzzeitige
Spannungsüberschreitungen zu sehen?
Nicht vergessen der ADC soll laut Datenblatt mit 50 bis 200 kHz messen!
>> 2. extene Quarz 4 Mhz> ADCSRA = (1<<ADEN) | (1<<ADPS1) | (1<<ADPS0); // Frequenzvorteiler> // setzen auf 8 (1) und ADC aktivieren (1)
Vorteiler 8 ist nicht ausreichend. Stell mal einen höheren ein.
Dabei auch beachten, dass die 2.56V kein in Stein gemeisselter Wert
sind. Der Wert hängt von der Bandgap Referenz ab und die ist abhängig
von der Versorgungsspannung und der Temperatur.
Ich würde versuchsweise mal AVcc als Referenz einstellen.
ADMUX |= (1<<REFS0); // AVcc als Referenz verwenden
Danke für die Antwort.
Aber ist das Problem immer noch nich gelöst, weil für unterschiedlichen
Werte wird anstatt 1023, jetzt 16 angezeigt.
Initialisierung:
Ich habe keine Software um Schaltungen aufzuzeichnen, ich hoffe dies
wird verständlich sein:
AREF----100nF---GND
Analog eingang Spannung-----100K poti-----Masse
|
|
PCO
Ich habe der Spannung am AREF von atmega8 gemessen.
Ich merke, dass die Spannung wackeln (Es wird immer weniger).
Ist das normal? wieso verändern sich die Spannung?
danke für euere Hilfe
>>> PORTC = 0xFF; //Enables Pull-Up on PC0>> Analog Strom vom Laser ----- direckt an PINC.0>Warum schaltest du die Pull-ups an PC0 an?
Ist das verbessert? IMHO ist es kontraproduktiv per Pull-up Vcc auf den
ADC Messeingang zu geben.
> Analog eingang Spannung-----100K poti-----Masse> |> |> PCO
Das 100k Poti hättest du ganz am Anfang erwähnen müssen!
Warum ist das überhaupt drin?
Wo steht der Schleifer bei deinen Experimenten?
Was sagt das Atmega8 Datenblatt zu dem u.U. riesigen Widerstand in der
zu messenden Leitung?
Kommt durch diese künstliche Einschürung der Messleitung innerhalb der
Messzeit überhaupt genügend Saft an den ADC?
Aus dem Atmega8 Datenblatt:
"The ADC is optimized for analog signals with an output impedance of
approximately 10 kΩ or less. If such a source is used, the sampling time
will be negligible. If a source with higher impedance is used, the
sampling time will depend on how long time the source needs to charge
the S/H capacitor, with can vary widely. The user is recommended to only
use low impedant sources with slowly varying signals, since this
minimizes the required charge transfer to the S/H capacitor."
> Initialisierung:> void ADC_Init (uint8_t Mux_channel)
Initialisierst du jetzt zweimal? Einmal in dem ADC_Init() und einmal in
dem ReadChannel() aus
Beitrag "Re: ADC wert über RS232 senden"
Würde ich nicht machen. Ich würde bei einer Programmversion bleiben.
> Aber ist das Problem immer noch nich gelöst, weil für unterschiedlichen> Werte wird anstatt 1023, jetzt 16 angezeigt.
Du hast leider keinen Quellcode angegeben, bei der man sieht, wie du den
ADC Wert einlesen tust.
Die Timer-Variante (Beitrag "ADC wert über RS232 senden")
hatten wir ja schon verworfen
Und die ReadChannel()-Variante
(Beitrag "Re: ADC wert über RS232 senden") hast du
anscheinend abgeändert indem du eine ADC_Init() geschrieben hast.
Offen ist: Wie und wo wird ADC_Init() aufgerufen. Wie und wo werden die
Ergebnisse der Wandlung eingelesen, gibt es ein verändertes
ReadChannel()?
>>Mal 'ne ganz blöde Frage: AVCC ist aber schon auch angeschlossen, oder?
ADMUX |= (1<<REFS0); entspricht im Datenblatt AVCC with external
capacitor at AREF pin
deswegen habe ich
AREF----100nF---GND
bei mir ist gar nichts an AVCC angeschlossen.
habe ich etwas falsch verstanden!!?
Wie sieht es hiermit aus:
> Wie ist das STK500 gejumpert, speziell der VTARGET und der AREF Jumper?> Bei "interne Referenzspannung" im Programm würde ich den AREF Jumper> entfernen.
>> AVcc und AGND muss immer angeschlossen werden. Das sollte aber auf>>dem STK500 automatisch erledigt werden (Vcc---AVcc und GND---AGND)
ich finde keine Pin für AVCC und AGND um die beide anzuschliessen, ich
nehme also an dass es vom STK500 schon gemacht wurde.
am PORTE/AUX
schliessen ich an
PE4 (REF)-------100nF--------PE5 (GND)
ist das so richtig ?
Ja.
Woher kommt deine Messspannung und was ist deren Bezugspunkt (Masse,
GND)? Ist dieses GND das gleiche, wie das GND des STK500 oder - im
anderen Fall - sind beide GNDs miteinander verbunden? => Schaltplan?
Deine Messungen mit dem Multimeter: Wo genau befinden sich die
Kontaktpunkte der Messleitungen an der Schaltung?
Bereits gestellte und noch offene Fragen:
Was ist mit dem 100k Poti?
Wie sieht der aktuelle Quellcode aus?
>>Das 100k Poti hättest du ganz am Anfang erwähnen müssen!>>Warum ist das überhaupt drin?>>Wo steht der Schleifer bei deinen Experimenten?
ICh will eine Spanung von ov bis 2,05v messen, um sicher zu sein, dass
meine zu messende Spannung nicht die Internal Ref 2.56 überschreit,
benutze ich das Poti.
Wo der Schleifer steht, kann ich nicht genau sagen.
Aber für Spannung eingang 1,8v zum Beipiel habe ich 1,6v am PCO.
Und jetzt benutze ich 1k Poti.
Analog eingang Spannung-----1K poti-----Masse
|
|
PCO
>>Woher kommt deine Messspannung und was ist deren Bezugspunkt (Masse,>>GND)? Ist dieses GND das gleiche, wie das GND des STK500 oder - im>>anderen Fall - sind beide GNDs miteinander verbunden? => Schaltplan?
Messspannung kommt aus einer Laser (DT50 vom Sick)
(GND = Masse) verbunden mit GND von STK500
>>Wie sieht der aktuelle Quellcode aus?
* 4... 20 mA Analogausgang + zusätzlich 1x PNP Schaltausgang
5
^^^^^^^^^^^^^^^^^^^^^^^^
6
* Display mit intuitiv bedienbarer Menüführung
7
* Fremdlichtsicher bis 40 klux
8
* Großer Betriebstemperaturbereich von -30 °C bis +65 °C
9
* Robustes Druckguss-Zinkgehäuse
Der Sensor gibt also analog 4 mA (minimaler Abstand) bis 20 mA
(maximaler Abstand) aus.
Das wäre in den Bereich ca. 0V (minimaler Abstand) bis ca. Aref
(maximaler Abstand) umzuformen.
Das Umformen kann nach dem Ohmschen Gesetz U = R * I mit einem
Widerstand gemacht werden.
Sensor Out o----------+----------o ADC0 (PC0) µC
|
#
R #
#
|
Sensor GND o----------+----------o AGND (GND) µC
Angenommen du hast Aref auf Vcc (5V) eingestellt. Dann soll bei Imax. =
20 mA die maximale Spannung 5V an R abfallen:
R <= 5V / 20 mA <= 250 Ohm
Wenn du für R einen 1 kOhm Poti nimmst:
Sensor Out o----------+
|
#
R #<---------o ADC0 (PC0) µC
#
|
Sensor GND o----------+----------o AGND (GND) µC
Dann fällt an R eine max. Spannung von 20 mA * 1 kOhm = 20V ab, wenn der
Sensor versucht 20 mA auszugeben. Damit der Sensor das schafft müsste er
allerdings eine Spannung von 20V an Sensor Out ausgeben. Bei einer
Betriebsspannung des DT50 von 10..30V wäre das sogar denkbar.
Bei dem 100 kOhm Poti kann der Sensor allerdings nur bis zu seiner
Betriebsspannung schuften, die 2000 V um 20 mA zu treiben, wird er nicht
schaffen...
Steht in diesen Fällen der Schleifer des Potis ganz oben, ist der µC
vielleicht Geschichte, weil die Absolute Maximal Electrical Ratings
überschritten werden (ADC0 Eingangsspannung >> Vcc+0,5V).
Zurück zu den 250 Ohm...
Beim unteren Messwert (Mindestabstand) liefert der Sensor 4 mA. Das
ergibt nach der Umformung U = 4 mA * 250 Ohm = 1 V.
Dein 4 mA .. 20 mA Sensorsignal wird also in ein ca. 1 V .. ca. 5 V
Spannungssignal umgeformt. Das ca. weil die Präzision von R und
Leitungswiderstände zusammenwirken. Du musst deinen ADC Messwert
kalibrieren.
Wenn du max. 2,56 V haben willst, dann wäre R <= 2,56 V / 20 mA <= "128"
Ohm. Der Messbereich wäre dann 0,512V...2,56V
>>>Wie sieht der aktuelle Quellcode aus?> ADMUX |= (1<<REFS0); // AVcc als Referenz verwenden> ADMUX |= (0<<ADLAR); //The result is right adjusted
Hier fehlt das Einschalten der ADC Single Conversion. Die nächste
Schleife ist deswegen endlos. Das gezeigte Programm kann keine Werte
ausgeben!
> while ( ADCSRA & (1<<ADSC) ) {> ; // auf Abschluss der Konvertierung warten> }
Abhilfe:
ADCSRA |= (1<<ADSC); // eine Wandlung "single conversion" auslösen
vor der while-Schleife einfügen.
Stopp, Fehler meinerseits, die Schleife ist nicht endlos, sondern
rauscht direkt durch. Der Sinn der Schleife (Dummyreadout des ersten
ADC-Wertes) wird verfehlt. Die vorgeschlagene Zeile sollte dennoch
eingefügt werden.
vielen Dank Stefan
Wenn du max. 2,56 V haben willst, dann wäre R <= 2,56 V / 20 mA <= "128"
Ohm. Der Messbereich wäre dann 0,512V...2,56V
ich habe einen Wiederstand R=100 Ohm genohmen und so angeschlossen
Sensor Out o----------+----------o ADC0 (PC0) µC
|
#
R #
#
|
Sensor GND o----------+----------o AGND (GND) µC
>>Dein 4 mA .. 20 mA Sensorsignal wird also in ein ca. 1 V .. ca. 5 V>>Spannungssignal umgeformt. Das ca. weil die Präzision von R und>>Leitungswiderstände zusammenwirken. Du musst deinen ADC Messwert>>kalibrieren.
Wie mache ich die Kalibrierung?
versuche ich noch es hinzukriegen.
und melde mich dann wieder
>>Abhilfe:>>ADCSRA |= (1<<ADSC); // eine Wandlung "single conversion" auslösen
ja, schon korriegiert
vielen vielen Dank
problem gelöst.
Problem
vorher:
GND sensor -------GND voltmeter(12v Betriebsspannung des DT50 )
lösung:
)
Sensor Out o----------+
|
#
R #<---------o ADC0 (PC0) µC
#
|
Sensor GND o----------+----------o AGND (GND) µC