Forum: Mikrocontroller und Digitale Elektronik ATMega88, Taktrate zur Laufzeit ändern gibt Messfehler am ADC


von Daniel S. (daniel_s49)


Lesenswert?

Moin,

ich habe einen ATMega88p, der mit externem Quarz auf 7.37MHz betrieben 
wird. Um Strom zu sparen ist die CKDIV8-Fuse gesetzt, so dass er mit 
921kHz startet.
Während der Laufzeit gibt es dann ein Event an einem Pin und darauf hin 
schalte ich den Takt hoch, lese für 20ms einen ADC aus und schalte 
danach wieder runter. Das Hochschalten des Prozessortakts funktioniert 
und auch die Timer werden angepasst und laufen danach mit der richtigen 
Geschwindigkeit.
Wenn ich den ADC abfrage, solange ich noch langsam bin, bekomme ich 
einen Wert von 60. Nachdem ich hochgeschaltet habe, messe ich 80.
Die Quelle, die ich messe, ist in beiden Fällen konstant bei 0.3V. Meine 
Referenz (VCC mit 100nF an AVCC) schwankt von einer Situation zur 
anderen um 50mV. Rechnerisch sollte das 1-2 ADC-Punkte ausmachen, aber 
nicht 20.

Die AD-Wandlung wird bei Bedarf gestartet und erst dann wird das 
ADEN-Bit gesetzt. Wenn die Messung beendet ist, wird dieses Bit zurück 
gesetzt. Der Wechsel des ADC-Prescalers passiert zu einem Zeitpunkt, an 
dem der ADC nicht läuft.

ADC-Modul:
1
__monitor void InitADC(TProzessorTakt ProzessorTakt)      // IAR-Compiler: __monitor = nicht durch Interrupts unterbrechbar
2
{
3
  BYTE n; 
4
5
  //Variablen rücksetzen
6
  for (n=0;n<ANZAHL_ADC_KANAELE;n++) 
7
    ADCWerte[n]=0;
8
  
9
  WandlungszyklusAbgeschlossenFlag=false;    
10
    
11
  //ADC initialisieren
12
  ADCSetProzessorTakt(ProzessorTakt);
13
  
14
  //AD-Pins von der normalen Eingangsverarbeitung abtrennen, um Strom zu sparen
15
  for (n=0;n<ANZAHL_ADC_KANAELE;n++)
16
    DIDR0|=(1<<(ADCReihenfolge[n]));
17
 
18
  //ADC deaktivieren
19
  PRR_Bit0=1;
20
}
21
22
23
//Setzt die Einstellungen für einen bestimmten Prozessortakt
24
__monitor void ADCSetProzessorTakt(TProzessorTakt ProzessorTakt)
25
{
26
  //Statusregister setzen: 
27
  //Interrupt enable, single shot, conv time
28
  switch (ProzessorTakt)
29
  {
30
    case PTAKT_921kHz:    
31
      ADCSRA=0x0B;   //Prescaler=8, InterruptEnable
32
      break;
33
34
    case PTAKT_7370kHz:     
35
      ADCSRA=0x0E;   //Prescaler=64
36
      break;
37
  }
38
}

