Forum: Mikrocontroller und Digitale Elektronik Attiny412 Temperatur intern


von MCU (Gast)


Lesenswert?

Hallo,

ich möchte die interne Temperatur des Attiny412 auslesen sowie den ADC 
nutzen.
Hierzu habe ich zwei ADC Inits geschrieben:
1
void adc_init(void) {    
2
    ADC0.CTRLC = 0x10 | ADC_PRESC2_bm | ADC_PRESC1_bm | ADC_PRESC0_bm;
3
    ADC0.MUXPOS = ADC_MUXPOS_AIN6_gc; // AIN6 PA6
4
    ADC0.CTRLA |= ADC_ENABLE_bm;
5
}
6
7
void adc_init_temperature(void) {    
8
    VREF.CTRLA = 0x10;    
9
    ADC0.CTRLC = 0x0;
10
    ADC0.MUXPOS = 0x1E;
11
    ADC0.CTRLD = 0x5 << 5; // INITDLY > 32 us x f_clk_adc
12
    ADC0.SAMPCTRL = 0x0F; // SAMPLE > 32us x f_clk_adc
13
    ADC0.CTRLC = 0x1 << 6; // 5pf SAMPCAP
14
    ADC0.CTRLA |= ADC_ENABLE_bm;
15
}
16
17
uint16_t temperature_get(void) {
18
    ADC0.COMMAND = 0x1;
19
    for(uint8_t i = 0; i < 100; i++)
20
    _delay_ms(1);
21
    return ADC0.RESH << 8 | ADC0.RESL;
22
}

(Ja ich weiß, kann man auch mit ISR machen, aber zum testen reicht ein 
delay :))

Bei recht konstanter Raumtemperatur mache ich folgendes:

1. adc_init_temperature() und dann Temperatur auslesen. Ich erhalte eine 
plausible Temperatur. z.b. 0x201 oder mit Finger drauf 0x208
2. adc_init() und dann ADC auslesen. Funktioniert bestens.
3. adc_init_temperature() und dann Temperatur auslesen. Das Ergebnis 
weicht stark vom ersten ab und ist nicht mehr sinnvoll z.b. 0x101 oder 
mit Finger drauf 0x107
4. adc_init() und dann ADC auslesen. Funktioniert bestens.

Was mache ich falsch? Warum erhalte ich einen falschen Wert beim zweiten 
mal auslesen der internen Temperatur des MCU?

von Apollo M. (Firma: @home) (majortom)


Lesenswert?

... denke, sieht so aus weil
ADC0.CTRLC = 0x10, hier wird VDD (>1.1V) als Ref. selected UND danach 
für Tp internal Ref. 1.1V ... jetzt braucht der Klotz Zeit um die 
vorhandenen internen Chipkapazitäten auf 1.1V zu entladen. Das wäre auch 
plausibel mit deinen beobachten Werten, die viel kleiner sind, weil der 
ADC länger Vref >1.1V sieht.

Es gibt verschiedenen Lsg. dazu, z.B. kurz warten nach Vref change oder 
Rescaling und Vref 2.5V oder auch 4.3V je nach Tp, Vdd, Fclk ...

ODER setze mal VREF.CTRLB ...  dann wäre die Zeit zum Einschwingen von 
Vref Thema, aber deine Beobachtung spricht eher für den ersten Hinweis.

Bit 1 – ADC0REFEN ADC0 Reference Force Enable
Writing a '1' to this bit forces the voltage reference for the ADC0 to 
be running, even if it not requested.
Writing a '0' to this bit allows to automatic enable/disable of the 
reference source by the peripheral.


... und z.B. ADC0.CTRLC = 0x10 ... , Magic Numbers zu sehen ist ganz 
böse!

: Bearbeitet durch User
von MCU (Gast)


Lesenswert?

Ich habe nach dem VREF setzen 300ms eingebaut.
Ich bekomme jetzt beim ersten und zweiten lesen 0x103 bei beiden raus. 
Das Problem daran ist aber, dass es zu wenig ist. Sprich es wären ca. 
150K. Da ich noch tippen kann, denke ich, es ist wärmer.

Am konvertieren der Temperatur sollte es nicht liegen, die habe ich im 
Grunde 1:1 aus dem Datenblatt übernommen. Werfe ich da 0x204 rein, kommt 
ca. 2X°C bzw. knapp 296k raus, was schon hin kommt.

