2 * This file is part of Cleanflight and Betaflight.
4 * Cleanflight and Betaflight are free software. You can redistribute
5 * this software and/or modify this software under the terms of the
6 * GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option)
10 * Cleanflight and Betaflight are distributed in the hope that they
11 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this software.
18 * If not, see <http://www.gnu.org/licenses/>.
27 #ifdef USE_HARDWARE_REVISION_DETECTION
29 #include "build/debug.h"
31 #include "drivers/adc_impl.h"
32 #include "drivers/io_types.h"
33 #include "drivers/io.h"
34 #include "drivers/io_impl.h"
35 #include "drivers/rcc.h"
36 #include "drivers/time.h"
38 #include "hardware_revision.h"
40 #undef DEBUG_HARDWARE_REVISION_ADC
41 #undef DEBUG_HARDWARE_REVISION_TABLE
43 uint8_t hardwareRevision
= 0;
45 // Do ADC on IDDetectPin and determine revision
46 // If VREFINT is used, we can (probably) get a pretty good precision
47 // that we can distinguish tens of different voltages.
49 #define ADC_ID_DETECT_PIN PC2
51 typedef struct idDetect_s
{
56 // To deploy the analog ID detection in production:
57 // - Need some theoretical evaluation and experimentation to determine
58 // IDDET_ERROR value (ADC with VREFINT compensation is quite accurate).
59 // - Do some planning on revision numbering scheme.
60 // - Divider value planning for the scheme (separation).
62 #define IDDET_RATIO(highside, lowside) ((lowside) * 1000 / ((lowside) + (highside)))
63 #define IDDET_ERROR 12
65 static idDetect_t idDetectTable
[] = {
67 { IDDET_RATIO(10000, 10000), 1 },
70 { IDDET_RATIO(10000, 10000), 1 },
78 #define VREFINT_CAL_ADDR 0x1FFF7A2A
80 static void adcIDDetectInit(void)
82 idDetectTag
= IO_TAG(ADC_ID_DETECT_PIN
);
83 IOConfigGPIO(IOGetByTag(idDetectTag
), IO_CONFIG(GPIO_Mode_AN
, 0, GPIO_OType_OD
, GPIO_PuPd_NOPULL
));
85 RCC_ClockCmd(RCC_APB2(ADC1
), ENABLE
);
87 ADC_CommonInitTypeDef ADC_CommonInitStructure
;
89 ADC_CommonStructInit(&ADC_CommonInitStructure
);
90 ADC_CommonInitStructure
.ADC_Mode
= ADC_Mode_Independent
;
91 ADC_CommonInitStructure
.ADC_Prescaler
= ADC_Prescaler_Div8
;
92 ADC_CommonInitStructure
.ADC_DMAAccessMode
= ADC_DMAAccessMode_Disabled
;
93 ADC_CommonInitStructure
.ADC_TwoSamplingDelay
= ADC_TwoSamplingDelay_5Cycles
;
94 ADC_CommonInit(&ADC_CommonInitStructure
);
96 ADC_InitTypeDef ADC_InitStructure
;
98 ADC_StructInit(&ADC_InitStructure
);
99 ADC_InitStructure
.ADC_ContinuousConvMode
= ENABLE
;
100 ADC_InitStructure
.ADC_Resolution
= ADC_Resolution_12b
;
101 ADC_InitStructure
.ADC_ExternalTrigConv
= ADC_ExternalTrigConv_T1_CC1
;
102 ADC_InitStructure
.ADC_ExternalTrigConvEdge
= ADC_ExternalTrigConvEdge_None
;
103 ADC_InitStructure
.ADC_DataAlign
= ADC_DataAlign_Right
;
104 ADC_InitStructure
.ADC_NbrOfConversion
= 2; // Not used
105 ADC_InitStructure
.ADC_ScanConvMode
= ENABLE
;
106 ADC_Init(ADC1
, &ADC_InitStructure
);
108 ADC_Cmd(ADC1
, ENABLE
);
110 ADC_TempSensorVrefintCmd(ENABLE
);
111 delayMicroseconds(10); // Maximum startup time for internal sensors (DM00037051 5.3.22 & 24)
113 uint32_t channel
= adcChannelByTag(idDetectTag
);
115 ADC_InjectedDiscModeCmd(ADC1
, DISABLE
);
116 ADC_InjectedSequencerLengthConfig(ADC1
, 2);
117 ADC_InjectedChannelConfig(ADC1
, channel
, 1, ADC_SampleTime_480Cycles
);
118 ADC_InjectedChannelConfig(ADC1
, ADC_Channel_Vrefint
, 2, ADC_SampleTime_480Cycles
);
121 static void adcIDDetectDeinit(void)
123 ADC_Cmd(ADC1
, DISABLE
);
125 IOConfigGPIO(IOGetByTag(idDetectTag
), IOCFG_IPU
);
128 static void adcIDDetectStart(void)
130 ADC_ClearFlag(ADC1
, ADC_FLAG_JEOC
);
131 ADC_SoftwareStartInjectedConv(ADC1
);
134 static void adcIDDetectWait(void)
136 while (ADC_GetFlagStatus(ADC1
, ADC_FLAG_JEOC
) == RESET
) {
141 static uint16_t adcIDDetectReadIDDet(void)
143 return ADC_GetInjectedConversionValue(ADC1
, ADC_InjectedChannel_1
);
146 static uint16_t adcIDDetectReadVrefint(void)
148 return ADC_GetInjectedConversionValue(ADC1
, ADC_InjectedChannel_2
);
152 #if defined(OMNINXT7)
154 #include "drivers/adc_impl.h"
156 static adcDevice_t adcIDDetHardware
=
159 .rccADC
= RCC_APB2(ADC1
),
160 #if !defined(USE_DMA_SPEC)
161 .DMAy_Streamx
= ADC1_DMA_STREAM
,
162 .channel
= DMA_CHANNEL_0
166 // XXX adcIDDetectInitDevice is an exact copy of adcInitDevice() from adc_stm32f7xx.c. Export and use?
168 static void adcIDDetectInitDevice(adcDevice_t
*adcdev
, int channelCount
)
170 adcdev
->ADCHandle
.Init
.ClockPrescaler
= ADC_CLOCK_SYNC_PCLK_DIV8
;
171 adcdev
->ADCHandle
.Init
.ContinuousConvMode
= ENABLE
;
172 adcdev
->ADCHandle
.Init
.Resolution
= ADC_RESOLUTION_12B
;
173 adcdev
->ADCHandle
.Init
.ExternalTrigConv
= ADC_EXTERNALTRIGCONV_T1_CC1
;
174 adcdev
->ADCHandle
.Init
.ExternalTrigConvEdge
= ADC_EXTERNALTRIGCONVEDGE_NONE
;
175 adcdev
->ADCHandle
.Init
.DataAlign
= ADC_DATAALIGN_RIGHT
;
176 adcdev
->ADCHandle
.Init
.NbrOfConversion
= channelCount
;
177 #ifdef USE_ADC_INTERNAL
178 // Multiple injected channel seems to require scan conversion mode to be
179 // enabled even if main (non-injected) channel count is 1.
180 adcdev
->ADCHandle
.Init
.ScanConvMode
= ENABLE
;
182 adcdev
->ADCHandle
.Init
.ScanConvMode
= channelCount
> 1 ? ENABLE
: DISABLE
; // 1=scan more that one channel in group
184 adcdev
->ADCHandle
.Init
.DiscontinuousConvMode
= DISABLE
;
185 adcdev
->ADCHandle
.Init
.NbrOfDiscConversion
= 0;
186 adcdev
->ADCHandle
.Init
.DMAContinuousRequests
= ENABLE
;
187 adcdev
->ADCHandle
.Init
.EOCSelection
= DISABLE
;
188 adcdev
->ADCHandle
.Instance
= adcdev
->ADCx
;
190 if (HAL_ADC_Init(&adcdev
->ADCHandle
) != HAL_OK
)
192 /* Initialization Error */
196 static void adcIDDetectInit(void)
198 idDetectTag
= IO_TAG(ADC_ID_DETECT_PIN
);
200 IOConfigGPIO(IOGetByTag(idDetectTag
), IO_CONFIG(GPIO_MODE_ANALOG
, 0, GPIO_NOPULL
));
202 adcIDDetectInitDevice(&adcIDDetHardware
, 2);
204 ADC_InjectionConfTypeDef iConfig
;
206 iConfig
.InjectedSamplingTime
= ADC_SAMPLETIME_480CYCLES
;
207 iConfig
.InjectedOffset
= 0;
208 iConfig
.InjectedNbrOfConversion
= 2;
209 iConfig
.InjectedDiscontinuousConvMode
= DISABLE
;
210 iConfig
.AutoInjectedConv
= DISABLE
;
211 iConfig
.ExternalTrigInjecConv
= 0; // Don't care
212 iConfig
.ExternalTrigInjecConvEdge
= 0; // Don't care
214 iConfig
.InjectedChannel
= ADC_CHANNEL_VREFINT
;
215 iConfig
.InjectedRank
= 1;
217 if (HAL_ADCEx_InjectedConfigChannel(&adcIDDetHardware
.ADCHandle
, &iConfig
) != HAL_OK
) {
218 /* Channel Configuration Error */
221 iConfig
.InjectedChannel
= adcChannelByTag(idDetectTag
);
222 iConfig
.InjectedRank
= 2;
224 if (HAL_ADCEx_InjectedConfigChannel(&adcIDDetHardware
.ADCHandle
, &iConfig
) != HAL_OK
) {
225 /* Channel Configuration Error */
229 static void adcIDDetectDeinit(void)
231 HAL_ADC_DeInit(&adcIDDetHardware
.ADCHandle
);
232 IOConfigGPIO(IOGetByTag(idDetectTag
), IOCFG_IPU
);
235 static void adcIDDetectStart(void)
237 HAL_ADCEx_InjectedStart(&adcIDDetHardware
.ADCHandle
);
240 static void adcIDDetectWait(void)
242 while (HAL_ADCEx_InjectedPollForConversion(&adcIDDetHardware
.ADCHandle
, 0) != HAL_OK
) {
247 static uint16_t adcIDDetectReadVrefint(void)
249 return HAL_ADCEx_InjectedGetValue(&adcIDDetHardware
.ADCHandle
, ADC_INJECTED_RANK_1
);
252 static uint16_t adcIDDetectReadIDDet(void)
254 return HAL_ADCEx_InjectedGetValue(&adcIDDetHardware
.ADCHandle
, ADC_INJECTED_RANK_2
);
258 void detectHardwareRevision(void)
262 uint32_t vrefintValue
= 0;
263 uint32_t iddetValue
= 0;
265 for (int i
= 0 ; i
< 16 ; i
++) {
268 iddetValue
+= adcIDDetectReadIDDet();
269 vrefintValue
+= adcIDDetectReadVrefint();
275 uint32_t iddetRatio
= (iddetValue
* vrefintValue
) / *(uint16_t *)VREFINT_CAL_ADDR
;
276 iddetRatio
= iddetRatio
* 1000 / 4096;
278 #ifdef DEBUG_HARDWARE_REVISION_ADC
279 debug
[0] = *(uint16_t *)VREFINT_CAL_ADDR
;
280 debug
[1] = vrefintValue
;
281 debug
[2] = iddetValue
;
282 debug
[3] = iddetRatio
;
285 for (size_t entry
= 0; entry
< ARRAYLEN(idDetectTable
); entry
++) {
286 #ifdef DEBUG_HARDWARE_REVISION_TABLE
287 debug
[0] = iddetRatio
;
288 debug
[1] = idDetectTable
[entry
].ratio
- IDDET_ERROR
;
289 debug
[2] = idDetectTable
[entry
].ratio
+ IDDET_ERROR
;
291 if (idDetectTable
[entry
].ratio
- IDDET_ERROR
< iddetRatio
&& iddetRatio
< idDetectTable
[entry
].ratio
+ IDDET_ERROR
) {
292 hardwareRevision
= idDetectTable
[entry
].revision
;
300 void updateHardwareRevision(void)
305 // XXX Can be gone as sensors/gyro.c is not calling this anymore
306 ioTag_t
selectMPUIntExtiConfigByHardwareRevision(void)