main:
1
#pragma optimize=none
2
__monitor static void SetzeTaktrate(TProzessorTakt ProzessorTakt) {
3
  
4
  
5
  if (AktuellerTakt == ProzessorTakt)
6
    return;
7
  
8
  AktuellerTakt = ProzessorTakt;
9
  
10
  //Master clock prescaler setzen
11
  if (ProzessorTakt == PTAKT_921kHz) {
12
    CLKPR=0x80;  //Enable Prescalere setting
13
    CLKPR=0x03;  //8-fach Prescaler
14
    __no_operation();  
15
  } 
16
  else {
17
    CLKPR=0x80;  //Enable Prescalere setting
18
    CLKPR=0x00;  //kein Prescaler
19
    __no_operation();  
20
  }
21
  
22
  TimersSetProzessorTakt(ProzessorTakt);
23
  ADCSetProzessorTakt(ProzessorTakt);
24
}
25
26
27
28
static void Init(void) {  
29
  CLKPR=0x80;  //Enable Prescalere setting
30
  CLKPR=0x03;  //8-fach Prescaler
31
  __no_operation(); 
32
  
33
  
34
  //Power management: Nicht benutzte Peripherie abschalten  
35
  ACSR=0x80;   //Analog-Comparator abschalten    
36
  PRR = (1<<PRTIM2) | (1<<PRSPI) | (1<<PRTWI); //TWI, SPI, Timer2 abschalten
37
  
38
  //ADC initialisieren
39
  InitADC(PTAKT_921kHz);      
40
41
  // globale Variablen initialisieren
42
  // [...]
43
44
  EnableWatchdog();
45
46
  //Interrupts scharf schalten 
47
  __enable_interrupt();
48
}
49
50
51
52
int main( void )
53
{
54
  Init();
55
  
56
  while (1) {
57
58
    TriggerWatchdog();
59
60
    if (EventAmPin) {
61
      SetzeTaktrate(PTAKT_7370kHz);
62
    }
63
    else if (20ms Abgelaufen) {
64
      SetzeTaktrate(PTAKT_921kHz);
65
    }
66
  
67
  }


Um Effekte durch das Hin- und Herschalten auszuschließen, habe ich das 
"SetzeTaktrate(PTAKT_921kHz);", also das Runterschalten auskommentiert. 
Ich messe trotzdem die ganze Zeit den falschen Wert von 80.

Ich vermute, dass ich irgendwas ganz Dummes übersehen habe (irgendwelche 
Flags zurücksetzen, bevor Prescaler geändert wird, oder so) und der ADC 
nun mit falschen Einstellungen auf der zu schnellen Prozessorclock 
läuft. Im Datenblatt finde ich aber nichts, was mir hilft und 
Codebeispiele aus dem Internet sehen auch alle ähnlich aus.

Hat jemand von euch eine Idee, über welchen Fallstrick ich hier gerade 
falle?

Danke
Daniel

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

Sinf alle ADC Werte falsch, oder nur der erste?

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Daniel S. schrieb:
> Hat jemand von euch eine Idee, über welchen Fallstrick ich hier gerade
> falle?

 Nicht unbedingt, aber die erste Messung verwerfen und erst die zweite
 Messung verwenden, hat noch nie geschadet...

von Stefan F. (Gast)


Lesenswert?

Ha, zwei Dumme - ein Gedanke.

von Marc H. (marchorby)


Lesenswert?

Es gibt beim ADC zwei Typen in Funktion die du vorher setzten musst. 
Freerunning mode und single. Zudem kannst du die interne Referenz nutzen 
und umschalten! AVCC, INTERNAL, EXTERNAL, INTERNAL_1.1, INTERNAL_2.56 
etc. Das steht abe im DB welchen du hast...

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Stefan U. schrieb:
> Ha, zwei Dumme - ein Gedanke.

 Ja ;)

von S. Landolt (Gast)


Lesenswert?

> Ha, zwei Dumme - ein Gedanke.
Hier kommt der Dritte: ich finde nicht einmal die Stellen, an denen 
der ADC gestartet und ausgelesen wird.

von Stefan S. (chiefeinherjar)


Lesenswert?

S. Landolt schrieb:
>> Ha, zwei Dumme - ein Gedanke.
> Hier kommt der Dritte: ich finde nicht einmal die Stellen, an denen der
> ADC gestartet und ausgelesen wird.

Dito... Und ich dachte, ich wäre der einzige dumme...
Oder der Code ist nicht vollständig.

von Daniel S. (daniel_s49)


Lesenswert?

Stefan S. schrieb:
> Oder der Code ist nicht vollständig.

Der Code ist natürlich nicht komplett. Ich wollte es möglichst kurz 
halten, weil es eh keiner liest, wenn ich 1000 Zeilen Code poste.


Stefan U. schrieb:
> Sinf alle ADC Werte falsch, oder nur der erste?