Ich versteh es nicht :-(
1
uint16_t adc_intern_temp_conv(uint16_t temperature_adc_convert) {
2
   int8_t sigrow_offset = SIGROW.TEMPSENSE1; // Read signed value from signature row
3
   uint8_t sigrow_gain = SIGROW.TEMPSENSE0;
4
   // Read unsigned value from signature row
5
   // ADC conversion result with 1.1 V internal reference
6
   uint32_t temp = temperature_adc_convert - sigrow_offset;
7
   temp *= sigrow_gain; // Result might overflow 16 bit variable (10bit+8bit)
8
   temp += 0x80;
9
   // Add 1/2 to get correct rounding on division below
10
   // Divide result to get Kelvin
11
   return 100 * (temp >>= 8);
12
}
13
14
void adc_init_temperature(void) {
15
    VREF.CTRLB = 0x2; // Ref konstant eingeschaltet
16
    VREF.CTRLA = 0x10; // VRef = 1,1V für Temperaturmessung
17
    ADC0.CTRLC = 0x05; // Prescaler div 64
18
    
19
     for(uint8_t i = 0; i < 100; i++)
20
    _delay_ms(3);
21
    
22
    ADC0.MUXPOS = 0x1E;
23
    ADC0.CTRLD = 0x5 << 5; // INITDLY > 32 us x f_clk_adc
24
    ADC0.SAMPCTRL = 0x0F; // SAMPLE > 32us x f_clk_adc
25
    ADC0.CTRLC = 0x1 << 6; // 5pf SAMPCAP
26
27
    ADC0.CTRLA |= ADC_ENABLE_bm;
28
}

von Apollo M. (Firma: @home) (majortom)


Lesenswert?

MCU schrieb:
> Ich versteh es nicht :-(

ok, rechne mal rückwärts mit den beiden Tp Messwerten Vref aus, mal 
schauen wie die Werte aussehen.

von MCU (Gast)


Lesenswert?

Zurückrechnen auf VRef geht nicht.
Ich lese den ADC Wert aus dem Register, der Wert wird mit dem Geräte 
spezifischen Wert (Verstärkung und Offset) multipliziert bzw. abgezogen. 
Dabei wird, wenn ich das richtig verstehe, angenommen, dass VREF 1,1V 
ist. Raus kommt dann ein Wert in Kelvin.

Ich habe mal etwas getestet:

Wenn ich VREF auf 1,5V setze, steigt der ADC Wert über VREF 1,1V
Wenn ich VREF auf 2,5V setze, sinkt der ADC Wert unter VREF 1,5V
Wenn ich VREF auf 4,5V setze, sinkt der ADC Wert unter VREF 2,5V

Irgendwas ist hier doch faul. Ich weiss nicht weiter.

Freue mich über jegliche Hinweise.

von Apollo M. (Firma: @home) (majortom)


Lesenswert?

Teste und spiele mal mit den Funktionen zur Messung von Vcc/Vbat und Tp.
1
//macros
2
#define SBIT(r,bp) (r) |= (1<<(bp))
3
#define CBIT(r,bp) (r) &= ~(1<<(bp))
4
#define SBITM(r,gm,v) (r) &= (~(gm)) | (v)
5
6
#define ADC_START() {ADC0.COMMAND = ADC_STCONV_bm; while (!(ADC0.INTFLAGS & ADC_RESRDY_bm));} //wait on single conv end
7
8
void initADC() {  
9
  VREF.CTRLA |= VREF_ADC0REFSEL_1V1_gc; //int. ref
10
  //SBIT(VREF.CTRLB, VREF_ADC0REFEN_bp); //adc0 vref permanent on
11
  ADC0.CTRLC = ADC_PRESC_DIV64_gc; //prescaler
12
  SBIT(ADC0.CTRLC, ADC_SAMPCAP_bp); //reduced sample cap
13
  SBITM(ADC0.CTRLD, ADC_INITDLY_gm, 3); //0-5, 0/16-256*fclk_adc delay before sampling, !>32us
14
  SBITM(ADC0.SAMPCTRL, ADC_SAMPLEN_gm, 4); //sample length x*fclk_adc, max: 512, default: 2
15
}
16
17
void initVBAT() {
18
  SBITM(ADC0.CTRLC, ADC_REFSEL_INTREF_gc, 1); //vcc ref.
19
  ADC0.MUXPOS = ADC_MUXPOS_INTREF_gc; 
20
  SBIT(ADC0.CTRLA, ADC_ENABLE_bp);
21
}
22
23
uint16_t getVBAT() { //get vcc
24
  initVBAT();
25
  ADC_START();
26
  uint16_t vbat = (uint16_t) (1023 * 11 / (1 + ADC0.RES)); //prevent div zero
27
  CBIT(ADC0.CTRLA, ADC_ENABLE_bp); //adc off
28
  return vbat*100; //vbat [mV]
29
}
30
31
void initTp() {
32
  SBITM(ADC0.CTRLC, ADC_REFSEL_INTREF_gc, 0); //int. ref.
33
  ADC0.MUXPOS = ADC_MUXPOS_TEMPSENSE_gc;
34
  SBIT(ADC0.CTRLA, ADC_ENABLE_bp);
35
}
36
37
uint16_t getTp() {  
38
  initTp();
39
  ADC_START();    
40
  uint32_t tp = ADC0.RES - SIGROW.TEMPSENSE1; //adjust offset, signature row
41
  CBIT(ADC0.CTRLA, ADC_ENABLE_bp); //adc off
42
  tp *= SIGROW.TEMPSENSE0; //adjust gain error
43
  tp += 0x80; //add 0.5 for correct rounding by div
44
  tp >>= 8; //div to get kelvin
45
  return (uint16_t) (tp*100 - 27315); //tp [°C *100] 
46
}

: Bearbeitet durch User
von avra (Gast)



Lesenswert?

eventuell ........

2.  Silicon Errata Issues
2.1  Errata Details
- Erratum is not applicable.
X Erratum is applicable.
* This silicon revision was never released to production.
2.2  Device
2.2.1  The Temperature Sensor is Not Calibrated on Parts with Date Code 
727, 728 and 1728 (Year 2017,
Week 27/28)
The temperature sensor is not calibrated on parts with date code 727/728 
(used on QFN packages) and 1728 (used
on SOIC packages).
Work around
If temperature sensor calibration data is required, devices with the 
affected date code may be returned through the
Microchip RMA service. Devices with this date code are no longer shipped 
by Microchip.

mfg

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.