This repository has been archived by the owner on Aug 27, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 199
/
analog-avr.c
116 lines (91 loc) · 2.75 KB
/
analog-avr.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
/** \file
\brief Analog subsystem, AVR specific part.
*/
#if defined TEACUP_C_INCLUDE && defined __AVR__
#include "pinio.h"
#include "memory_barrier.h"
static uint8_t adc_counter = 0;
static volatile uint16_t BSS adc_result[NUM_TEMP_SENSORS];
//! Configure all registers, start interrupt loop
void analog_init() {
if (analog_mask) { // At least one temp sensor uses an analog channel.
// clear ADC bit in power reduction register because of ADC use.
#ifdef PRR
PRR &= ~MASK(PRADC);
#elif defined PRR0
PRR0 &= ~MASK(PRADC);
#endif
// select reference signal to use, set right adjusted results and select ADC input 0
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
// clear analog inputs in the data direction register(s)
AIO0_DDR &= ~analog_mask;
#ifdef AIO8_DDR
AIO8_DDR &= ~(analog_mask >> 8);
#endif
// disable the analog inputs for digital use.
DIDR0 = analog_mask & 0xFF;
#ifdef DIDR2
DIDR2 = (analog_mask >> 8) & 0xFF;
#endif
// Enable the ADC.
ADCSRA |= MASK(ADIE);
} /* analog_mask */
}
/**
Start a new ADC conversion.
*/
void start_adc() {
ADCSRA |= MASK(ADSC);
}
/*! 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) {
// store next result
adc_result[adc_counter] = ADC;
// next channel
do {
adc_counter++;
if (adc_counter >= sizeof(adc_channel))
adc_counter = 0;
} while (adc_channel[adc_counter] == 255);
// start next conversion
ADMUX = (adc_channel[adc_counter] & 0x07) | REFERENCE;
#ifdef MUX5
if (adc_channel[adc_counter] & 0x08)
ADCSRB |= MASK(MUX5);
else
ADCSRB &= ~MASK(MUX5);
#endif
// If there is another channel to read, start a new conversion.
if (adc_counter != 0) {
ADCSRA |= MASK(ADSC);
}
}
}
/** Read analog value from saved result array.
\param channel Channel to be read. Channel numbering starts at zero.
\return Analog reading, 10-bit right aligned.
*/
uint16_t analog_read(uint8_t index) {
uint16_t result = 0;
#ifdef AIO8_PIN
if (index < sizeof(adc_channel) && adc_channel[index] < 16) {
#else
if (index < sizeof(adc_channel) && adc_channel[index] < 8) {
#endif
ATOMIC_START
// Atomic 16-bit copy.
result = adc_result[index];
ATOMIC_END
}
return result;
}
#endif /* defined TEACUP_C_INCLUDE && defined __AVR__ */