forked from Traumflug/Teacup_Firmware
-
Notifications
You must be signed in to change notification settings - Fork 0
/
analog.c
121 lines (98 loc) · 2.73 KB
/
analog.c
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 "analog.h"
/** \file
\brief Analog subsystem
*/
#include "temp.h"
#include <avr/interrupt.h>
/* OR-combined mask of all channels */
#undef DEFINE_TEMP_SENSOR
//! automagically generate analog_mask from DEFINE_TEMP_SENSOR entries in config.h
#define DEFINE_TEMP_SENSOR(name, type, pin, additional) | (((type == TT_THERMISTOR) || (type == TT_AD595)) ? 1 << (pin) : 0)
#ifdef AIO8_PIN
static const uint16_t analog_mask = 0
#else
static const uint8_t analog_mask = 0
#endif
#include "config.h"
;
#undef DEFINE_TEMP_SENSOR
#ifdef AIO8_PIN
#define AINDEX_MASK 0x1F
#define AINDEX_MAX 15
#define AINDEX_CURRENT ((ADMUX & 0x07) | ((ADCSRB & MASK(MUX5))?0x08:0))
#else
#define AINDEX_MASK 0x0F
#define AINDEX_MAX 7
#define AINDEX_CURRENT (ADMUX & 0x07)
#endif
static uint8_t adc_counter;
static volatile uint16_t adc_result[AINDEX_MAX + 1] __attribute__ ((__section__ (".bss")));
//! Configure all registers, start interrupt loop
void analog_init() {
if (analog_mask > 0) {
#ifdef PRR
PRR &= ~MASK(PRADC);
#elif defined PRR0
PRR0 &= ~MASK(PRADC);
#endif
ADMUX = REFERENCE;
// ADC frequency must be less than 200khz or we lose precision. At 16MHz system clock, we must use the full prescale value of 128 to get an ADC clock of 125khz.
ADCSRA = MASK(ADEN) | MASK(ADPS2) | MASK(ADPS1) | MASK(ADPS0);
#ifdef ADCSRB
ADCSRB = 0;
#endif
adc_counter = 0;
AIO0_DDR &= ~analog_mask;
DIDR0 = analog_mask & 0xFF;
#ifdef DIDR2
DIDR2 = (analog_mask >> 8) & 0xFF;
#endif
// now we start the first conversion and leave the rest to the interrupt
ADCSRA |= MASK(ADIE) | MASK(ADSC);
} /* analog_mask > 0 */
}
/*! Analog Interrupt
This is where we read our analog value and store it in an array for later retrieval
*/
ISR(ADC_vect, ISR_NOBLOCK) {
// emulate free-running mode but be more deterministic about exactly which result we have, since this project has long-running interrupts
if (analog_mask > 0) {
// store next result
adc_result[AINDEX_CURRENT] = ADC;
// find next channel
do {
adc_counter++;
adc_counter &= AINDEX_MAX;
} while ((analog_mask & (1 << adc_counter)) == 0);
// start next conversion
ADMUX = (adc_counter & 0x07) | REFERENCE;
#ifdef MUX5
if (adc_counter & 0x08)
ADCSRB |= MASK(MUX5);
else
ADCSRB &= ~MASK(MUX5);
#endif
ADCSRA |= MASK(ADSC);
}
}
/*! Read analog value from saved result array
\param channel Channel to be read
\return analog reading, 10-bit right aligned
*/
uint16_t analog_read(uint8_t channel) {
if (analog_mask > 0) {
uint16_t r;
uint8_t sreg;
// save interrupt flag
sreg = SREG;
// disable interrupts
cli();
// atomic 16-bit copy
r = adc_result[channel];
// restore interrupt flag
SREG = sreg;
return r;
} else {
return 0;
}
}