Add Automatic Precharge Control for modified HIA4V1 boards

This commit is contained in:
mvgalen 2024-12-21 20:33:20 +01:00
parent f138f97905
commit dccfca17ea
6 changed files with 302 additions and 27 deletions

View file

@ -12,6 +12,7 @@
#include "src/communication/contactorcontrol/comm_contactorcontrol.h" #include "src/communication/contactorcontrol/comm_contactorcontrol.h"
#include "src/communication/equipmentstopbutton/comm_equipmentstopbutton.h" #include "src/communication/equipmentstopbutton/comm_equipmentstopbutton.h"
#include "src/communication/nvm/comm_nvm.h" #include "src/communication/nvm/comm_nvm.h"
#include "src/communication/precharge_control/precharge_control.h"
#include "src/communication/rs485/comm_rs485.h" #include "src/communication/rs485/comm_rs485.h"
#include "src/communication/seriallink/comm_seriallink.h" #include "src/communication/seriallink/comm_seriallink.h"
#include "src/datalayer/datalayer.h" #include "src/datalayer/datalayer.h"
@ -115,6 +116,10 @@ void setup() {
init_contactors(); init_contactors();
#ifdef PRECHARGE_CONTROL
init_precharge_control();
#endif // PRECHARGE_CONTROL
init_rs485(); init_rs485();
init_serialDataLink(); init_serialDataLink();
@ -242,6 +247,9 @@ void core_loop(void* task_time_us) {
previousMillis10ms = millis(); previousMillis10ms = millis();
led_exe(); led_exe();
handle_contactors(); // Take care of startup precharge/contactor closing handle_contactors(); // Take care of startup precharge/contactor closing
#ifdef PRECHARGE_CONTROL
handle_precharge_control();
#endif // PRECHARGE_CONTROL
} }
END_TIME_MEASUREMENT_MAX(time_10ms, datalayer.system.status.time_10ms_us); END_TIME_MEASUREMENT_MAX(time_10ms, datalayer.system.status.time_10ms_us);
@ -293,6 +301,30 @@ void core_loop(void* task_time_us) {
if (check_pause_2s.elapsed()) { if (check_pause_2s.elapsed()) {
emulator_pause_state_transmit_can_battery(); emulator_pause_state_transmit_can_battery();
} }
static bms_status_enum previous_state = FAULT;
if (previous_state != datalayer.battery.status.bms_status) {
switch (datalayer.battery.status.bms_status) {
case ACTIVE:
logging.printf("BMS state changed to: OK\n");
break;
case UPDATING:
logging.printf("BMS state changed to: UPDATING\n");
break;
case FAULT:
logging.printf("BMS state changed to: FAULT\n");
break;
case INACTIVE:
logging.printf("BMS state changed to: INACTIVE\n");
break;
case STANDBY:
logging.printf("BMS state changed to: STANDBY\n");
break;
default:
logging.printf("BMS state changed to: ??\n");
break;
}
previous_state = datalayer.battery.status.bms_status;
}
vTaskDelayUntil(&xLastWakeTime, xFrequency); vTaskDelayUntil(&xLastWakeTime, xFrequency);
} }

View file

@ -70,6 +70,9 @@
/* Shunt/Contactor settings */ /* Shunt/Contactor settings */
//#define BMW_SBOX // SBOX relay control & battery current/voltage measurement //#define BMW_SBOX // SBOX relay control & battery current/voltage measurement
/* Automatic Precharge settings. If you have a battery that expects an external voltage applied before opening contactors (within the battery), configure this section */
//#define PRECHARGE_CONTROL //Enable this line to control a modified HIA4V1 (see wiki) by PWM on the PRECHARGE_PIN.
/* Other options */ /* Other options */
//#define LOG_TO_SD //Enable this line to log diagnostic data to SD card //#define LOG_TO_SD //Enable this line to log diagnostic data to SD card
//#define DEBUG_VIA_USB //Enable this line to have the USB port output serial diagnostic data while program runs (WARNING, raises CPU load, do not use for production) //#define DEBUG_VIA_USB //Enable this line to have the USB port output serial diagnostic data while program runs (WARNING, raises CPU load, do not use for production)

View file

