-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathadc.c
More file actions
127 lines (104 loc) · 4.31 KB
/
adc.c
File metadata and controls
127 lines (104 loc) · 4.31 KB
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
122
123
124
125
126
127
#include <stdlib.h>
#include <math.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include "adc.h"
#include "gpio.h"
#include "uart.h"
#define ABS(a) (((a) < 0) ? -(a) : (a))
#define ADC_TIMEBASE_VALUE ((uint8_t) ceil(F_CPU*0.000001))
#define ADC_SAMPLE_DURATION 50
#define ADC_SAMPLE_NUM ADC_SAMPNUM_ACC16_gc
#define ADC_PGA_GAIN(g) ADC0.PGACTRL = ((g << 5) | ADC_PGABIASSEL_1_4X_gc | ADC_PGAEN_bm)
#if defined(CELL_NAION)
#define ADC_REFSEL_BMS ADC_REFSEL_2048MV_gc
#elif defined(CELL_LTO)
#define ADC_REFSEL_BMS ADC_REFSEL_1024MV_gc
#else
#error "CELL_LTO or CELL_NAION must be defined"
#endif
static adc_gain_e adc_gain = ADC_GAIN_16X;
const static adc_channels_t adc_channels[ADC_CHANNELS_NUM] = {
// Differential measurement of current on the shunt resistor.
// This measurement should use gain amplifier (PGA).
{.muxpos = BMS_ADC_BP | ADC_VIA_PGA_gc, .muxneg = BMS_ADC_BN | ADC_VIA_PGA_gc, .diff = ADC_DIFF_bm},
// Single ended measurements of the voltages on battery and output
// and temperature of the internal MCU sensor. No PGA.
{.muxpos = BMS_ADC_BATT, .muxneg = ADC_MUXNEG_GND_gc, .diff = 0},
{.muxpos = BMS_ADC_OUT, .muxneg = ADC_MUXNEG_GND_gc, .diff = 0},
{.muxpos = BMS_ADC_TEMPSENSE, .muxneg = ADC_MUXNEG_GND_gc, .diff = 0},
};
static adc_result_t adc_results = {
.new_measurement = 0,
.active_channel = 0,
.adc = {0, 0, 0, 0},
.meas.raw = {0, 0, 0, 0},
};
ISR(ADC0_RESRDY_vect) {
uint8_t start = 0;
ADC0.INTFLAGS = ADC_RESRDY_bm;
adc_results.adc[adc_results.active_channel] = (int32_t)ADC0.RESULT;
if(adc_results.active_channel == ADC_CHANNELS_NUM - 1) {
adc_results.new_measurement = 1;
adc_results.active_channel = 0;
} else {
start = ADC_START_IMMEDIATE_gc;
adc_results.active_channel++;
}
ADC0.MUXPOS = adc_channels[adc_results.active_channel].muxpos;
ADC0.MUXNEG = adc_channels[adc_results.active_channel].muxneg;
ADC0.COMMAND = start | ADC_MODE_BURST_gc | adc_channels[adc_results.active_channel].diff;
}
void adc_init(void) {
ADC0.CTRLA = 1 << ADC_ENABLE_bp // ADC Enable: enabled
| 1 << ADC_RUNSTDBY_bp; // Run standby mode: enabled
ADC0.CTRLB = ADC_PRESC_DIV2_gc; // CLK_ADC = CLK_PER / PRESCALER
ADC0.CTRLC = ADC_REFSEL_BMS | (ADC_TIMEBASE_VALUE << ADC_TIMEBASE_gp);
ADC0.CTRLE = ADC_SAMPLE_DURATION;
ADC0.CTRLF = ADC_SAMPLE_NUM;
ADC_PGA_GAIN(adc_gain); // Enable the gain amplifier. Will be used only for some measurements.
ADC0.INTCTRL = ADC_RESRDY_bm;
ADC0.MUXPOS = adc_channels[adc_results.active_channel].muxpos;
ADC0.MUXNEG = adc_channels[adc_results.active_channel].muxneg;
ADC0.COMMAND = ADC_MODE_BURST_gc | adc_channels[adc_results.active_channel].diff;
}
void adc_start(void) {
ADC0.COMMAND |= ADC_START_IMMEDIATE_gc;
adc_results.new_measurement = false;
}
void adc_stop(void) {
ADC0.COMMAND &= ADC_START_STOP_gc;
}
bool adc_get_measurements(adc_measurement_t **meas) {
if(!adc_results.new_measurement) {
return false;
}
for(uint8_t i = 0; i < ADC_CHANNELS_NUM; i++) {
adc_results.meas.raw[i] = adc_results.adc[i] >> 2;
if(adc_channels[i].muxneg & ADC_VIA_PGA_gc) {
uint32_t abs_value = ABS(adc_results.adc[i]);
adc_results.meas.raw[i] /= (1 << adc_gain);
if(abs_value > 0x7000) {
adc_gain = (adc_gain > 0) ? (adc_gain - 1) : adc_gain;
} else if(abs_value < 0x1000) {
adc_gain = (adc_gain < ADC_GAIN_MAX - 1) ? (adc_gain + 1) : adc_gain;
}
ADC_PGA_GAIN(adc_gain);
}
}
*meas = &(adc_results.meas.ch);
return true;
}
adc_gain_e adc_get_gain(void) {
return adc_gain;
}
uint16_t adc_to_temperature(uint16_t value) {
int8_t sigrow_offset = SIGROW.TEMPSENSE1; // Read signed offset from signature row
uint8_t sigrow_gain = SIGROW.TEMPSENSE0; // Read unsigned gain/slope from signature row
value >>= 2; // 10-bit MSb of ADC result with 1.024V internal reference
uint32_t temp = value - sigrow_offset;
temp *= sigrow_gain; // Result might overflow 16-bit variable (10-bit + 8- bit)
temp += 0x80; // Add 256/2 to get correct integer rounding on division below
temp >>= 8; // Divide result by 256 to get processed temperature in
return temp >> 2;
}