Forum: Mikrocontroller und Digitale Elektronik ATtiny44, ADC-Problem mit interner Spannungsreferenz bei externem Clock


von Ralph S. (jjflash)


Lesenswert?

Ich habe hier ein für mich relativ unwitziges Problem. Das Problem tritt 
bei allen Vertretern von ATtiny24 bis ATtiny84 auf (mit je 3 fabrikneuen 
Controllern getestet).

Was ich möchte:
---------------
Ich möchte einen analogen Wert einlesen (vorzugsweise an PA3 - Channel 
3) bei Verwendung der internen Spannungsreferenz einlesen.

Ausgangszustand:

- Nutzung ATtiny44 mit externem Quarz 16MHz mit Ziehkondensatoren 22pF, 
Fuses-LO = 0xDE, Fuses-HI = 0xDF

- Nutzung ATtiny44 mit internem 8 MHz Takt, Fuses-LO = 0xE2, Fuses-Hi = 
0xDF

- Am ATtiny ist ein TM1637 über PA1 für CLK und PA2 für DIO 
angeschlossen (und jaaaaaaaaaaa, ich habe 1000 mal kontrolliert, ob ich 
auch nur die Anschlüsse in DDRA verwende). Der TM1637 wird mittels 
Bitbanging betrieben, keinerlei Hardware (außer eben den GPIO-Pins) 
werden verwendet.

- im ATtiny laufen derzeit keinerlei Timer/Counter und auch kein 
Interrupt

C-Funktionen des ADC's:
1
/* --------------------------------------------------------
2
                           adc_init
3
4
     initialisiert den Analog-Digitalwandler
5
6
     Uebergabe:
7
          vref     0: Vcc; 1= Spg. PA0; 2= 1.1V interne Ref.
8
          channel  0: PA0 .. 5: PA5
9
10
11
   -------------------------------------------------------- */
12
void adc_init(uint8_t vref, uint8_t channel)
13
{
14
  ADMUX = (vref << 6) | channel;                          // Referenzspannung und Analogeingang waehlen
15
16
  ADCSRA = (1 << ADEN) | (1 << ADPS0);                    // ADC enable ADPS0= 1;  ADPS1, ADPS2 = 0 => adc-clock = F_CPU / 2
17
  ADCSRB = 0;                                             // ADLAR = 0, ADC wird rechtsbuendig eingelesen
18
}
19
20
uint16_t adc_getvalue(void)
21
{
22
  uint16_t result;
23
24
  ADCSRA |= 1 << ADSC;                      // ADC starten
25
  while (ADCSRA & ( 1 << ADSC));            // warten bis ADC fertig ist
26
27
  // wichtig: erst niederwertiges Byte lesen
28
  result = ADCL;
29
  result |= (uint16_t)(ADCH << 8);
30
  return result; 
31
}

Und in der Main:
1
  adc_init(2, 3);
2
3
  while(1)
4
  {
5
    _delay_ms(500);
6
    mwert= adc_getvalue();
7
    tm16_setdez6digit_nonull(mwert,0 );
8
  }

Lasse ich den Code mit internem Taktgeber (8MHz) laufen funktioniert 
alles wie es soll, lasse ich den Code mit externem Taktgeber laufen, 
erhalte ich als Ausgabewert von adc_getvalue immer den Wert 1023 
(0x3ff), egal welche Spannung ich anlege.

Initialisiere ich den ADC so, dass er die Vcc als Referenzspannung mit

  adc_init(2, 3);

verwenden soll, liefert der ADC in beiden Fällen, externer Quarz und 
interner Taktgeber, die erwarteten Werte.

Was mache ich falsch oder woran liegt das, dass ich die interne 
Spannungsreferenz bei externem Takt nicht nutzen kann, bei internem Takt 
aber schon.

Wieso habe ich dieses Verhalten bei Vcc als Spannungsreferenz nicht?

------------------------------------------------

PS: die letzte Zeit habe ich fast ausschließlich STM32 gemacht und den 
ATtiny44 lange nicht mehr angefasst gehabt (bestimmt ein halbes Jahr). 
Es kann also sein, dass ich einen äußerst dummen Fehler gemacht habe 
(sogar eher wahrscheinlich, denn wenn nicht, würde es laufen).

Gruß,

JJ

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Ralph S. schrieb:
> Was mache ich falsch oder woran liegt das, dass ich die interne
> Spannungsreferenz bei externem Takt nicht nutzen kann, bei internem Takt
> aber schon.