@ -6,6 +6,8 @@
#include "../devboard/utils/events.h" #include "../devboard/utils/events.h"
#include "MEB-BATTERY.h" #include "MEB-BATTERY.h"
#define PRECHARGE_CONTROL
/* /*
TODO list TODO list
- Check value mappings on the PID polls - Check value mappings on the PID polls
@ -77,7 +79,7 @@ static bool BMS_fault_performance = false; //Error: Battery performance is limi
static uint16_t BMS_current = 16300; static uint16_t BMS_current = 16300;
static bool BMS_fault_emergency_shutdown_crash = static bool BMS_fault_emergency_shutdown_crash =
false; //Error: Safety-critical error (crash detection) Battery contactors are already opened / will be opened immediately Signal is read directly by the EMS and initiates an AKS of the PWR and an active discharge of the DC link false; //Error: Safety-critical error (crash detection) Battery contactors are already opened / will be opened immediately Signal is read directly by the EMS and initiates an AKS of the PWR and an active discharge of the DC link
static uint32_t BMS_voltage_intermediate = 0; static uint32_t BMS_voltage_intermediate = 2000;
static uint32_t BMS_voltage = 1480; static uint32_t BMS_voltage = 1480;
static uint8_t BMS_status_voltage_free = static uint8_t BMS_status_voltage_free =
0; //0=Init, 1=BMS intermediate circuit voltage-free (U_Zwkr < 20V), 2=BMS intermediate circuit not voltage-free (U_Zwkr >/= 25V, hysteresis), 3=Error 0; //0=Init, 1=BMS intermediate circuit voltage-free (U_Zwkr < 20V), 2=BMS intermediate circuit not voltage-free (U_Zwkr >/= 25V, hysteresis), 3=Error
@ -360,6 +362,23 @@ CAN_frame MEB_14C = {
.ID = 0x14C, //CRC needed, static content otherwise .ID = 0x14C, //CRC needed, static content otherwise
.data = {0x38, 0x0A, 0xFF, 0x01, 0x01, 0xFF, 0x01, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, .data = {0x38, 0x0A, 0xFF, 0x01, 0x01, 0xFF, 0x01, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE,
0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x25, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE}}; 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x25, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE}};
uint32_t can_msg_received = 0;
#define RX_0x17F0007B 0x0001
#define RX_0x12DD54D0 0x0002
#define RX_0x12DD54D1 0x0004
#define RX_0x12DD54D2 0x0008
#define RX_0x1A555550 0x0010
#define RX_0x1A555551 0x0020
#define RX_0x1A5555B2 0x0040
#define RX_0x16A954A6 0x0080
#define RX_0x1A5555B0 0x0100
#define RX_0x1A5555B1 0x0200
#define RX_0x5A2 0x0400
#define RX_0x5CA 0x0800
#define RX_0x0CF 0x1000
#define RX_DEFAULT 0xE000
/** Calculate the CRC checksum for VAG CAN Messages /** Calculate the CRC checksum for VAG CAN Messages
* *
* The method used is described in Chapter "7.2.1.2 8-bit 0x2F polynomial CRC Calculation". * The method used is described in Chapter "7.2.1.2 8-bit 0x2F polynomial CRC Calculation".
@ -624,6 +643,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
first_can_msg = last_can_msg_timestamp; first_can_msg = last_can_msg_timestamp;
switch (rx_frame.ID) { switch (rx_frame.ID) {
case 0x17F0007B: // BMS 500ms case 0x17F0007B: // BMS 500ms
can_msg_received |= RX_0x17F0007B;
component_protection_active = (rx_frame.data.u8[0] & 0x01); component_protection_active = (rx_frame.data.u8[0] & 0x01);
shutdown_active = ((rx_frame.data.u8[0] & 0x02) >> 1); shutdown_active = ((rx_frame.data.u8[0] & 0x02) >> 1);
transportation_mode_active = ((rx_frame.data.u8[0] & 0x02) >> 1); transportation_mode_active = ((rx_frame.data.u8[0] & 0x02) >> 1);
@ -643,7 +663,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
instrumentation_cluster_request = ((rx_frame.data.u8[1] & 0x40) >> 6); //True/false instrumentation_cluster_request = ((rx_frame.data.u8[1] & 0x40) >> 6); //True/false
break; break;
case 0x12DD54D0: // BMS Limits 100ms case 0x12DD54D0: // BMS Limits 100ms
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; can_msg_received |= RX_0x12DD54D0;
max_discharge_power_watt = max_discharge_power_watt =
((rx_frame.data.u8[6] & 0x07) << 10) | (rx_frame.data.u8[5] << 2) | (rx_frame.data.u8[4] & 0xC0) >> 6; //*100 ((rx_frame.data.u8[6] & 0x07) << 10) | (rx_frame.data.u8[5] << 2) | (rx_frame.data.u8[4] & 0xC0) >> 6; //*100
max_discharge_current_amp = max_discharge_current_amp =
@ -652,7 +672,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
max_charge_current_amp = ((rx_frame.data.u8[4] & 0x3F) << 7) | (rx_frame.data.u8[3] >> 1); //*0.2 max_charge_current_amp = ((rx_frame.data.u8[4] & 0x3F) << 7) | (rx_frame.data.u8[3] >> 1); //*0.2
break; break;
case 0x12DD54D1: // BMS 100ms case 0x12DD54D1: // BMS 100ms
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; can_msg_received |= RX_0x12DD54D1;
if (rx_frame.data.u8[6] != 0xFE || rx_frame.data.u8[7] != 0xFF) { // Init state, values below invalid if (rx_frame.data.u8[6] != 0xFE || rx_frame.data.u8[7] != 0xFF) { // Init state, values below invalid
battery_SOC = ((rx_frame.data.u8[3] & 0x0F) << 7) | (rx_frame.data.u8[2] >> 1); //*0.05 battery_SOC = ((rx_frame.data.u8[3] & 0x0F) << 7) | (rx_frame.data.u8[2] >> 1); //*0.05
usable_energy_amount_Wh = (rx_frame.data.u8[7] << 8) | rx_frame.data.u8[6]; //*5 usable_energy_amount_Wh = (rx_frame.data.u8[7] << 8) | rx_frame.data.u8[6]; //*5
@ -663,7 +683,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
warning_support = (rx_frame.data.u8[1] & 0x70) >> 4; warning_support = (rx_frame.data.u8[1] & 0x70) >> 4;
break; break;
case 0x12DD54D2: // BMS 100ms case 0x12DD54D2: // BMS 100ms
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; can_msg_received |= RX_0x12DD54D2;
battery_heating_active = (rx_frame.data.u8[4] & 0x40) >> 6; battery_heating_active = (rx_frame.data.u8[4] & 0x40) >> 6;
heating_request = (rx_frame.data.u8[5] & 0xE0) >> 5; heating_request = (rx_frame.data.u8[5] & 0xE0) >> 5;
cooling_request = (rx_frame.data.u8[5] & 0x1C) >> 2; cooling_request = (rx_frame.data.u8[5] & 0x1C) >> 2;
@ -671,6 +691,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
power_battery_heating_req_watt = rx_frame.data.u8[7]; power_battery_heating_req_watt = rx_frame.data.u8[7];
break; break;
case 0x1A555550: // BMS 500ms case 0x1A555550: // BMS 500ms
can_msg_received |= RX_0x1A555550;
balancing_active = (rx_frame.data.u8[1] & 0xC0) >> 6; balancing_active = (rx_frame.data.u8[1] & 0xC0) >> 6;
charging_active = (rx_frame.data.u8[2] & 0x01); charging_active = (rx_frame.data.u8[2] & 0x01);
max_energy_Wh = ((rx_frame.data.u8[6] & 0x1F) << 8) | rx_frame.data.u8[5]; //*40 max_energy_Wh = ((rx_frame.data.u8[6] & 0x1F) << 8) | rx_frame.data.u8[5]; //*40
@ -679,6 +700,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
isolation_resistance_kOhm = (((rx_frame.data.u8[3] & 0x1F) << 7) | rx_frame.data.u8[2] >> 1); //*5 isolation_resistance_kOhm = (((rx_frame.data.u8[3] & 0x1F) << 7) | rx_frame.data.u8[2] >> 1); //*5
break; break;
case 0x1A555551: // BMS 500ms case 0x1A555551: // BMS 500ms
can_msg_received |= RX_0x1A555551;
battery_heating_installed = (rx_frame.data.u8[1] & 0x20) >> 5; battery_heating_installed = (rx_frame.data.u8[1] & 0x20) >> 5;
error_NT_circuit = (rx_frame.data.u8[1] & 0x40) >> 6; error_NT_circuit = (rx_frame.data.u8[1] & 0x40) >> 6;
pump_1_control = rx_frame.data.u8[2] & 0x0F; //*10, percent pump_1_control = rx_frame.data.u8[2] & 0x0F; //*10, percent
@ -691,6 +713,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
return_temperature_C = rx_frame.data.u8[7]; //*0,5 -40 return_temperature_C = rx_frame.data.u8[7]; //*0,5 -40
break; break;
case 0x1A5555B2: // BMS case 0x1A5555B2: // BMS
can_msg_received |= RX_0x1A5555B2;
performance_index_discharge_peak_temperature_percentage = performance_index_discharge_peak_temperature_percentage =
(((rx_frame.data.u8[3] & 0x07) << 6) | rx_frame.data.u8[2] >> 2); //*0.2 (((rx_frame.data.u8[3] & 0x07) << 6) | rx_frame.data.u8[2] >> 2); //*0.2
performance_index_charge_peak_temperature_percentage = performance_index_charge_peak_temperature_percentage =
@ -699,6 +722,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
temperature_status_charge = (((rx_frame.data.u8[2] & 0x03) << 1) | rx_frame.data.u8[1] >> 7); temperature_status_charge = (((rx_frame.data.u8[2] & 0x03) << 1) | rx_frame.data.u8[1] >> 7);
break; break;
case 0x16A954A6: // BMS case 0x16A954A6: // BMS
can_msg_received |= RX_0x16A954A6;
BMS_16A954A6_CRC = rx_frame.data.u8[0]; // Can be used to check CAN signal integrity later on BMS_16A954A6_CRC = rx_frame.data.u8[0]; // Can be used to check CAN signal integrity later on
BMS_16A954A6_counter = (rx_frame.data.u8[1] & 0x0F); // Can be used to check CAN signal integrity later on BMS_16A954A6_counter = (rx_frame.data.u8[1] & 0x0F); // Can be used to check CAN signal integrity later on
isolation_fault = (rx_frame.data.u8[2] & 0xE0) >> 5; isolation_fault = (rx_frame.data.u8[2] & 0xE0) >> 5;
@ -897,12 +921,14 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
//hybrid_01_response_fd_data (Whole frame) //hybrid_01_response_fd_data (Whole frame)
break; break;
case 0x1A5555B0: // BMS 1000ms cyclic case 0x1A5555B0: // BMS 1000ms cyclic
can_msg_received |= RX_0x1A5555B0;
duration_discharge_power_watt = ((rx_frame.data.u8[6] & 0x0F) << 8) | rx_frame.data.u8[5]; duration_discharge_power_watt = ((rx_frame.data.u8[6] & 0x0F) << 8) | rx_frame.data.u8[5];
duration_charge_power_watt = (rx_frame.data.u8[7] << 4) | rx_frame.data.u8[6] >> 4; duration_charge_power_watt = (rx_frame.data.u8[7] << 4) | rx_frame.data.u8[6] >> 4;
maximum_voltage = ((rx_frame.data.u8[3] & 0x3F) << 4) | rx_frame.data.u8[2] >> 4; maximum_voltage = ((rx_frame.data.u8[3] & 0x3F) << 4) | rx_frame.data.u8[2] >> 4;
minimum_voltage = (rx_frame.data.u8[4] << 2) | rx_frame.data.u8[3] >> 6; minimum_voltage = (rx_frame.data.u8[4] << 2) | rx_frame.data.u8[3] >> 6;
break; break;
case 0x1A5555B1: // BMS 1000ms cyclic case 0x1A5555B1: // BMS 1000ms cyclic
can_msg_received |= RX_0x1A5555B1;
// All realtime_ have same enumeration, 0 = no fault, 1 = error level 1, 2 error level 2, 3 error level 3 // All realtime_ have same enumeration, 0 = no fault, 1 = error level 1, 2 error level 2, 3 error level 3
realtime_overcurrent_monitor = ((rx_frame.data.u8[3] & 0x01) << 2) | rx_frame.data.u8[2] >> 6; realtime_overcurrent_monitor = ((rx_frame.data.u8[3] & 0x01) << 2) | rx_frame.data.u8[2] >> 6;
realtime_CAN_communication_fault = (rx_frame.data.u8[3] & 0x0E) >> 1; realtime_CAN_communication_fault = (rx_frame.data.u8[3] & 0x0E) >> 1;
@ -938,6 +964,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
DC_voltage_chargeport = (rx_frame.data.u8[7] << 4) | (rx_frame.data.u8[6] >> 4); DC_voltage_chargeport = (rx_frame.data.u8[7] << 4) | (rx_frame.data.u8[6] >> 4);
break; break;
case 0x5A2: // BMS 500ms normal, 100ms fast case 0x5A2: // BMS 500ms normal, 100ms fast
can_msg_received |= RX_0x5A2;
BMS_5A2_CRC = rx_frame.data.u8[0]; // Can be used to check CAN signal integrity later on BMS_5A2_CRC = rx_frame.data.u8[0]; // Can be used to check CAN signal integrity later on
BMS_5A2_counter = (rx_frame.data.u8[1] & 0x0F); // Can be used to check CAN signal integrity later on BMS_5A2_counter = (rx_frame.data.u8[1] & 0x0F); // Can be used to check CAN signal integrity later on
service_disconnect_switch_missing = (rx_frame.data.u8[1] & 0x20) >> 5; service_disconnect_switch_missing = (rx_frame.data.u8[1] & 0x20) >> 5;
@ -953,6 +980,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
} }
break; break;
case 0x5CA: // BMS 500ms case 0x5CA: // BMS 500ms
can_msg_received |= RX_0x5CA;
BMS_5CA_CRC = rx_frame.data.u8[0]; // Can be used to check CAN signal integrity later on BMS_5CA_CRC = rx_frame.data.u8[0]; // Can be used to check CAN signal integrity later on
BMS_5CA_counter = (rx_frame.data.u8[1] & 0x0F); // Can be used to check CAN signal integrity later on BMS_5CA_counter = (rx_frame.data.u8[1] & 0x0F); // Can be used to check CAN signal integrity later on
balancing_request = (rx_frame.data.u8[5] & 0x08) >> 3; //True/False balancing_request = (rx_frame.data.u8[5] & 0x08) >> 3; //True/False
@ -967,18 +995,42 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
((rx_frame.data.u8[5] & 0x07) << 8) | rx_frame.data.u8[4]; //*50 ! Not usable, seems to always contain 0x7F0 ((rx_frame.data.u8[5] & 0x07) << 8) | rx_frame.data.u8[4]; //*50 ! Not usable, seems to always contain 0x7F0
break; break;
case 0x0CF: //BMS 10ms case 0x0CF: //BMS 10ms
can_msg_received |= RX_0x0CF;
BMS_0CF_CRC = rx_frame.data.u8[0]; // Can be used to check CAN signal integrity later on BMS_0CF_CRC = rx_frame.data.u8[0]; // Can be used to check CAN signal integrity later on
BMS_0CF_counter = (rx_frame.data.u8[1] & 0x0F); // Can be used to check CAN signal integrity later on BMS_0CF_counter = (rx_frame.data.u8[1] & 0x0F); // Can be used to check CAN signal integrity later on
BMS_welded_contactors_status = (rx_frame.data.u8[1] & 0x60) >> 5; BMS_welded_contactors_status = (rx_frame.data.u8[1] & 0x60) >> 5;
BMS_ext_limits_active = (rx_frame.data.u8[1] & 0x80) >> 7; BMS_ext_limits_active = (rx_frame.data.u8[1] & 0x80) >> 7;
BMS_mode = (rx_frame.data.u8[2] & 0x07); BMS_mode = (rx_frame.data.u8[2] & 0x07);
switch (BMS_mode) { switch (BMS_mode) {
case 1: case 1: // HV_ACTIVE
case 3: case 3: // EXTERN CHARGING
case 4: case 4: // AC_CHARGING
case 6: // DC_CHARGING
if (!datalayer.system.status.battery_allows_contactor_closing)
logging.printf("MEB Contactors closed\n");
if (datalayer.battery.status.bms_status != FAULT)
datalayer.battery.status.bms_status = ACTIVE;
datalayer.system.status.battery_allows_contactor_closing = true; datalayer.system.status.battery_allows_contactor_closing = true;
break; break;
case 5: // Error
if (datalayer.system.status.battery_allows_contactor_closing)
logging.printf("MEB Contactors opened\n");
datalayer.battery.status.bms_status = FAULT;
datalayer.system.status.battery_allows_contactor_closing = false;
break;
case 7: // Init
if (datalayer.system.status.battery_allows_contactor_closing)
logging.printf("MEB Contactors opened\n");
if (datalayer.battery.status.bms_status != FAULT)
datalayer.battery.status.bms_status = INACTIVE;
datalayer.system.status.battery_allows_contactor_closing = false;
break;
case 2: // BALANCING
default: default:
if (datalayer.system.status.battery_allows_contactor_closing)
logging.printf("MEB Contactors opened\n");
if (datalayer.battery.status.bms_status != FAULT)
datalayer.battery.status.bms_status = STANDBY;
datalayer.system.status.battery_allows_contactor_closing = false; datalayer.system.status.battery_allows_contactor_closing = false;
} }
BMS_HVIL_status = (rx_frame.data.u8[2] & 0x18) >> 3; BMS_HVIL_status = (rx_frame.data.u8[2] & 0x18) >> 3;
@ -1451,13 +1503,21 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
default: default:
break; break;
} }
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
if (can_msg_received == 0xFFFF) {
if (datalayer.battery.status.bms_status == INACTIVE)
datalayer.battery.status.bms_status = STANDBY;
}
} }
void transmit_can_battery() { void transmit_can_battery() {
unsigned long currentMillis = millis(); unsigned long currentMillis = millis();
// Send 10ms CAN Message // Send 10ms CAN Message
if (currentMillis > last_can_msg_timestamp + 500) { if (datalayer.system.settings.equipment_stop_active || currentMillis > last_can_msg_timestamp + 500) {
can_msg_received = RX_DEFAULT;
first_can_msg = 0; first_can_msg = 0;
if (datalayer.battery.status.bms_status != FAULT)
datalayer.battery.status.bms_status = INACTIVE;
} }
if (currentMillis - previousMillis10ms >= INTERVAL_10_MS) { if (currentMillis - previousMillis10ms >= INTERVAL_10_MS) {
@ -1525,18 +1585,27 @@ void transmit_can_battery() {
previousMillis100ms = currentMillis; previousMillis100ms = currentMillis;
//HV request and DC/DC control lies in 0x503 //HV request and DC/DC control lies in 0x503
MEB_503.data.u8[3] = 0x00;
if (datalayer.battery.status.bms_status != FAULT && first_can_msg > 0 && currentMillis > first_can_msg + 2000) { if (datalayer.battery.status.bms_status != FAULT && /*first_can_msg > 0 && currentMillis > first_can_msg + 2000*/
(datalayer.battery.status.bms_status == STANDBY || datalayer.battery.status.bms_status == ACTIVE) &&
(labs(((int32_t)datalayer.battery.status.voltage_dV) -
((int32_t)datalayer_extended.meb.BMS_voltage_intermediate_dV)) < 200)) {
if (MEB_503.data.u8[3] == BMS_TARGET_HV_OFF) {
logging.printf("MEB Requesting HV\n");
}
MEB_503.data.u8[1] = 0xB0; MEB_503.data.u8[1] = 0xB0;
MEB_503.data.u8[3] = BMS_TARGET_HV_ON; //BMS_TARGET_AC_CHARGING; //TODO, should we try AC_2 or DC charging? MEB_503.data.u8[3] = BMS_TARGET_AC_CHARGING; //TODO, should we try AC_2 or DC charging?
MEB_503.data.u8[5] = 0x82; // Bordnetz Active MEB_503.data.u8[5] = 0x82; // Bordnetz Active
MEB_503.data.u8[6] = 0xE0; // Request emergency shutdown HV system == 0, false MEB_503.data.u8[6] = 0xE0; // Request emergency shutdown HV system == 0, false
} else if (first_can_msg > 0 && currentMillis > first_can_msg + 2000) { //FAULT STATE, open contactors } else if (first_can_msg > 0 && currentMillis > first_can_msg + 2000 && BMS_mode != 0 &&
BMS_mode != 7) { //FAULT STATE, open contactors
MEB_503.data.u8[1] = 0x90; MEB_503.data.u8[1] = 0x90;
MEB_503.data.u8[3] = BMS_TARGET_HV_OFF; MEB_503.data.u8[3] = BMS_TARGET_HV_OFF;
MEB_503.data.u8[5] = 0x80; // Bordnetz Inactive MEB_503.data.u8[5] = 0x80; // Bordnetz Inactive
MEB_503.data.u8[6] = MEB_503.data.u8[6] =
0xE3; // Request emergency shutdown HV system == init (3) (not sure if we dare activate this, this is done with 0xE1) 0xE3; // Request emergency shutdown HV system == init (3) (not sure if we dare activate this, this is done with 0xE1)
} else {
MEB_503.data.u8[3] = 0;
} }
MEB_503.data.u8[1] = ((MEB_503.data.u8[1] & 0xF0) | counter_100ms); MEB_503.data.u8[1] = ((MEB_503.data.u8[1] & 0xF0) | counter_100ms);
MEB_503.data.u8[0] = vw_crc_calc(MEB_503.data.u8, MEB_503.DLC, MEB_503.ID); MEB_503.data.u8[0] = vw_crc_calc(MEB_503.data.u8, MEB_503.DLC, MEB_503.ID);
@ -1549,7 +1618,7 @@ void transmit_can_battery() {
MEB_272.data.u8[5] = DC_FASTCHARGE_NO_START_REQUEST; //DC_FASTCHARGE_VEHICLE; //DC charging MEB_272.data.u8[5] = DC_FASTCHARGE_NO_START_REQUEST; //DC_FASTCHARGE_VEHICLE; //DC charging
//Klemmen status //Klemmen status
MEB_3C0.data.u8[2] = 0x00; //0x02; //bit to signal that KL_15 is ON // Always 0 in start4.log MEB_3C0.data.u8[2] = 0x02; //bit to signal that KL_15 is ON // Always 0 in start4.log
MEB_3C0.data.u8[1] = ((MEB_3C0.data.u8[1] & 0xF0) | counter_100ms); MEB_3C0.data.u8[1] = ((MEB_3C0.data.u8[1] & 0xF0) | counter_100ms);
MEB_3C0.data.u8[0] = vw_crc_calc(MEB_3C0.data.u8, MEB_3C0.DLC, MEB_3C0.ID); MEB_3C0.data.u8[0] = vw_crc_calc(MEB_3C0.data.u8, MEB_3C0.DLC, MEB_3C0.ID);

View file

@ -0,0 +1,132 @@
#include "precharge_control.h"
#include "../../datalayer/datalayer.h"
#include "../../datalayer/datalayer_extended.h"
#include "../../include.h"
// Parameters
#ifdef PRECHARGE_CONTROL
enum State { PRECHARGE_IDLE, START_PRECHARGE, PRECHARGE, PRECHARGE_OFF, COMPLETED };
State prechargeStatus = PRECHARGE_IDLE;
#define MAX_ALLOWED_FAULT_TICKS 1000
#define MAX_PRECHARGE_TIME_MS 15000 // Maximum time precharge may be enabled
#define Precharge_default_PWM_Freq 22000
#define Precharge_min_PWM_Freq 18000
#define Precharge_max_PWM_Freq 34000
#define PWM_Res 8
#define PWM_OFF_DUTY 0
#define PWM_Precharge_Channel 0
unsigned long prechargeStartTime = 0;
static uint32_t freq = Precharge_default_PWM_Freq;
uint16_t delta_freq = 1;
static int32_t prev_external_voltage = 20000;
// Initialization functions
void init_precharge_control() {
// Setup PWM Channel Frequency and Resolution
logging.printf("Precharge control initialised\n");
pinMode(PRECHARGE_PIN, OUTPUT);
digitalWrite(PRECHARGE_PIN, LOW);
}
// Main functions
void handle_precharge_control() {
unsigned long currentTime = millis();
#ifdef MEB_BATTERY
int32_t target_voltage = datalayer.battery.status.voltage_dV;
int32_t external_voltage = datalayer_extended.meb.BMS_voltage_intermediate_dV;
#endif
// Handle actual state machine. This first turns on Negative, then Precharge, then Positive, and finally turns OFF precharge
switch (prechargeStatus) {
case PRECHARGE_IDLE:
if (datalayer.battery.status.bms_status == STANDBY && datalayer.system.status.inverter_allows_contactor_closing &&
!datalayer.system.settings.equipment_stop_active) {
prechargeStatus = START_PRECHARGE;
}
break;
case START_PRECHARGE:
freq = Precharge_default_PWM_Freq;
ledcAttachChannel(PRECHARGE_PIN, freq, PWM_Res, PWM_Precharge_Channel);
ledcWriteTone(PRECHARGE_PIN, freq); // Set frequency and set dutycycle to 50%
prechargeStartTime = currentTime;
prechargeStatus = PRECHARGE;
logging.printf("Precharge: Starting sequence\n");
break;
case PRECHARGE:
// Check if external voltage measurement changed, for instance with the MEB batteries, the external voltage is only updated every 100ms.
if (prev_external_voltage != external_voltage) {
prev_external_voltage = external_voltage;
if (labs(target_voltage - external_voltage) > 150) {
delta_freq = 2000;
} else if (labs(target_voltage - external_voltage) > 80) {
delta_freq = labs(target_voltage - external_voltage) * 6;
} else {
delta_freq = labs(target_voltage - external_voltage) * 3;
}
if (target_voltage > external_voltage) {
freq += delta_freq;
} else {
freq -= delta_freq;
}
if (freq > Precharge_max_PWM_Freq)
freq = Precharge_max_PWM_Freq;
if (freq < Precharge_min_PWM_Freq)
freq = Precharge_min_PWM_Freq;
logging.printf("Precharge: Target: %d V Extern: %d V Frequency: %u\n", target_voltage / 10,
external_voltage / 10, freq);
ledcWriteTone(PRECHARGE_PIN, freq);
}
if ((datalayer.battery.status.bms_status != STANDBY && datalayer.battery.status.bms_status != ACTIVE) ||
datalayer.system.settings.equipment_stop_active) {
pinMode(PRECHARGE_PIN, OUTPUT);
digitalWrite(PRECHARGE_PIN, LOW);
prechargeStatus = PRECHARGE_IDLE;
logging.printf("Precharge: Disabling Precharge bms not standby/active or equipment stop\n");
} else if (currentTime - prechargeStartTime >= MAX_PRECHARGE_TIME_MS) {
pinMode(PRECHARGE_PIN, OUTPUT);
digitalWrite(PRECHARGE_PIN, LOW);
prechargeStatus = PRECHARGE_OFF;
datalayer.battery.status.bms_status = FAULT;
logging.printf("Precharge: Disabled (timeout reached) -> PRECHARGE_OFF\n");
// Add event
} else if (datalayer.system.status.battery_allows_contactor_closing) {
pinMode(PRECHARGE_PIN, OUTPUT);
digitalWrite(PRECHARGE_PIN, LOW);
prechargeStatus = COMPLETED;
logging.printf("Precharge: Disabled (contacts closed) -> COMPLETED\n");
}
break;
case COMPLETED:
if (datalayer.system.settings.equipment_stop_active || datalayer.battery.status.bms_status != ACTIVE) {
prechargeStatus = PRECHARGE_IDLE;
logging.printf("Precharge: equipment stop activated -> IDLE\n");
}
break;
case PRECHARGE_OFF:
if (!datalayer.system.status.battery_allows_contactor_closing ||
!datalayer.system.status.inverter_allows_contactor_closing ||
datalayer.system.settings.equipment_stop_active || datalayer.battery.status.bms_status != FAULT) {
prechargeStatus = PRECHARGE_IDLE;
pinMode(PRECHARGE_PIN, OUTPUT);
digitalWrite(PRECHARGE_PIN, LOW);
logging.printf("Precharge: equipment stop activated -> IDLE\n");
}
break;
default:
break;
}
}
#endif // PRECHARGE_CONTROL

View file

@ -0,0 +1,35 @@
#ifndef _PRECHARGE_CONTROL_H_
#define _PRECHARGE_CONTROL_H_
#include "../../include.h"
#include "../../devboard/utils/events.h"
/**
* @brief Contactor initialization
*
* @param[in] void
*
* @return void
*/
void init_precharge_control();
/**
* @brief Handle contactors
*
* @param[in] void
*
* @return void
*/
void handle_precharge_control();
/**
* @brief Handle contactors of battery 2
*
* @param[in] void
*
* @return void
*/
void handle_contactors_battery2();
#endif // _PRECHARGE_CONTROL_H_

View file

@ -79,6 +79,7 @@ void update_machineryprotection() {
// Battery is empty. Do not allow further discharge. // Battery is empty. Do not allow further discharge.
// Normally the BMS will send 0W allowed, but this acts as an additional layer of safety // Normally the BMS will send 0W allowed, but this acts as an additional layer of safety
if (datalayer.battery.status.bms_status == ACTIVE) {
if (datalayer.battery.status.reported_soc == 0) { //Scaled SOC% value is 0.00% if (datalayer.battery.status.reported_soc == 0) { //Scaled SOC% value is 0.00%
if (!battery_empty_event_fired) { if (!battery_empty_event_fired) {
set_event(EVENT_BATTERY_EMPTY, 0); set_event(EVENT_BATTERY_EMPTY, 0);
@ -89,6 +90,7 @@ void update_machineryprotection() {
clear_event(EVENT_BATTERY_EMPTY); clear_event(EVENT_BATTERY_EMPTY);
battery_empty_event_fired = false; battery_empty_event_fired = false;
} }
}
// Battery is extremely degraded, not fit for secondlifestorage! // Battery is extremely degraded, not fit for secondlifestorage!
if (datalayer.battery.status.soh_pptt < 2500) { if (datalayer.battery.status.soh_pptt < 2500) {
@ -309,10 +311,12 @@ void emulator_pause_state_transmit_can_battery() {
allowed_to_send_CAN = (!emulator_pause_CAN_send_ON || emulator_pause_status == NORMAL); allowed_to_send_CAN = (!emulator_pause_CAN_send_ON || emulator_pause_status == NORMAL);
if (previous_allowed_to_send_CAN && !allowed_to_send_CAN) { if (previous_allowed_to_send_CAN && !allowed_to_send_CAN) {
logging.printf("Safety: Pausing CAN sending");
//completely force stop the CAN communication //completely force stop the CAN communication
ESP32Can.CANStop(); ESP32Can.CANStop();
} else if (!previous_allowed_to_send_CAN && allowed_to_send_CAN) { } else if (!previous_allowed_to_send_CAN && allowed_to_send_CAN) {
//resume CAN communication //resume CAN communication
logging.printf("Safety: Resuming CAN sending");
ESP32Can.CANInit(); ESP32Can.CANInit();
} }
} }