SimpleFOClibrary 2.4.0
Loading...
Searching...
No Matches
current_sense/hardware_specific/silabs/efr32_mcu.cpp
Go to the documentation of this file.
1#if defined(ARDUINO_ARCH_SILABS)
2#include <pinDefinitions.h>
3#include <pins_arduino.h>
4
5#include <em_device.h>
6#include <em_prs.h>
7#include "efr32_mcu.h"
8#include "../../../drivers/hardware_specific/silabs/efr32_mcu.h"
9
10#ifndef _ADC_VOLTAGE
11#define _ADC_VOLTAGE 3.3f
12#endif
13
14#ifndef _ADC_RESOLUTION
15#define _ADC_RESOLUTION 4095.0f
16#endif
17
18#ifndef _CLK_SRC_ADC_FREQ
19#define _CLK_SRC_ADC_FREQ 20000000
20#endif
21
22#ifndef _CLK_ADC_FREQ
23#define _CLK_ADC_FREQ 10000000
24#endif
25
26extern void _getPrsSourceAndUnderflowSignal(
27 TIMER_TypeDef *timer,
28 uint32_t *source,
29 uint32_t *signal);
30
31static void _adcBusAllocate(
32 uint8_t port,
33 uint8_t pin
34) {
35 switch (port) {
36#if (GPIO_PA_COUNT > 0)
37 case gpioPortA:
38 if (0 == pin % 2) {
39 if ((GPIO->ABUSALLOC & _GPIO_ABUSALLOC_AEVEN0_MASK) == GPIO_ABUSALLOC_AEVEN0_TRISTATE) {
40 GPIO->ABUSALLOC |= GPIO_ABUSALLOC_AEVEN0_ADC0;
41 } else if ((GPIO->ABUSALLOC & _GPIO_ABUSALLOC_AEVEN1_MASK) == GPIO_ABUSALLOC_AEVEN1_TRISTATE) {
42 GPIO->ABUSALLOC |= GPIO_ABUSALLOC_AEVEN1_ADC0;
43 } else {}
44 } else {
45 if ((GPIO->ABUSALLOC & _GPIO_ABUSALLOC_AODD0_MASK) == GPIO_ABUSALLOC_AODD0_TRISTATE) {
46 GPIO->ABUSALLOC |= GPIO_ABUSALLOC_AODD0_ADC0;
47 } else if ((GPIO->ABUSALLOC & _GPIO_ABUSALLOC_AODD1_MASK) == GPIO_ABUSALLOC_AODD1_TRISTATE) {
48 GPIO->ABUSALLOC |= GPIO_ABUSALLOC_AODD1_ADC0;
49 } else {
50 // MISRA
51 }
52 }
53 break;
54#endif
55
56#if (GPIO_PB_COUNT > 0)
57 case gpioPortB:
58 if (0 == pin % 2) {
59 if ((GPIO->BBUSALLOC & _GPIO_BBUSALLOC_BEVEN0_MASK) == GPIO_BBUSALLOC_BEVEN0_TRISTATE) {
60 GPIO->BBUSALLOC |= GPIO_BBUSALLOC_BEVEN0_ADC0;
61 } else if ((GPIO->BBUSALLOC & _GPIO_BBUSALLOC_BEVEN1_MASK) == GPIO_BBUSALLOC_BEVEN1_TRISTATE) {
62 GPIO->BBUSALLOC |= GPIO_BBUSALLOC_BEVEN1_ADC0;
63 } else {
64 // MISRA
65 }
66 } else {
67 if ((GPIO->BBUSALLOC & _GPIO_BBUSALLOC_BODD0_MASK) == GPIO_BBUSALLOC_BODD0_TRISTATE) {
68 GPIO->BBUSALLOC |= GPIO_BBUSALLOC_BODD0_ADC0;
69 } else if ((GPIO->BBUSALLOC & _GPIO_BBUSALLOC_BODD1_MASK) == GPIO_BBUSALLOC_BODD1_TRISTATE) {
70 GPIO->BBUSALLOC |= GPIO_BBUSALLOC_BODD1_ADC0;
71 } else {
72 // MISRA
73 }
74 }
75 break;
76#endif
77
78#if (GPIO_PC_COUNT > 0 || GPIO_PD_COUNT > 0)
79 case gpioPortC:
80 case gpioPortD:
81 if (0 == pin % 2) {
82 if ((GPIO->CDBUSALLOC & _GPIO_CDBUSALLOC_CDEVEN0_MASK) == GPIO_CDBUSALLOC_CDEVEN0_TRISTATE) {
83 GPIO->CDBUSALLOC |= GPIO_CDBUSALLOC_CDEVEN0_ADC0;
84 } else if ((GPIO->CDBUSALLOC & _GPIO_CDBUSALLOC_CDEVEN1_MASK) == GPIO_CDBUSALLOC_CDEVEN1_TRISTATE) {
85 GPIO->CDBUSALLOC |= GPIO_CDBUSALLOC_CDEVEN1_ADC0;
86 } else {
87 // MISRA
88 }
89 } else {
90 if ((GPIO->CDBUSALLOC & _GPIO_CDBUSALLOC_CDODD0_MASK) == GPIO_CDBUSALLOC_CDODD0_TRISTATE) {
91 GPIO->CDBUSALLOC |= GPIO_CDBUSALLOC_CDODD0_ADC0;
92 } else if ((GPIO->CDBUSALLOC & _GPIO_CDBUSALLOC_CDODD1_MASK) == GPIO_CDBUSALLOC_CDODD1_TRISTATE) {
93 GPIO->CDBUSALLOC |= GPIO_CDBUSALLOC_CDODD1_ADC0;
94 } else {
95 // MISRA
96 }
97 }
98 break;
99#endif
100 }
101}
102
103static void _adcConfig(
104 EFR32AdcInstance *inst,
105 const int pin
106) {
107 if (!inst) return;
108 inst->port = getSilabsPortFromArduinoPin(pinToPinName(pin));
109 inst->pin = getSilabsPinFromArduinoPin(pinToPinName(pin));
110}
111
112static float _readAdc(
113 EFR32CurrentSenseParams *params,
114 const int pin
115) {
116 if (!params) return 0.0f;
117
118 for (uint8_t i = 0; i < SILABS_MAX_ANALOG; ++i) {
119 if (!_isset(params->pins[i])) continue;
120 if (pin == params->pins[i]) {
121 return params->buffer[i] * params->adc_voltage_conv;
122 }
123 }
124 return 0.0f;
125}
126
127static bool _dmaTransferFinishedCb(
128 unsigned int channel,
129 unsigned int sequenceNo,
130 void *data
131) {
132 _UNUSED(sequenceNo);
133
134 EFR32CurrentSenseParams *params = (EFR32CurrentSenseParams *) data;
135 if (!params || !params->adc || (params->dmaChannel != channel))
136 return false;
137
138 CORE_DECLARE_IRQ_STATE;
139 CORE_ENTER_ATOMIC();
140 params->dataReady = true;
141 CORE_EXIT_ATOMIC();
142
143 return false;
144}
145
146static void _currentSenseInitDMA(
147 EFR32CurrentSenseParams *params,
148 DMADRV_Callback_t fn,
149 void *data
150) {
151 if (!params) return;
152
153 // Initialize DMA with default parameters
154 DMADRV_Init();
155
156 DMADRV_AllocateChannel(&params->dmaChannel, NULL);
157
158 // Trigger LDMA transfer on IADC scan completion
159 LDMA_TransferCfg_t transferCfg =
160 LDMA_TRANSFER_CFG_PERIPHERAL(ldmaPeripheralSignal_IADC0_IADC_SCAN);
161
162 params->descriptor =
163 (LDMA_Descriptor_t)LDMA_DESCRIPTOR_LINKREL_P2M_WORD(&(params->adc->SCANFIFODATA),
164 params->buffer,
165 params->noAdcChannels,
166 0);
167
168 DMADRV_LdmaStartTransfer(params->dmaChannel,
169 &transferCfg,
170 &params->descriptor,
171 fn,
172 params);
173}
174
175static void _currentSenseInitScan(
176 EFR32CurrentSenseParams *params
177) {
178 if (!params || !params->adc) return;
179
180 // Enable Clock
181 CMU_ClockEnable(cmuClock_IADC0, true);
182 CMU_ClockEnable(cmuClock_GPIO, true);
183
184 // Use the FSRC0 as the IADC clock so it can run in EM2
185 CMU_ClockSelectSet(cmuClock_IADCCLK, cmuSelect_FSRCO);
186
187 for (uint8_t i = 0; i < params->noAdcChannels; ++i) {
188 GPIO_PinModeSet(params->inst[i].port, params->inst[i].pin, gpioModeDisabled, 0);
189 }
190
191 IADC_Init_t init = IADC_INIT_DEFAULT;
192 init.warmup = iadcWarmupKeepWarm;
193 init.srcClkPrescale = IADC_calcSrcClkPrescale(params->adc, _CLK_SRC_ADC_FREQ, 0);
194
195 IADC_CfgReference_t adcRef;
196 switch (params->vRef) {
197 case 1200: adcRef = iadcCfgReferenceInt1V2; break;
198 case 1250: adcRef = iadcCfgReferenceExt1V25; break;
199 case 3300: adcRef = iadcCfgReferenceVddx; break;
200 case 2640: adcRef = iadcCfgReferenceVddX0P8Buf; break;
201 default: return;
202 }
203
204 IADC_AllConfigs_t allConfigs = IADC_ALLCONFIGS_DEFAULT;
205 allConfigs.configs[0].reference = adcRef;
206 allConfigs.configs[0].vRef = params->vRef;
207 allConfigs.configs[0].adcClkPrescale = IADC_calcAdcClkPrescale(params->adc,
208 _CLK_ADC_FREQ,
209 0,
210 iadcCfgModeNormal,
211 init.srcClkPrescale);
212
213 // Reset the ADC
214 IADC_reset(params->adc);
215
216 // Only configure the ADC if it is not already running
217 if (params->adc->CTRL == _IADC_CTRL_RESETVALUE) {
218 IADC_init(params->adc, &init, &allConfigs);
219 }
220
221 IADC_InitScan_t initScan = IADC_INITSCAN_DEFAULT;
222 if ((params->mode == CS_LO_SIDE) || (params->mode == CS_HI_SIDE)) {
223 // Note: CS_HI_SIDE not implemented
224 initScan.triggerSelect = iadcTriggerSelPrs0PosEdge;
225 }
226 initScan.fifoDmaWakeup = true;
227
228 IADC_ScanTable_t scanTable = IADC_SCANTABLE_DEFAULT;
229 for (uint8_t i = 0; i < params->noAdcChannels; ++i) {
230 scanTable.entries[i].posInput = IADC_portPinToPosInput(params->inst[i].port, params->inst[i].pin);
231 scanTable.entries[i].negInput = iadcNegInputGnd;
232 scanTable.entries[i].includeInScan = true;
233 }
234
235 // Initialize IADC
236 IADC_init(params->adc, &init, &allConfigs);
237
238 // Initialize Scan
239 IADC_initScan(params->adc, &initScan, &scanTable);
240
241 // Allocate
242 for (uint8_t i = 0; i < params->noAdcChannels; ++i) {
243 _adcBusAllocate(params->inst[i].port, params->inst[i].pin);
244 }
245}
246
247static void _currentSenseConfig(
248 EFR32CurrentSenseParams *params,
249 int adcPins[SILABS_MAX_ANALOG]
250) {
251 if (!params) return;
252
253 uint8_t noAdcChannels = 0;
254 for (int i = 0; i < SILABS_MAX_ANALOG; ++i) {
255 if (!_isset(adcPins[i])) continue;
256 if (params->firstIndex == 0xFF) params->firstIndex = i;
257 _adcConfig(&params->inst[noAdcChannels], adcPins[i]);
258 params->pins[noAdcChannels] = adcPins[i];
259 ++noAdcChannels;
260 }
261
262 params->noAdcChannels = noAdcChannels;
263}
264
265static void _currentSenseDeinit(
266 EFR32CurrentSenseParams *params
267) {
268 if (!params) return;
269
270 DMADRV_StopTransfer(params->dmaChannel);
271 DMADRV_FreeChannel(params->dmaChannel);
272
273 IADC_reset(params->adc);
274}
275
276static void _currentSenseStartScan(
277 EFR32CurrentSenseParams *params
278) {
279 if (!params || !params->adc) return;
280
281 IADC_command(params->adc, iadcCmdStartScan);
282}
283
284static void _currentSenseStopScan(
285 EFR32CurrentSenseParams *params
286) {
287 if (!params) return;
288
289 IADC_command(params->adc, iadcCmdStopScan);
290}
291
292static void _currentSenseStopTranfer(
293 EFR32CurrentSenseParams *params
294) {
295 if (!params || !params->adc) return;
296
297 DMADRV_PauseTransfer(params->dmaChannel);
298}
299
300static void _currentSenseStartTranfer(
301 EFR32CurrentSenseParams *params
302) {
303 if (!params || !params->adc) return;
304
305 DMADRV_ResumeTransfer(params->dmaChannel);
306}
307
308////////////////////////////////////////////////////////////////////////////////
309// Low Side Mode
310////////////////////////////////////////////////////////////////////////////////
311
313 const int pin,
314 const void *cs_params
315) {
316 EFR32CurrentSenseParams *params = (EFR32CurrentSenseParams *) cs_params;
317 if (!params) return 0.0f;
318
319 return _readAdc(params, pin);
320}
321
323 const void* driver_params,
324 const int pinA,
325 const int pinB,
326 const int pinC
327) {
328 _UNUSED(driver_params);
329
330 EFR32CurrentSenseParams *params = new EFR32CurrentSenseParams {
331 .adc_voltage_conv = (_ADC_VOLTAGE) / (_ADC_RESOLUTION),
332 .firstIndex = 0xFF,
333 .noAdcChannels = 0,
334 .vRef = SILABS_ADC_VREF,
335 .prsChannel = SILABS_ADC_PRS_CHANNEL,
336 .mode = CS_LO_SIDE,
337 .adc = SILABS_DEFAULT_ADC_PERPHERAL,
338 };
339
341
342 int adcPins[3] = { pinA, pinB, pinC };
343
344 _currentSenseConfig(params, adcPins);
345 _currentSenseInitScan(params);
346 _currentSenseInitDMA(params, NULL, params);
347 _currentSenseStartScan(params);
348
349 return params;
350}
351
353 void *driver_params,
354 void *cs_params
355) {
356 EFR32DriverParams *driver = (EFR32DriverParams *) driver_params;
357 EFR32CurrentSenseParams *params = (EFR32CurrentSenseParams *) cs_params;
358
359 if (!driver || !params) return SIMPLEFOC_CURRENT_SENSE_INIT_FAILED;
360
361 uint32_t prsSource, prsSignal;
362
363 CMU_ClockEnable(cmuClock_PRS, true);
364
365 _getPrsSourceAndUnderflowSignal(driver->inst[0].h.timer, &prsSource, &prsSignal);
366 PRS_SourceAsyncSignalSet(params->prsChannel, prsSource, prsSignal);
367 PRS_ConnectConsumer(params->prsChannel, prsTypeAsync, prsConsumerIADC0_SCANTRIGGER);
368
369 return cs_params;
370}
371
372////////////////////////////////////////////////////////////////////////////////
373// Inline Mode
374////////////////////////////////////////////////////////////////////////////////
375
377 const int pin,
378 const void *cs_params
379) {
380 EFR32CurrentSenseParams *params = (EFR32CurrentSenseParams *) cs_params;
381 if (!params || !_isset(pin) || (params->firstIndex == 0xFF)) return 0.0f;
382
383 if (params->pins[params->firstIndex] == pin) {
384 CORE_DECLARE_IRQ_STATE;
385 CORE_ENTER_ATOMIC();
386 params->dataReady = false;
387 CORE_EXIT_ATOMIC();
388
389 _currentSenseStartScan(params);
390
391 while (!params->dataReady) {}
392 }
393
394 return _readAdc(params, pin);
395}
396
398 const void* driver_params,
399 const int pinA,
400 const int pinB,
401 const int pinC
402) {
403 _UNUSED(driver_params);
404
405 EFR32CurrentSenseParams *params = new EFR32CurrentSenseParams {
406 .adc_voltage_conv = (_ADC_VOLTAGE) / (_ADC_RESOLUTION),
407 .firstIndex = 0xFF,
408 .noAdcChannels = 0,
409 .vRef = SILABS_ADC_VREF,
410 .prsChannel = SILABS_ADC_PRS_CHANNEL,
411 .mode = CS_INLINE,
412 .adc = SILABS_DEFAULT_ADC_PERPHERAL,
413 };
414
416
417 int adcPins[3] = { pinA, pinB, pinC };
418
419 _currentSenseConfig(params, adcPins);
420 _currentSenseInitScan(params);
421 _currentSenseInitDMA(params, _dmaTransferFinishedCb, params);
422
423 return params;
424}
425
426#endif
void * _configureADCInline(const void *driver_params, const int pinA, const int pinB, const int pinC=NOT_SET)
float _readADCVoltageInline(const int pinA, const void *cs_params)
void * _driverSyncLowSide(void *driver_params, void *cs_params)
#define SIMPLEFOC_CURRENT_SENSE_INIT_FAILED
void * _configureADCLowSide(const void *driver_params, const int pinA, const int pinB, const int pinC=NOT_SET)
float _readADCVoltageLowSide(const int pinA, const void *cs_params)
const int const int const int pinC
GenericCurrentSenseParams * params
#define _UNUSED(v)
Definition foc_utils.h:14
#define _isset(a)
Definition foc_utils.h:13