ATtiny44, ADC-Problem mit interner Spannungsreferenz bei externem Clock

OP #6030956
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
(Firma: matzetronics) #6030983
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.
OP #6030991
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 !
OP #6030995
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
(Firma: matzetronics) #6031002
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.
OP #6031069
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

: - )
Gast #6031107
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
OP #6033996
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
(Firma: matzetronics) #6034005
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.
OP #6034986
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).
Angehängte Dateien:

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren