Sehr geehrtes Forum,
ich habe folgendes Problem:
1. Ich möchte gerne bei meinem Atmega88PA bei Kanal ADC0 eine Spannung
messen. Ich habe des Weiteren eine LED an Port D0.
2. Ich lasse eine LED an und ausschalten (so oft, dass man die LED damit
dimmen kann). Die Zeit, in welcher die LED an ist, ist von der, in
welcher die LED aus ist, verschieden. Eine Schaltperiode soll 20ms
dauern, 1 bis 2 ms davon darf die LED an sein, im anderen Teil aus.
3. Die gemessene Spannung soll für eine Varianz zwischen 1 und 2 ms
sorgen, das heißt z.B. dass bei einer gemessenen Spannung von 2,5 V mit
Referenz Spannung von 5V 1,5ms, bei einer gemessenen Spannung von 0 V
nur 1 ms, und bei einer gemessenen Spannung von 5V 2 ms gewartet wird.
4. Ich habe hier diesen Code in C (ich habe voher immer in Assembler
programmiert) und weiß nicht, warum das ganze nicht funktioniert
5. Ich denke es liegt daran, dass ich den Kanal nicht richtig gewählt
habe, aber es gibt nirgends, auch nicht im Datenblatt des Atmega eine
wirkliche Tabelle, in der man sehen kann wie man die Bits 0 bis 4 von
ADMUX einzustellen hat, um den ADC0 anzusteuern.
6. Ich habe den Mikrocontroller nicht getaktet oder einen Quarz
drangeschlossen, dementsprechend müsste er also die Frequenz 1 MHz
haben.
7. Bitte habt etwas Rücksicht, bin totaler Anfänger.
Danke für eure Antworten,
m.f.G. Developer_X
K. R. schrieb:> Sehr geehrtes Forum,> ich habe folgendes Problem:> 1. Ich möchte gerne bei meinem Atmega88PA bei Kanal ADC0 eine Spannung> messen. Ich habe des Weiteren eine LED an Port D0.> 2. Ich lasse eine LED an und ausschalten (so oft, dass man die LED damit> dimmen kann). Die Zeit, in welcher die LED an ist, ist von der, in> welcher die LED aus ist, verschieden. Eine Schaltperiode soll 20ms> dauern, 1 bis 2 ms davon darf die LED an sein, im anderen Teil aus.
Sag doch gleich, dass du einen Servotester baust.
> ADCSRA |= (1<<5); // Start Conversion
5?
Das ADSC Bit ist an Bitposition 6 und nicht an 5.
Genau deshalb schreibt man das auch so
1
ADCSRA|=(1<<ADSC);
und nicht mit Zahlenwerten für die Bitposition. Denn dann kann man das
alles erstens lesen und zweitens macht man zumindest nicht diese
bescheuerten Fehler.
Den Rest hab ich nicht mehr durchgesehen. Ich bin doch nicht wahsnsinnig
und vergleiche alle deine Zahlenwerte mit dem Datenblatt, ob und was du
da eigentlich alles eingestellt hast.
Schau dir die ADC Routinen im AVR-GCC-Tutorial an. (Nach ADC suchen.
Dort gibt es fertige Routinen(*). Die kannst du verwenden und die sind
dann auch lesbar. Im Gegensatz zu deinem Machwerk)
> 5. Ich denke es liegt daran, dass ich den Kanal nicht richtig gewählt> habe
Ich denke es liegt daran, dass du erst mal ein bisschen C lernen
solltest. SO wie du das machst, so programmiert man einfach nicht. Da
hättest du bei Assembler auch bleiben können.
(*) Hier
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Analoge_Ein-_und_Ausgabe
ist der Link auf den ADC Artikel vom C-Tutorial.
> 7. Bitte habt etwas Rücksicht, bin totaler Anfänger.
Dann fang mit was einfacherem an.
Dort wo alle anfangen. LED einschalten, LED ausschalten, LED blinken
lassen. Dieses Projekt ist 5 Schuhnummern zu groß für dich.
/* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man liest
19
also einen Wert und verwirft diesen, um den ADC "warmlaufen zu lassen" */
20
21
ADCSRA |= (1<<ADSC); // eine ADC-Wandlung
22
while (ADCSRA & (1<<ADSC) ) { // auf Abschluss der Konvertierung warten
23
}
24
/* ADCW muss einmal gelesen werden, sonst wird Ergebnis der nächsten
25
Wandlung nicht übernommen. */
26
(void) ADCW;
27
}
28
/* ADC Einzelmessung */
29
uint16_t ADC_Read( uint8_t channel )
30
{
31
// Kanal waehlen, ohne andere Bits zu beeinflußen
32
ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);
33
ADCSRA |= (1<<ADSC); // eine Wandlung "single conversion"
34
while (ADCSRA & (1<<ADSC) ) { // auf Abschluss der Konvertierung warten
35
}
36
37
uint16_t x = ADCL; // mit uint16_t x
38
x += (ADCH<<8); // in zwei Zeilen (LSB/MSB-Reihenfolge und
39
// C-Operatorpriorität sichergestellt)
40
41
return x; // ADC auslesen und zurückgeben
42
}
43
44
int main (void)
45
{
46
uint16_t adcval;
47
uint16_t to_wait;
48
uint16_t wait_v;
49
50
// ADC Initialisieren
51
ADC_Init();
52
53
// LED PortD0 initialisieren
54
DDRD = (1<<0);
55
PORTD |= (0<<0);
56
57
while( 1 )
58
{
59
60
// Lade ADC_Value
61
adcval = ADC_Read(0); // Kanal 0
62
63
// LED Anschalten
64
PORTD ^= (1<<0);
65
66
// Warten
67
wait_v = 0;
68
to_wait = 1000+adcval;
69
while(wait_v<to_wait)
70
wait_v++;
71
72
// LED Ausschalten
73
PORTD ^= (1<<0);
74
75
// Warten
76
wait_v = 0;
77
to_wait = 20000-adcval;
78
while(wait_v<to_wait)
79
wait_v++;
80
}
81
82
return 0;
83
}
Ich habe die Routinen ADC Init und ADC Read aus dem Tutorial übernommen
und
ADC Read angepasst, da mein Atmega88PA kein ADCW, sondern nur ADCL und
ADCH Register besitzt.
Allerdings reagiert der MC immer noch überhaupt kein bisschen wenn ich
an meinem POTI drehe.
Habt ihr eine Idee woran es liegen könnte?
mir fällt auf, dass die warteschleifen überhaupt nicht funktionieren
ganz unabhängig vom ADC Problem
1
int main (void)
2
{
3
uint16_t to_wait;
4
uint16_t wait_v;
5
6
// LED PortD0 initialisieren
7
DDRD = (1<<0);
8
PORTD |= (0<<0);
9
10
while( 1 )
11
{
12
// LED Anschalten
13
PORTD ^= (1<<0);
14
15
// Warten
16
wait_v = 0;
17
to_wait = 10;
18
while(wait_v<to_wait)
19
wait_v++;
20
21
// LED Ausschalten
22
PORTD ^= (1<<0);
23
24
// Warten
25
wait_v = 0;
26
to_wait = 1000;
27
while(wait_v<to_wait)
28
wait_v++;
29
}
30
31
return 0;
32
}
Die Zeit, in welcher die LED an ist, ist so klein, dass die LED nicht
leuchten dürfte, oder ganz schwach, aber fakt ist, dass die LED
angeschalten wird, und die schleife dann nicht verlassen wird.
Was ist denn an dieser einfachen Schleife falsch?
/* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man liest
19
also einen Wert und verwirft diesen, um den ADC "warmlaufen zu lassen" */
20
21
ADCSRA |= (1<<ADSC); // eine ADC-Wandlung
22
while (ADCSRA & (1<<ADSC) ) { // auf Abschluss der Konvertierung warten
23
}
24
/* ADCW muss einmal gelesen werden, sonst wird Ergebnis der nächsten
25
Wandlung nicht übernommen. */
26
(void) ADCW;
27
}
28
/* ADC Einzelmessung */
29
uint16_t ADC_Read( uint8_t channel )
30
{
31
// Kanal waehlen, ohne andere Bits zu beeinflußen
32
ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);
33
ADCSRA |= (1<<ADSC); // eine Wandlung "single conversion"
34
while (ADCSRA & (1<<ADSC) ) { // auf Abschluss der Konvertierung warten
35
}
36
37
uint16_t x = ADCL; // mit uint16_t x
38
x += (ADCH<<8); // in zwei Zeilen (LSB/MSB-Reihenfolge und
39
// C-Operatorpriorität sichergestellt)
40
41
return x; // ADC auslesen und zurückgeben
42
}
43
44
int main (void)
45
{
46
uint16_t adcval;
47
uint16_t to_wait;
48
uint16_t wait_v;
49
50
// ADC Initialisieren
51
ADC_Init();
52
53
// LED PortD0 initialisieren
54
DDRD = (1<<PD0);
55
PORTD |= (0<<PD0);
56
57
while( 1 )
58
{
59
60
// Lade ADC_Value
61
adcval = ADC_Read(1); // Kanal 0
62
63
// LED Anschalten
64
PORTD ^= (1<<PD0);
65
66
// Warten
67
// adcval = 10;
68
wait_v = 0;
69
to_wait = 500+adcval;
70
to_wait /= 10;
71
while(wait_v<to_wait)
72
{
73
wait_v++;
74
asm volatile ("nop");
75
asm volatile ("nop");
76
asm volatile ("nop");
77
asm volatile ("nop");
78
asm volatile ("nop");
79
asm volatile ("nop");
80
asm volatile ("nop");
81
asm volatile ("nop");
82
}
83
84
// LED Ausschalten
85
PORTD ^= (1<<PD0);
86
87
// Warten
88
wait_v = 0;
89
to_wait = 20000-adcval;
90
to_wait /= 10;
91
while(wait_v<to_wait)
92
{
93
wait_v++;
94
asm volatile ("nop");
95
asm volatile ("nop");
96
asm volatile ("nop");
97
asm volatile ("nop");
98
asm volatile ("nop");
99
asm volatile ("nop");
100
asm volatile ("nop");
101
asm volatile ("nop");
102
}
103
}
104
105
return 0;
106
}
Liegt vielleicht daran dass in C schleifen die sinnlos sind vom Compiler
optimiert werden wenn man nicht extra von Assembler ne NOP Operation
aufruft.
Danke trotzdem euch allen.
Klappt jetzt wunderbar. :)