Generell wird bei dir der ADC viel zu hoch getaktet. Du benutzt einen 
Prescaler mit Teiler 2 und sowohl bei 8MHz und vor allem bei 16 MHz ist 
das jenseits dessen, was Microchip vorschreibt, denn mehr als 1Mhz ist 
nicht empfohlen.
Die besten Resultate gibts bei 50-200kHz ADC Takt.

: Bearbeitet durch User
von Karl M. (Gast)


Lesenswert?

Hallo, warum nutzt du nicht ADCW, zum lesen eines 10 bit Ergebnisses?
Was ist mit der korrekten Geschwindigkeit des ADC Wanders - Warum setzt 
du die nicht?

von Ralph S. (jjflash)


Lesenswert?

Grrrrr, kaum schickt man eine Problemanfrage hier ab, liest seinen 
eigenen Thread und dann fällt es einem wie Schuppen von den Augen, das 
Problem ist das hier:
1
    ADCSRA = (1 << ADEN) | (1 << ADPS0);                  // ADC enable ADPS0= 1;  ADPS1, ADPS2 = 0 => adc-clock = F_CPU / 2

==> der Takt für den ADC ist hier für 16MHz zu schnell. Hier steht 
jetzt:

  #if (F_CPU > 8000000)
    ADCSRA = (1 << ADEN) | (1 << ADPS1);                  // ADC enable 
ADPS1= 1;  ADPS0, ADPS2 = 0 => adc-clock = F_CPU / 4
  #else
    ADCSRA = (1 << ADEN) | (1 << ADPS0);                  // ADC enable 
ADPS0= 1;  ADPS1, ADPS2 = 0 => adc-clock = F_CPU / 2
  #endif

Somit: Entschuldigt die Störung !

von Ralph S. (jjflash)


Lesenswert?

Matthias S. schrieb:
> Generell wird bei dir der ADC viel zu hoch getaktet. Du benutzt einen
> Prescaler mit Teiler 2 und sowohl bei 8MHz und vor allem bei 16 MHz ist
> das jenseits dessen, was Microchip vorschreibt, denn mehr als 1Mhz ist
> nicht empfohlen.
> Die besten Resultate gibts bei 50-200kHz ADC Takt.

Nach dem Abschicken der Anfrage habe ich das auch gesehen gehabt ! Sorry 
der Belästigung wegen

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Ralph S. schrieb:
> #if (F_CPU > 8000000)
>     ADCSRA = (1 << ADEN) | (1 << ADPS1);                  // ADC enable
> ADPS1= 1;  ADPS0, ADPS2 = 0 => adc-clock = F_CPU / 4
>   #else
>     ADCSRA = (1 << ADEN) | (1 << ADPS0);                  // ADC enable
> ADPS0= 1;  ADPS1, ADPS2 = 0 => adc-clock = F_CPU / 2
>   #endif

Das ist immer noch viel zu schnell. Bei 8 Mhz tuts ein :32 Teiler und 
bei 16MHz dann der :64. Damit bist du bei 250kHz. Immer noch zu schnell, 
aber klappt schon besser.

von Ralph S. (jjflash)


Lesenswert?

Matthias S. schrieb:
> Das ist immer noch viel zu schnell. Bei 8 Mhz tuts ein :32 Teiler und
> bei 16MHz dann der :64. Damit bist du bei 250kHz. Immer noch zu schnell,
> aber klappt schon besser.

Ich habe jetzt grundsätzlich :64 eingestellt, egal ob der mit 8 oder 16 
MHz läuft. Für die Anwendung für den ich den brauche ist das immer noch 
schnell genug. Selbst dann, wenn der Tiny mit nur 1 MHz getaktet ist.

Vielen Dank

PS: Datenblatt lesen ist durch nichts zu ersetzen, außer durch noch mehr 
Datenblatt lesen ... und zu einem gewissen Prozentsatz das Forum hier 
lesen

: - )

von Karl M. (Gast)


Lesenswert?

Hallo Ralph S.,

ich berechne - lasse berechnen - den passenden Teiler durch einige 
Macros.
Dann folgen noch einige Präprozessoranweisungen, die die korrekte 
ADC-Frequenz, anhand zweier Randbedingungen, berechnen.
1
#define MIN_ADC_CLOCK (uint32_t)(50000)  // see datasheet
2
#define MAX_ADC_CLOCK (uint32_t)(200000) // see datasheet
3
#define ADC_CLOCK(d)  (uint32_t)(F_CPU/(d))
4
5
#define CALC_ADC_FREQ(d) ( (MAX_ADC_CLOCK >= ADC_CLOCK(d)) && (MIN_ADC_CLOCK <= ADC_CLOCK(d)) )
6
7
#define SET_ADC_PRESCALER(a2,a1,a0) ADCSRA |= ((a2<<ADPS2)|(a1<<ADPS1)|(a0<<ADPS0))
8
9
#define CLR_ADC_PRESCALER() ADCSRA &= ^((1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0))
10
11
CLR_ADC_PRESCALER(); // clear: ADPS[2:0] := 000
12
13
#if CALC_ADC_FREQ(2)
14
  SET_ADC_PRESCALER(0,0,1);
