Forum: Mikrocontroller und Digitale Elektronik Spannung messen und LED dimmen


von A. B. (developer_x)


Lesenswert?

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
1
#define F_CPU 1000000UL
2
 
3
#include <avr/io.h>
4
#include <util/delay.h>
5
 
6
int main (void)
7
{
8
  DDRD = (1<<0);
9
  PORTD |= (0<<0);
10
11
  ADCSRA |= (0<<0);  // Taktgeschwindigkeit
12
  ADCSRA |= (0<<1);
13
  ADCSRA |= (1<<2);
14
  ADCSRA |= (0<<3);  // Interrupt
15
  // ADCSRA |= (1<<4); Wenn Umwandlung erfolgt  
16
  ADCSRA |= (0<<6);  // Free Run AUS
17
  ADCSRA |= (1<<7);  // Einschalten
18
19
  ADMUX |= (1<<0); // Kanal einstellung
20
  ADMUX |= (0<<1);
21
  ADMUX |= (0<<2);
22
  ADMUX |= (0<<3);
23
  ADMUX |= (0<<4);
24
25
  ADMUX |= (0<<5); //  Egal
26
27
  ADMUX |= (1<<6); // Referenzspannungseinstellungen
28
  ADMUX |= (0<<7);
29
30
  ADCSRA |= (1<<5);  // Start Conversion
31
  
32
  while( 1 ) 
33
  {
34
    PORTD ^= (1<<0); // anschalten
35
36
//  Warten bis Messung abgeschlossen
37
    while((ADCSRA & 4)) 
38
      ;
39
  uint16_t t = ADCH;
40
  t *= 2;
41
  t *= 2;
42
  t *= 2;
43
  t *= 2;
44
  t *= 2;
45
  t *= 2;
46
  t *= 2;
47
  t *= 2;
48
49
    uint16_t y = 1000;
50
  y += t;
51
  y /= 2;
52
  uint16_t b = 0;
53
  while(b<y)
54
    b++;
55
56
    ADCSRA |= (1<<5);  // Start Conversion
57
58
59
60
61
  PORTD ^= (1<<0); // ausschalten
62
//  Rest Warten
63
    uint16_t z = 20000-y;
64
  z/=2;
65
  uint16_t a = 0;
66
  while(a<z)
67
    a++;
68
69
  }
70
 
71
  return 0;
72
}
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

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

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.

: Bearbeitet durch User
von A. B. (developer_x)


Lesenswert?

Ok, sry.
Ich habe meinen Code ein bisschen verändert. Ich denke jetzt sollte er 
leserlich sein.
1
#define F_CPU 1000000UL
2
 
3
#include <avr/io.h>
4
#include <util/delay.h>
5
 
6
 void ADC_Init(void) 
7
 {
8
  // die Versorgungsspannung AVcc als Refernz wählen:
9
  ADMUX = (1<<REFS0);    
10
  // oder interne Referenzspannung als Referenz für den ADC wählen:
11
  // ADMUX = (1<<REFS1) | (1<<REFS0);
12
 
13
  // Bit ADFR ("free running") in ADCSRA steht beim Einschalten
14
  // schon auf 0, also single conversion
15
  ADCSRA = (1<<ADPS1) | (1<<ADPS0);     // Frequenzvorteiler
16
  ADCSRA |= (1<<ADEN);                  // ADC aktivieren
17
 
18
  /* 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?

von A. B. (developer_x)


Lesenswert?

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?

: Bearbeitet durch User
von A. B. (developer_x)


Lesenswert?

Seltsamerweise klappt das so doch.
1
#define F_CPU 1000000UL
2
 
3
#include <avr/io.h>
4
#include <util/delay.h>
5
 
6
 void ADC_Init(void) 
7
 {
8
  // die Versorgungsspannung AVcc als Refernz wählen:
9
  ADMUX = (1<<REFS0);    
10
  // oder interne Referenzspannung als Referenz für den ADC wählen:
11
  // ADMUX = (1<<REFS1) | (1<<REFS0);
12
 
13
  // Bit ADFR ("free running") in ADCSRA steht beim Einschalten
14
  // schon auf 0, also single conversion
15
  ADCSRA = (1<<ADPS1) | (1<<ADPS0);     // Frequenzvorteiler
16
  ADCSRA |= (1<<ADEN);                  // ADC aktivieren
17
 
18
  /* 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. :)

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.