stm32_common: divide into parts for easy reuse
This commit is contained in:
parent
0db6439670
commit
5a11a2a2b4
|
@ -26,322 +26,13 @@ extern "C" {
|
|||
};
|
||||
#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:
|
||||
/* 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:
|
||||
criticalError("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(ObdCode::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(ObdCode::CUSTOM_OBD_HIGH_FREQUENCY, "PWM Frequency too high %d hz on pin \"%s\"", frequency, msg);
|
||||
return;
|
||||
}
|
||||
|
||||
const PWMConfig pwmcfg = {
|
||||
.frequency = c_timerFrequency,
|
||||
.period = m_period,
|
||||
.callback = nullptr,
|
||||
.channels = {
|
||||
{PWM_OUTPUT_ACTIVE_HIGH, nullptr},
|
||||
{PWM_OUTPUT_ACTIVE_HIGH, nullptr},
|
||||
{PWM_OUTPUT_ACTIVE_HIGH, nullptr},
|
||||
{PWM_OUTPUT_ACTIVE_HIGH, nullptr}
|
||||
},
|
||||
.cr2 = 0,
|
||||
#if STM32_PWM_USE_ADVANCED
|
||||
.bdtr = 0,
|
||||
#endif
|
||||
.dier = 0,
|
||||
};
|
||||
|
||||
// Start the timer running
|
||||
pwmStart(m_driver, &pwmcfg);
|
||||
|
||||
// Set initial duty cycle
|
||||
setDuty(duty);
|
||||
}
|
||||
|
||||
void setDuty(float duty) override {
|
||||
if (!m_driver) {
|
||||
criticalError("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];
|
||||
}
|
||||
}
|
||||
|
||||
criticalError("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.
|
||||
|
@ -374,26 +65,6 @@ void jump_to_openblt() {
|
|||
}
|
||||
#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
|
||||
|
||||
#ifdef AT32F4XX
|
||||
|
@ -478,256 +149,6 @@ EXTERNC int getRemainingStack(thread_t *otp) {
|
|||
#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
|
||||
criticalError("SPI AF not available");
|
||||
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);
|
||||
#else
|
||||
criticalError("SPI1 not available in this binary");
|
||||
#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);
|
||||
#else
|
||||
criticalError("SPI2 not available in this binary");
|
||||
#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);
|
||||
#else
|
||||
criticalError("SPI3 not available in this binary");
|
||||
#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 */
|
||||
#else
|
||||
criticalError("SPI4 not available in this binary");
|
||||
#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
|
||||
criticalError("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) \
|
||||
|
|
|
@ -4,6 +4,11 @@ HW_LAYER_PORT_CPP += \
|
|||
$(HW_STM32_PORT_DIR)/serial_over_usb/usbconsole.cpp \
|
||||
$(HW_STM32_PORT_DIR)/stm32_pins.cpp \
|
||||
$(HW_STM32_PORT_DIR)/stm32_common.cpp \
|
||||
$(HW_STM32_PORT_DIR)/stm32_common_adc.cpp \
|
||||
$(HW_STM32_PORT_DIR)/stm32_common_can.cpp \
|
||||
$(HW_STM32_PORT_DIR)/stm32_common_pwm.cpp \
|
||||
$(HW_STM32_PORT_DIR)/stm32_common_serial.cpp \
|
||||
$(HW_STM32_PORT_DIR)/stm32_common_spi.cpp \
|
||||
$(HW_STM32_PORT_DIR)/stm32_icu.cpp \
|
||||
$(HW_STM32_PORT_DIR)/backup_ram.cpp \
|
||||
$(HW_STM32_PORT_DIR)/microsecond_timer_stm32.cpp \
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
/**
|
||||
* @file stm32_common_adc.cpp
|
||||
* @brief Low level common STM32 code
|
||||
*
|
||||
* @date Mar 28, 2019
|
||||
* @author Andrey Belomutskiy, (c) 2012-2020
|
||||
*/
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#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:
|
||||
/* 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:
|
||||
criticalError("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 */
|
|
@ -0,0 +1,53 @@
|
|||
/**
|
||||
* @file stm32_common_can.cpp
|
||||
* @brief Low level common STM32 code
|
||||
*
|
||||
* @date Mar 28, 2019
|
||||
* @author Andrey Belomutskiy, (c) 2012-2020
|
||||
*/
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#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
|
||||
criticalError("invalid CAN pins tx %s and rx %s", hwPortname(pinTx), hwPortname(pinRx));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#endif /* EFI_CAN_SUPPORT */
|
|
@ -0,0 +1,198 @@
|
|||
/**
|
||||
* @file stm32_common_pwm.cpp
|
||||
* @brief Low level common STM32 code
|
||||
*
|
||||
* @date Mar 28, 2019
|
||||
* @author Andrey Belomutskiy, (c) 2012-2020
|
||||
*/
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#define _2_MHZ 2'000'000
|
||||
|
||||
#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 %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(ObdCode::CUSTOM_OBD_HIGH_FREQUENCY, "PWM Frequency too high %d hz on pin \"%s\"", frequency, msg);
|
||||
return;
|
||||
}
|
||||
|
||||
const PWMConfig pwmcfg = {
|
||||
.frequency = c_timerFrequency,
|
||||
.period = m_period,
|
||||
.callback = nullptr,
|
||||
.channels = {
|
||||
{PWM_OUTPUT_ACTIVE_HIGH, nullptr},
|
||||
{PWM_OUTPUT_ACTIVE_HIGH, nullptr},
|
||||
{PWM_OUTPUT_ACTIVE_HIGH, nullptr},
|
||||
{PWM_OUTPUT_ACTIVE_HIGH, nullptr}
|
||||
},
|
||||
.cr2 = 0,
|
||||
#if STM32_PWM_USE_ADVANCED
|
||||
.bdtr = 0,
|
||||
#endif
|
||||
.dier = 0,
|
||||
};
|
||||
|
||||
// Start the timer running
|
||||
pwmStart(m_driver, &pwmcfg);
|
||||
|
||||
// Set initial duty cycle
|
||||
setDuty(duty);
|
||||
}
|
||||
|
||||
void setDuty(float duty) override {
|
||||
if (!m_driver) {
|
||||
criticalError("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];
|
||||
}
|
||||
}
|
||||
|
||||
criticalError("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 /* HAL_USE_PWM */
|
|
@ -0,0 +1,29 @@
|
|||
/**
|
||||
* @file stm32_common.cpp
|
||||
* @brief Low level common STM32 code
|
||||
*
|
||||
* @date Mar 28, 2019
|
||||
* @author Andrey Belomutskiy, (c) 2012-2020
|
||||
*/
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#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*/
|
|
@ -0,0 +1,215 @@
|
|||
/**
|
||||
* @file stm32_common_spi.cpp
|
||||
* @brief Low level common STM32 code
|
||||
*
|
||||
* @date Mar 28, 2019
|
||||
* @author Andrey Belomutskiy, (c) 2012-2020
|
||||
*/
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#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
|
||||
criticalError("SPI AF not available");
|
||||
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);
|
||||
#else
|
||||
criticalError("SPI1 not available in this binary");
|
||||
#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);
|
||||
#else
|
||||
criticalError("SPI2 not available in this binary");
|
||||
#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);
|
||||
#else
|
||||
criticalError("SPI3 not available in this binary");
|
||||
#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 */
|
||||
#else
|
||||
criticalError("SPI4 not available in this binary");
|
||||
#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 */
|
Loading…
Reference in New Issue