blob: 5dd5d4d06472488d8289905ecd73ca8eec58d0ea (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
#include <avr/io.h>
#include "arch.h"
#include "driver/adc.h"
#include "driver/gpio.h"
int16_t AVRADC::getTemp_mdegC(int16_t offset)
{
// Measure temperature probe with internal 2.56V bandgap reference
ADMUX = _BV(REFS1) | _BV(REFS0) | 0x07;
ADCSRB = _BV(MUX5);
// Enable ADC with /64 prescaler
ADCSRA = _BV(ADEN) | _BV(ADPS2) | _BV(ADPS1);
// Wait for bandgap and temperature references to stabilise
arch.delay_ms(1);
// Start conversion
ADCSRA |= _BV(ADSC);
// Wait for conversion to complete
while (ADCSRA & _BV(ADSC)) ;
// TODO this is for atmega328p, not atmega32u4
// typical values: 242 mV @ -45 degC
// typical values: 314 mV @ +25 degC
// typical values: 380 mV @ +85 degC
// slope: 0.9090.. degC / mV at 25 .. 85 degC
// -> approx. 286.5 mV @ 0 degC / approx -260.45 degC @ 0 mV
// -> T[degC] = ADC[mV] * 0.91 - 261
// -> T[mdegC] = ADC[mV] * 91 - 26100
// slope: 0.9722.. mV / degC at -45 .. 25 degC
// slope: 0.942 mV / degC at -45 .. 85 degC
uint8_t adcr_l = ADCL;
uint8_t adcr_h = ADCH;
uint16_t adcr = adcr_l + (adcr_h << 8);
uint16_t vadc = 1100L * adcr / 1023L;
// adjust for chip-specific variations
vadc += offset;
int16_t temp_mdegc = vadc * 91 - 26100L;
// Disable ADC
ADCSRA &= ~_BV(ADEN);
return temp_mdegc;
}
uint16_t AVRADC::getVCC_mV()
{
// Measure internal 1.1V bandgap using VCC as reference
ADMUX = _BV(REFS0) | 0x1e;
ADCSRB = 0;
// Enable ADC with /64 prescaler
ADCSRA = _BV(ADEN) | _BV(ADPS2) | _BV(ADPS1);
// Wait for bandgap to stabilise
arch.delay_ms(1);
// Start conversion
ADCSRA |= _BV(ADSC);
// Wait for conversion to complete
while (ADCSRA & _BV(ADSC)) ;
uint8_t adcr_l = ADCL;
uint8_t adcr_h = ADCH;
uint16_t adcr = adcr_l + (adcr_h << 8);
uint16_t vcc = 1100L * 1023 / adcr;
// Disable ADC
ADCSRA &= ~_BV(ADEN);
return vcc;
}
uint16_t AVRADC::getVBat_mV(bool controlCharger)
{
// Measure VBat/2 (via voltage divider on PB5 / ADC12) using VCC as reference.
// Caution: PB5 is also connected to the white user LED.
ADMUX = _BV(REFS0) | 0x04;
ADCSRB = _BV(MUX5);
// Enable ADC with /64 prescaler
ADCSRA = _BV(ADEN) | _BV(ADPS2) | _BV(ADPS1);
if (controlCharger) {
// Turn on Vbat/2 voltage divider (and charger!)
gpio.output(GPIO::pb0, 1);
}
// Wait for things to stabilise
arch.delay_ms(1);
// Start conversion
ADCSRA |= _BV(ADSC);
// Wait for conversion to complete
while (ADCSRA & _BV(ADSC)) ;
uint8_t adcr_l = ADCL;
uint8_t adcr_h = ADCH;
uint16_t adcr = adcr_l + (adcr_h << 8);
uint16_t vbat = 4200L * adcr / 1023;
// Disable ADC
ADCSRA &= ~_BV(ADEN);
if (controlCharger) {
// Turn off voltage divider (and charger!)
gpio.output(GPIO::pb0, 0);
}
return vbat;
}
AVRADC adc;
|