This commit is contained in:
rha 2024-12-21 19:22:04 +02:00
parent 6ad5c41b04
commit bcc69647fe
4 changed files with 137 additions and 93 deletions

View file

@ -3,24 +3,22 @@
#include "../datalayer/datalayer.h"
#include "BMW-SBOX.h"
#define MAX_ALLOWED_FAULT_TICKS 1000
/* NOTE: modify the precharge time constant below to account for the resistance and capacitance of the target system.
* t=3RC at minimum, t=5RC ideally
*/
#define PRECHARGE_TIME_MS 160 // Time before negative contactor engages and precharging starts
#define NEGATIVE_CONTACTOR_TIME_MS 1000 // Precharge time before precharge resistor is bypassed by positive contactor
#define POSITIVE_CONTACTOR_TIME_MS 2000 // Precharge relay lead time after positive contactor has been engaged
enum State { DISCONNECTED, PRECHARGE, NEGATIVE, POSITIVE, PRECHARGE_OFF, COMPLETED, SHUTDOWN_REQUESTED };
State contactorStatus = DISCONNECTED;
enum SboxState { DISCONNECTED, PRECHARGE, NEGATIVE, POSITIVE, PRECHARGE_OFF, COMPLETED, SHUTDOWN_REQUESTED };
SboxState contactorStatus = DISCONNECTED;
unsigned long prechargeStartTime = 0;
unsigned long negativeStartTime = 0;
unsigned long positiveStartTime = 0;
unsigned long timeSpentInFaultedMode = 0;
unsigned long LastMsgTime = 0; // will store last time a 20ms CAN Message was send
unsigned long LastAvgTime = 0; // Last current storage time
uint32_t avg_mA_array[10];
uint32_t avg_sum;
uint8_t k; //avg array pointer
uint8_t CAN100_cnt=0;
@ -46,6 +44,8 @@ uint8_t reverse_bits(uint8_t byte) {
return reversed;
}
/** CRC8, both inverted, poly 0x31 **/
uint8_t calculateCRC(CAN_frame CAN) {
uint8_t crc = 0;
for (size_t i = 0; i < CAN.DLC; i++) {
@ -65,21 +65,43 @@ uint8_t calculateCRC(CAN_frame CAN) {
}
void receive_can_shunt(CAN_frame rx_frame) {
unsigned long currentTime = millis();
if(rx_frame.ID ==0x200)
{
datalayer.shunt.measured_amperage_mA=((rx_frame.data.u8[2]<<24)| (rx_frame.data.u8[1]<<16)| (rx_frame.data.u8[0]<<8))/256;
}
else if(rx_frame.ID ==0x210) //Battery voltage
/** Calculate 1S avg current **/
if(LastAvgTime+100<currentTime)
{
datalayer.shunt.measured_voltage_mV==((rx_frame.data.u8[2]<<24)| (rx_frame.data.u8[1]<<16)| (rx_frame.data.u8[0]<<8))/256;
LastAvgTime=currentTime;
if(k>9) {
k=0;
}
avg_mA_array[k]=datalayer.shunt.measured_amperage_mA;
k++;
avg_sum=0;
for (uint8_t i = 0; i < 10; i++)
{
avg_sum=avg_sum+avg_mA_array[i];
}
datalayer.shunt.measured_avg1S_amperage_mA=avg_sum/10;
}
}
else if(rx_frame.ID ==0x210) //SBOX input (battery side) voltage
{
datalayer.shunt.measured_voltage_mV=((rx_frame.data.u8[2]<<16)| (rx_frame.data.u8[1]<<8)| (rx_frame.data.u8[0]));
}
else if(rx_frame.ID ==0x220) //SBOX output voltage
{
datalayer.shunt.measured_outvoltage_mV==((rx_frame.data.u8[2]<<24)| (rx_frame.data.u8[1]<<16)| (rx_frame.data.u8[0]<<8))/256;
datalayer.shunt.measured_outvoltage_mV=((rx_frame.data.u8[2]<<16)| (rx_frame.data.u8[1]<<8)| (rx_frame.data.u8[0]));
}
}
void send_can_shunt() {
unsigned long currentTime = millis();
// Send 20ms CAN Message
if (currentTime - LastMsgTime >= INTERVAL_20_MS) {
LastMsgTime = currentTime;
// First check if we have any active errors, incase we do, turn off the battery
if (datalayer.battery.status.bms_status == FAULT) {
timeSpentInFaultedMode++;
@ -98,12 +120,13 @@ void send_can_shunt() {
return; // A fault scenario latches the contactor control. It is not possible to recover without a powercycle (and investigation why fault occured)
}
// After that, check if we are OK to start turning on the battery
// After that, check if we are OK to start turning on the contactors
if (contactorStatus == DISCONNECTED) {
datalayer.shunt.contactors_engaged = false;
SBOX_100.data.u8[0]=0x55; // All open
if (datalayer.system.status.battery_allows_contactor_closing &&
datalayer.system.status.inverter_allows_contactor_closing && !datalayer.system.settings.equipment_stop_active && ( datalayer.shunt.measured_voltage_mV => MINIMUM_INPUT_VOLTAGE*1000)) {
datalayer.system.status.inverter_allows_contactor_closing && !datalayer.system.settings.equipment_stop_active && ( datalayer.shunt.measured_voltage_mV > MINIMUM_INPUT_VOLTAGE*1000)) {
contactorStatus = PRECHARGE;
}
}
@ -116,37 +139,48 @@ void send_can_shunt() {
}
}
unsigned long currentTime = millis();
// Handle actual state machine. This first turns on Precharge, then Negative, then Positive, and finally turns OFF precharge
switch (contactorStatus) {
case PRECHARGE:
SBOX_100.data.u8[0]=0x86; // Precharge relay only
prechargeStartTime = currentTime;
contactorStatus = NEGATIVE;
#ifdef DEBUG_VIA_USB
Serial.println("S-BOX Precharge relay engaged");
#endif
break;
case NEGATIVE:
if (currentTime - prechargeStartTime >= PRECHARGE_TIME_MS) {
if (currentTime - prechargeStartTime >= CONTACTOR_CONTROL_T1) {
SBOX_100.data.u8[0]=0xA6; // Precharge + Negative
negativeStartTime = currentTime;
contactorStatus = POSITIVE;
datalayer.shunt.precharging = true;
#ifdef DEBUG_VIA_USB
Serial.println("S-BOX Negative relay engaged");
#endif
}
break;
case POSITIVE:
if (currentTime - negativeStartTime >= NEGATIVE_CONTACTOR_TIME_MS && (datalayer.shunt.measured_voltage_mV * MAX_PRECHARGE_RESISTOR_VOLTAGE_PERCENT < datalalyer.shunt.measured_outvoltage_mV)) {
if (currentTime - negativeStartTime >= CONTACTOR_CONTROL_T2 && (datalayer.shunt.measured_voltage_mV * MAX_PRECHARGE_RESISTOR_VOLTAGE_PERCENT < datalayer.shunt.measured_outvoltage_mV)) {
SBOX_100.data.u8[0]=0xAA; // Precharge + Negative + Positive
positiveStartTime = currentTime;
contactorStatus = PRECHARGE_OFF;
datalayer.shunt.precharging = false;
#ifdef DEBUG_VIA_USB
Serial.println("S-BOX Positive relay engaged");
#endif
}
break;
case PRECHARGE_OFF:
if (currentTime - positiveStartTime >= POSITIVE_CONTACTOR_TIME_MS) {
if (currentTime - positiveStartTime >= CONTACTOR_CONTROL_T3) {
SBOX_100.data.u8[0]=0x6A; // Negative + Positive
contactorStatus = COMPLETED;
#ifdef DEBUG_VIA_USB
Serial.println("S-BOX Precharge relay released");
#endif
datalayer.shunt.contactors_engaged = true;
}
break;
@ -156,9 +190,6 @@ void send_can_shunt() {
break;
}
// Send 20ms CAN Message
if (currentTime - LastMsgTime >= INTERVAL_20_MS) {
LastMsgTime = currentTime;
CAN100_cnt++;
if (CAN100_cnt>0x0E) {
CAN100_cnt=0;
@ -172,6 +203,7 @@ void send_can_shunt() {
}
void setup_can_shunt() {
datalayer.system.info.shunt_protocol[63] = 'BMW SBOX\0';
strncpy(datalayer.system.info.shunt_protocol, "BMW SBOX", 63);
datalayer.system.info.shunt_protocol[63] = '\0';
}
#endif

View file

@ -7,8 +7,16 @@ void transmit_can(CAN_frame* tx_frame, int interface);
/** Minimum input voltage required to enable relay control **/
#define MINIMUM_INPUT_VOLTAGE 250
/** Maximum allowable percentage of input voltage across the precharge resistor to close the positive relay **/
#define MAX_PRECHARGE_RESISTOR_VOLTAGE_PERCENT 96
/** Maximum allowable percentage of input voltage across the precharge resistor to engage the positive relay. **/
/** SAFETY FEATURE: If precharge resistor is faulty, positive contactor will not be engaged **/
#define MAX_PRECHARGE_RESISTOR_VOLTAGE_PERCENT 0.99
/* NOTE: modify the T2 time constant below to account for the resistance and capacitance of the target system.
* t=3RC at minimum, t=5RC ideally
*/
#define CONTACTOR_CONTROL_T1 5000 // Time before negative contactor engages and precharging starts
#define CONTACTOR_CONTROL_T2 5000 // Precharge time before precharge resistor is bypassed by positive contactor
#define CONTACTOR_CONTROL_T3 2000 // Precharge relay lead time after positive contactor has been engaged
#endif

View file

@ -129,7 +129,11 @@ typedef struct {
/** measured output voltage in mV (eg. S-BOX) **/
uint32_t measured_outvoltage_mV = 0;
/** measured amperage in mA (eg. S-BOX) **/
uint32_t measured_amperage_mA = 0;
int32_t measured_amperage_mA = 0;
/** Sum of current readings during measuring period **/
int32_t measured_avg1S_amperage_mA = 0;
/** Number of samples **/
uint16_t measured_sum_amperage_count = 0;
/** True if contactors are precharging state */
bool precharging = false;
/** True if the contactor controlled by battery-emulator is closed */

View file

@ -42,7 +42,7 @@ String settings_processor(const String& var) {
#endif
#ifdef CAN_SHUNT_SELECTED
content += "<h4 style='color: white;'>Shunt Interface: <span id='BMW S-BOX'>" +
content += "<h4 style='color: white;'>Shunt Interface: <span id='Shunt'>" +
String(getCANInterfaceName(can_config.shunt)) + "</span></h4>";
#endif //CAN_SHUNT_SELECTED