mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-05 02:39:57 +02:00
Add Automatic Precharge Control for modified HIA4V1 boards
This commit is contained in:
parent
f138f97905
commit
dccfca17ea
6 changed files with 302 additions and 27 deletions
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
@ -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_
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue