diff --git a/Makefile b/Makefile index f4e81af..fc32bdd 100644 --- a/Makefile +++ b/Makefile @@ -35,7 +35,7 @@ CFLAGS = -Os -ggdb3 -Wall -Wextra -Iinclude/ -Ilibopeninv/include -Ilibopencm3/ -DCONTROL=CTRL_$(CONTROL) -DCTRL_SINE=0 -DCTRL_FOC=1 \ -mcpu=cortex-m3 -mthumb -std=gnu99 -ffunction-sections -fdata-sections CPPFLAGS = -Os -ggdb3 -Wall -Wextra -Iinclude/ -Ilibopeninv/include -Ilibopencm3/include \ - -fno-common -std=c++11 -pedantic -DSTM32F1 -DT_DEBUG=$(TERMINAL_DEBUG) \ + -fno-common -std=c++14 -pedantic -DSTM32F1 -DT_DEBUG=$(TERMINAL_DEBUG) \ -DCONTROL=CTRL_$(CONTROL) -DCTRL_SINE=0 -DCTRL_FOC=1 \ -ffunction-sections -fdata-sections -fno-builtin -fno-rtti -fno-exceptions -fno-unwind-tables -mcpu=cortex-m3 -mthumb @@ -51,7 +51,7 @@ OBJSL = stm32_sine.o hwinit.o stm32scheduler.o params.o terminal.o terminal_prj my_string.o digio.o sine_core.o my_fp.o fu.o inc_encoder.o printf.o anain.o \ temp_meas.o param_save.o throttle.o errormessage.o pwmgeneration.o \ picontroller.o terminalcommands.o vehiclecontrol.o \ - stm32_can.o canmap.o canhardware.o cansdo.o GD31xxOI.o + stm32_can.o canmap.o canhardware.o cansdo.o GD31xxOI.o crc8.o teslamodel3.o ifeq ($(CONTROL), SINE) OBJSL += pwmgeneration-sine.o diff --git a/include/digio_prj.h b/include/digio_prj.h index 2359986..25aac7a 100644 --- a/include/digio_prj.h +++ b/include/digio_prj.h @@ -35,6 +35,8 @@ DIG_IO_ENTRY(v5_ctrl,,,) \ DIG_IO_ENTRY(intb_in,,,) \ DIG_IO_ENTRY(inta_in,,,) \ + DIG_IO_ENTRY(gate_cs_hi,,,) \ + DIG_IO_ENTRY(gate_sd_hi,,,) \ //...Then we assign the physical GPIOs per variant @@ -91,6 +93,29 @@ DIG_IO_ENTRY(intb_in, GPIOE, GPIO8, PinMode::INPUT_FLT) \ DIG_IO_ENTRY(inta_in, GPIOE, GPIO9, PinMode::INPUT_FLT) \ +#define DIG_IO_LIST_TESLAM3 \ + DIG_IO_ENTRY(cruise_in, GPIOB, GPIO2, PinMode::INPUT_PD) \ + DIG_IO_ENTRY(start_in, GPIOD, GPIO7, PinMode::INPUT_FLT) \ + DIG_IO_ENTRY(brake_in, GPIOE, GPIO4, PinMode::INPUT_FLT) \ + DIG_IO_ENTRY(mprot_in, GPIOE, GPIO5, PinMode::INPUT_PU) \ + DIG_IO_ENTRY(fwd_in, GPIOA, GPIO4, PinMode::INPUT_FLT) \ + DIG_IO_ENTRY(rev_in, GPIOA, GPIO3, PinMode::INPUT_FLT) \ + DIG_IO_ENTRY(emcystop_in, GPIOC, GPIO7, PinMode::INPUT_FLT) \ + DIG_IO_ENTRY(bk_in, GPIOB, GPIO12, PinMode::INPUT_FLT) \ + DIG_IO_ENTRY(bms_in, GPIOC, GPIO8, PinMode::INPUT_PD) \ + DIG_IO_ENTRY(ocur_in, GPIOE, GPIO2, PinMode::INPUT_FLT) \ + DIG_IO_ENTRY(desat_in, GPIOC, GPIO9, PinMode::INPUT_FLT) \ + DIG_IO_ENTRY(dcsw_out, GPIOC, GPIO10, PinMode::OUTPUT) \ + DIG_IO_ENTRY(fan_out, GPIOC, GPIO15, PinMode::OUTPUT) /* map to unused pin by default */ \ + DIG_IO_ENTRY(vtg_out, GPIOC, GPIO14, PinMode::OUTPUT) \ + DIG_IO_ENTRY(prec_out, GPIOD, GPIO15, PinMode::OUTPUT) \ + DIG_IO_ENTRY(led_out, GPIOC, GPIO12, PinMode::OUTPUT) \ + DIG_IO_ENTRY(err_out, GPIOD, GPIO14, PinMode::OUTPUT) \ + DIG_IO_ENTRY(temp0_out, GPIOD, GPIO13, PinMode::OUTPUT) \ + DIG_IO_ENTRY(speed_out, GPIOD, GPIO12, PinMode::OUTPUT) \ + DIG_IO_ENTRY(brk_out, GPIOD, GPIO11, PinMode::OUTPUT) \ + DIG_IO_ENTRY(gate_cs_hi, GPIOE, GPIO10, PinMode::OUTPUT) \ + DIG_IO_ENTRY(gate_sd_hi, GPIOE, GPIO11, PinMode::OUTPUT) \ #define DIG_IO_BLUEPILL \ DIG_IO_ENTRY(brake_in, GPIOB, GPIO9, PinMode::INPUT_FLT) \ diff --git a/include/errormessage_prj.h b/include/errormessage_prj.h index 0c4fc87..36715b2 100644 --- a/include/errormessage_prj.h +++ b/include/errormessage_prj.h @@ -24,5 +24,7 @@ ERROR_MESSAGE_ENTRY(TMPMMAX, ERROR_DERATE) \ ERROR_MESSAGE_ENTRY(CANCRC, ERROR_STOP) \ ERROR_MESSAGE_ENTRY(CANCOUNTER, ERROR_STOP) \ + ERROR_MESSAGE_ENTRY(GATEDRIVEINITFAIL, ERROR_STOP) \ + ERROR_MESSAGE_ENTRY(GATEDRIVEFAULT, ERROR_STOP) \ #endif // ERRORMESSAGE_PRJ_H_INCLUDED diff --git a/include/hw/stgap1as_gate_driver.h b/include/hw/stgap1as_gate_driver.h new file mode 100644 index 0000000..a9a1359 --- /dev/null +++ b/include/hw/stgap1as_gate_driver.h @@ -0,0 +1,405 @@ +/* + * This file is part of the stm32-sine project. + * + * Copyright (C) 2021 David J. Fiddes + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef HW_STGAP1AS_GATE_DRIVER_H +#define HW_STGAP1AS_GATE_DRIVER_H + +/* + * Defined Constants and Types for the ST Micro STGAP1AS Automotive + * galvanically isolated advanced single gate driver + * + * Definitions from datasheet DocID029344 Rev 4 from + * https://www.st.com/en/power-management/stgap1as.html + * + */ + +/** @defgroup spi_crc SPI Communication CRC Constants +@{*/ + +/** SPI communication crc polynomial corresponding to x^8 + x^2 + x + 1 */ +#define STGAP1AS_SPI_CRC_POLYNOMIAL 0x07 + +/** SPI communication CRC initial value */ +#define STGAP1AS_SPI_CRC_INIT_VALUE 0xFF + +/**@}*/ + +/** @defgroup spi_commands SPI Commands +@{*/ + +/** Device configuration start */ +#define STGAP1AS_CMD_START_CONFIG (0b00101010) + +/** Device configuration/check completed */ +#define STGAP1AS_CMD_STOP_CONFIG (0b00111010) + +/** No operation */ +#define STGAP1AS_CMD_NOP (0b00000000) + +#define STGAP1AS_CMD_REG_MASK (0b11100000) +#define STGAP1AS_CMD_WRITE_REG_VALUE (0b10000000) +#define STGAP1AS_CMD_WRITE_REG_MASK 0x1F +/** Write register */ +#define STGAP1AS_CMD_WRITE_REG(x) (STGAP1AS_CMD_WRITE_REG_VALUE | (x & STGAP1AS_CMD_WRITE_REG_MASK)) + +#define STGAP1AS_CMD_READ_REG_VALUE (0b10100000) +#define STGAP1AS_CMD_READ_REG_MASK 0x1F +/** Read register */ +#define STGAP1AS_CMD_READ_REG(x) (STGAP1AS_CMD_READ_REG_VALUE | (x & STGAP1AS_CMD_READ_REG_MASK)) + +/** Reset all the status registers */ +#define STGAP1AS_CMD_RESET_STATUS (0b11010000) + +/** Global reset */ +#define STGAP1AS_CMD_GLOBAL_RESET (0b11101010) + +/** Device enters in standby mode */ +#define STGAP1AS_CMD_SLEEP (0b11110101) + +/**@}*/ + +/** @defgroup registers Registers +@{*/ + +#define STGAP1AS_REG_CFG1_MASK 0xFF +/** CFG1 register (low voltage side) */ +#define STGAP1AS_REG_CFG1 0x0C + +#define STGAP1AS_REG_CFG2_MASK 0xFF +/** CFG2 register (isolated side) */ +#define STGAP1AS_REG_CFG2 0x1D + +#define STGAP1AS_REG_CFG3_MASK 0xFF +/** CFG3 register (isolated side) */ +#define STGAP1AS_REG_CFG3 0x1E + +#define STGAP1AS_REG_CFG4_MASK 0x3F +/** CFG4 register (isolated side) */ +#define STGAP1AS_REG_CFG4 0x1F + +#define STGAP1AS_REG_CFG5_MASK 0x0F +/** CFG5 register (isolated side) */ +#define STGAP1AS_REG_CFG5 0x19 + +#define STGAP1AS_REG_STATUS1_MASK 0xFF +/** STATUS1 register (low voltage side) */ +#define STGAP1AS_REG_STATUS1 0x02 + +#define STGAP1AS_REG_STATUS2_MASK 0x06 +/** STATUS2 register (low voltage side) */ +#define STGAP1AS_REG_STATUS2 0x01 + +#define STGAP1AS_REG_STATUS3_MASK 0x1F +/** STATUS3 register (low voltage side) */ +#define STGAP1AS_REG_STATUS3 0x0A + +#define STGAP1AS_REG_TEST1_MASK 0x1F +/** TEST1 register (isolated side) */ +#define STGAP1AS_REG_TEST1 0x11 + +#define STGAP1AS_REG_DIAG1CFG_MASK 0xFF +/** DIAG1CFG register (low voltage side) */ +#define STGAP1AS_REG_DIAG1CFG 0x05 + +#define STGAP1AS_REG_DIAG2CFG_MASK 0xFF +/** DIAG2CFG register (low voltage side) */ +#define STGAP1AS_REG_DIAG2CFG 0x06 + +/**@}*/ + +/** @defgroup reg_cfg1 CFG1 Register Flags +@{*/ + +/** SPI communication protocol CRC enable */ +#define STGAP1AS_REG_CFG1_CRC_SPI (1 << 7) + +/** Supply voltage UVLOD enable */ +#define STGAP1AS_REG_CFG1_UVLOD_EN (1 << 6) + +/** SD pin reset STATUS registers */ +#define STGAP1AS_REG_CFG1_SD_FLAG (1 << 5) + +/** DIAG2 pin work as open drain output */ +#define STGAP1AS_REG_CFG1_DIAG_EN (1 << 4) + +/** Deadtime in ns */ +#define STGAP1AS_REG_CFG1_DT_DISABLED ((0b00) << 2) +#define STGAP1AS_REG_CFG1_DT_250NS ((0b01) << 2) +#define STGAP1AS_REG_CFG1_DT_800NS ((0b10) << 2) +#define STGAP1AS_REG_CFG1_DT_1200NS ((0b11) << 2) + +/** Input deglitch time in ns */ +#define STGAP1AS_REG_CFG1_IN_FILTER_DISABLED ((0b00) << 0) +#define STGAP1AS_REG_CFG1_IN_FILTER_160NS ((0b01) << 0) +#define STGAP1AS_REG_CFG1_IN_FILTER_500NS ((0b10) << 0) +#define STGAP1AS_REG_CFG1_IN_FILTER_70NS ((0b11) << 0) + +/**@}*/ + +/** @defgroup reg_cfg2 CFG2 Register Flags +@{*/ + +/** Sense threshold */ +#define STGAP1AS_REG_CFG2_SENSE_100MV ((0b000) << 5) +#define STGAP1AS_REG_CFG2_SENSE_125MV ((0b001) << 5) +#define STGAP1AS_REG_CFG2_SENSE_150MV ((0b010) << 5) +#define STGAP1AS_REG_CFG2_SENSE_175MV ((0b011) << 5) +#define STGAP1AS_REG_CFG2_SENSE_200MV ((0b100) << 5) +#define STGAP1AS_REG_CFG2_SENSE_250MV ((0b101) << 5) +#define STGAP1AS_REG_CFG2_SENSE_300MV ((0b110) << 5) +#define STGAP1AS_REG_CFG2_SENSE_400MV ((0b111) << 5) + +/** Desaturation current */ +#define STGAP1AS_REG_CFG2_DESAT_CUR_250UA ((0b00) << 3) +#define STGAP1AS_REG_CFG2_DESAT_CUR_500UA ((0b01) << 3) +#define STGAP1AS_REG_CFG2_DESAT_CUR_750UA ((0b10) << 3) +#define STGAP1AS_REG_CFG2_DESAT_CUR_1000UA ((0b11) << 3) + +/** Desaturation threshold */ +#define STGAP1AS_REG_CFG2_DESAT_TH_3V ((0b000) << 0) +#define STGAP1AS_REG_CFG2_DESAT_TH_4V ((0b001) << 0) +#define STGAP1AS_REG_CFG2_DESAT_TH_5V ((0b010) << 0) +#define STGAP1AS_REG_CFG2_DESAT_TH_6V ((0b011) << 0) +#define STGAP1AS_REG_CFG2_DESAT_TH_7V ((0b100) << 0) +#define STGAP1AS_REG_CFG2_DESAT_TH_8V ((0b101) << 0) +#define STGAP1AS_REG_CFG2_DESAT_TH_9V ((0b110) << 0) +#define STGAP1AS_REG_CFG2_DESAT_TH_10V ((0b111) << 0) + +/**@}*/ + +/** @defgroup reg_cfg3 CFG3 Register Flags +@{*/ + +/** 2-level turn off threshold in V */ +#define STGAP1AS_REG_CFG3_2LTO_TH_7V ((0b0000) << 4) +#define STGAP1AS_REG_CFG3_2LTO_TH_7_5V ((0b0001) << 4) +#define STGAP1AS_REG_CFG3_2LTO_TH_8V ((0b0010) << 4) +#define STGAP1AS_REG_CFG3_2LTO_TH_8_5V ((0b0011) << 4) +#define STGAP1AS_REG_CFG3_2LTO_TH_9V ((0b0100) << 4) +#define STGAP1AS_REG_CFG3_2LTO_TH_9_5V ((0b0101) << 4) +#define STGAP1AS_REG_CFG3_2LTO_TH_10V ((0b0110) << 4) +#define STGAP1AS_REG_CFG3_2LTO_TH_10_5V ((0b0111) << 4) +#define STGAP1AS_REG_CFG3_2LTO_TH_11V ((0b1000) << 4) +#define STGAP1AS_REG_CFG3_2LTO_TH_11_5V ((0b1001) << 4) +#define STGAP1AS_REG_CFG3_2LTO_TH_12V ((0b1010) << 4) +#define STGAP1AS_REG_CFG3_2LTO_TH_12_5V ((0b1011) << 4) +#define STGAP1AS_REG_CFG3_2LTO_TH_13V ((0b1100) << 4) +#define STGAP1AS_REG_CFG3_2LTO_TH_13_5V ((0b1101) << 4) +#define STGAP1AS_REG_CFG3_2LTO_TH_14V ((0b1110) << 4) +#define STGAP1AS_REG_CFG3_2LTO_TH_14_5V ((0b1111) << 4) + +/** 2-level turn off time in us */ +#define STGAP1AS_REG_CFG3_2LTO_TIME_DISABLED ((0b0000) << 0) +#define STGAP1AS_REG_CFG3_2LTO_TIME_0_75US ((0b0001) << 0) +#define STGAP1AS_REG_CFG3_2LTO_TIME_1_00US ((0b0010) << 0) +#define STGAP1AS_REG_CFG3_2LTO_TIME_1_50US ((0b0011) << 0) +#define STGAP1AS_REG_CFG3_2LTO_TIME_2_00US ((0b0100) << 0) +#define STGAP1AS_REG_CFG3_2LTO_TIME_2_50US ((0b0101) << 0) +#define STGAP1AS_REG_CFG3_2LTO_TIME_3_00US ((0b0110) << 0) +#define STGAP1AS_REG_CFG3_2LTO_TIME_3_50US ((0b0111) << 0) +#define STGAP1AS_REG_CFG3_2LTO_TIME_3_75US ((0b1000) << 0) +#define STGAP1AS_REG_CFG3_2LTO_TIME_4_00US ((0b1001) << 0) +#define STGAP1AS_REG_CFG3_2LTO_TIME_4_25US ((0b1010) << 0) +#define STGAP1AS_REG_CFG3_2LTO_TIME_4_50US ((0b1011) << 0) +#define STGAP1AS_REG_CFG3_2LTO_TIME_4_75US ((0b1100) << 0) +#define STGAP1AS_REG_CFG3_2LTO_TIME_5_00US ((0b1101) << 0) +#define STGAP1AS_REG_CFG3_2LTO_TIME_5_25US ((0b1110) << 0) +#define STGAP1AS_REG_CFG3_2LTO_TIME_5_50US ((0b1111) << 0) + +/**@}*/ + +/** @defgroup reg_cfg4 CFG4 Register Flags +@{*/ + +/** VH and VL supply voltages OVLO enable */ +#define STGAP1AS_REG_CFG4_OVLO_EN (1 << 5) + +/** UVLO protection management */ +#define STGAP1AS_REG_CFG4_UVLO_LATCHED (1 << 4) + +/** VL negative supply voltage UVLO threshold in V */ +#define STGAP1AS_REG_CFG4_VLON_TH_DISABLED ((0b00) << 2) +#define STGAP1AS_REG_CFG4_VLON_TH_NEG_3V ((0b01) << 2) +#define STGAP1AS_REG_CFG4_VLON_TH_NEG_5V ((0b10) << 2) +#define STGAP1AS_REG_CFG4_VLON_TH_NEG_7V ((0b11) << 2) + +/** VH positive supply voltage UVLO threshold in V */ +#define STGAP1AS_REG_CFG4_VHON_TH_DISABLED ((0b00) << 0) +#define STGAP1AS_REG_CFG4_VHON_TH_10V ((0b01) << 0) +#define STGAP1AS_REG_CFG4_VHON_TH_12V ((0b10) << 0) +#define STGAP1AS_REG_CFG4_VHON_TH_14V ((0b11) << 0) + +/**@}*/ + +/** @defgroup reg_cfg5 CFG5 Register Flags +@{*/ + +/** 2LTO active only after a fault event */ +#define STGAP1AS_REG_CFG5_2LTO_EN (1 << 3) + +/** Miller clamp feature enable */ +#define STGAP1AS_REG_CFG5_CLAMP_EN (1 << 2) + +/** Desaturation comparator eanble */ +#define STGAP1AS_REG_CFG5_DESAT_EN (1 << 1) + +/** Sense comparator enable */ +#define STGAP1AS_REG_CFG5_SENSE_EN (1 << 0) + +/**@}*/ + +/** @defgroup reg_status1 STATUS1 Register Flags +@{*/ + +/** VH overvoltage flag */ +#define STGAP1AS_REG_STATUS1_OVLOH (1 << 7) + +/** VL overvoltage flag */ +#define STGAP1AS_REG_STATUS1_OVLOL (1 << 6) + +/** Desaturation flag */ +#define STGAP1AS_REG_STATUS1_DESAT (1 << 5) + +/** Sense flag */ +#define STGAP1AS_REG_STATUS1_SENSE (1 << 4) + +/** VH undervoltage flag */ +#define STGAP1AS_REG_STATUS1_UVLOH (1 << 3) + +/** VL undervoltage flag */ +#define STGAP1AS_REG_STATUS1_UVLOL (1 << 2) + +/** Thermal shutdown protection flag */ +#define STGAP1AS_REG_STATUS1_TSD (1 << 1) + +/** Thermal warning flag */ +#define STGAP1AS_REG_STATUS1_TWN (1 << 0) + +/**@}*/ + +/** @defgroup reg_status2 STATUS2 Register Flags +@{*/ + +/** Register or communication error on isolated side */ +#define STGAP1AS_REG_STATUS2_REGERRR (1 << 2) + +/** ASC pin status */ +#define STGAP1AS_REG_STATUS2_ASC (1 << 1) + +/**@}*/ + +/** @defgroup reg_status3 STATUS3 Register Flags +@{*/ + +/** Deadtime error flag */ +#define STGAP1AS_REG_STATUS3_DT_ERR (1 << 4) + +/** SPI communication error flag */ +#define STGAP1AS_REG_STATUS3_SPI_ERR (1 << 3) + +/** Register or communication error on low voltage side */ +#define STGAP1AS_REG_STATUS3_REGERRL (1 << 2) + +/** VDD overvoltage flag */ +#define STGAP1AS_REG_STATUS3_OVLOD (1 << 1) + +/** VDD undervoltage flag */ +#define STGAP1AS_REG_STATUS3_UVLOD (1 << 0) + +/**@}*/ + +/** @defgroup reg_test1 TEST1 Register Flags +@{*/ + +/** GOFF to gate path check */ +#define STGAP1AS_REG_TEST1_GOFFCHK (1 << 4) + +/** GON to gate path check */ +#define STGAP1AS_REG_TEST1_GONCHK (1 << 3) + +/** DESAT comparator check */ +#define STGAP1AS_REG_TEST1_DESCHK (1 << 2) + +/** SENSE comparator check */ +#define STGAP1AS_REG_TEST1_SNSCHK (1 << 1) + +/** SENSE resistor check */ +#define STGAP1AS_REG_TEST1_RCHK (1 << 0) + +/**@}*/ + +/** @defgroup reg_diag1 DIAG1CFG Register Flags +@{*/ + +/** DIAG1 on SPI communication error */ +#define STGAP1AS_REG_DIAG1CFG_SPI_REG_ERR (1 << 7) + +/** DIAG1 on VDD power supply failure */ +#define STGAP1AS_REG_DIAG1CFG_UVLOD_OVLOD (1 << 6) + +/** DIAG1 on Undervoltage failure */ +#define STGAP1AS_REG_DIAG1CFG_UVLOH_UVLOL (1 << 5) + +/** DIAG1 on Overvoltage failure */ +#define STGAP1AS_REG_DIAG1CFG_OVLOH_OVLOL (1 << 4) + +/** DIAG1 on Desaturation and sense detection */ +#define STGAP1AS_REG_DIAG1CFG_DESAT_SENSE (1 << 3) + +/** DIAG1 on ASC feedback */ +#define STGAP1AS_REG_DIAG1CFG_ASC_DT_ERR (1 << 2) + +/** DIAG1 on Thermal shutdown protection flag */ +#define STGAP1AS_REG_DIAG1CFG_TSD (1 << 1) + +/** DIAG1 on Thermal warning flag */ +#define STGAP1AS_REG_DIAG1CFG_TWN (1 << 0) + +/**@}*/ + +/** @defgroup reg_diag2 DIAG2CFG Register Flags +@{*/ + +/** DIAG2 on SPI communication error */ +#define STGAP1AS_REG_DIAG2CFG_SPI_REG_ERR (1 << 7) + +/** DIAG2 on VDD power supply failure */ +#define STGAP1AS_REG_DIAG2CFG_UVLOD_OVLOD (1 << 6) + +/** DIAG2 on Undervoltage failure */ +#define STGAP1AS_REG_DIAG2CFG_UVLOH_UVLOL (1 << 5) + +/** DIAG2 on Overvoltage failure */ +#define STGAP1AS_REG_DIAG2CFG_OVLOH_OVLOL (1 << 4) + +/** DIAG2 on Desaturation and sense detection */ +#define STGAP1AS_REG_DIAG2CFG_DESAT_SENSE (1 << 3) + +/** DIAG2 on ASC feedback */ +#define STGAP1AS_REG_DIAG2CFG_ASC_DT_ERR (1 << 2) + +/** DIAG2 on Thermal shutdown protection flag */ +#define STGAP1AS_REG_DIAG2CFG_TSD (1 << 1) + +/** DIAG2 on Thermal warning flag */ +#define STGAP1AS_REG_DIAG2CFG_TWN (1 << 0) + +/**@}*/ + +#endif /* HW_STGAP1AS_GATE_DRIVER_H */ diff --git a/include/hwdefs.h b/include/hwdefs.h index d1f456d..83f7e32 100644 --- a/include/hwdefs.h +++ b/include/hwdefs.h @@ -45,7 +45,7 @@ typedef enum { - HW_REV1, HW_REV2, HW_REV3, HW_TESLA, HW_BLUEPILL, HW_PRIUS, HW_MINI, HW_LEAF2, HW_LEAF3, HW_BMWI3, HW_MG + HW_REV1, HW_REV2, HW_REV3, HW_TESLA, HW_BLUEPILL, HW_PRIUS, HW_MINI, HW_LEAF2, HW_LEAF3, HW_BMWI3, HW_MG, HW_TESLAM3 } HWREV; extern HWREV hwRev; diff --git a/include/hwinit.h b/include/hwinit.h index 11b5692..12d50ff 100644 --- a/include/hwinit.h +++ b/include/hwinit.h @@ -35,6 +35,7 @@ void rtc_setup(void); void tim_setup(void); void tim5_setup(void); void spi_setup(void); +void spi_setup_teslam3(void); HWREV detect_hw(void); void write_bootloader_pininit(bool bootprec, bool pwmActiveLow); HWREV io_setup(); diff --git a/include/teslagatedriver.h b/include/teslagatedriver.h new file mode 100644 index 0000000..eed7099 --- /dev/null +++ b/include/teslagatedriver.h @@ -0,0 +1,436 @@ +/* + * This file is part of the stm32-sine project. + * + * Copyright (C) 2021 David J. Fiddes + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef TESLAGATEDRIVER_H +#define TESLAGATEDRIVER_H + +#include "hw/stgap1as_gate_driver.h" +#include +#include +#include + +#ifndef DEVICE_DELAY_US +#error \ + "Please define DEVICE_DELAY_US(x) as a macro which will delay the specified number of micro-seconds" +#endif + +namespace tesla { + +// +//! \brief Control the STGAP1AS gate driver chain on the Tesla M3 inverter +//! +//! \param SpiDriverT SPI interface driver for the gate driver +//! +template +class GateDriver +{ +public: + static bool Init(); + static bool IsFaulty(); + static void Enable(); + static void Disable(); + +private: + enum ChipMask + { + All = 0x3F, + Odd = 0x15, + Even = 0x2A + }; + struct Register + { + uint16_t reg; + uint16_t value; + ChipMask mask; + uint16_t validBitMask; + }; + +private: + static const uint16_t NumDriverChips = 6; + static const Register GateDriverRegisterSetup[]; + static const uint16_t RegisterSetupSize; + static const Register NullGateDriverRegister; + +private: + typedef uint16_t DataBuffer[NumDriverChips]; + +private: + static void SetupGateDrivers(); + static bool VerifyGateDriverConfig(); + + static void SendCommand(uint16_t cmd); + static void WriteRegister(const Register& reg); + static void ReadRegister(uint16_t regNum, uint16_t* values); + static bool VerifyRegister( + uint16_t regNum, + uint16_t validBits, + uint16_t value); + + static uint16_t InvertByte(uint16_t input); + static uint16_t BuildCommand(uint16_t cmd); +}; + +// +//! \brief STGAP1AS gate driver register set up sequence +//! +//! The register set up sequence for each of 6 chips on the Tesla Model 3 +//! Inverter. Settings are applied to all chips or high/low-side drivers as +//! required +//! +template +const typename GateDriver::Register + GateDriver::GateDriverRegisterSetup[] = { + { STGAP1AS_REG_CFG1, + STGAP1AS_REG_CFG1_CRC_SPI | STGAP1AS_REG_CFG1_SD_FLAG | + STGAP1AS_REG_CFG1_DT_800NS | STGAP1AS_REG_CFG1_IN_FILTER_500NS, + All, + STGAP1AS_REG_CFG1_MASK }, + { STGAP1AS_REG_CFG2, + STGAP1AS_REG_CFG2_DESAT_CUR_500UA | STGAP1AS_REG_CFG2_DESAT_TH_8V, + All, + STGAP1AS_REG_CFG2_MASK }, + { STGAP1AS_REG_CFG3, + STGAP1AS_REG_CFG3_2LTO_TH_10V | STGAP1AS_REG_CFG3_2LTO_TIME_DISABLED, + All, + STGAP1AS_REG_CFG3_MASK }, + { STGAP1AS_REG_CFG4, + STGAP1AS_REG_CFG4_UVLO_LATCHED | STGAP1AS_REG_CFG4_VLON_TH_NEG_3V | + STGAP1AS_REG_CFG4_VHON_TH_12V, + Odd, + STGAP1AS_REG_CFG4_MASK }, + { STGAP1AS_REG_CFG4, + STGAP1AS_REG_CFG4_UVLO_LATCHED | STGAP1AS_REG_CFG4_VLON_TH_DISABLED | + STGAP1AS_REG_CFG4_VHON_TH_12V, + Even, + STGAP1AS_REG_CFG4_MASK }, + { STGAP1AS_REG_CFG5, + STGAP1AS_REG_CFG5_2LTO_EN | STGAP1AS_REG_CFG5_DESAT_EN, + All, + STGAP1AS_REG_CFG5_MASK }, + { STGAP1AS_REG_DIAG1CFG, + STGAP1AS_REG_DIAG1CFG_UVLOD_OVLOD | + STGAP1AS_REG_DIAG1CFG_UVLOH_UVLOL | + STGAP1AS_REG_DIAG1CFG_OVLOH_OVLOL | + STGAP1AS_REG_DIAG1CFG_DESAT_SENSE | STGAP1AS_REG_DIAG1CFG_TSD, + All, + STGAP1AS_REG_DIAG1CFG_MASK }, + { STGAP1AS_REG_DIAG2CFG, 0, All, STGAP1AS_REG_DIAG2CFG } + }; + +template +const uint16_t GateDriver::RegisterSetupSize = + sizeof(GateDriver::GateDriverRegisterSetup) / + sizeof(GateDriverRegisterSetup[0]); + +// Delays from STGAP1AS datasheet Table 6. DC operation electrical +// characteristics - SPI Section +static const __attribute__((__unused__)) int ResetStatusDelay = 50; // uSec +static const __attribute__((__unused__)) int LocalRegReadDelay = 1; // uSec +static const __attribute__((__unused__)) int RemoteRegReadDelay = 30; // uSec +static const __attribute__((__unused__)) int StartConfigDelay = 22; // uSec +static const __attribute__((__unused__)) int StopConfigDelay = 5; // uSec +static const __attribute__((__unused__)) int OtherCommandDelay = 1; // uSec + +// +//! \brief Set up the isolated gate drivers +//! +//! \return bool - True if gate drivers successfully initialised and verified +//! +template +bool GateDriver::Init() +{ + SpiDriverT::Init(); + SetupGateDrivers(); + if (VerifyGateDriverConfig()) + { + return !IsFaulty(); + } + else + { + return false; + } +} + +// +//! \brief Check for a fault on all gate drivers +//! +//! \return bool - There is a fault on one or more gate drivers +//! +template +bool GateDriver::IsFaulty() +{ + bool status1 = + VerifyRegister(STGAP1AS_REG_STATUS1, STGAP1AS_REG_STATUS1_MASK, 0); + DEVICE_DELAY_US(LocalRegReadDelay); + bool status2 = + VerifyRegister(STGAP1AS_REG_STATUS2, STGAP1AS_REG_STATUS2_MASK, 0); + DEVICE_DELAY_US(LocalRegReadDelay); + bool status3 = + VerifyRegister(STGAP1AS_REG_STATUS3, STGAP1AS_REG_STATUS3_MASK, 0); + + return !(status1 && status2 && status3); +} + +// +//! \brief Enable the gate drivers +//! +template +void GateDriver::Enable() +{ + // De-assert the gate driver shutdown line + SpiDriverT::Resume(); +} + +// +//! \brief Disable the gate drivers +//! +template +void GateDriver::Disable() +{ + // Assert the gate driver shutdown line + SpiDriverT::Shutdown(); +} + +// +//! \brief Run through the set up sequence for all gate driver chips +//! +template +void GateDriver::SetupGateDrivers() +{ + SendCommand(STGAP1AS_CMD_RESET_STATUS); + DEVICE_DELAY_US(ResetStatusDelay); + + SendCommand(STGAP1AS_CMD_START_CONFIG); + DEVICE_DELAY_US(StartConfigDelay); + + for (uint16_t i = 0; i < RegisterSetupSize; i++) + { + WriteRegister(GateDriverRegisterSetup[i]); + DEVICE_DELAY_US(OtherCommandDelay); + } + + SendCommand(STGAP1AS_CMD_STOP_CONFIG); + DEVICE_DELAY_US(StopConfigDelay); +} + +// +//! \brief Verify the configuration has been correctly set up +//! +//! \return bool - true if the configuration has been verified +//! +template +bool GateDriver::VerifyGateDriverConfig() +{ + uint16_t regValues[NumDriverChips]; + bool result = true; + for (uint16_t i = 0; i < RegisterSetupSize; i++) + { + const Register& reg = GateDriverRegisterSetup[i]; + ReadRegister(reg.reg, regValues); + DEVICE_DELAY_US(RemoteRegReadDelay); + + uint16_t mask = 1; + for (int chip = 0; chip < NumDriverChips; chip++) + { + if (reg.mask & mask) + { + // Validate the CRC of the returned value. The "don't care" + // bits in some registers can vary from read to read and are + // included in the CRC. We can't precompute the CRC once and + // compare. + uint16_t value = regValues[chip] >> 8; + uint16_t crc = regValues[chip] & 0xFF; + + uint16_t computedCrc = crc8(value, STGAP1AS_SPI_CRC_INIT_VALUE); + + // Mask off the "don't care" bits from the value before + // comparing + value = value & reg.validBitMask; + + result = result && (crc == computedCrc) && (value == reg.value); + } + mask = mask << 1; + } + } + + return result; +} + +// +//! \brief Send a command to all driver chips +//! +//! \param cmd STGAP1AS_CMD_xx command to send +//! +template +void GateDriver::SendCommand(uint16_t cmd) +{ + DataBuffer cmdBuffer; + for (uint16_t i = 0; i < NumDriverChips; i++) + { + cmdBuffer[i] = BuildCommand(cmd); + } + + SpiDriverT::SendData(cmdBuffer, NULL); +} + +// +//! \brief Write a specific register +//! +//! \param reg Register structure detailing the register, value and required +//! chips +//! +template +void GateDriver::WriteRegister(const Register& reg) +{ + uint16_t cmd = STGAP1AS_CMD_WRITE_REG(reg.reg); + uint16_t cmdCrc = crc8(cmd, STGAP1AS_SPI_CRC_INIT_VALUE); + uint16_t dataCrc = crc8(reg.value, cmdCrc); + + // Invert the CRC as required by the STGAP1AS SPI protocol + cmdCrc = InvertByte(cmdCrc); + dataCrc = InvertByte(dataCrc); + + cmd = cmd << 8 | cmdCrc; + + const uint16_t nop = BuildCommand(STGAP1AS_CMD_NOP); + + // Assemble a command buffer with all the commands or nops required for + // each chip + DataBuffer cmdBuffer; + uint16_t mask = 1; + for (int chip = 0; chip < NumDriverChips; chip++) + { + cmdBuffer[chip] = reg.mask & mask ? cmd : nop; + mask = mask << 1; + } + + // Send the register write command ignoring any response (which is + // undefined) + SpiDriverT::SendData(cmdBuffer, NULL); + + DEVICE_DELAY_US(OtherCommandDelay); + + // Send the register data to be written ignoring any response (which is + // undefined) + DataBuffer cmdDataBuffer; + + uint16_t data = reg.value << 8 | dataCrc; + mask = 1; + for (int chip = 0; chip < NumDriverChips; chip++) + { + cmdDataBuffer[chip] = reg.mask & mask ? data : nop; + mask = mask << 1; + } + + SpiDriverT::SendData(cmdDataBuffer, NULL); +} + +// +//! \brief Read a specific register +//! +//! \param regNum Register number to read +//! \param values Register values array retrieved from all chips +//! +template +void GateDriver::ReadRegister(uint16_t regNum, uint16_t* values) +{ + // Send the register read command ignoring any response (which is + // undefined) + DataBuffer cmdBuffer; + + for (uint16_t i = 0; i < NumDriverChips; i++) + { + cmdBuffer[i] = BuildCommand(STGAP1AS_CMD_READ_REG(regNum)); + } + + SpiDriverT::SendData(cmdBuffer, NULL); + + // Pessimistic for local reg reads but we'll assume that's not + // performance critical + DEVICE_DELAY_US(RemoteRegReadDelay); + + // Send a NOP while reading the data back from the register + DataBuffer nopBuffer; + + for (uint16_t i = 0; i < NumDriverChips; i++) + { + nopBuffer[i] = BuildCommand(STGAP1AS_CMD_NOP); + } + + SpiDriverT::SendData(nopBuffer, values); +} + +// +//! \brief Verify the status of a given register +//! +//! \param regNum Register number to read +//! \param validBits Which bits in the register value do we care about +//! \param value Desired register value +//! \return True if the status matches the expected value +//! +template +bool GateDriver::VerifyRegister( + uint16_t regNum, + uint16_t validBits, + uint16_t value) +{ + DataBuffer values; + ReadRegister(regNum, values); + + bool result = true; + for (int chip = 0; chip < NumDriverChips; chip++) + { + uint16_t actualValue = values[chip] >> 8; + uint16_t actualCrc = values[chip] & 0xFF; + + uint16_t computedCrc = crc8(actualValue, STGAP1AS_SPI_CRC_INIT_VALUE); + + // Mask off the "don't care" bits from the value before comparing + actualValue = actualValue & validBits; + + result = result && (computedCrc == actualCrc) && (actualValue == value); + } + + return result; +} + +// +//! \brief Invert and mask off the lower 8 bits only +//! +//! \return The inverted value +//! +template +uint16_t GateDriver::InvertByte(uint16_t input) +{ + return ~input & 0xFF; +} + +// +//! \brief Build a command with it's CRC +//! +//! \return The complete command +//! +template +uint16_t GateDriver::BuildCommand(uint16_t cmd) +{ + return cmd << 8 | InvertByte(crc8(STGAP1AS_SPI_CRC_INIT_VALUE, cmd)); +} + +} // namespace tesla + +#endif // TESLAGATEDRIVER_H diff --git a/include/teslamodel3.h b/include/teslamodel3.h new file mode 100644 index 0000000..57ccb8d --- /dev/null +++ b/include/teslamodel3.h @@ -0,0 +1,34 @@ +/* + * This file is part of the stm32-sine project. + * + * Copyright (C) 2025 David J. Fiddes + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef TESLAMODEL3_H +#define TESLAMODEL3_H + +/** + * \brief Encapsulate all of the Tesla Model 3 Inverter board specific + * functionality + */ +class TeslaModel3 +{ +public: + static void Initialize(); + static void CyclicFunction(); +}; + + +#endif // TESLAMODEL3_H diff --git a/sinus.cbp b/sinus.cbp index 7954fb0..831252e 100644 --- a/sinus.cbp +++ b/sinus.cbp @@ -76,12 +76,15 @@ + + + @@ -1740,6 +1743,7 @@ + diff --git a/src/hwinit.cpp b/src/hwinit.cpp index dde404b..4396cdd 100644 --- a/src/hwinit.cpp +++ b/src/hwinit.cpp @@ -87,6 +87,36 @@ void spi_setup() spi_enable(SPI1); } +void spi_setup_teslam3() +{ + gpio_primary_remap(AFIO_MAPR_SWJ_CFG_JTAG_OFF_SW_ON, AFIO_MAPR_SPI1_REMAP); + + // The STGAP1AS requires CPOL = 0, CPHA = 1 and 16-bit MSB transfers + spi_reset(SPI1); + spi_init_master( + SPI1, + SPI_CR1_BAUDRATE_FPCLK_DIV_8, + SPI_CR1_CPOL_CLK_TO_0_WHEN_IDLE, + SPI_CR1_CPHA_CLK_TRANSITION_2, + SPI_CR1_DFF_16BIT, + SPI_CR1_MSBFIRST); + spi_set_full_duplex_mode(SPI1); + + // Enable software slave management so we have correct behaviour for the 6 + // daisy-chained gate driver chips + spi_enable_software_slave_management(SPI1); + spi_set_nss_high(SPI1); + spi_set_unidirectional_mode(SPI1); + + gpio_set_mode( + GPIOB, + GPIO_MODE_OUTPUT_50_MHZ, + GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, + GPIO5 | GPIO3); + gpio_set_mode(GPIOB, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO4); + spi_enable(SPI1); +} + static bool is_floating(uint32_t port, uint16_t pin) { //A pin is considered floating if its state can be controlled with the internal 30k pull-up/down resistor @@ -142,6 +172,7 @@ static HWREV ReadVariantResistor() if ((result1 + result2) < 30) return HW_BMWI3; //connected to MISO so always low else if (result2 > 3700) return HW_MINI; //might have to compare this against result1 later else if (result1 > 327 && result1 < 347) return HW_MG; + else if (result1 > 395 && result1 < 419) return HW_TESLAM3; else if (result1 > 510 && result1 < 630) return HW_LEAF3; else return HW_MINI; } @@ -208,6 +239,9 @@ HWREV io_setup() case HW_MG: DIG_IO_CONFIGURE(DIG_IO_LIST_MG); break; + case HW_TESLAM3: + DIG_IO_CONFIGURE(DIG_IO_LIST_TESLAM3); + break; case HW_BLUEPILL: ANA_IN_CONFIGURE(ANA_IN_LIST_BLUEPILL); DIG_IO_CONFIGURE(DIG_IO_BLUEPILL); diff --git a/src/stm32_sine.cpp b/src/stm32_sine.cpp index 5826b4e..625f092 100644 --- a/src/stm32_sine.cpp +++ b/src/stm32_sine.cpp @@ -48,6 +48,7 @@ #include "cansdo.h" #include "GD31xxOI.h" #include "delay.h" +#include "teslamodel3.h" #define PRINT_JSON 0 @@ -102,6 +103,11 @@ static void Ms100Task(void) if (Param::GetInt(Param::canperiod) == CAN_PERIOD_100MS) canMap->SendAll(); + + if (hwRev == HW_TESLAM3) + { + TeslaModel3::CyclicFunction(); + } } static void RunCharger(float udc) @@ -414,6 +420,10 @@ extern "C" int main(void) MotorVoltage::SetMaxAmp(SineCore::MAXAMP); PwmGeneration::SetCurrentOffset(2048, 2048); + if (hwRev == HW_TESLAM3) + { + TeslaModel3::Initialize(); + } Stm32Scheduler s(hwRev == HW_BLUEPILL ? TIM4 : TIM2); //We never exit main so it's ok to put it on stack scheduler = &s; diff --git a/src/teslamodel3.cpp b/src/teslamodel3.cpp new file mode 100644 index 0000000..c0db519 --- /dev/null +++ b/src/teslamodel3.cpp @@ -0,0 +1,156 @@ +/* + * This file is part of the stm32-sine project. + * + * Copyright (C) 2025 David J. Fiddes + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "teslamodel3.h" +#include "digio.h" +#include "hwinit.h" +#include "errormessage.h" +#include + +// Define the delay macro needed by the tesla::GateDriver header-only class +#include "delay.h" +#define DEVICE_DELAY_US(x) uDelay(x) + +#include "teslagatedriver.h" + +/** + * \brief Low level hardware interface to the STGAP1AS gate driver chain in the + * Tesla M3 inverter + */ +class GateDriverInterface +{ +public: + static const uint16_t NumDriverChips = 6; + + typedef uint16_t DataBuffer[NumDriverChips]; + +public: + static void Init(); + static void SendData(DataBuffer writeData, DataBuffer readData); + static void Shutdown(); + static void Resume(); + static bool IsShutdown(); +}; + +/** + * \brief Assemble a Gate Driver using the STM32 specific SPI driver and the + * generic Tesla Gate Driver class + */ +typedef tesla::GateDriver TeslaM3GateDriver; + +/** + * \brief Set up the SPI bus connected to the STGAP1AS gate drivers + */ +void GateDriverInterface::Init() +{ + // Start with the ~SD line asserted (i.e. in Shutdown state) + DigIo::gate_sd_hi.Clear(); + + // De-assert the SPI ~CS line before configuration to avoid glitches + DigIo::gate_cs_hi.Set(); + spi_setup_teslam3(); +} + +/** + * \brief Initiate an SPI transaction with the gate drivers + * + * Encapsulates all of the hardware dependent work required to communicate with + * an array of daisy-chained STGAP1AS gate driver chips. Inter-message timing + * delays are not included. + * + * \param writeData Data to be sent to the STGAP1AS driver chips + * \param readData Data buffer for data read from the chips. May be NULL if the + * received data is not required + */ +void GateDriverInterface::SendData(DataBuffer writeData, DataBuffer readData) +{ + // Manually assert the ~CS pin and add a delay to allow it to settle and + // match the required set-up time for the STGAP1AS + DigIo::gate_cs_hi.Clear(); + uDelay(1); + + // Run the SPI transaction + for (uint16_t i = 0; i < NumDriverChips; i++) + { + uint16_t result = spi_xfer(SPI1, writeData[i]); + + if (readData) + { + readData[i] = result; + } + + // Safe but pessimistic delay between words. Tesla use a 2 clock-cycle + // gap on their inverter. + uDelay(1); + } + + // Manually de-assert the ~CS pin and ensure that we have waited sufficient + // time for the data being sent by the chips to arrive + uDelay(1); + DigIo::gate_cs_hi.Set(); +} + +/** + * \brief Assert the ~SD line on the STGAP1AS gate drivers allowing them to be + * configured + */ +void GateDriverInterface::Shutdown() +{ + DigIo::gate_sd_hi.Clear(); +} + +/** + * \brief De-assert the ~SD line on the STGAP1AS gate drivers to allow them to + * run normally + */ +void GateDriverInterface::Resume() +{ + DigIo::gate_sd_hi.Set(); +} + +/** + * \brief Return whether the STGAP1AS gate drivers are enabled + */ +bool GateDriverInterface::IsShutdown() +{ + return DigIo::gate_sd_hi.Get(); +} + +/** + * \brief Set up the the Tesla Model 3 gate drivers + */ +void TeslaModel3::Initialize() +{ + if (!TeslaM3GateDriver::Init()) + { + ErrorMessage::Post(ERR_GATEDRIVEINITFAIL); + } +} + +/** + * \brief Periodically check the gate drivers are happy + */ +void TeslaModel3::CyclicFunction() +{ + if (TeslaM3GateDriver::IsFaulty()) + { + ErrorMessage::Post(ERR_GATEDRIVEFAULT); + //TODO: Shutdown the inverter? + } +}