Hallo Leute!
Ich bin neu hier und habe natürlich eine Frage:
Ich versuche zurzeit einen Temperatursensor zu bauen.
Die Temperatur habe ich schon in Form von Spannung (10mV / °Kelvin).
Nun will ich diese in Form von Grad auf 3 Siebensegmentanzeigen
ausgeben. (falls die Temperatur dreistellig ist).
Dies ist das erste mal dass ich mit einem ADC arbeite, deswegen
funktioniert es natürlich nicht gleich..
Hier der Code:
1
#include<avr/io.h>
2
3
#define LOOP 5
4
#define COMM_ANODE_R 0
5
#define COMM_ANODE_M 0
6
#define COMM_ANODE_L 0
7
8
voidADC_init(void);
9
voidtest_readout(void);
10
doublemeasurement(void);
11
voidsev_segm_l(intnumber);
12
voidsev_segm_m(intnumber);
13
voidsev_segm_r(intnumber);
14
15
voidmain()
16
{
17
18
DDRB=0xFF;// als Ausgang
19
DDRC=0xFF;
20
DDRD=0xFF;
21
22
doubleerg;
23
24
25
ADC_init();
26
test_readout();
27
28
while(1)
29
{
30
erg=measurement();
31
32
erg*=19,1;//4,87V / 255 = 19,1 mV
33
erg*=10;
34
35
erg/=10;
36
37
sev_segm_l((int)(erg/100));
38
// die letzte Stelle des Ergebnises
39
sev_segm_m((int)(erg/10-(erg/100)*10));
40
// die mittlere Stelle...
41
sev_segm_r((int)(erg-(erg/10)*10));
42
// usw.
43
}
44
45
}
46
47
voidADC_init(void)
48
{
49
/*
50
51
ADEN=0; aktiviert ADC (Analog-Digital-Converter)
52
53
ADSC=0; setzt auf (nicht)freilaufenden Betrieb
54
Der ADC misst (nicht)durchgehend
55
56
ADATE=1; Für freilaufenden Modus
57
58
ADIF=0; Interrupt-Flag aus
59
60
ADIE=0; Kein Interrupt Flag
61
62
ADPS0...ADPS2 : Teilungsfaktor
63
CPU_Clk/Fakt. = >40kHz & <150 kHz
64
65
*/
66
67
ADCSRA=0b01000101;
68
69
/*
70
71
REFS0..REFS1 Referenzspannungsmodus
72
73
ADLAR Darstellung des Ergebnisses
74
75
MUX Pinnummer des eingehenden A/D Ports
76
77
*/
78
79
ADMUX=0b00000000;
80
}
81
82
voidtest_readout(void)
83
{
84
85
doubledummy_erg;
86
87
88
ADCSRA|=(1<<ADSC);// "Warmlaufrunde" für ADC
89
90
while(ADCSRA&(1<<ADSC))
91
{
92
//auf Abschluss der Konvertierung warten
93
}
94
95
dummy_erg=ADCW;//ADCW muss einmal gelesen werden
96
//sonst wird das Ergebnis der
97
//nächsten Wandlung nicht übernommen
98
99
}
100
101
doublemeasurement(void)
102
{
103
104
doubleerg=0;
105
106
107
for(inti=0;i<LOOP;i++)// zur Genauigkeit wird der
108
{// Mittelwert von LOOP-Messungen
109
// verwendet
110
ADCSRA|=(1<<ADSC);// Eine Wandlung
111
112
while(ADCSRA&(1<<ADSC))
113
{
114
;//auf Abschluss der Konvertierung warten
115
}
116
117
erg+=ADCW;
118
}
119
120
erg/=LOOP;
121
122
returnerg;
123
124
}
125
126
voidsev_segm_l(intnumber)
127
{
128
if(COMM_ANODE_L)
129
{
130
switch(number)
131
{
132
case0:
133
134
//wobei PORT1 = a usw.
135
PORTB=~(0b00111111);
136
break;
137
138
case1:
139
140
PORTB=~(0b00000110);
141
break;
142
143
case2:
144
145
PORTB=~(0b01011011);
146
break;
147
148
case3:
149
150
PORTB=~(0b01001111);
151
break;
152
153
case4:
154
155
PORTB=~(0b01100110);
156
break;
157
158
case5:
159
160
PORTB=~(0b01101101);
161
break;
162
163
case6:
164
165
PORTB=~(0b01111101);
166
break;
167
168
case7:
169
170
PORTB=~(0b00000111);
171
break;
172
173
case8:
174
175
PORTB=~(0b01111111);
176
break;
177
178
case9:
179
180
PORTB=~(0b01101111);
181
break;
182
183
default:
184
185
PORTB=0b01000000;
186
break;
187
}
188
}
189
190
else
191
{
192
switch(number)
193
{
194
case0:
195
196
//wobei PORT1 = a usw.
197
PORTB=0b00111111;
198
break;
199
200
case1:
201
202
PORTB=0b00000110;
203
break;
204
205
case2:
206
207
PORTB=0b01011011;
208
break;
209
210
case3:
211
212
PORTB=0b01001111;
213
break;
214
215
case4:
216
217
PORTB=0b01100110;
218
break;
219
220
case5:
221
222
PORTB=0b01101101;
223
break;
224
225
case6:
226
227
PORTB=0b01111101;
228
break;
229
230
case7:
231
232
PORTB=0b00000111;
233
break;
234
235
case8:
236
237
PORTB=0b01111111;
238
break;
239
240
case9:
241
242
PORTB=0b01101111;
243
break;
244
245
default:
246
247
PORTB=0b01000000;
248
break;
249
}
250
}
251
}
252
253
voidsev_segm_m(intnumber)
254
{
255
if(COMM_ANODE_M)
256
{
257
switch(number)
258
{
259
case0:
260
261
//wobei PORT1 = a usw.
262
PORTC=~(0b00111111);
263
break;
264
265
case1:
266
267
PORTC=~(0b00000110);
268
break;
269
270
case2:
271
272
PORTC=~(0b01011011);
273
break;
274
USW...
Die Siebensegmentanzeigen geben nur Nullen aus.
Die analoge Spannung habe ich auf den PORT A0 angelegt. Ist das bei
diesem Code richtig?
Mfg
Christoph
Also in diner ADC_init funktion schaltest du den ADC nicht ein sondern
startest ihn blos (du setzt enable auf 0 aber start auf 1). es müsste
eigentlich genau umgekehrt sein:
ADCSRA=0b10000101;
und nicht ADCSRA=0b01000101;
probier das mal ... mir fallen in dem code zwar noch andre sachen auf
aber die sollten zumindest die konvertierung nicht verhindern.
Gerald D. schrieb:
> Also in diner ADC_init funktion schaltest du den ADC nicht ein sondern> startest ihn blos (du setzt enable auf 0 aber start auf 1). es müsste> eigentlich genau umgekehrt sein:>> ADCSRA=0b10000101;>> und nicht ADCSRA=0b01000101;>>> probier das mal ... mir fallen in dem code zwar noch andre sachen auf> aber die sollten zumindest die konvertierung nicht verhindern.
Und ändere deine Syntax
ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS0);
ist doch leichter zu lesen als
ADCSRA=0b10000101
beispielsweise verwendest du den datentyp double für das ergebnis. ich
meine es ist ein atmega32 ... mit limitiertem speicher und befehlssatz.
eine double operation benötigt unmengen an rechenleistung ... wennste
stattdessen einen int16 hernimmst und die werte schön aufskalierst
kommst auch auf das selbe ergebnis ohne rießen platzbedarf und
performanceverlust.
weiters ...
erg*=19,1;
wieso kompiliert dieser ausdruck ? sind commazahlen nicht eigentlich mit
punkt festgelegt?
oder die zeilen
erg*=10;
erg/=10;
nacheinander wirken ein wenig naja überflüssig, weis nicht was du damit
erreichen willst, da es sich ja um einen double handelt.
aber das sind nur eben kleinigkeiten die hier nicht ins gewicht fallen
aber bei projekten die mehr logik erfordern dir den speicher aussaugen
könnten :)
Karl heinz Buchegger schrieb:
>> Und ändere deine Syntax>> ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS0);>> ist doch leichter zu lesen als>> ADCSRA=0b10000101
Okay, mach ich ;)
So, folgendes:
Die erste Siebensegmentanzeige zeigt 0 an.
Die zweite - (also den default-Wert bei der switch-case Anweisung)
Die dritte : PortC gibt bei Pin 4 einen Wechselspannung mit dem RMS-Wert
von 0,6V aus. Der Rest ist wie bei der ersten Siebensegmentanzeige.
Nur zur Info: Die angelegte Wechselspannung beträgt ungefähr 2,9V.
Liefert das STK500 bei AVCC und AGND die nötige Spannung automatisch?
Mfg
Christoph
Christoph Anlauf schrieb:
> Okay, probier ich!>> Schonmal Danke für die Hilfe ;)>> Welche Dinge fallen dir denn noch auf?
Oh. Da gibt es noch jede Menge.
Zb. Schon mal was von Arrays gehört?
1
unsignedchardigits[]={0b00111111,// 0
2
0b00000110,// 1
3
0b01011011,// 2
4
0b01001111,// 3
5
0b01100110,// 4
6
0b01101101,// 5
7
0b01111101,// 6
8
0b00000111,// 7
9
0b01111111,// 8
10
0b01101111,// 9
11
0b01000000// alles andere
12
};
13
14
voidsev_segm_l(unsignedcharnumber)
15
{
16
17
if(COMM_ANODE_L)
18
{
19
if(number<10)
20
PORTB=~digits[number];
21
else
22
PORTB=~digits[10];
23
}
24
else
25
{
26
if(number<10)
27
PORTB=digits[number];
28
else
29
PORTB=digits[10];
30
}
31
}
32
33
voidsev_segm_m(unsignedcharnumber)
34
{
35
36
if(COMM_ANODE_M)
37
{
38
if(number<10)
39
PORTA=~digits[number];
40
else
41
PORTA=~digits[10];
42
}
43
else
44
{
45
if(number<10)
46
PORTA=digits[number];
47
else
48
PORTA=digits[10];
49
}
50
}
51
52
....
Ist doch viel kürzer :-)
Die richtigen Datentypen benutzen. int ist kein Allheilmittel für alles.
>Soweit ich weis wird beim stk500 avcc auf die versorgungsspannung also>5V gesetzt. außer man setzt den jumper um und stellt was eigenes ein.
Sowas kann man auch im AVRStudio nachgucken.
Die Spannung ist in einem bestimmten Bereich frei einstellbar;
grundsätzlich aber kleiner als VCC.
Zuerst eine Ausbesserung meinerseits :
"Die angelegte Wechselspannung beträgt ungefähr 2,9V."
Hierbei meinte ich analoge Spannung^^.
Gerald D. schrieb:
>>> erg*=10;> erg/=10;>>
Das ist Absicht, wegen der Übersicht. So wird mein Gedankenvorgang
gezeigt ;)
Karl heinz Buchegger schrieb:
>Ist doch viel kürzer :-)>>Die richtigen Datentypen benutzen. int ist kein Allheilmittel für alles.
Natürlich kenne ich Arrays!
Ja, das ist ja jetzt nur der Prototyp Code. ;)
Mfg
Christoph
>>>>>> erg*=10;>> erg/=10;>>>>>> Das ist Absicht, wegen der Übersicht. So wird mein Gedankenvorgang> gezeigt ;)
hihi ;) ... hast in deinen gedanken an eine andere schreibweise für noop
instruktionen gedacht ? ;)
Also hier mal ein paar Daten:
Die Taktfrequenz ist bei 4 Mhz.
Somit stimmt der Teiler hoffentlich ;)
Der AREF-Jumper auf dem STK500 ist gesetzt.
Hmm, sonst noch irgendwelche Tipps?
Mfg
Christoph
Gerald D. schrieb:
>>>>>>>>> erg*=10;>>> erg/=10;>>>>>>>>>> Das ist Absicht, wegen der Übersicht. So wird mein Gedankenvorgang>> gezeigt ;)>> hihi ;) ... hast in deinen gedanken an eine andere schreibweise für noop> instruktionen gedacht ? ;)
Na klar doch ;)
Auch wenn ihr keine Noobs seit, habt ihr den Code dadurch wahrscheinlich
trotzdem schneller kapiert^^. (Wenn ihr über den Zusammenhang überhaupt
eine Gedanken verliert)
>Die Taktfrequenz ist bei 4 Mhz.>Somit stimmt der Teiler hoffentlich ;)
Wie hast du das eingestellt?
Das STK500 kann maximal 3,686MHz von sich aus erzeugen.
Der AREF-Jumper auf dem STK500 ist gesetzt.
Damit sollte zumindest die interne Referenz angeschlossen sein.
Wie hoch die Spannung ist, kannst du im AVRStudio auslesen und
einstellen.
(1) erg=measurement();
(2) erg*=19,1; //4,87V / 255 = 19,1 mV
(3) erg*=10;
(4) erg/=10;
Ahlso:
nach dem schritt 1 sollte erg einen wert von 0-1024 haben (0-2^10) da
der adc eine 10 Bit genauigkeit hat.
bei schritt 2 multiplizierst du dann erg mit 19.1 ... aber nimmst
gleiczeitig an, nur eine adc auflösung von 8 bit zu haben da du durch
255 dividierst. das wäre mal der erste wiederspruch
schritte 3 und 4 wären dann mal überflüssig
danach gibst du erg auf der 7 segmentanzeige aus. wo du hergehst und
völlig wirr herummultiplizierst und dividierst. also ich kann aus der
ausgabe auch nicht wirklich was schließen.
STK500-Besitzer schrieb:
>>Die Taktfrequenz ist bei 4 Mhz.>>Somit stimmt der Teiler hoffentlich ;)> Wie hast du das eingestellt?> Das STK500 kann maximal 3,686MHz von sich aus erzeugen.>> Der AREF-Jumper auf dem STK500 ist gesetzt.> Damit sollte zumindest die interne Referenz angeschlossen sein.> Wie hoch die Spannung ist, kannst du im AVRStudio auslesen und> einstellen.
Es heißt im STK500 zwar 4 Mhz int. Clk, aber sind tatsächlich nur 3,6
Mhz.
Gerald D. schrieb:
> (1) erg=measurement();> (2) erg*=19,1; //4,87V / 255 = 19,1 mV> (3) erg*=10;> (4) erg/=10;>>> Ahlso:>> nach dem schritt 1 sollte erg einen wert von 0-1024 haben (0-2^10) da> der adc eine 10 Bit genauigkeit hat.>> bei schritt 2 multiplizierst du dann erg mit 19.1 ... aber nimmst> gleiczeitig an, nur eine adc auflösung von 8 bit zu haben da du durch> 255 dividierst. das wäre mal der erste wiederspruch>> schritte 3 und 4 wären dann mal überflüssig>> danach gibst du erg auf der 7 segmentanzeige aus. wo du hergehst und> völlig wirr herummultiplizierst und dividierst. also ich kann aus der> ausgabe auch nicht wirklich was schließen.
So stimmt es aber, keine Ahnung was ich mir vorher dabei gedacht habe..
erg*=4,7; //4,87V / 1023 = 4,7 mV
erg/=10; //Ergebnis in °Kelvin
erg-=273.15*10 //Ergebnis in °Celsius
Nachtrag:
Dass mit der Rechnerei im Funktionsaufruf war nur ein Versuch folgendes
hinzubekommen:
Ich möchte einfach nur eine 3-stellige Zahl auf 3 Anzeigen aufteilen.
Also z.b die Zahl ist 450.
1.Anzeige: 4
2. 5
3. 0
Aber nichts sagen, ich bin gerade am Überlegen ;-)
Christoph Anlauf schrieb:
> erg*=4,7; //4,87V / 1023 = 4,7 mV
ist immer noch falsch.
Das ist keine Multiplikation mit 4.7 (man beachte den Dezimalpunkt(!))
sondern eine Multiplikation mit 7
Christoph Anlauf schrieb:
> So ich hab's jetzt!> Anscheinend wird in C nicht automatisch gerundet.
Nein.
Das steht aber auch in jedem noch so grindigem C-Buch.
Bei der Konvertierung von double auf int, werden Kommastellen
abgeschnitten.
>Wird das Ergebnis einer Division in C gerundet?>Wenn nicht, habe ich eine Lösung^^
Wie sieht die aus?
Die einfachste Methode ist:
double x;
int y;
y = x+0.5; // Aufrunden