ATtiny45 ADC Problem

#5601815
Lesenswert?

Ich möchte mit einem ATtiny45 eine Spannung messen und abhängig davon 
eine LED ein- bzw. ausschalten. Programmiert wird er tiny mittels ISP.
Im Anhang ist die Schaltung zu finden.

Das Problem ist nun, dass am ADC-Eingang PB4 immer 0 gelesen wird.
Ich habe die Schaltung auch schon mit und ohne Kondensator an AREF 
ausprobiert, und den ADC auch laut Datenblatt dementsprechend 
konfiguriert. Jedoch ohne Erfolg.

Bei den ATmega war der ADC noch nie ein Problem, jedoch bereitet mir der 
tiny nun etwas Kopfzerbrechen.

Bitte um Hilfe.

Edit: Den Schaltplan habe ich irrtümlich doppelt hochgeladen. Code 
richtiggestellt

Hier nun der Code:
1
#define F_CPU 1000000
2

3
#include <avr/io.h>
4
#include <util/delay.h>
5

6
void init_adc(void) {
7
  uint16_t result;
8

9
  ADMUX = (1<<REFS2) | (1 << REFS1);
10
  ADCSRA = (1 << ADEN) | (1 << ADPS1) | (1<<ADPS0);
11
  DIDR0 = (1<<ADC1D) | (1<<ADC2D) | (1<<ADC3D);
12

13
  ADCSRA |= (1 << ADSC);
14
  while (ADCSRA & (1 << ADSC)) {
15
    asm("nop");
16
  }
17

18
  result = ADCW;
19
}
20

21
uint16_t adc_read(uint8_t channel) {
22
  ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);
23
  ADCSRA |= (1 << ADSC);
24
  while (ADCSRA & (1 << ADSC))
25
    ;
26
  return ADCW;
27
}
28

29
uint16_t adc_read_avg(uint8_t channel, uint8_t average) {
30
  uint16_t result = 0;
31
  uint8_t i;
32

33
  ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);
34

35
  for (i = 0; i < average; ++i) {
36
    ADCSRA |= (1 << ADSC);
37
    while (ADCSRA & (1 << ADSC))
38
        ;
39
    result += ADCW;
40
  }
41

42
  return result / average;
43
}
44

45
void init()
46
{
47
  DDRB |= (1<<PB1);
48
}
49

50
int main(void)
51
{
52
  init();
53
  init_adc();
54
    uint16_t adc;
55
  
56
    while (1) 
57
    {
58
    adc = adc_read_avg(PB4, 5);
59
    
60
    if(adc< 500)
61
    {
62
      PORTB |= (1<<PB1);
63
    }
64
    else
65
    {
66
      PORTB &= ~(1<<PB1);
67
    }
68
    }
69
}
Angehängte Dateien:
Gast #5601821
Lesenswert?

Hallo!

Du weisst schon, dass an PB4 ADC2 liegt?
Dann solltest Du auch die richtigen ADC-Kanal auswählen.

http://ww1.microchip.com/downloads/en/devicedoc/atmel-2586-avr-8-bit-microcontroller-attiny25-attiny45-attiny85_datasheet.pdf

Man findet im Datenblatt unter "Table 17-4 Input Channel Selections" die 
richtige Zuordnung:
ADC2 (PB4): MUX[3:0] = 0010

Es lohnt sich also das Datenblatt auch zu lesen!
Gast #5601878
Lesenswert?

Georg G. schrieb:
> Bei init setzt du ref2, bei read löscht du es wieder.
> So gewollt?
> ref0 ist implizit 0?
1
void init_adc(void) {
2
  uint16_t result;
3

4
  ADMUX = (1<<REFS2) | (1 << REFS1); //internal 2.56V Voltage Reference without external bypass capacitor, disconnected from PB0 (AREF)
5

6
  ADCSRA = (1 << ADEN) | (1 << ADPS1) | (1<<ADPS0);
7
  DIDR0 = (1<<ADC1D) | (1<<ADC2D) | (1<<ADC3D);
8

9
  ADCSRA |= (1 << ADSC);
10
  while (ADCSRA & (1 << ADSC)) ; // add
11
  result = ADCW;
12
}
1
uint16_t adc_read(uint8_t channel) {
2
// add das 0x1F ist eine Maske, um die Bits in MUX[3:0] zu löschen. Ok!
3
  ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);
4
  ADCSRA |= (1 << ADSC);
5
  while (ADCSRA & (1 << ADSC)) ;
6
  return ADCW;
7
}

> "5" für Kanalwahl ist "Differenz zwischen ADC2 und ADC2, 20-fach
> verstärkt. Macht imho wenig Sinn.

5 ist die Anzahl der Werte über die der Mittelwert gebildet wird.
#5601882
Lesenswert?

Roland K. schrieb:
> cell1 soll natürlich adc sein.
> PB4 als Kanal sollte doch passen?

Dann mach erstmal deinen Code sauber, sprich: Mach ein ordentliches 
Beispiel, dass den Fehler zeigt. Meist kommt man bei dieser Arbeit dann 
ganz alleine auf den Fehler.
Aktuell passen da einige Dinge nicht. Das mit dem cell1 hab ich ja 
angesprochen, das "Problem" mit den Referenzquellen Georg.

EDITH
Roland K. schrieb:
> OK, habe den Fehler:
> PB4 hat den Wert 4 im Headerfile.
> Beim ADC muss ich aber den Wert 2 verwenden.
> Dachte PB4 hat den Wert 2.

Ach, da wurd ja PB4 übergeben in der ADC-Routine, das war mir gar nicht 
aufgefallen. Ist natürlich Quatsch, da muss das ADC2 rein, das Karl 
angesprochen hatte ;)
#5601954
Lesenswert?

Habe meine read-Funktionen nun angepasst und als Übergabewert verwende 
ich jetzt eigene Konstanten mit den richtigen Werten.
Danke für die Hilfe!
1
uint16_t adc_read(uint8_t channel) {
2
  ADMUX &= 0b11111100;
3
  ADMUX |= channel;
4
  
5
  ADCSRA |= (1 << ADSC);
6
  while (ADCSRA & (1 << ADSC))
7
    ;
8
  return ADCW;
9
}
10

11
uint16_t adc_read_avg(uint8_t channel, uint8_t average) {
12
  uint16_t result = 0;
13
  uint8_t i;
14

15
  ADMUX &= 0b11111100;
16
  ADMUX |= channel;
17

18
  for (i = 0; i < average; ++i) {
19
    ADCSRA |= (1 << ADSC);
20
    while (ADCSRA & (1 << ADSC))
21
        ;
22
    result += ADCW;
23
  }
24

25
  return result / average;
26
}
Persönliche Seite #5602057
Lesenswert?

Roland K. schrieb:
> ADMUX &= 0b11111100;
>   ADMUX |= channel;

Du bist echt ein Held, aus soetwas schönem wie

// add 0x0F ist eine Maske, um die Bits in MUX[3:0] zu löschen.
  ADMUX = (ADMUX & ~(0x0F)) | (channel & 0x0F);

kann nur durch Überheblichkeit deine Variante werden. Du kannst den 
Compiler nicht überlisten, nur die Lesbarkeit deines Codes verringern. 
Zudem hast du einen Fehler eingebracht. In der originalen Schreibweise 
wird das höherwertige Nibble von channel maskiert. In deiner Variante 
brettert es volle Acht in ADMUX, auch noch als OR, du kannst mithin nur 
Bits hinzufügen, keine Löschen. Sei dir versichert: das kannst du nicht 
wollen.

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