Forum: Mikrocontroller und Digitale Elektronik Atmega8 ADC \ eeprom


von Patrick K. (live5)


Lesenswert?

Hi,

nach stunden des Probierens und suchen hier meine Fragen....
Ich möchte am Analog eingang einen Strom messen, und diesen Wert ins 
eeporm speichern damit ich ihn nach dem einschalten wieder habe, das 
Ganze wird an einer 12V 3Ah Batterie angeschloßen.

Zum Testen hab ich Versucht and den PC0 einen Widerstand hängen und den 
auf die 5V vom µC gehängt, aber nach einen Spannungswert hat es mir 
nicht ausgesehen.. (hab es um den Wert zu sehen in den eeprom 
gepseichert)

1) wie könnte ich den adc wert sonst ausgeben ohne lcd display bzw. 
eeprom ?

2) Soweit ich das richtig nachgelesen habe bekomme ich vom analog 
eingang aber nur einen Spannungs wert und damit ich ich auf meinen Strom 
komme müsste das dann so aussehen
               u = ADC*5.0;
               i =  u/r;

3) Daher das eeprom  auf ca. 100.000 cyclen brenzt ist, wird es nicht 
sinnvoll  sein den Wert immer zu beschreiben.
    Meine Idee wäre so eine art dauerplus damit der µC immer unter 
spannung (Ruhemodus oder so) ist und der Wert im RAM somit erhalten 
bleibt aber geht das ohne das die Baterie gleich leer ist ?
1
#include <avr/io.h>
2
#include <util/delay.h>
3
#include <avr/eeprom.h>
4
5
uint16_t eeLastValueAddr EEMEM = 1;
6
uint16_t lastValue = 0;
7
8
int main()
9
{
10
    // Set PORTD as Output
11
  DDRD = (1<<PD7)| (1<<PD6)|(1<<PD5) | (1<<PD4) | (1<<PD3) | (1<<PD2)| (1<<PD1)| (1<<PD0);
12
13
  // Set PortB (PB0,PB1) as Input
14
    PORTB |= (1<<PB0);
15
  PORTB |= (1<<PB1);
16
17
    // Set Reference to AVCC and input to ADC0
18
    ADMUX = (1<<REFS0);
19
20
    // Enable ADC, set prescaler to 16
21
    // Fadc=Fcpu/prescaler=1000000/16=62.5kHz
22
    // Fadc should be between 50kHz and 200kHz
23
    ADCSRA = (1<<ADFR)|(1<<ADEN)|(1<<ADPS2);
24
25
    // Start the first conversion
26
    ADCSRA |= (1<<ADSC);
27
28
    while (1)
29
  {
30
        if (!(PINB & (1<<PB0)) && !(PINB & (1<<PB0)))
31
        {
32
            //Ruecksetzung des Wertes
33
      lastValue = 0;
34
            eeprom_write_word (&eeLastValueAddr, lastValue);
35
        }
36
        if ((PINB & (1<<PB0)))
37
        {
38
        //Analog Wert lesen und in eeprom schreiben
39
      lastValue = ADC;
40
            eeprom_write_word(&eeLastValueAddr, lastValue);
41
      Set_LED();
42
        }
43
44
        //Wartezeit fuer Taster abfrage
45
        _delay_ms(100);
46
    }
47
}
48
49
void Set_LED()
50
{
51
  switch (lastValue)
52
  {
53
    case > 700:
54
      PORTD = 0b00000000;
55
      break;
56
57
    case > 600:
58
      PORTD = 0b10000000;
59
      break;
60
61
    case > 500:
62
      PORTD = 0b11000000;
63
      break;
64
65
    case > 400:
66
      PORTD = 0b11100000;
67
      break;
68
69
    case > 300:
70
      PORTD = 0b11110000;
71
      break;
72
73
    case > 200:
74
      PORTD = 0b11111000;
75
      break;
76
77
    case > 100:
78
      PORTD = 0b11111100;
79
      break;
80
81
    case >50:
82
      PORTD = 0b11111110;
83
      break;
84
      
85
    case 0:
86
      PORTD = 0b11111111;
87
      break;
88
  }
89
}

von Rainer U. (r-u)


Lesenswert?

1) seriell - ggf. mit einem USB-seriell (TTL)Adapter und einem 
Terminalprogramm

2) Du bekommst Werte zwischen 0 und 1023. Was sie bedeuten hängt von der 
Referenzspannung ab. Strom messen willst Du über einen Shunt? Mach mal 
ne Skizze.

3) Miss mit einem anderen ADC die Betriebsspannung, und nur wenn sie 
einbricht, speicherst Du im Eeprom. Meist hält der Elko hinter der 
Spannungsversorgung so lange durch.

: Bearbeitet durch User
von Michael K. (tschoeatsch)


Lesenswert?

Hi Patrick,
dein AD-Wandler liefert einen Wert zwischen 0 und 1023 (bei 10 bit 
Auflösung) Liegt dein Vref an 5V, dann bedeutet 0=0V, 1023=5V. Du musst 
also rechnen: Vgemessen=ADWert*Vref/1023. Warum brauchst du den Wert 
nach dem Wiedereinschalten? Du kannst den Wert im eeprom ja auch nur 
dann aktualisieren, wenn er sich ändert. Mit zu deinem code kann ich 
nichts sagen, da ich kein C kann.
Grüße von tschoeatsch

von Patrick K. (live5)


Lesenswert?

ok, danke

@Rainer:
Also dann würde ich z.B PC1 die AVCC (5V) messen und wenn diese unter 
4.5V
abfällt könnte ich ihn in den set_sleep_mode(SLEEP_MODE_PWR_DOWN) 
setzetn.
Wenn am INT0 wieder 5V anliegen sollte der Interrup reagieren und ihn 
wieder starten.

Ist das so richtig oder habe ich einen denkfehler ?

@Michael:
denn Wert möchte ich nach dem einschalten nur zwecks interesse sehen 
eventuell mit einer History über die letzen 10 Werte oder so,
momentan noch eher für Testzwecke.

Das mit dem eeprom werde ich lassen wegen der begrenzenten schreib 
zyklen.

von Rainer U. (r-u)


Lesenswert?

Der sleep mode spart nur Energie - wenn die Betriebsspannung weg ist, 
ist der Ram-Inhalt (die Variable) auch weg, egal ob sleep mode oder 
nicht.

Wenn die Betriebsspannung dann wiederkommt, startet das Programm von 
vorn (z.B. durch die empfohlene Reset-Beschaltung) - ganz ohne 
Interrupt.

von Norbert S. (norberts)


Lesenswert?

Patrick Kag schrieb:
> Also dann würde ich z.B PC1 die AVCC (5V) messen und wenn diese unter
> 4.5V
> abfällt könnte ich ihn in den set_sleep_mode(SLEEP_MODE_PWR_DOWN)
> setzetn.

hi,

damit verbraucht er nur weniger strom und geht auch aus, wenn die 
betriebsspannung ganz weg ist.
ausserdem wird der gemessene wert immer nahezu 1023 sein, wenn avcc die 
referenz ist und du avcc misst.

da hakt es noch an vielen ecken, mach mal einen schaltplan.

gruß,
norbert

von Karl H. (kbuchegg)


Lesenswert?

Patrick Kag schrieb:

> Zum Testen hab ich Versucht and den PC0 einen Widerstand hängen und den
> auf die 5V vom µC gehängt, aber nach einen Spannungswert hat es mir
> nicht ausgesehen.. (hab es um den Wert zu sehen in den eeprom
> gepseichert)

Das klingt nicht richtig.
Zum Testen verwendest du am bersten ein Potentiometer, das du so 
verschaltest
1
      +5V +
2
          |
3
         +-+
4
    10k  | |
5
       --------------- zum ADC Pin
6
         | |
7
         +-+
8
          |
9
          + GND

mit dem Poti kannst du dir dann eine Spannung zwischen 0 und 5V 
einstellen, die du mit dem ADC messen kannst.
Aber mit einem einzelnen Widerstand wirst du da nichts reissen. Durch 
den ADC fliesst so wenig Strom durch den Widerstand, das du da keinen 
Spannungsabfall feststellen kannst. Es sei denn der Widerstand ist recht 
groß, aber dann kriegst du wieder andere Probleme mit dem ADC.


