fome-fw/firmware/hw_layer/ports/stm32/stm32_common.cpp

829 lines
21 KiB
C++

/**
* @file stm32_common.cpp
* @brief Low level common STM32 code
*
* @date Mar 28, 2019
* @author Andrey Belomutskiy, (c) 2012-2020
*/
#include "pch.h"
#include <rusefi/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
#if EFI_USE_OPENBLT
/* communication with OpenBLT that is plain C, not to modify external file */
extern "C" {
#include "openblt/shared_params.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*, 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:
/* todo: what is upper range ADC is used while lower range ADC is not used? how do we still mark pin used?
external muxes for internal ADC #3350
firmwareError(ObdCode::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(ObdCode::OBD_PCM_Processor_Fault, "getAdcChannel %d", (int)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(ObdCode::CUSTOM_OBD_LOW_FREQUENCY, "PWM Frequency too low %.1f 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(ObdCode::CUSTOM_OBD_HIGH_FREQUENCY, "PWM Frequency too high %.1f 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(ObdCode::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;
}
};
}
/**
* Could this be unified with getIcuParams() method?
*/
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, /*channel*/0, /*AF*/1};
case Gpio::A9: return stm32_pwm_config{&PWMD1, /*channel*/1, /*AF*/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(ObdCode::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
static void reset_and_jump(void) {
#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
// and now reboot
NVIC_SystemReset();
}
void jump_to_bootloader() {
// leave DFU breadcrumb which assembly startup code would check, see [rusefi][DFU] section in assembly code
*((unsigned long *)0x2001FFF0) = 0xDEADBEEF; // End of RAM
reset_and_jump();
}
void jump_to_openblt() {
#if EFI_USE_OPENBLT
/* safe to call on already inited shares area */
SharedParamsInit();
/* Store sing to stay in OpenBLT */
SharedParamsWriteByIndex(0, 0x01);
reset_and_jump();
#endif
}
#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[6] = { false, 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
#if STM32_SPI_USE_SPI4
if (driver == &SPID4) {
return EFI_SPI4_AF;
}
#endif
#if STM32_SPI_USE_SPI5
if (driver == &SPID5) {
return EFI_SPI5_AF;
}
#endif
#if STM32_SPI_USE_SPI6
if (driver == &SPID6) {
return EFI_SPI6_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;
case SPI_DEVICE_4:
return engineConfiguration->spi4misoPin;
case SPI_DEVICE_5:
return engineConfiguration->spi5misoPin;
case SPI_DEVICE_6:
return engineConfiguration->spi6misoPin;
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;
case SPI_DEVICE_4:
return engineConfiguration->spi4mosiPin;
case SPI_DEVICE_5:
return engineConfiguration->spi5mosiPin;
case SPI_DEVICE_6:
return engineConfiguration->spi6mosiPin;
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;
case SPI_DEVICE_4:
return engineConfiguration->spi4sckPin;
case SPI_DEVICE_5:
return engineConfiguration->spi5sckPin;
case SPI_DEVICE_6:
return engineConfiguration->spi6sckPin;
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) {
#if STM32_SPI_USE_SPI1
initSpiModule(&SPID1, getSckPin(device),
getMisoPin(device),
getMosiPin(device));
#endif /* STM32_SPI_USE_SPI1 */
}
if (device == SPI_DEVICE_2) {
#if STM32_SPI_USE_SPI2
initSpiModule(&SPID2, getSckPin(device),
getMisoPin(device),
getMosiPin(device));
#endif /* STM32_SPI_USE_SPI2 */
}
if (device == SPI_DEVICE_3) {
#if STM32_SPI_USE_SPI3
initSpiModule(&SPID3, getSckPin(device),
getMisoPin(device),
getMosiPin(device));
#endif /* STM32_SPI_USE_SPI3 */
}
if (device == SPI_DEVICE_4) {
#if STM32_SPI_USE_SPI4
initSpiModule(&SPID4, getSckPin(device),
getMisoPin(device),
getMosiPin(device));
#endif /* STM32_SPI_USE_SPI4 */
}
if (device == SPI_DEVICE_5) {
#if STM32_SPI_USE_SPI5
initSpiModule(&SPID5, getSckPin(device),
getMisoPin(device),
getMosiPin(device));
#endif /* STM32_SPI_USE_SPI5 */
}
if (device == SPI_DEVICE_6) {
#if STM32_SPI_USE_SPI6
initSpiModule(&SPID6, getSckPin(device),
getMisoPin(device),
getMosiPin(device));
#endif /* STM32_SPI_USE_SPI6 */
}
}
void initSpiModule(SPIDriver *driver, brain_pin_e sck, brain_pin_e miso, brain_pin_e mosi) {
/**
* 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)) | PAL_STM32_OSPEED_HIGHEST);
efiSetPadMode("SPI master out", mosi, PAL_MODE_ALTERNATE(getSpiAf(driver)) | 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)) | 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(ObdCode::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