Forum: Mikrocontroller und Digitale Elektronik ATtiny85 ADC mit interne REV


von Dustin S. (dustin_s)


Lesenswert?

Hallöchen,

auf der Seite: https://elektro.turanis.de/html/prj306/index.html
ist ganz gut beschrieben wie man eine Spannung direkt am Eingang eines 
ATtinys mit Hilfe der internen Referenzspannung misst.

Meine Frage ist nun, wie muss ich die Funktion umschreiben, damit man 
eine Spannung mit der Internen Referenzspannung an einem anderen Pin zb. 
PIN2 messen kann?
1
void setup()
2
{
3
    Serial.begin(9600);
4
}
5
6
void loop()
7
{
8
    Serial.println(String(getVcc()/1000.0) + "V");
9
    delay(1000);
10
}
11
12
int getVcc()
13
{
14
    // reads internal 1V1 reference against VCC
15
    #if defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny44__)
16
        // for ATtiny84
17
        ADMUX = _BV(MUX5) | _BV(MUX0);
18
    #elif defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny45__)
19
        // for ATtiny85/45
20
        ADMUX = _BV(MUX3) | _BV(MUX2);
21
    #elif defined(__AVR_ATmega1284P__)
22
        // for ATmega1284
23
        ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
24
    #else
25
        // for ATmega328
26
        ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
27
    #endif
28
29
    delay(5); // wait for Vref to settle
30
31
    ADCSRA |= _BV(ADSC); // reading ADC
32
    while (bit_is_set(ADCSRA, ADSC));
33
34
    uint8_t low = ADCL;
35
    unsigned int adcVal = (ADCH << 8) | low;
36
37
    // discard previous result
38
    ADCSRA |= _BV(ADSC); // Convert
39
    while (bit_is_set(ADCSRA, ADSC));
40
    low = ADCL;
41
    adcVal = (ADCH << 8) | low;
42
43
    return ((long)1024 * 1100) / adcVal;
44
}


mit freundlichen Grüßen
Dustin

von EAF (Gast)


Lesenswert?

Dustin S. schrieb:
> damit man
> eine Spannung mit der Internen Referenzspannung an einem anderen Pin zb.
> PIN2 messen kann?


analogReference() und analogRead() funktioniert nicht?

von Christian S. (roehrenvorheizer)


Lesenswert?

Hallo,

Ich habe mal die relevanten Stellen aus dem Datenlatt heraus kopiert, da 
ich sie sowieso lesen mußte. Im Link wird ja gar nicht erklärt, welcher 
Eingang warum und wie genutzt wird. Eine sensationell erschöpfend 
ausführliche  Anleitung.

https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2586-AVR-8-bit-Microcontroller-ATtiny25-ATtiny45-ATtiny85_Datasheet.pdf

"The analog input channel and differential gain are selected by writing 
to the MUX[3:0] bits in ADMUX. Any of the
four ADC input pins ADC[3:0] can be selected as single ended inputs to 
the ADC.

17.13.1 ADMUX – ADC Multiplexer Selection Register

• Bits 3:0 – MUX[3:0]: Analog Channel and Gain Selection Bits
The value of these bits selects which combination of analog inputs are 
connected to the ADC. In case of differential
input (ADC0 - ADC1 or ADC2 - ADC3), gain selection is also made with th 
ese bits. Selecting ADC2 or ADC0 as
both inputs to the differential gain stage enables offset measurements. 
Selecting the single-ended channel ADC4 enables the temperature sensor. 
Refer to Table 17-4 for details. If these bits are changed during a 
conversion, the change will not go into effect until this conversion is 
complete (ADIF in ADCSRA is set)."

Um auf Deinen PIN2 = PB3 zu schalten, gilt:
MUX[3:0]   0011 ADC3 (PB3), also MuUX1=1 und MUX0 = 0.


Deshalb hier:
    #elif defined(_AVR_ATtiny85_) || defined(_AVR_ATtiny45_)

        // for ATtiny85/45

        ADMUX = _BV(MUX3) | _BV(MUX2);
ändern zu:

    #elif defined(_AVR_ATtiny85_) || defined(_AVR_ATtiny45_)

        // for ATtiny85/45

        ADMUX = _BV(MUX1) | _BV(MUX0);

