Skyward boardcore
Loading...
Searching...
No Matches
InternalADC.cpp
Go to the documentation of this file.
1/* Copyright (c) 2020 Skyward Experimental Rocketry
2 * Author: Alberto Nidasio
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22
23#include "InternalADC.h"
24
26#include <utils/ClockUtils.h>
27
28static constexpr int ADC_RESOLUTION = 4095;
29
30namespace Boardcore
31{
32
33#if defined(STM32F407xx) || defined(STM32F429xx)
34#define CAL_PT1_VALUE ((uint16_t volatile*)((uint32_t)0x1FFF7A2C))
35#define CAL_PT2_VALUE ((uint16_t volatile*)((uint32_t)0x1FFF7A2E))
36static const float CAL_PT1_TEMP = 30;
37static const float CAL_PT2_TEMP = 110;
38static const float CAL_V_DDA = 3.3f;
39#elif defined(STM32F767xx) || defined(STM32F769xx) || defined(STM32F756xx)
40#define CAL_PT1_VALUE ((uint16_t volatile*)((uint32_t)0x1FF0F44C))
41#define CAL_PT2_VALUE ((uint16_t volatile*)((uint32_t)0x1FF0F44E))
42static const float CAL_PT1_TEMP = 30;
43static const float CAL_PT2_TEMP = 110;
44static const float CAL_V_DDA = 3.3f;
45#else
46#warning This micro controller does not have a calibrated temperature sensor or is not currently supported by this driver
47#endif
48
49#if defined(STM32F407xx) || defined(STM32F205xx)
50static const InternalADC::Channel TEMP_CH = InternalADC::CH16;
51static const InternalADC::Channel VBAT_CH = InternalADC::CH18;
52static const float VBAT_DIV = 2.0f;
53#elif defined(STM32F429xx) || defined(STM32F767xx) || defined(STM32F769xx) || \
54 defined(STM32F756xx)
55static const InternalADC::Channel TEMP_CH = InternalADC::CH18;
56static const InternalADC::Channel VBAT_CH = InternalADC::CH18;
57static const float VBAT_DIV = 4.0f;
58#endif
59
60// Error the user if the current target is missing the V_DDA_VOLTAGE macro
61// If it is missing you need to define it, preferably in the board_settings.h
62// file in miosix. Check your board schematic to find the voltage value.
63#ifndef V_DDA_VOLTAGE
64#warning Missing V_DDA_VOLTAGE definition for current target, using default value of 3.0V
65#define V_DDA_VOLTAGE 3.0f
66#endif
67
68InternalADC::InternalADC(ADC_TypeDef* adc) : adc(adc)
69{
70#ifndef INTERNAL_ADC_WITHOUT_CALIBRATION
71 loadCalibrationValues();
72#endif
73
74 resetRegisters();
76
77 // Set the clock divider for the analog circuitry to the highest value (/8).
78 // Currently there is no need to speed up ADC reading. For this reason we
79 // use the safest setting.
80 // If it will need to be changed you need to check the datasheet for the
81 // maximum frequency the analog circuitry supports and compare it with the
82 // parent clock (APB2). Also you need to take into account the sampling time
83 // for the temperature sensor.
84 ADC->CCR |= ADC_CCR_ADCPRE_1 | ADC_CCR_ADCPRE_0;
85
86 for (int i = 0; i < CH_NUM; i++)
87 channelsEnabled[i] = false;
88}
89
91{
92 resetRegisters();
94}
95
97{
98 // Turn on the ADC
99 adc->CR2 |= ADC_CR2_ADON;
100
101 return true;
102}
103
104bool InternalADC::selfTest() { return true; }
105
107{
108 InternalADCData newData;
110
111 for (int i = 0; i < CH16; i++)
112 {
113 if (channelsEnabled[i])
114 {
115 newData.voltage[i] = readChannel(static_cast<Channel>(i));
116 newData.voltage[i] =
117 newData.voltage[i] * V_DDA_VOLTAGE / ADC_RESOLUTION;
118 }
119 }
120
130 if (tempEnabled)
131 {
132 ADC->CCR |= ADC_CCR_TSVREFE;
133 miosix::delayUs(12); // Temperature sensor startup time
134 auto temperatureRawValue = readChannel(static_cast<Channel>(TEMP_CH));
135 ADC->CCR &= ~ADC_CCR_TSVREFE;
136
137 // Conversion
138 if (temperatureRawValue != 0)
139 {
140 newData.temperature =
141 temperatureRawValue * V_DDA_VOLTAGE / ADC_RESOLUTION;
142
143#ifdef INTERNAL_ADC_WITHOUT_CALIBRATION
144 // Default conversion
145 newData.temperature = ((newData.temperature - 0.76) / 0.0025) + 25;
146#else
147 // Factory calibration
148 newData.temperature = newData.temperature - calPt1Voltage;
149 newData.temperature /= calSlope;
150 newData.temperature += CAL_PT1_TEMP;
151#endif
152 }
153 else
154 {
155 newData.temperature = 0;
156 }
157 }
158
159 if (vbatEnabled)
160 {
161 ADC->CCR |= ADC_CCR_VBATE;
162 auto vbatVoltageRawValue = readChannel(static_cast<Channel>(VBAT_CH));
163 ADC->CCR &= ~ADC_CCR_VBATE;
164
165 newData.vBat =
166 vbatVoltageRawValue * V_DDA_VOLTAGE / ADC_RESOLUTION * VBAT_DIV;
167 }
168
169 return newData;
170}
171
173{
174 channelsEnabled[channel] = true;
175
176 setChannelSampleTime(channel, sampleTime);
177}
178
180{
181 channelsEnabled[channel] = false;
182}
183
185{
186 tempEnabled = true;
187 enableChannel(TEMP_CH, sampleTime);
188}
189
191{
192 tempEnabled = false;
193 if (!vbatEnabled || TEMP_CH != VBAT_CH)
194 disableChannel(TEMP_CH);
195}
196
198{
199 vbatEnabled = true;
200 enableChannel(VBAT_CH, sampleTime);
201}
202
204{
205 vbatEnabled = false;
206 if (!tempEnabled || TEMP_CH != VBAT_CH)
207 disableChannel(VBAT_CH);
208}
209
211{
212 ADCData data;
214 data.voltage = getLastSample().voltage[channel];
215 data.channelId = channel;
216 return data;
217}
218
226
228{
229 ADCData data;
231 data.voltage = getLastSample().vBat;
232 data.channelId = VBAT_CH;
233 return data;
234}
235
236inline void InternalADC::resetRegisters()
237{
238 // Reset the ADC configuration
239 adc->CR1 = 0;
240 adc->CR2 = 0;
241 adc->SMPR1 = 0;
242 adc->SMPR2 = 0;
243 adc->JOFR1 = 0;
244 adc->JOFR2 = 0;
245 adc->JOFR3 = 0;
246 adc->JOFR4 = 0;
247 adc->HTR = 0;
248 adc->LTR = 0;
249 adc->SQR1 = 0;
250 adc->SQR2 = 0;
251 adc->SQR3 = 0;
252 adc->JSQR = 0;
253}
254
255inline void InternalADC::setChannelSampleTime(Channel channel,
256 SampleTime sampleTime)
257{
258 if (channel <= 9)
259 {
260 adc->SMPR2 &= ~(0x7 << (channel * 3));
261 adc->SMPR2 |= sampleTime << (channel * 3);
262 }
263 else
264 {
265 adc->SMPR1 &= ~(0x7 << ((channel - 10) * 3));
266 adc->SMPR1 |= sampleTime << ((channel - 10) * 3);
267 }
268}
269
270uint16_t InternalADC::readChannel(Channel channel)
271{
272 // Assuming that ADC_SQR1_L remains 0 (1 conversion)
273
274 // Select channel
275 adc->SQR3 = channel;
276
277 // Start conversion
278 adc->CR2 |= ADC_CR2_SWSTART;
279
280 while (!(adc->SR & ADC_SR_EOC))
281 ;
282
283 return static_cast<uint16_t>(adc->DR);
284}
285
286#ifndef INTERNAL_ADC_WITHOUT_CALIBRATION
287void InternalADC::loadCalibrationValues()
288{
289 calPt1Voltage = static_cast<float>(*CAL_PT1_VALUE);
290 calPt1Voltage *= CAL_V_DDA / ADC_RESOLUTION;
291
292 calPt2Voltage = static_cast<float>(*CAL_PT2_VALUE);
293 calPt2Voltage *= CAL_V_DDA / ADC_RESOLUTION;
294
295 calSlope = calPt2Voltage - calPt1Voltage;
296 calSlope /= CAL_PT2_TEMP - CAL_PT1_TEMP;
297}
298#endif
299
300} // namespace Boardcore
#define V_DDA_VOLTAGE
bool init() override
ADC Initialization.
TemperatureData getTemperature()
void enableChannel(Channel channel, SampleTime sampleTime=CYCLES_480)
ADCData getVoltage(Channel channel)
SampleTime
Conversion sample time. See reference manual.
Definition InternalADC.h:86
void enableVbat(SampleTime sampleTime=CYCLES_480)
void disableChannel(Channel channel)
bool selfTest() override
Check if the sensor is working.
InternalADCData sampleImpl() override
Read a data sample from the sensor. In case of errors, the method should return the last available co...
void enableTemperature(SampleTime sampleTime=CYCLES_480)
InternalADC(ADC_TypeDef *adc)
Resets the ADC configuration and automatically enables the peripheral clock.
Channel
ADC channels enumeration.
Definition InternalADC.h:57
virtual InternalADCData getLastSample()
Definition Sensor.h:131
bool disablePeripheralClock(void *peripheral)
Disables a peripheral clock source from the APB1 and APB2 peripheral buses.
Definition ClockUtils.h:531
bool enablePeripheralClock(void *peripheral)
Enables a peripheral clock source from the APB1 and APB2 peripheral buses.
Definition ClockUtils.h:155
uint64_t getTimestamp()
Returns the current timer value in microseconds.
Driver for the VN100S IMU.
Structure to handle ADC data.
Definition SensorData.h:337
uint64_t voltageTimestamp
Definition SensorData.h:338