15
#elif CALC_ADC_FREQ(4)
16
  SET_ADC_PRESCALER(0,1,0);
17
#elif CALC_ADC_FREQ(8)
18
  SET_ADC_PRESCALER(0,1,1);
19
#elif CALC_ADC_FREQ(16)
20
  SET_ADC_PRESCALER(1,0,0);
21
#elif CALC_ADC_FREQ(32)
22
  SET_ADC_PRESCALER(1,0,1);
23
#elif CALC_ADC_FREQ(64)
24
  SET_ADC_PRESCALER(1,1,0);
25
#elif CALC_ADC_FREQ(128)
26
  SET_ADC_PRESCALER(1,1,1);
27
#else // default
28
  SET_ADC_PRESCALER(1,1,1);
29
#endif

von Ralph S. (jjflash)


Lesenswert?

: - ) wie gesagt, es hat sich alles erledigt. Wer (wie ich) schlicht 
nicht auf den Takt des ADC geachtet hat, braucht sich nicht wundern wenn 
es nicht funktioniert.

Meine originale Routine war aus einem Softwareprojekt bei dem der Tiny 
mit 1 MHZ gelaufen ist (und da hatte das dann logischerweise auch 
funktioniert gehabt).

Ich hab mir in meine Sourcen einen wichtigen Vermerk gesetzt und nun 
sollte mir so etwas nicht mehr passieren !

Gruß,

JJ

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Ralph S. schrieb:
> Meine originale Routine war aus einem Softwareprojekt bei dem der Tiny
> mit 1 MHZ gelaufen ist (und da hatte das dann logischerweise auch
> funktioniert gehabt)

Das ist etwas verwunderlich, weil selbst mit 1MHz der ADC gnadenlos mit 
500kHz betrieben worden wäre.
Wir hatten mal darüber hier diskutiert. Erstaunlich war z.B., das bei 
Zimmertemperatur sowas durchaus funktionieren kann, aber wenn es kälter 
wird, klappts dann nicht mehr - so bei einem Tiny85, der hier zu jeder 
Jahreszeit dicht unterm Dach läuft. Ab etwa 0°C stieg der mit 500kHz 
getaktete ADC aus.

von Ralph S. (jjflash)


Angehängte Dateien:

Lesenswert?

Matthias S. schrieb:
> Das ist etwas verwunderlich, weil selbst mit 1MHz der ADC gnadenlos mit
> 500kHz betrieben worden wäre.

Die 500 kHz sind für mich nicht verwunderlich, denn auf Seite 180 des 
Datenblatt steht (für ADC Betrieb: single ended channels):

Clock frequency min: 50 kHz
Clock frequency max: 1000 kHz

(ich hab meine alte Dokumentationen gefunden und dort auch gesehen 
gehabt, dass dort handschriftlich von mir steht: lt. Datenblatt nicht 
mehr als 1 MHz).

Mein "Forscherdrang" jetzt hat jetzt allerdings ergeben (kein Mist, ich 
habe es wirklich getestet), dass der ADC sogar noch mit 4 MHz Takt 
funktioniert hat (nur eben mit 8 MHz dann nicht mehr).

In meinem Projekt läuft er jetzt mit 1 MHz (und funktioniert dort so 
eingestellt auch gut, mit allen mir verfügbaren ATtiny24 bis ATtiny84 
getestet).

Aber man kann ja zusätzlich in ADCSRA das Bit ADPS1 setzen, dann läuft 
der mit 1/64 F_CPU, was bei 16MHz dann einem ADC-Takt von 250 kHz 
entspricht.

(verrückt, welch Thread ein kleiner Fehler nach sich zieht, aber 
andererseits bin ich froh, dass anderen sofort etwas auffällt, wenn man 
selbst ein Brett vor dem Kopf hat).

von Ralph S. (jjflash)


Lesenswert?

PS: Kältespray werde ich morgen draufsprühen um zu sehen ob er dann 
immer noch läuft !

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.