In ADMUX sollen die beiden untersten Bits gesetzt sein. Bit 2 = 0 und 
Bit 3 = 0. Korrektur erwünscht, falls ich mich da vertan haben sollte.

mfg

: Bearbeitet durch User
von Dustin S. (dustin_s)


Lesenswert?

Vielen Dank für die Antwort.

zum probieren habe ich das so geändert:
1
#include "SoftwareSerial.h"
2
3
#define Rx  1 //
4
#define Tx  2 //
5
6
SoftwareSerial mySerial(Rx, Tx);
7
8
void setup() {
9
  pinMode(Rx, INPUT);
10
  pinMode(Tx, OUTPUT);
11
  mySerial.begin(9600);
12
13
}
14
15
void loop() {
16
  int vcc = readVccPIN() ;
17
  mySerial.println (vcc);
18
  delay(1000);
19
20
}
21
22
int readVccPIN()
23
{
24
25
     ADMUX = _BV(MUX1) | _BV(MUX0); // Auslesen an PIN2    
26
 
27
    delay(5); // wait for Vref to settle
28
    ADCSRA |= _BV(ADSC); // reading ADC
29
    while (bit_is_set(ADCSRA, ADSC));
30
    uint8_t low = ADCL;
31
    unsigned int adcVal = (ADCH << 8) | low;
32
    // discard previous result
33
    ADCSRA |= _BV(ADSC); // Convert
34
    while (bit_is_set(ADCSRA, ADSC));
35
    low = ADCL;
36
    adcVal = (ADCH << 8) | low;
37
    return ((long)1024 * 1100) / adcVal;
38
}

Also der PIN2 reagiert schon mal aber
wenn ich nun an PIN2 eine Spannung anlege dann kommen da nur komische 
Werte raus.
PIN2 gegen GND = -1
PIN2 gegen 0,95V = 5313

Die zu messende Spannung war immer unter 1,1V
Der Tiny85 wird mit 5V versorgt

gibt es noch eine andere Lösung oder habe ich etwas falsch gemacht ?

mfg

von Georg M. (g_m)


Lesenswert?

Was bedeutet
1
// reads internal 1V1 reference against VCC
?

von S. Landolt (Gast)


Lesenswert?

> Was bedeutet ...

Das frage ich mich auch - hängt wohl mit dem merkwürdigen Bruch nach 
return zusammen: je größer der ADC-Wert, umso kleiner der Return-Wert.

Wo wird der ADC eigentlich eingeschaltet, ich sehe kein ADEN?
Und 'interne REF'? Wird in ADMUX nicht explizit gesetzt, folglich gilt 
'VCC used as Voltage Reference, disconnected from PB0 (AREF)'.

von Georg M. (g_m)


Lesenswert?

Measure VCC/Battery Voltage Without Using I/O Pin on tinyAVR and megaAVR

https://ww1.microchip.com/downloads/en/Appnotes/00002447A.pdf

von S. Landolt (Gast)


Lesenswert?

Ja, so dachte ich mir das; Dustin S. möchte aber die Spannung an 
ADC3=PB3 gegen die interne 1.1 V-Referenz messen - da hat er wohl etwas 
verwechselt.

von EAF (Gast)


Lesenswert?

Ich habe da auch noch 2 Fragen:
1. Wozu das Gehampel mit ADCL und ADCL, wenn es doch auch ADCW gibt?
2. Warum ist analogRead() nicht schmackhaft genug?

von S. Landolt (Gast)


Lesenswert?

Ein Versuch:
1. Die Zeile 'ADMUX =...' ergänzen um '| _BV(REFS1)'
2. Unmittelbar danach den ADC einschalten mit 'ADCSRA = _BV(ADEN)'
3. return: Im Datenblatt nachlesen (empfiehlt sich allgemein) unter 'ADC 
Conversion Result', Unterpunkt 'Single Ended Conversion', dort steht die 
benötigte Gleichung, muss nur noch umgestellt werden.
4. nicht zu optimistisch sein (wieder laut Datenblatt): 'Internal 
Voltage Reference 1.0 .. 1.1 .. 1.2 V'.

von Dustin S. (dustin_s)


Lesenswert?