> 1) wie könnte ich den adc wert sonst ausgeben ohne lcd display bzw.
> eeprom ?

Deine tatsächlich beste Wahl wäre entweder erst mal ein LCD in Betrieb 
zu nehmen oder die UART in Betrieb zu nehmen, damit du dir Zahlenwerte 
direkt ausgeben lassen kannst.
Meiner Meinung nach ist es gerade als Neuling ein Fehler, auf 
vernünftige Debug Hilfen, wie zum Beispiel Möglichkeiten zur Ausgabe, zu 
verzichten. Selbst wenn deine endgltige Schaltung kein Display haben 
wird, ist es trotzdem eine gute Idee, in der Entwicklungsversion 
irgendeine Form der Ausgabe zur Verfügung zu haben. Wie du das mit ein 
paar LED gemacht hast - das kann natürlich reichen. Mit den gleichen 
Pins hättest du aber auch ein LCD ansteuern können und damit wesentlich 
mehr Flexibilität beim Debuggen gehabt.

> 2) Soweit ich das richtig nachgelesen habe bekomme ich vom analog
> eingang aber nur einen Spannungs wert

Dazu haben andere schon etwas gesagt.
Ich möchte eigentlich dazu nur ergänzen: Ich denke, du machst wieder den 
Kardinalfehler Nummer 1 aller Neulinge. Du willst zu viel auf einmal, 
indem du sofort auf das endgültige Ziel losgehst. Dazu schreibst du Code 
und nichts funktioniert. Dann ist das Gejammer groß und man muss auf die 
Neulinge einreden, wie auf ein krankes Kind, bis man sie dazu bringt, 
mit einem ganz einfachen Programm anzufangen und das dann schrittweise 
und mit viel testen bis zum endgültigen Programm auszubauen.


