-
-
Notifications
You must be signed in to change notification settings - Fork 222
/
stm32_common.cpp
768 lines (649 loc) · 20.2 KB
/
stm32_common.cpp
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
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
/**
* @file stm32_common.cpp
* @brief Low level common STM32 code
*
* @date Mar 28, 2019
* @author Andrey Belomutskiy, (c) 2012-2020
*/
#include "pch.h"
#include "expected.h"
#include "hardware.h"
#ifdef STM32F4XX
#include "stm32f4xx_hal_flash.h"
#elif defined(STM32F7XX)
#include "stm32f7xx_hal_flash.h"
#elif defined(STM32H7XX)
#include "stm32h7xx_hal_flash.h"
#endif
#define _2_MHZ 2'000'000
#if EFI_PROD_CODE
#include "mpu_util.h"
#include "backup_ram.h"
#endif /* EFI_PROD_CODE */
#if HAL_USE_ADC
// ADC_CHANNEL_IN0 // PA0
// ADC_CHANNEL_IN1 // PA1
// ADC_CHANNEL_IN2 // PA2
// ADC_CHANNEL_IN3 // PA3
// ADC_CHANNEL_IN4 // PA4
// ADC_CHANNEL_IN5 // PA5 - this is also TIM2_CH1
// ADC_CHANNEL_IN6 // PA6
// ADC_CHANNEL_IN7 // PA7
// ADC_CHANNEL_IN8 // PB0
// ADC_CHANNEL_IN9 // PB1
// ADC_CHANNEL_IN10 // PC0
// ADC_CHANNEL_IN11 // PC1
// ADC_CHANNEL_IN12 // PC2
// ADC_CHANNEL_IN13 // PC3
// ADC_CHANNEL_IN14 // PC4
// ADC_CHANNEL_IN15 // PC5
brain_pin_e getAdcChannelBrainPin(const char *msg, adc_channel_e hwChannel) {
static_assert(EFI_ADC_NONE == ADC_CHANNEL_NONE);
// todo: replace this with an array :)
switch (hwChannel) {
case EFI_ADC_0:
return Gpio::A0;
case EFI_ADC_1:
return Gpio::A1;
case EFI_ADC_2:
return Gpio::A2;
case EFI_ADC_3:
return Gpio::A3;
case EFI_ADC_4:
return Gpio::A4;
case EFI_ADC_5:
return Gpio::A5;
case EFI_ADC_6:
return Gpio::A6;
case EFI_ADC_7:
return Gpio::A7;
case EFI_ADC_8:
return Gpio::B0;
case EFI_ADC_9:
return Gpio::B1;
case EFI_ADC_10:
return Gpio::C0;
case EFI_ADC_11:
return Gpio::C1;
case EFI_ADC_12:
return Gpio::C2;
case EFI_ADC_13:
return Gpio::C3;
case EFI_ADC_14:
return Gpio::C4;
case EFI_ADC_15:
return Gpio::C5;
default:
firmwareError(CUSTOM_ERR_ADC_UNKNOWN_CHANNEL, "Unknown hw channel %d [%s]", hwChannel, msg);
return Gpio::Invalid;
}
}
adc_channel_e getAdcChannel(brain_pin_e pin) {
switch (pin) {
case Gpio::A0:
return EFI_ADC_0;
case Gpio::A1:
return EFI_ADC_1;
case Gpio::A2:
return EFI_ADC_2;
case Gpio::A3:
return EFI_ADC_3;
case Gpio::A4:
return EFI_ADC_4;
case Gpio::A5:
return EFI_ADC_5;
case Gpio::A6:
return EFI_ADC_6;
case Gpio::A7:
return EFI_ADC_7;
case Gpio::B0:
return EFI_ADC_8;
case Gpio::B1:
return EFI_ADC_9;
case Gpio::C0:
return EFI_ADC_10;
case Gpio::C1:
return EFI_ADC_11;
case Gpio::C2:
return EFI_ADC_12;
case Gpio::C3:
return EFI_ADC_13;
case Gpio::C4:
return EFI_ADC_14;
case Gpio::C5:
return EFI_ADC_15;
case Gpio::Unassigned:
return EFI_ADC_NONE;
default:
firmwareError(OBD_PCM_Processor_Fault, "getAdcChannel %d", pin);
return EFI_ADC_ERROR;
}
}
// deprecated - inline?
ioportid_t getAdcChannelPort(const char *msg, adc_channel_e hwChannel) {
brain_pin_e brainPin = getAdcChannelBrainPin(msg, hwChannel);
return getHwPort(msg, brainPin);
}
// deprecated - inline?
int getAdcChannelPin(adc_channel_e hwChannel) {
brain_pin_e brainPin = getAdcChannelBrainPin("get_pin", hwChannel);
return getHwPin("get_pin", brainPin);
}
#endif /* HAL_USE_ADC */
#if EFI_PROD_CODE
#if HAL_USE_PWM
namespace {
struct stm32_pwm_config {
PWMDriver* const Driver;
const uint8_t Channel;
const uint8_t AlternateFunc;
};
class stm32_hardware_pwm : public hardware_pwm {
public:
bool hasInit() const {
return m_driver != nullptr;
}
// 2MHz, 16-bit timer gets us a usable frequency range of 31hz to 10khz
static constexpr uint32_t c_timerFrequency = _2_MHZ;
void start(const char* msg, const stm32_pwm_config& config, float frequency, float duty) {
m_driver = config.Driver;
m_channel = config.Channel;
m_period = c_timerFrequency / frequency;
// These timers are only 16 bit - don't risk overflow
if (m_period > 0xFFF0) {
firmwareError(CUSTOM_OBD_LOW_FREQUENCY, "PWM Frequency too low %f hz on pin \"%s\"", frequency, msg);
return;
}
// If we have too few usable bits, we run out of resolution, so don't allow that either.
// 200 counts = 0.5% resolution
if (m_period < 200) {
firmwareError(CUSTOM_OBD_HIGH_FREQUENCY, "PWM Frequency too high %d hz on pin \"%s\"", frequency, msg);
return;
}
const PWMConfig pwmcfg = {
c_timerFrequency,
m_period,
nullptr,
{
{PWM_OUTPUT_ACTIVE_HIGH, nullptr},
{PWM_OUTPUT_ACTIVE_HIGH, nullptr},
{PWM_OUTPUT_ACTIVE_HIGH, nullptr},
{PWM_OUTPUT_ACTIVE_HIGH, nullptr}
},
0,
0
};
// Start the timer running
pwmStart(m_driver, &pwmcfg);
// Set initial duty cycle
setDuty(duty);
}
void setDuty(float duty) override {
if (!m_driver) {
firmwareError(OBD_PCM_Processor_Fault, "Attempted to set duty on null hard PWM device");
return;
}
pwm_lld_enable_channel(m_driver, m_channel, getHighTime(duty));
}
private:
PWMDriver* m_driver = nullptr;
uint8_t m_channel = 0;
uint32_t m_period = 0;
pwmcnt_t getHighTime(float duty) const {
return m_period * duty;
}
};
}
static expected<stm32_pwm_config> getConfigForPin(brain_pin_e pin) {
switch (pin) {
#if STM32_PWM_USE_TIM1
case Gpio::A8: return stm32_pwm_config{&PWMD1, 0, 1};
case Gpio::A9: return stm32_pwm_config{&PWMD1, 1, 1};
case Gpio::A10: return stm32_pwm_config{&PWMD1, 2, 1};
case Gpio::A11: return stm32_pwm_config{&PWMD1, 3, 1};
case Gpio::E9: return stm32_pwm_config{&PWMD1, 0, 1};
case Gpio::E11: return stm32_pwm_config{&PWMD1, 1, 1};
case Gpio::E13: return stm32_pwm_config{&PWMD1, 2, 1};
case Gpio::E14: return stm32_pwm_config{&PWMD1, 3, 1};
#endif
#if STM32_PWM_USE_TIM2
case Gpio::A15: return stm32_pwm_config{&PWMD2, 0, 1};
case Gpio::B3: return stm32_pwm_config{&PWMD2, 1, 1};
case Gpio::B10: return stm32_pwm_config{&PWMD2, 2, 1};
case Gpio::B11: return stm32_pwm_config{&PWMD2, 3, 1};
#endif
#if STM32_PWM_USE_TIM3
case Gpio::B4: return stm32_pwm_config{&PWMD3, 0, 2};
case Gpio::B5: return stm32_pwm_config{&PWMD3, 1, 2};
case Gpio::C6: return stm32_pwm_config{&PWMD3, 0, 2};
case Gpio::C7: return stm32_pwm_config{&PWMD3, 1, 2};
#endif
#if STM32_PWM_USE_TIM4
case Gpio::B6: return stm32_pwm_config{&PWMD4, 0, 2};
case Gpio::B7: return stm32_pwm_config{&PWMD4, 1, 2};
case Gpio::B8: return stm32_pwm_config{&PWMD4, 2, 2};
case Gpio::B9: return stm32_pwm_config{&PWMD4, 3, 2};
case Gpio::D12: return stm32_pwm_config{&PWMD4, 0, 2};
case Gpio::D13: return stm32_pwm_config{&PWMD4, 1, 2};
case Gpio::D14: return stm32_pwm_config{&PWMD4, 2, 2};
case Gpio::D15: return stm32_pwm_config{&PWMD4, 3, 2};
#endif
#if STM32_PWM_USE_TIM5
case Gpio::A0: return stm32_pwm_config{&PWMD5, 0, 2};
case Gpio::A1: return stm32_pwm_config{&PWMD5, 1, 2};
case Gpio::A2: return stm32_pwm_config{&PWMD5, 2, 2};
case Gpio::A3: return stm32_pwm_config{&PWMD5, 3, 2};
#endif
#if STM32_PWM_USE_TIM8
#if !STM32_PWM_USE_TIM3
// If TIM3 is not used, put these pins on TIM8 instead..
// See https://github.com/rusefi/rusefi/issues/639
// See https://github.com/rusefi/rusefi/pull/3032
case Gpio::C6: return stm32_pwm_config{&PWMD8, 0, 3};
case Gpio::C7: return stm32_pwm_config{&PWMD8, 1, 3};
#endif
case Gpio::C8: return stm32_pwm_config{&PWMD8, 2, 3};
case Gpio::C9: return stm32_pwm_config{&PWMD8, 3, 3};
#endif
#if STM32_PWM_USE_TIM9
case Gpio::E5: return stm32_pwm_config{&PWMD9, 0, 3};
case Gpio::E6: return stm32_pwm_config{&PWMD9, 1, 3};
#endif
default: return unexpected;
}
};
static stm32_hardware_pwm hardPwms[5];
stm32_hardware_pwm* getNextPwmDevice() {
for (size_t i = 0; i < efi::size(hardPwms); i++) {
if (!hardPwms[i].hasInit()) {
return &hardPwms[i];
}
}
firmwareError(OBD_PCM_Processor_Fault, "Run out of hardware PWM devices!");
return nullptr;
}
/*static*/ hardware_pwm* hardware_pwm::tryInitPin(const char* msg, brain_pin_e pin, float frequencyHz, float duty) {
// Hardware PWM can't do very slow PWM - the timer counter is only 16 bits, so at 2MHz counting, that's a minimum of 31hz.
if (frequencyHz < 50) {
return nullptr;
}
auto cfg = getConfigForPin(pin);
// This pin can't do hardware PWM
if (!cfg) {
return nullptr;
}
if (stm32_hardware_pwm* device = getNextPwmDevice()) {
device->start(msg, cfg.Value, frequencyHz, duty);
// Finally connect the timer to physical pin
efiSetPadMode(msg, pin, PAL_MODE_ALTERNATE(cfg.Value.AlternateFunc));
return device;
}
return nullptr;
}
#endif
void jump_to_bootloader() {
#ifdef STM32H7XX
// H7 needs a forcible reset of the USB peripheral(s) in order for the bootloader to work properly.
// If you don't do this, the bootloader will execute, but USB doesn't work (nobody knows why)
// See https://community.st.com/s/question/0D53W00000vQEWsSAO/stm32h743-dfu-entry-doesnt-work-unless-boot0-held-high-at-poweron
RCC->AHB1ENR &= ~(RCC_AHB1ENR_USB1OTGHSEN | RCC_AHB1ENR_USB2OTGFSEN);
#endif
// leave DFU breadcrumb which assembly startup code would check, see [rusefi][DFU] section in assembly code
*((unsigned long *)0x2001FFF0) = 0xDEADBEEF; // End of RAM
// and now reboot
NVIC_SystemReset();
}
#endif /* EFI_PROD_CODE */
#if EFI_AUX_SERIAL
static bool isValidUART6TxPin(brain_pin_e pin) {
return pin == Gpio::C6 || pin == Gpio::G14;
}
static bool isValidUART6RxPin(brain_pin_e pin) {
return pin == Gpio::C7 || pin == Gpio::G9;
}
bool isValidSerialTxPin(brain_pin_e pin) {
return isValidUART6TxPin(pin);
}
bool isValidSerialRxPin(brain_pin_e pin) {
return isValidUART6RxPin(pin);
}
#endif /*EFI_AUX_SERIAL*/
#if EFI_PROD_CODE
BOR_Level_t BOR_Get(void) {
FLASH_OBProgramInitTypeDef FLASH_Handle;
/* Read option bytes */
HAL_FLASHEx_OBGetConfig(&FLASH_Handle);
/* Return BOR value */
return (BOR_Level_t) FLASH_Handle.BORLevel;
}
BOR_Result_t BOR_Set(BOR_Level_t BORValue) {
if (BOR_Get() == BORValue) {
return BOR_Result_Ok;
}
FLASH_OBProgramInitTypeDef FLASH_Handle;
FLASH_Handle.BORLevel = (uint32_t)BORValue;
FLASH_Handle.OptionType = OPTIONBYTE_BOR;
HAL_FLASH_OB_Unlock();
HAL_FLASHEx_OBProgram(&FLASH_Handle);
HAL_StatusTypeDef status = HAL_FLASH_OB_Launch();
HAL_FLASH_OB_Lock();
if (status != HAL_OK) {
return BOR_Result_Error;
}
return BOR_Result_Ok;
}
void baseMCUInit(void) {
// looks like this holds a random value on start? Let's set a nice clean zero
DWT->CYCCNT = 0;
BOR_Set(BOR_Level_1); // one step above default value
}
extern uint32_t __main_stack_base__;
typedef struct port_intctx intctx_t;
EXTERNC int getRemainingStack(thread_t *otp) {
#if CH_DBG_ENABLE_STACK_CHECK
// this would dismiss coverity warning - see http://rusefi.com/forum/viewtopic.php?f=5&t=655
// coverity[uninit_use]
register intctx_t *r13 asm ("r13");
otp->activeStack = r13;
int remainingStack;
if (ch.dbg.isr_cnt > 0) {
// ISR context
remainingStack = (int)(r13 - 1) - (int)&__main_stack_base__;
} else {
remainingStack = (int)(r13 - 1) - (int)otp->wabase;
}
otp->remainingStack = remainingStack;
return remainingStack;
#else
UNUSED(otp);
return 99999;
#endif /* CH_DBG_ENABLE_STACK_CHECK */
}
#if HAL_USE_SPI
bool isSpiInitialized[5] = { false, false, false, false, false };
static int getSpiAf(SPIDriver *driver) {
#if STM32_SPI_USE_SPI1
if (driver == &SPID1) {
return EFI_SPI1_AF;
}
#endif
#if STM32_SPI_USE_SPI2
if (driver == &SPID2) {
return EFI_SPI2_AF;
}
#endif
#if STM32_SPI_USE_SPI3
if (driver == &SPID3) {
return EFI_SPI3_AF;
}
#endif
return -1;
}
brain_pin_e getMisoPin(spi_device_e device) {
switch(device) {
case SPI_DEVICE_1:
return engineConfiguration->spi1misoPin;
case SPI_DEVICE_2:
return engineConfiguration->spi2misoPin;
case SPI_DEVICE_3:
return engineConfiguration->spi3misoPin;
default:
break;
}
return Gpio::Unassigned;
}
brain_pin_e getMosiPin(spi_device_e device) {
switch(device) {
case SPI_DEVICE_1:
return engineConfiguration->spi1mosiPin;
case SPI_DEVICE_2:
return engineConfiguration->spi2mosiPin;
case SPI_DEVICE_3:
return engineConfiguration->spi3mosiPin;
default:
break;
}
return Gpio::Unassigned;
}
brain_pin_e getSckPin(spi_device_e device) {
switch(device) {
case SPI_DEVICE_1:
return engineConfiguration->spi1sckPin;
case SPI_DEVICE_2:
return engineConfiguration->spi2sckPin;
case SPI_DEVICE_3:
return engineConfiguration->spi3sckPin;
default:
break;
}
return Gpio::Unassigned;
}
void turnOnSpi(spi_device_e device) {
if (isSpiInitialized[device])
return; // already initialized
isSpiInitialized[device] = true;
if (device == SPI_DEVICE_1) {
// todo: introduce a nice structure with all fields for same SPI
#if STM32_SPI_USE_SPI1
// scheduleMsg(&logging, "Turning on SPI1 pins");
initSpiModule(&SPID1, getSckPin(device),
getMisoPin(device),
getMosiPin(device),
engineConfiguration->spi1SckMode,
engineConfiguration->spi1MosiMode,
engineConfiguration->spi1MisoMode);
#endif /* STM32_SPI_USE_SPI1 */
}
if (device == SPI_DEVICE_2) {
#if STM32_SPI_USE_SPI2
// scheduleMsg(&logging, "Turning on SPI2 pins");
initSpiModule(&SPID2, getSckPin(device),
getMisoPin(device),
getMosiPin(device),
engineConfiguration->spi2SckMode,
engineConfiguration->spi2MosiMode,
engineConfiguration->spi2MisoMode);
#endif /* STM32_SPI_USE_SPI2 */
}
if (device == SPI_DEVICE_3) {
#if STM32_SPI_USE_SPI3
// scheduleMsg(&logging, "Turning on SPI3 pins");
initSpiModule(&SPID3, getSckPin(device),
getMisoPin(device),
getMosiPin(device),
engineConfiguration->spi3SckMode,
engineConfiguration->spi3MosiMode,
engineConfiguration->spi3MisoMode);
#endif /* STM32_SPI_USE_SPI3 */
}
if (device == SPI_DEVICE_4) {
#if STM32_SPI_USE_SPI4
// scheduleMsg(&logging, "Turning on SPI4 pins");
/* there are no configuration fields for SPI4 in engineConfiguration, rely on board init code
* it should set proper functions for SPI4 pins */
#endif /* STM32_SPI_USE_SPI4 */
}
}
void initSpiModule(SPIDriver *driver, brain_pin_e sck, brain_pin_e miso,
brain_pin_e mosi,
int sckMode,
int mosiMode,
int misoMode) {
/**
* See https://github.com/rusefi/rusefi/pull/664/
*
* Info on the silicon defect can be found in this document, section 2.5.2:
* https://www.st.com/content/ccc/resource/technical/document/errata_sheet/0a/98/58/84/86/b6/47/a2/DM00037591.pdf/files/DM00037591.pdf/jcr:content/translations/en.DM00037591.pdf
*/
efiSetPadMode("SPI clock", sck, PAL_MODE_ALTERNATE(getSpiAf(driver)) | sckMode | PAL_STM32_OSPEED_HIGHEST);
efiSetPadMode("SPI master out", mosi, PAL_MODE_ALTERNATE(getSpiAf(driver)) | mosiMode | PAL_STM32_OSPEED_HIGHEST);
// Activate the internal pullup on MISO: SD cards indicate "busy" by holding MOSI low,
// so in case there is no SD card installed, the line could float low and indicate that
// the (non existent) card is busy. We pull the line high to indicate "not busy" in case
// of a missing card.
efiSetPadMode("SPI master in ", miso, PAL_MODE_ALTERNATE(getSpiAf(driver)) | misoMode | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUPDR_PULLUP);
}
void initSpiCs(SPIConfig *spiConfig, brain_pin_e csPin) {
spiConfig->end_cb = nullptr;
ioportid_t port = getHwPort("spi", csPin);
ioportmask_t pin = getHwPin("spi", csPin);
spiConfig->ssport = port;
spiConfig->sspad = pin;
efiSetPadMode("chip select", csPin, PAL_STM32_MODE_OUTPUT);
}
#ifdef STM32H7XX
// H7 SPI clock is set to 80MHz
// fast mode is 80mhz/2 = 40MHz
SPIConfig mmc_hs_spicfg = {
.circular = false,
.end_cb = NULL,
.ssport = NULL,
.sspad = 0,
.cfg1 = 7 // 8 bits per byte
| 0 /* MBR = 0, divider = 2 */,
.cfg2 = 0
};
// Slow mode is 80mhz/4 = 20MHz
SPIConfig mmc_ls_spicfg = {
.circular = false,
.end_cb = NULL,
.ssport = NULL,
.sspad = 0,
.cfg1 = 7 // 8 bits per byte
| SPI_CFG1_MBR_0 /* MBR = 001, divider = 4 */,
.cfg2 = 0
};
#else /* not STM32H7XX */
// SD cards are good up to 25MHz in "slow" mode, and 50MHz in "fast" mode
// 168mhz F4:
// Slow mode is 10.5 or 5.25 MHz, depending on which SPI device
// Fast mode is 42 or 21 MHz
// 216mhz F7:
// Slow mode is 13.5 or 6.75 MHz
// Fast mode is 54 or 27 MHz (technically out of spec, needs testing!)
SPIConfig mmc_hs_spicfg = {
.circular = false,
.end_cb = NULL,
.ssport = NULL,
.sspad = 0,
.cr1 = SPI_BaudRatePrescaler_2,
.cr2 = 0
};
SPIConfig mmc_ls_spicfg = {
.circular = false,
.end_cb = NULL,
.ssport = NULL,
.sspad = 0,
.cr1 = SPI_BaudRatePrescaler_8,
.cr2 = 0
};
#endif
#endif /* HAL_USE_SPI */
#if EFI_CAN_SUPPORT
static bool isValidCan1RxPin(brain_pin_e pin) {
return pin == Gpio::A11 || pin == Gpio::B8 || pin == Gpio::D0;
}
static bool isValidCan1TxPin(brain_pin_e pin) {
return pin == Gpio::A12 || pin == Gpio::B9 || pin == Gpio::D1;
}
static bool isValidCan2RxPin(brain_pin_e pin) {
return pin == Gpio::B5 || pin == Gpio::B12;
}
static bool isValidCan2TxPin(brain_pin_e pin) {
return pin == Gpio::B6 || pin == Gpio::B13;
}
bool isValidCanTxPin(brain_pin_e pin) {
return isValidCan1TxPin(pin) || isValidCan2TxPin(pin);
}
bool isValidCanRxPin(brain_pin_e pin) {
return isValidCan1RxPin(pin) || isValidCan2RxPin(pin);
}
CANDriver* detectCanDevice(brain_pin_e pinRx, brain_pin_e pinTx) {
if (pinRx == Gpio::Unassigned && pinTx == Gpio::Unassigned) {
return nullptr;
}
#if STM32_CAN_USE_CAN1 || STM32_CAN_USE_FDCAN1
if (isValidCan1RxPin(pinRx) && isValidCan1TxPin(pinTx))
return &CAND1;
#endif
#if STM32_CAN_USE_CAN2 || STM32_CAN_USE_FDCAN2
if (isValidCan2RxPin(pinRx) && isValidCan2TxPin(pinTx))
return &CAND2;
#endif
firmwareError(OBD_PCM_Processor_Fault, "invalid CAN pins tx %s and rx %s", hwPortname(pinTx), hwPortname(pinRx));
return nullptr;
}
#endif /* EFI_CAN_SUPPORT */
#if defined(STM32F4) || defined(STM32F7) || defined(STM32H7)
#define HWREG(x) \
(*((volatile unsigned long *)(x)))
#define NVIC_FAULT_STAT 0xE000ED28 // Configurable Fault Status
#define NVIC_FAULT_STAT_BFARV 0x00008000 // Bus Fault Address Register Valid
#define NVIC_CFG_CTRL_BFHFNMIGN 0x00000100 // Ignore Bus Fault in NMI and
// Fault
#define NVIC_CFG_CTRL 0xE000ED14 // Configuration and Control
/**
* @brief Probe an address to see if can be read without generating a bus fault
* @details This function must be called with the processor in privileged mode.
* It:
* - Clear any previous indication of a bus fault in the BFARV bit
* - Temporarily sets the processor to Ignore Bus Faults with all interrupts and fault handlers disabled
* - Attempt to read from read_address, ignoring the result
* - Checks to see if the read caused a bus fault, by checking the BFARV bit is set
* - Re-enables Bus Faults and all interrupts and fault handlers
* @param[in] read_address The address to try reading a byte from
* @return Returns true if no bus fault occurred reading from read_address, or false if a bus fault occurred.
*/
bool ramReadProbe(volatile const char *read_address) {
bool address_readable = true;
/* Clear any existing indication of a bus fault - BFARV is write one to clear */
HWREG (NVIC_FAULT_STAT) |= NVIC_FAULT_STAT_BFARV;
HWREG (NVIC_CFG_CTRL) |= NVIC_CFG_CTRL_BFHFNMIGN;
asm volatile (" CPSID f;");
*read_address;
if ((HWREG (NVIC_FAULT_STAT) & NVIC_FAULT_STAT_BFARV) != 0)
{
address_readable = false;
}
asm volatile (" CPSIE f;");
HWREG (NVIC_CFG_CTRL) &= ~NVIC_CFG_CTRL_BFHFNMIGN;
return address_readable;
}
#endif
#if defined(STM32F4)
bool isStm32F42x() {
// really it's enough to just check 0x20020010
return ramReadProbe((const char *)0x20000010) && ramReadProbe((const char *)0x20020010) && !ramReadProbe((const char *)0x20070010);
}
#endif
// Stubs for per-board low power helpers
__attribute__((weak)) void boardPrepareForStop() {
// Default implementation - wake up on PA0 - boards should override this
palEnableLineEvent(PAL_LINE(GPIOA, 0), PAL_EVENT_MODE_RISING_EDGE);
}
/**
Standby uses special low power hardware - it always wakes on rising edge
*/
void boardPreparePA0ForStandby() {
#ifdef STM32F4XX
//Enable Wakeup Pin for PA0
PWR->CSR |= PWR_CSR_EWUP;
// Clear wakeup flag - it may be set if PA0 is already
// high when we enable it as a wake source
PWR->CR |= PWR_CR_CWUF; //Clear Wakeup Pin flag for PA0
#endif
#ifdef STM32F7XX
PWR->CSR2 |= PWR_CSR2_EWUP1; //EWUP1: Enable Wakeup pin for PA0
PWR->CR2 |= PWR_CR2_CWUPF1; //Clear Wakeup Pin flag for PA0
#endif
#ifdef STM32H7XX
// Wake on wakeup pin 0 - PA0
PWR->WKUPEPR = PWR_WKUPEPR_WKUPEN1;
// clear all possible wakeup bits
PWR->WKUPCR = 0xFFFFFFFF;
#endif
}
__attribute__((weak)) void boardPrepareForStandby() {
boardPreparePA0ForStandby();
}
#endif // EFI_PROD_CODE