habe den Tipp von S. Landolt probiert. Leider ohne Erfolg. Trotzdem 
Danke!

Allgemein möchte ich Eine Spannung messen, die unabhängig von der 
Versorgungsspannung ist.

analogRead() funktioniert leider nicht da sich der Wert verfälscht wenn 
die Versorgungspannung schwankt. Deshalb dachte ich man kann mit der 
Internen Referenzspannung arbeiten z.b. 1,1V dann könnte ich halt 
Spannungen bis 1,1V an einem Pin messen, wenn das irgendwie 
funktioniert..

Sorry bin gerade erst mit dem Programmieren angefangen.

von EAF (Gast)


Lesenswert?

Dustin S. schrieb:
> analogRead() funktioniert leider nicht da sich der Wert verfälscht wenn
> die Versorgungspannung schwankt.
Das ist wahr, so wie DU es tust!
Das muss aber nicht wahr bleiben.
Denn schließlich gibt es ja auch noch: analogRead()
(aber das sagte ich doch schon, oder?)

Also nochmal:
Was gefällt dir an analogReference() nicht?

von EAF (Gast)


Lesenswert?

EAF schrieb:
> Denn schließlich gibt es ja auch noch: analogRead()
> (aber das sagte ich doch schon, oder?)
Ähmm...
Denn schließlich gibt es ja auch noch: analogReference()
 analogReference(INTERNAL1V1);

Siehe:
https://github.com/SpenceKonde/ATTinyCore/blob/eeff8ee788227ca14a856f3d34dbc1578e0ac193/avr/variants/tinyX5/pins_arduino.h#L143

Beitrag #7206238 wurde vom Autor gelöscht.
von Dustin S. (dustin_s)


Lesenswert?

EAF schrieb:
> Ähmm...
> Denn schließlich gibt es ja auch noch: analogReference()
>  analogReference(INTERNAL1V1);

das funktioniert soweit ganz gut! Dankeschön :)

habe davor mit dem damellis/attiny statt TinyCore gearbeitet, da 
funktioniert analogReference() nicht.


Nur habe ich jetzt das Problem wenn ich TinyCore verwende funktioniert 
die Radiohead Libary nicht mehr aber gut dann ist das halt so .. falls 
noch jemand eine komplette Funktion mit Bits setzen, in der Arduino IDE 
und mit  damellis/attiny hat wäre das ein Traum :)

von S. Landolt (Gast)


Lesenswert?

> bin gerade erst mit dem Programmieren angefangen

Dann als allgemeiner Ratschlag: mit der Rückmeldung
> ... probiert. Leider ohne Erfolg ...
alleine ist keine Zusammenarbeit möglich. Es muss zumindest gezeigt 
werden, was genau gemacht wurde, in diesem Fall das aktuelle Programm 
vorstellen, und worin der 'Misserfolg' besteht, d.h. welche (falschen) 
Werte nun erscheinen.

von EAF (Gast)


Angehängte Dateien:

Lesenswert?

Dustin S. schrieb:
> funktioniert
> die Radiohead Libary nicht mehr
1. Von Radiohead war nie die Rede!
2. "funktioniert nicht" ist eine unzureichende Problem Beschreibung


Hier mal ein ADC Beispiel, welches mit dem Tiny85 funktionieren sollte.
Ungetestet.
1
#include <CombieAdc.h>
2
using Combie::Adc;
3
4
Adc adc;
5
6
7
#include "SoftwareSerial.h"
8
#define Rx  1 //
9
#define Tx  2 //
10
SoftwareSerial mySerial(Rx, Tx);
11
12
void setup() 
13
{
14
  pinMode(Rx, INPUT);
15
  pinMode(Tx, OUTPUT);
16
17
   mySerial.begin(9600);
18
19
   adc  .enable()
20
        .setClockDivisor() // automatisch
21
        .setReference(Adc::REF_11)
22
        .setSource(Adc::MUX_ADC0); 
23
}
24
25
void loop() 
26
{
27
  mySerial.println(adc);
28
  delay(1000);
29
}

von Dustin S. (dustin_s)


Lesenswert?

vielen Dank für die Mühe. Ich teste das später mal und werde mich in 
Zukunft bessern mit den Infos!

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.