Die Werte sind immer dann falsch, wenn ich den Takt über SetzeTaktrate() 
hochgeschaltet habe. Dann durchgängig, also spätestens bei der zweiten 
Messung sollte sich das wieder "eingependelt" haben. (aber ja, ich 
stimme euch da zu)
Heißt aber auch: solange ich langsam laufe sind die Werte richtig. Das 
spricht für mich dafür, dass das eigentliche Starten und Auslesen des 
ADCs funktioniert.

Außerdem: wenn ich die CKDIV8 rausnehme und von vornherein das komplette 
Programm mit dem schnellen Takt laufen lasse und nie runterschalte, dann 
stimmen die Ergebnisse auch. Es muss also irgendwas damit zu tun haben, 
dass ich den ADC in der Init() langsam einstelle und dann später 
hochschalte.

von S. Landolt (Gast)


Lesenswert?

> 1000 Zeilen Code
Dann wäre es doch, auch im eigenen Interesse, sinnvoll, das Programm auf 
die vielleicht zwei Dutzend Zeilen mit dem Problemumfeld zu reduzieren.

von Rene K. (xdraconix)


Lesenswert?

Ähm...


Jetzt mal ganz am Rande:

Du startest den µC mit 921khz (7,37Mhz/CKDIV8) - richtig.

Aber da bleibt er auch! Du setzt ausschließlich den Prescaler / 8 bei:
1
 if (ProzessorTakt == PTAKT_921kHz) {
2
    CLKPR=0x80;  //Enable Prescalere setting
3
    CLKPR=0x03;  //8-fach Prescaler
4
    __no_operation();  
5
  }

Dadurch läuft der µC auf 921.250hz / 8 = 115khz.

Um dann den Prozessor bei:
1
  else {
2
    CLKPR=0x80;  //Enable Prescalere setting
3
    CLKPR=0x00;  //kein Prescaler
4
    __no_operation();  
5
  }

... wieder auf 921khz zu setzen und nicht wie du annimmst auf 7370kHz.

Die CKDIV8 hat m.M.n nichts mit dem CLKPR Register zu tun.

EDIT:

Ahh.. Sorry, da hab ich falsch gelegen:

"The CKDIV8 Fuse determines the initial value of the CLKPS bits. If 
CKDIV8 is unprogrammed, the
CLKPS bits will be reset to “0000”. If CKDIV8 is programmed, CLKPS bits 
are reset to “0011”, giving a
division factor of 8 at start up."

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Daniel S. schrieb:
> wenn ich 1000 Zeilen Code poste.

1000 Zeilen nur für den ADC-Code sind entschieden zuviel.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Daniel S. schrieb:
> Während der Laufzeit gibt es dann ein Event an einem Pin und darauf hin
> schalte ich den Takt hoch, lese für 20ms einen ADC aus
Warum geht das Auslesen nicht mit der niedrigen Taktfrequenz?

> Wenn ich den ADC abfrage, solange ich noch langsam bin, bekomme ich
> einen Wert von 60. Nachdem ich hochgeschaltet habe, messe ich 80. Die
> Quelle, die ich messe, ist in beiden Fällen konstant bei 0.3V.
Misst du nur diese Quelle oder schaltest du den Multiplexer 
zwischendurch um? Falls das Zweitere: welche Spannung misst du vorher? 
"Verschleppt" sich eine Änderung dieser vorher gemessenen Spannung auf 
die nachfolgend gemessene?

: Bearbeitet durch Moderator
von Stefan F. (Gast)


Lesenswert?

Kann es sein, daß deine Signalquelle einen zu hohen Ausgangswiderstand 
(>10k Ohm) hat?

von Mike J. (linuxmint_user)


Lesenswert?

Daniel S. schrieb:
> lese für 20ms einen ADC aus

Also du liest eine Periode eines 50Hz Wechselspannungssignals aus?
Hast du einen 100p oder 1nF Kondensator an den ADC-Pin gehängt?
Wie groß sind deine Spannungsteiler-Widerstände?

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.