>   switch (lastValue)
>   {
>     case > 700:
was auch immer du da programmierst, korrektes C ist das nicht.
Ok, der GCC hat so manche Erweiterung, allerdings würde ich mir 
überlegen, ob ich die benutzen will. Ein
1
    if( lastValue > 700 )
2
      ...
3
    else if( lastValue > 600 )
4
      ...
5
    else if ...
ist ganz normales C, das jeder C Compiler versteht. Und durch den 
Wegfall der ganzen break ist das in Summe auch noch kürzer in der 
Schreibweise. Ich sehe hier keinen Vorteil in der Benutzung von 
irgendwelchen Erweiterungen im switch-case. Ich würde einen Vorteil 
darin sehen, wenn man sich die Anzahl der zu leuchtenden LED aus dem 
Wert ausrechnet und dann zb mit einem Array das auszugebende Muster 
bestimmt. So nach dem Muster
1
uint8_t Muster[] = { 0b11111111,      //   0
2
                     0b11111110,      // 128
3
                     0b11111100,      // 256,
4
                     ....
5
                   };
6
7
   AnzahlLed = lastValue / 128;
8
   PORTD = Muster[AnzahlLed];
Das wäre schön einfach und kurz. Aber das tust du ja nicht.


Zusammen mit den ADC Routinen aus dem AVR-GCC-Tuorial hätte dein erstes 
Testprogramm zum ADC so aussehen können
1
.....
2
3
uint8_t Muster[] = { 0b11111111,      //   0
4
                     0b11111110,      // 128
5
                     0b11111100,      // 256,
6
                     0b11111000,
7
                     0b11110000,
8
                     0b11100000,
9
                     0b11000000,
10
                     0b10000000,
11
                     0b00000000
12
                   };
13
14
15
int main()
16
{
17
  uint16_t value;
18
19
  DDRD = 0xFF;
20
21
  ADC_init();
22
23
  while( 1 ) {
24
    value = ADC_Read( 0 );
25
    if( value > 1023 )
26
      value = 1023;
27
    value /= 128;
28
    PORTD = muster[ value ];
29
  }
30
}

Poti an den ADC angeklemmt und die LEDs müssten bereits auf drehen am 
Poti reagieren, was dir die Gewissheit gibt, das der ADC mal prinzipiell 
arbeitet und du vernünftige Werte kriegst.

Ausbauen kann man immer noch. Aber das wäre mein erstes Testprogramm. 
Gedanken über EEPROM oder Sleep Modi kann ich mir immer noch machen. 
Aber sicher nicht jetzt, wenn es darum geht, dem ADC ein erstes 
Lebenszeichen zu entlocken. Und das beste drann: das hat keine Stunden 
gedauert sondern lediglich ein paar Minuten.

: Bearbeitet durch User
von Patrick K. (live5)


Angehängte Dateien:

Lesenswert?

Hallo Karl,

danke, für die Hilfe ich habe mich jetzt nur auf das Lesen es ADC 
konzentriert...
meine Schaltung hab ich auch dran gehängt.

Mein aktuelles Problem ist das der ADC wert > 205 und < 409 ist und das 
Immer sogar wenn nichts angeschlossen ist ?
Ich konnte den Fehler noch nicht finden, daher ich noch kein Poti habe 
verwende ich verschiedene widerstände.
1
 
2
#include <avr/io.h>
3
#include <util/delay.h>
4
#include <avr/eeprom.h>
5
6
uint16_t lastValue = 0;
7
uint16_t ADC_Read(uint8_t channel);
8
9
int main()
10
{
11
  // Set PORTD as Output
12
  DDRD = (1<<PD7)| (1<<PD6)|(1<<PD5) | (1<<PD4) | (1<<PD3) | (1<<PD2)| (1<<PD1)| (1<<PD0);
13
  PORTD |= ((1 << PD7) | (1 << PD6) |(1<<PD5) | (1<<PD4) | (1<<PD3) | (1<<PD2)| (1<<PD1)| (1<<PD0));
14
15
  ADC_init();
16
17
  while (1)
18
  {
19
    //0V = 0
20
    //1V = 205
21
    //2V = 409
22
    //3V = 614
23
    //4V = 816
24
    //5V = 1023
25
    //Vgemessen=lastValue*5/1024;
26
27
    lastValue = ADC_Read(0);
28
    if (lastValue = 0) {
29
       PORTD = 0b11111111;
30
    }
31
    else if (lastValue > 205) {
32
       PORTD = 0b11111110;
33
    }
34
    else if (lastValue < 409) {
35
      PORTD = 0b11111100;
36
    }
37
    else if (lastValue > 614) {
38
       PORTD = 0b11111000;
39
    }
40
    else if (lastValue > 816) {
41
      PORTD = 0b11110000;
42
    }
43
    else if (lastValue > 1023) {
44
      PORTD = 0b11100000;
45
    }
46
    }
47
}
48
49
/* ADC initialisieren */
50
void ADC_init(void) {
51
    // die Versorgungsspannung AVcc als Refernz wählen:
52
    ADMUX = (1<<REFS0);
53
    // oder interne Referenzspannung als Referenz für den ADC wählen:
54
    //ADMUX = (1<<REFS1) | (1<<REFS0);
55
    // Bit ADFR ("free running") in ADCSRA steht beim Einschalten
56
    // schon auf 0, also single conversion
57
    ADCSRA = (1<<ADPS1) | (1<<ADPS0);     // Frequenzvorteiler
58
    ADCSRA |= (1<<ADEN);                  // ADC aktivieren
59
60
    /* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man liest
61
     also einen Wert und verwirft diesen, um den ADC "warmlaufen zu lassen" */
62
63
64
    ADCSRA |= (1<<ADSC);                  // eine ADC-Wandlung
65
    while (ADCSRA & (1<<ADSC) ) {         // auf Abschluss der Konvertierung warten
66
    }
67
68
    /* ADCW muss einmal gelesen werden, sonst wird Ergebnis der nächsten
69
     Wandlung nicht übernommen. */
70
    (void) ADCW;
71
}
72
73
/* ADC Einzelmessung */
74
uint16_t ADC_Read(uint8_t channel)
75
{
76
    // Kanal waehlen, ohne andere Bits zu beeinflußen
77
    ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);
78
    ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
79
    while (ADCSRA & (1<<ADSC) ) {   // auf Abschluss der Konvertierung warten
80
    }
81
    return ADCW;                    // ADC auslesen und zurückgeben
82
}

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.