forked from Traumflug/Teacup_Firmware
-
Notifications
You must be signed in to change notification settings - Fork 0
/
analog-stm32.c
218 lines (174 loc) · 5.53 KB
/
analog-stm32.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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
/** \file
\brief Analog subsystem, ARM specific part.
STM32F4 goes a different way. The ADC don't have a register for
each channel. We are using DMA instead.
*/
#if defined TEACUP_C_INCLUDE && defined __ARM_STM32F411__
#include "cmsis-stm32f4xx.h"
#include "arduino.h"
#include "pinio.h"
#include "delay.h"
#include "temp.h"
// DMA ADC-buffer
#define OVERSAMPLE 6
static uint16_t BSS adc_buffer[OVERSAMPLE][NUM_TEMP_SENSORS];
// Private functions
void init_analog(void);
void init_dma(void);
/** Initialize the analog subsystem.
Initialize the ADC and start hardware scan for all sensors.
*/
void analog_init() {
if (NUM_TEMP_SENSORS) { // At least one channel in use.
init_dma();
init_analog();
}
}
/** Initialize all analog pins from config
Initialize the pins to analog mode, no pullup/no pulldown, highspeed
*/
void init_analog() {
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; //Enable clock
#undef DEFINE_TEMP_SENSOR
/*
config analog pins
1. analog mode
2. no pullup
3. high speed
*/
#define DEFINE_TEMP_SENSOR(name, type, pin, additional) \
SET_MODE(pin, 0x3); \
PULL_OFF(pin); \
SET_OSPEED(pin, 0x3);
#include "config_wrapper.h"
#undef DEFINE_TEMP_SENSOR
/* Set ADC parameters */
/* Set the ADC clock prescaler */
ADC->CCR |= ADC_CCR_ADCPRE;
ADC1->CR1 &= ~(ADC_CR1_RES);
ADC1->CR1 |= ADC_CR1_RES_0;
ADC1->CR1 |= ADC_CR1_SCAN | ADC_CR1_OVRIE;
ADC1->CR2 |= ADC_CR2_DMA;
/* Set ADC number of conversion */
// ((NUM_TEMP_SENSORS) - 1) << 20
ADC1->SQR1 &= ~(ADC_SQR1_L);
ADC1->SQR1 |= (NUM_TEMP_SENSORS - 1) << 20;
// for loop over each channel (0..15) for sequence
#undef DEFINE_TEMP_SENSOR
// for PIO ## ADC >= 10 SRPR1 and ADC -10, else SMPR 2
// 0x06 = 144 cycles
// subt line is to keep compiler happy
#define DEFINE_TEMP_SENSOR(name, type, pin, additional) \
if (NUM_TEMP_SENSORS) { \
uint32_t subt = (pin ## _ADC >= 10) ? 10 : 0; \
if (pin ## _ADC >= 10) { \
ADC1->SMPR1 |= (uint32_t)0x06 << (3 * ((pin ## _ADC) - subt)); \
} else { \
ADC1->SMPR2 |= (uint32_t)0x06 << (3 * ((pin ## _ADC) - subt)); \
} \
subt = (TEMP_SENSOR_ ## name <= 5) ? 0 : (TEMP_SENSOR_ ## name <= 11) ? 6 : 12; \
if (TEMP_SENSOR_ ## name <= 5) { \
ADC1->SQR3 |= pin ## _ADC << (5 * TEMP_SENSOR_ ## name - subt); \
} else \
if (TEMP_SENSOR_ ## name <= 11) { \
ADC1->SQR2 |= pin ## _ADC << (5 * (TEMP_SENSOR_ ## name - subt)); \
} else { \
ADC1->SQR1 |= pin ## _ADC << (5 * (TEMP_SENSOR_ ## name - subt)); \
} \
}
#include "config_wrapper.h"
#undef DEFINE_TEMP_SENSOR
ADC1->CR2 |= ADC_CR2_CONT;
ADC1->CR2 |= ADC_CR2_ADON; // A/D Converter ON / OFF
ADC1->CR2 |= ADC_CR2_SWSTART;
}
/**
Init the DMA for ADC
*/
void init_dma() {
RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN; // Enable clock
/**
We have two DMA streams for ADC1. (DMA2_Stream0 and DMA2_Stream4)
We take DMA2 Stream4.
See reference manual 9.3.3 channel selection (p. 166)
*/
// 1. Disable DMA-Stream
DMA2_Stream4->CR &= ~DMA_SxCR_EN;
while(DMA2_Stream4->CR & DMA_SxCR_EN); // we wait until it is disabled.
uint32_t tmp_CR = 0; //DMA2_Stream4->CR;
// 2. perihperal port register address
DMA2_Stream4->PAR = (uint32_t)&ADC1->DR;
// 3. memory address
DMA2_Stream4->M0AR = (uint32_t)adc_buffer;
// 4. total number of data items
DMA2_Stream4->NDTR = NUM_TEMP_SENSORS * OVERSAMPLE;
// 5. DMA channel
tmp_CR &= ~(DMA_SxCR_CHSEL);
// 7. priority
tmp_CR |= DMA_SxCR_PL;
// 8. FIFO
DMA2_Stream4->FCR &= ~(DMA_SxFCR_DMDIS);
// 9. config the rest
/*
* halfword for memory and periphal: the 12bit ADC is 16bit right aligned
* memory inc.: we read any adc and doing a step of 16bits after each conversion
* circular mode: repeat until inf
*/
tmp_CR &= ~(DMA_SxCR_DIR);
tmp_CR |= DMA_SxCR_MSIZE_0 |
DMA_SxCR_PSIZE_0 |
DMA_SxCR_MINC |
DMA_SxCR_CIRC |
DMA_SxCR_TCIE;
DMA2_Stream4->CR = tmp_CR;
// 10. Enable DMA-Stream
DMA2_Stream4->CR |= DMA_SxCR_EN;
while(!(DMA2_Stream4->CR & DMA_SxCR_EN));
NVIC_SetPriority(DMA2_Stream4_IRQn,
NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 3, 3));
NVIC_EnableIRQ(DMA2_Stream4_IRQn); // Enable interrupt generally.
}
/**
DMA2 Stream4 interrupt.
Happens every time the complete stream is written.
In that case we stop the continious conversion and clear the DMA bit.
Must have the same name as in cmsis-startup_stm32f411xe.s.
*/
void DMA2_Stream4_IRQHandler(void) {
DMA2->HIFCR = DMA_HIFCR_CTCIF4;
ADC1->CR2 &= ~(ADC_CR2_CONT | ADC_CR2_DMA);
}
/** Read analog value.
\param channel Channel to be read.
\return Analog reading, 10-bit right aligned.
*/
uint16_t analog_read(uint8_t index) {
if (NUM_TEMP_SENSORS > 0) {
uint16_t r = 0;
uint16_t temp;
uint32_t max_temp = 0;
uint32_t min_temp = UINT32_MAX;
for (uint8_t i = 0; i < OVERSAMPLE; i++) {
temp = adc_buffer[i][index];
max_temp = max_temp > temp ? max_temp : temp;
min_temp = min_temp < temp ? min_temp : temp;
r += temp;
}
r = (r - max_temp - min_temp) / (OVERSAMPLE - 2);
return r;
} else {
return 0;
}
}
/**
Start a new ADC conversion.
*/
void start_adc() {
/* To restart the DMA, clear (done in the TCI) and set the DMA bit.
Then enable the continious conversion and start the ADC again.
*/
ADC1->CR2 |= ADC_CR2_DMA |
ADC_CR2_CONT |
ADC_CR2_SWSTART;
}
#endif /* defined TEACUP_C_INCLUDE && defined __ARM_STM32F411__ */