mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-05 19:42:08 +02:00
Merge branch 'main' into bugfix/ioniq28-PID
This commit is contained in:
commit
d750201795
96 changed files with 3069 additions and 2501 deletions
|
@ -307,9 +307,7 @@ void BmwIXBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
|
||||
if ((rx_frame.data.u8[6] << 8 | rx_frame.data.u8[7]) == 10000 ||
|
||||
(rx_frame.data.u8[8] << 8 | rx_frame.data.u8[9]) == 10000) { //Qualifier Invalid Mode - Request Reboot
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Cell MinMax Qualifier Invalid - Requesting BMS Reset");
|
||||
#endif // DEBUG_LOG
|
||||
//set_event(EVENT_BATTERY_VALUE_UNAVAILABLE, (millis())); //Eventually need new Info level event type
|
||||
transmit_can_frame(&BMWiX_6F4_REQUEST_HARD_RESET);
|
||||
} else { //Only ingest values if they are not the 10V Error state
|
||||
|
@ -378,15 +376,11 @@ void BmwIXBattery::transmit_can(unsigned long currentMillis) {
|
|||
// Detect edge
|
||||
if (ContactorCloseRequest.previous == false && ContactorCloseRequest.present == true) {
|
||||
// Rising edge detected
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Rising edge detected. Resetting 10ms counter.");
|
||||
#endif // DEBUG_LOG
|
||||
counter_10ms = 0; // reset counter
|
||||
} else if (ContactorCloseRequest.previous == true && ContactorCloseRequest.present == false) {
|
||||
// Dropping edge detected
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Dropping edge detected. Resetting 10ms counter.");
|
||||
#endif // DEBUG_LOG
|
||||
counter_10ms = 0; // reset counter
|
||||
}
|
||||
ContactorCloseRequest.previous = ContactorCloseRequest.present;
|
||||
|
@ -465,12 +459,10 @@ void BmwIXBattery::setup(void) { // Performs one time setup at startup
|
|||
|
||||
void BmwIXBattery::HandleIncomingUserRequest(void) {
|
||||
// Debug user request to open or close the contactors
|
||||
#ifdef DEBUG_LOG
|
||||
logging.print("User request: contactor close: ");
|
||||
logging.print(userRequestContactorClose);
|
||||
logging.print(" User request: contactor open: ");
|
||||
logging.println(userRequestContactorOpen);
|
||||
#endif // DEBUG_LOG
|
||||
if ((userRequestContactorClose == false) && (userRequestContactorOpen == false)) {
|
||||
// do nothing
|
||||
} else if ((userRequestContactorClose == true) && (userRequestContactorOpen == false)) {
|
||||
|
@ -487,11 +479,9 @@ void BmwIXBattery::HandleIncomingUserRequest(void) {
|
|||
// set user request to false
|
||||
userRequestContactorClose = false;
|
||||
userRequestContactorOpen = false;
|
||||
// print error, as both these flags shall not be true at the same time
|
||||
#ifdef DEBUG_LOG
|
||||
// print error, as both these flags shall not be true at the same time
|
||||
logging.println(
|
||||
"Error: user requested contactors to close and open at the same time. Contactors have been opened.");
|
||||
#endif // DEBUG_LOG
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -499,16 +489,12 @@ void BmwIXBattery::HandleIncomingInverterRequest(void) {
|
|||
InverterContactorCloseRequest.present = datalayer.system.status.inverter_allows_contactor_closing;
|
||||
// Detect edge
|
||||
if (InverterContactorCloseRequest.previous == false && InverterContactorCloseRequest.present == true) {
|
||||
// Rising edge detected
|
||||
#ifdef DEBUG_LOG
|
||||
// Rising edge detected
|
||||
logging.println("Inverter requests to close contactors");
|
||||
#endif // DEBUG_LOG
|
||||
BmwIxCloseContactors();
|
||||
} else if (InverterContactorCloseRequest.previous == true && InverterContactorCloseRequest.present == false) {
|
||||
// Falling edge detected
|
||||
#ifdef DEBUG_LOG
|
||||
// Falling edge detected
|
||||
logging.println("Inverter requests to open contactors");
|
||||
#endif // DEBUG_LOG
|
||||
BmwIxOpenContactors();
|
||||
} // else: do nothing
|
||||
|
||||
|
@ -517,16 +503,12 @@ void BmwIXBattery::HandleIncomingInverterRequest(void) {
|
|||
}
|
||||
|
||||
void BmwIXBattery::BmwIxCloseContactors(void) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Closing contactors");
|
||||
#endif // DEBUG_LOG
|
||||
contactorCloseReq = true;
|
||||
}
|
||||
|
||||
void BmwIXBattery::BmwIxOpenContactors(void) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Opening contactors");
|
||||
#endif // DEBUG_LOG
|
||||
contactorCloseReq = false;
|
||||
counter_100ms = 0; // reset counter, such that keep contactors closed message sequence starts from the beginning
|
||||
}
|
||||
|
@ -552,46 +534,34 @@ void BmwIXBattery::HandleBmwIxCloseContactorsRequest(uint16_t counter_10ms) {
|
|||
if (counter_10ms == 0) {
|
||||
// @0 ms
|
||||
transmit_can_frame(&BMWiX_510);
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Transmitted 0x510 - 1/6");
|
||||
#endif // DEBUG_LOG
|
||||
} else if (counter_10ms == 5) {
|
||||
// @50 ms
|
||||
transmit_can_frame(&BMWiX_276);
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Transmitted 0x276 - 2/6");
|
||||
#endif // DEBUG_LOG
|
||||
} else if (counter_10ms == 10) {
|
||||
// @100 ms
|
||||
BMWiX_510.data.u8[2] = 0x04; // TODO: check if needed
|
||||
transmit_can_frame(&BMWiX_510);
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Transmitted 0x510 - 3/6");
|
||||
#endif // DEBUG_LOG
|
||||
} else if (counter_10ms == 20) {
|
||||
// @200 ms
|
||||
BMWiX_510.data.u8[2] = 0x10; // TODO: check if needed
|
||||
BMWiX_510.data.u8[5] = 0x80; // needed to close contactors
|
||||
transmit_can_frame(&BMWiX_510);
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Transmitted 0x510 - 4/6");
|
||||
#endif // DEBUG_LOG
|
||||
} else if (counter_10ms == 30) {
|
||||
// @300 ms
|
||||
BMWiX_16E.data.u8[0] = 0x6A;
|
||||
BMWiX_16E.data.u8[1] = 0xAD;
|
||||
transmit_can_frame(&BMWiX_16E);
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Transmitted 0x16E - 5/6");
|
||||
#endif // DEBUG_LOG
|
||||
} else if (counter_10ms == 50) {
|
||||
// @500 ms
|
||||
BMWiX_16E.data.u8[0] = 0x03;
|
||||
BMWiX_16E.data.u8[1] = 0xA9;
|
||||
transmit_can_frame(&BMWiX_16E);
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Transmitted 0x16E - 6/6");
|
||||
#endif // DEBUG_LOG
|
||||
ContactorState.closed = true;
|
||||
ContactorState.open = false;
|
||||
}
|
||||
|
@ -613,9 +583,7 @@ void BmwIXBattery::BmwIxKeepContactorsClosed(uint8_t counter_100ms) {
|
|||
0xC9, 0x3A, 0xF7}; // Explicit declaration, to prevent modification by other functions
|
||||
|
||||
if (counter_100ms == 0) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Sending keep contactors closed messages started");
|
||||
#endif // DEBUG_LOG
|
||||
// @0 ms
|
||||
transmit_can_frame(&BMWiX_510);
|
||||
} else if (counter_100ms == 7) {
|
||||
|
@ -631,9 +599,7 @@ void BmwIXBattery::BmwIxKeepContactorsClosed(uint8_t counter_100ms) {
|
|||
BMWiX_16E.data.u8[0] = 0x02;
|
||||
BMWiX_16E.data.u8[1] = 0xA7;
|
||||
transmit_can_frame(&BMWiX_16E);
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Sending keep contactors closed messages finished");
|
||||
#endif // DEBUG_LOG
|
||||
} else if (counter_100ms == 140) {
|
||||
// @14000 ms
|
||||
// reset counter (outside of this function)
|
||||
|
|
|
@ -122,9 +122,7 @@ bool BmwPhevBattery::storeUDSPayload(const uint8_t* payload, uint8_t length) {
|
|||
if (gUDSContext.UDS_bytesReceived + length > sizeof(gUDSContext.UDS_buffer)) {
|
||||
// Overflow => abort
|
||||
gUDSContext.UDS_inProgress = false;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("UDS Payload Overflow");
|
||||
#endif // DEBUG_LOG
|
||||
return false;
|
||||
}
|
||||
memcpy(&gUDSContext.UDS_buffer[gUDSContext.UDS_bytesReceived], payload, length);
|
||||
|
@ -134,9 +132,7 @@ bool BmwPhevBattery::storeUDSPayload(const uint8_t* payload, uint8_t length) {
|
|||
// If we’ve reached or exceeded the expected length, mark complete
|
||||
if (gUDSContext.UDS_bytesReceived >= gUDSContext.UDS_expectedLength) {
|
||||
gUDSContext.UDS_inProgress = false;
|
||||
// #ifdef DEBUG_LOG
|
||||
// logging.println("Recived all expected UDS bytes");
|
||||
// #endif // DEBUG_LOG
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -196,9 +192,7 @@ void BmwPhevBattery::wake_battery_via_canbus() {
|
|||
|
||||
change_can_speed(original_speed);
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Sent magic wakeup packet to SME at 100kbps...");
|
||||
#endif
|
||||
}
|
||||
|
||||
void BmwPhevBattery::update_values() { //This function maps all the values fetched via CAN to the battery datalayer
|
||||
|
@ -246,9 +240,7 @@ void BmwPhevBattery::update_values() { //This function maps all the values fetc
|
|||
datalayer.battery.status.cell_min_voltage_mV = 9999; //Stale values force stop
|
||||
datalayer.battery.status.cell_max_voltage_mV = 9999; //Stale values force stop
|
||||
set_event(EVENT_STALE_VALUE, 0);
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Stale Min/Max voltage values detected during charge/discharge sending - 9999mV...");
|
||||
#endif // DEBUG_LOG
|
||||
} else {
|
||||
|
||||
datalayer.battery.status.cell_min_voltage_mV = min_cell_voltage; //Value is alive
|
||||
|
@ -398,11 +390,7 @@ void BmwPhevBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
rx_frame.data.u8[4] ==
|
||||
0xAD) { //Balancing Status 01 Active 03 Not Active 7DLC F1 05 71 03 AD 6B 01
|
||||
balancing_status = (rx_frame.data.u8[6]);
|
||||
// #ifdef DEBUG_LOG
|
||||
|
||||
// logging.println("Balancing Status received");
|
||||
|
||||
// #endif // DEBUG_LOG
|
||||
//logging.println("Balancing Status received");
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -525,7 +513,6 @@ void BmwPhevBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
battery_current = ((int32_t)((gUDSContext.UDS_buffer[3] << 24) | (gUDSContext.UDS_buffer[4] << 16) |
|
||||
(gUDSContext.UDS_buffer[5] << 8) | gUDSContext.UDS_buffer[6])) *
|
||||
0.1;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.print("Received current/amps measurement data: ");
|
||||
logging.print(battery_current);
|
||||
logging.print(" - ");
|
||||
|
@ -538,7 +525,6 @@ void BmwPhevBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
logging.print(" ");
|
||||
}
|
||||
logging.println(); // new line at the end
|
||||
#endif // DEBUG_LOG
|
||||
}
|
||||
|
||||
//Cell Min/Max
|
||||
|
@ -553,7 +539,6 @@ void BmwPhevBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
min_cell_voltage = (gUDSContext.UDS_buffer[9] << 8 | gUDSContext.UDS_buffer[10]) / 10;
|
||||
max_cell_voltage = (gUDSContext.UDS_buffer[11] << 8 | gUDSContext.UDS_buffer[12]) / 10;
|
||||
} else {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Cell Min Max Invalid 65535 or 0...");
|
||||
logging.print("Received data: ");
|
||||
for (uint16_t i = 0; i < gUDSContext.UDS_bytesReceived; i++) {
|
||||
|
@ -565,7 +550,6 @@ void BmwPhevBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
logging.print(" ");
|
||||
}
|
||||
logging.println(); // new line at the end
|
||||
#endif // DEBUG_LOG
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -618,16 +602,12 @@ void BmwPhevBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
if (rx_frame.data.u8[6] > 0 && rx_frame.data.u8[6] < 255) {
|
||||
battery_temperature_min = (rx_frame.data.u8[6] - 50);
|
||||
} else {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Pre parsed Cell Temp Min is Invalid ");
|
||||
#endif
|
||||
}
|
||||
if (rx_frame.data.u8[7] > 0 && rx_frame.data.u8[7] < 255) {
|
||||
battery_temperature_max = (rx_frame.data.u8[7] - 50);
|
||||
} else {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Pre parsed Cell Temp Max is Invalid ");
|
||||
#endif
|
||||
}
|
||||
|
||||
break;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include <Arduino.h>
|
||||
#include "../communication/can/comm_can.h"
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../devboard/utils/logging.h"
|
||||
|
||||
uint8_t reverse_bits(uint8_t byte) {
|
||||
uint8_t reversed = 0;
|
||||
|
@ -122,9 +123,7 @@ void BmwSbox::transmit_can(unsigned long currentMillis) {
|
|||
SBOX_100.data.u8[0] = 0x86; // Precharge relay only
|
||||
prechargeStartTime = currentMillis;
|
||||
contactorStatus = NEGATIVE;
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("S-BOX Precharge relay engaged");
|
||||
#endif
|
||||
logging.println("S-BOX Precharge relay engaged");
|
||||
break;
|
||||
case NEGATIVE:
|
||||
if (currentMillis - prechargeStartTime >= CONTACTOR_CONTROL_T1) {
|
||||
|
@ -132,9 +131,7 @@ void BmwSbox::transmit_can(unsigned long currentMillis) {
|
|||
negativeStartTime = currentMillis;
|
||||
contactorStatus = POSITIVE;
|
||||
datalayer.shunt.precharging = true;
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("S-BOX Negative relay engaged");
|
||||
#endif
|
||||
logging.println("S-BOX Negative relay engaged");
|
||||
}
|
||||
break;
|
||||
case POSITIVE:
|
||||
|
@ -145,18 +142,14 @@ void BmwSbox::transmit_can(unsigned long currentMillis) {
|
|||
positiveStartTime = currentMillis;
|
||||
contactorStatus = PRECHARGE_OFF;
|
||||
datalayer.shunt.precharging = false;
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("S-BOX Positive relay engaged");
|
||||
#endif
|
||||
logging.println("S-BOX Positive relay engaged");
|
||||
}
|
||||
break;
|
||||
case PRECHARGE_OFF:
|
||||
if (currentMillis - 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
|
||||
logging.println("S-BOX Precharge relay released");
|
||||
datalayer.shunt.contactors_engaged = true;
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -116,7 +116,6 @@ void ChademoBattery::process_vehicle_charging_session(CAN_frame rx_frame) {
|
|||
x102_chg_session.ChargingCurrentRequest = newChargingCurrentRequest;
|
||||
x102_chg_session.TargetBatteryVoltage = newTargetBatteryVoltage;
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
//Note on p131
|
||||
uint8_t chargingrate = 0;
|
||||
if (x100_chg_lim.ConstantOfChargingRateIndication > 0) {
|
||||
|
@ -124,7 +123,6 @@ void ChademoBattery::process_vehicle_charging_session(CAN_frame rx_frame) {
|
|||
logging.print("Charge Rate (kW): ");
|
||||
logging.println(chargingrate);
|
||||
}
|
||||
#endif
|
||||
|
||||
//Table A.26—Charge control termination command patterns -- should echo x108 handling
|
||||
|
||||
|
@ -136,41 +134,31 @@ void ChademoBattery::process_vehicle_charging_session(CAN_frame rx_frame) {
|
|||
*/
|
||||
if ((CHADEMO_Status == CHADEMO_INIT && vehicle_permission) ||
|
||||
(x102_chg_session.s.status.StatusVehicleChargingEnabled && !vehicle_permission)) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Inconsistent charge/discharge state.");
|
||||
#endif
|
||||
CHADEMO_Status = CHADEMO_FAULT;
|
||||
return;
|
||||
}
|
||||
|
||||
if (x102_chg_session.f.fault.FaultBatteryOverVoltage) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Vehicle indicates fault, battery over voltage.");
|
||||
#endif
|
||||
CHADEMO_Status = CHADEMO_STOP;
|
||||
return;
|
||||
}
|
||||
|
||||
if (x102_chg_session.f.fault.FaultBatteryUnderVoltage) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Vehicle indicates fault, battery under voltage.");
|
||||
#endif
|
||||
CHADEMO_Status = CHADEMO_STOP;
|
||||
return;
|
||||
}
|
||||
|
||||
if (x102_chg_session.f.fault.FaultBatteryCurrentDeviation) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Vehicle indicates fault, battery current deviation. Possible EVSE issue?");
|
||||
#endif
|
||||
CHADEMO_Status = CHADEMO_STOP;
|
||||
return;
|
||||
}
|
||||
|
||||
if (x102_chg_session.f.fault.FaultBatteryVoltageDeviation) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Vehicle indicates fault, battery voltage deviation. Possible EVSE issue?");
|
||||
#endif
|
||||
CHADEMO_Status = CHADEMO_STOP;
|
||||
return;
|
||||
}
|
||||
|
@ -183,18 +171,14 @@ void ChademoBattery::process_vehicle_charging_session(CAN_frame rx_frame) {
|
|||
|
||||
//FIXME condition nesting or more stanzas needed here for clear determination of cessation reason
|
||||
if (CHADEMO_Status == CHADEMO_POWERFLOW && EVSE_mode == CHADEMO_CHARGE && !vehicle_permission) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("State of charge ceiling reached or charging interrupted, stop charging");
|
||||
#endif
|
||||
CHADEMO_Status = CHADEMO_STOP;
|
||||
return;
|
||||
}
|
||||
|
||||
if (vehicle_permission && CHADEMO_Status == CHADEMO_NEGOTIATE) {
|
||||
CHADEMO_Status = CHADEMO_EV_ALLOWED;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("STATE shift to CHADEMO_EV_ALLOWED in process_vehicle_charging_session()");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -203,23 +187,17 @@ void ChademoBattery::process_vehicle_charging_session(CAN_frame rx_frame) {
|
|||
// consider relocating
|
||||
if (vehicle_permission && CHADEMO_Status == CHADEMO_EVSE_PREPARE && priorTargetBatteryVoltage == 0 &&
|
||||
newTargetBatteryVoltage > 0 && x102_chg_session.s.status.StatusVehicleChargingEnabled) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("STATE SHIFT to EVSE_START reached in process_vehicle_charging_session()");
|
||||
#endif
|
||||
CHADEMO_Status = CHADEMO_EVSE_START;
|
||||
return;
|
||||
}
|
||||
|
||||
if (vehicle_permission && evse_permission && CHADEMO_Status == CHADEMO_POWERFLOW) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("updating vehicle request in process_vehicle_charging_session()");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("UNHANDLED CHADEMO STATE, try unplugging chademo cable, reboot emulator, and retry!");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -231,8 +209,7 @@ void ChademoBattery::process_vehicle_charging_limits(CAN_frame rx_frame) {
|
|||
x200_discharge_limits.MinimumBatteryDischargeLevel = rx_frame.data.u8[6];
|
||||
x200_discharge_limits.MaxRemainingCapacityForCharging = rx_frame.data.u8[7];
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
/* unsigned long currentMillis = millis();
|
||||
/* unsigned long currentMillis = millis();
|
||||
if (currentMillis - previousMillis5000 >= INTERVAL_5_S) {
|
||||
previousMillis5000 = currentMillis;
|
||||
logging.println("x200 Max remaining capacity for charging/discharging:");
|
||||
|
@ -240,16 +217,13 @@ void ChademoBattery::process_vehicle_charging_limits(CAN_frame rx_frame) {
|
|||
logging.println(0xFF - x200_discharge_limits.MaxRemainingCapacityForCharging);
|
||||
}
|
||||
*/
|
||||
#endif
|
||||
|
||||
if (get_measured_voltage() <= x200_discharge_limits.MinimumDischargeVoltage && CHADEMO_Status > CHADEMO_NEGOTIATE) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("x200 minimum discharge voltage met or exceeded, stopping.");
|
||||
logging.print("Measured: ");
|
||||
logging.print(get_measured_voltage());
|
||||
logging.print("Minimum voltage: ");
|
||||
logging.print(x200_discharge_limits.MinimumDischargeVoltage);
|
||||
#endif
|
||||
CHADEMO_Status = CHADEMO_STOP;
|
||||
}
|
||||
}
|
||||
|
@ -264,7 +238,6 @@ void ChademoBattery::process_vehicle_discharge_estimate(CAN_frame rx_frame) {
|
|||
x201_discharge_estimate.ApproxDischargeCompletionTime = ((rx_frame.data.u8[2] << 8) | rx_frame.data.u8[1]);
|
||||
x201_discharge_estimate.AvailableVehicleEnergy = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[3]);
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
if (currentMillis - previousMillis5000 >= INTERVAL_5_S) {
|
||||
previousMillis5000 = currentMillis;
|
||||
logging.print("x201 availabile vehicle energy, completion time: ");
|
||||
|
@ -272,7 +245,6 @@ void ChademoBattery::process_vehicle_discharge_estimate(CAN_frame rx_frame) {
|
|||
logging.print("x201 approx vehicle completion time: ");
|
||||
logging.println(x201_discharge_estimate.ApproxDischargeCompletionTime);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void ChademoBattery::process_vehicle_dynamic_control(CAN_frame rx_frame) {
|
||||
|
@ -615,10 +587,8 @@ void ChademoBattery::transmit_can(unsigned long currentMillis) {
|
|||
// TODO need an update_evse_dynamic_control(..) function above before we send 118
|
||||
// 110.0.0
|
||||
if (x102_chg_session.ControlProtocolNumberEV >= 0x03) { //Only send the following on Chademo 2.0 vehicles?
|
||||
#ifdef DEBUG_LOG
|
||||
//FIXME REMOVE
|
||||
logging.println("REMOVE: proto 2.0");
|
||||
#endif
|
||||
transmit_can_frame(&CHADEMO_118);
|
||||
}
|
||||
}
|
||||
|
@ -656,16 +626,12 @@ void ChademoBattery::handle_chademo_sequence() {
|
|||
/* ------------------- State override conditions checks ------------------- */
|
||||
/* ------------------------------------------------------------------------------ */
|
||||
if (CHADEMO_Status >= CHADEMO_EV_ALLOWED && x102_chg_session.s.status.StatusVehicleShifterPosition) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Vehicle is not parked, abort.");
|
||||
#endif
|
||||
CHADEMO_Status = CHADEMO_STOP;
|
||||
}
|
||||
|
||||
if (CHADEMO_Status >= CHADEMO_EV_ALLOWED && !vehicle_permission) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Vehicle charge/discharge permission ended, stop.");
|
||||
#endif
|
||||
CHADEMO_Status = CHADEMO_STOP;
|
||||
}
|
||||
|
||||
|
@ -678,25 +644,19 @@ void ChademoBattery::handle_chademo_sequence() {
|
|||
plug_inserted = digitalRead(pin7);
|
||||
|
||||
if (!plug_inserted) {
|
||||
#ifdef DEBUG_LOG
|
||||
// Commented unless needed for debug
|
||||
// logging.println("CHADEMO plug is not inserted.");
|
||||
#endif
|
||||
// Commented unless needed for debug
|
||||
// logging.println("CHADEMO plug is not inserted.");
|
||||
return;
|
||||
}
|
||||
|
||||
CHADEMO_Status = CHADEMO_CONNECTED;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("CHADEMO plug is inserted. Provide EVSE power to vehicle to trigger initialization.");
|
||||
#endif
|
||||
|
||||
break;
|
||||
case CHADEMO_CONNECTED:
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
// Commented unless needed for debug
|
||||
//logging.println("CHADEMO_CONNECTED State");
|
||||
#endif
|
||||
/* plug_inserted is .. essentially a volatile of sorts, so verify */
|
||||
if (plug_inserted) {
|
||||
/* If connection is detectable, jumpstart handshake by
|
||||
|
@ -729,17 +689,13 @@ void ChademoBattery::handle_chademo_sequence() {
|
|||
/* Vehicle and EVSE dance */
|
||||
//TODO if pin 4 / j goes high,
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
// Commented unless needed for debug
|
||||
// logging.println("CHADEMO_NEGOTIATE State");
|
||||
#endif
|
||||
// Commented unless needed for debug
|
||||
// logging.println("CHADEMO_NEGOTIATE State");
|
||||
x109_evse_state.s.status.ChgDischStopControl = 1;
|
||||
break;
|
||||
case CHADEMO_EV_ALLOWED:
|
||||
#ifdef DEBUG_LOG
|
||||
// Commented unless needed for debug
|
||||
logging.println("CHADEMO_EV_ALLOWED State");
|
||||
#endif
|
||||
// If we are in this state, vehicle_permission was already set to true...but re-verify
|
||||
// that pin 4 (j) reads high
|
||||
if (vehicle_permission) {
|
||||
|
@ -754,10 +710,8 @@ void ChademoBattery::handle_chademo_sequence() {
|
|||
}
|
||||
break;
|
||||
case CHADEMO_EVSE_PREPARE:
|
||||
#ifdef DEBUG_LOG
|
||||
// Commented unless needed for debug
|
||||
logging.println("CHADEMO_EVSE_PREPARE State");
|
||||
#endif
|
||||
/* TODO voltage check of output < 20v
|
||||
* insulation test hypothetically happens here before triggering PIN 10 high
|
||||
* see Table A.28—Requirements for the insulation test for output DC circuit
|
||||
|
@ -790,19 +744,15 @@ void ChademoBattery::handle_chademo_sequence() {
|
|||
//state changes to CHADEMO_EVSE_START only upon receipt of charging session request
|
||||
break;
|
||||
case CHADEMO_EVSE_START:
|
||||
#ifdef DEBUG_LOG
|
||||
// Commented unless needed for debug
|
||||
logging.println("CHADEMO_EVSE_START State");
|
||||
#endif
|
||||
datalayer.system.status.battery_allows_contactor_closing = true;
|
||||
x109_evse_state.s.status.ChgDischStopControl = 1;
|
||||
x109_evse_state.s.status.EVSE_status = 0;
|
||||
|
||||
CHADEMO_Status = CHADEMO_EVSE_CONTACTORS_ENABLED;
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Initiating contactors");
|
||||
#endif
|
||||
|
||||
/* break rather than fall through because contactors are not instantaneous;
|
||||
* worth giving it a cycle to finish
|
||||
|
@ -810,18 +760,14 @@ void ChademoBattery::handle_chademo_sequence() {
|
|||
|
||||
break;
|
||||
case CHADEMO_EVSE_CONTACTORS_ENABLED:
|
||||
#ifdef DEBUG_LOG
|
||||
// Commented unless needed for debug
|
||||
logging.println("CHADEMO_EVSE_CONTACTORS State");
|
||||
#endif
|
||||
|
||||
/* check whether contactors ready, because externally dependent upon inverter allow during discharge */
|
||||
if (contactors_ready) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Contactors ready");
|
||||
logging.print("Voltage: ");
|
||||
logging.println(get_measured_voltage());
|
||||
#endif
|
||||
/* transition to POWERFLOW state if discharge compatible on both sides */
|
||||
if (x109_evse_state.discharge_compatible && x102_chg_session.s.status.StatusVehicleDischargeCompatible &&
|
||||
(EVSE_mode == CHADEMO_DISCHARGE || EVSE_mode == CHADEMO_BIDIRECTIONAL)) {
|
||||
|
@ -840,10 +786,8 @@ void ChademoBattery::handle_chademo_sequence() {
|
|||
/* break or fall through ? TODO */
|
||||
break;
|
||||
case CHADEMO_POWERFLOW:
|
||||
#ifdef DEBUG_LOG
|
||||
// Commented unless needed for debug
|
||||
logging.println("CHADEMO_POWERFLOW State");
|
||||
#endif
|
||||
/* POWERFLOW for charging, discharging, and bidirectional */
|
||||
/* Interpretation */
|
||||
if (x102_chg_session.s.status.StatusVehicleShifterPosition) {
|
||||
|
@ -860,9 +804,7 @@ void ChademoBattery::handle_chademo_sequence() {
|
|||
}
|
||||
|
||||
if (get_measured_voltage() <= x200_discharge_limits.MinimumDischargeVoltage) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("x200 minimum discharge voltage met or exceeded, stopping.");
|
||||
#endif
|
||||
CHADEMO_Status = CHADEMO_STOP;
|
||||
}
|
||||
|
||||
|
@ -871,10 +813,8 @@ void ChademoBattery::handle_chademo_sequence() {
|
|||
x109_evse_state.s.status.EVSE_status = 1;
|
||||
break;
|
||||
case CHADEMO_STOP:
|
||||
#ifdef DEBUG_LOG
|
||||
// Commented unless needed for debug
|
||||
logging.println("CHADEMO_STOP State");
|
||||
#endif
|
||||
/* back to CHADEMO_IDLE after teardown */
|
||||
x109_evse_state.s.status.ChgDischStopControl = 1;
|
||||
x109_evse_state.s.status.EVSE_status = 0;
|
||||
|
@ -899,17 +839,13 @@ void ChademoBattery::handle_chademo_sequence() {
|
|||
|
||||
break;
|
||||
case CHADEMO_FAULT:
|
||||
#ifdef DEBUG_LOG
|
||||
// Commented unless needed for debug
|
||||
logging.println("CHADEMO_FAULT State");
|
||||
#endif
|
||||
/* Once faulted, never departs CHADEMO_FAULT state unless device is power cycled as a safety measure */
|
||||
x109_evse_state.s.status.EVSE_error = 1;
|
||||
x109_evse_state.s.status.ChgDischError = 1;
|
||||
x109_evse_state.s.status.ChgDischStopControl = 1;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("CHADEMO fault encountered, tearing down to make safe");
|
||||
#endif
|
||||
digitalWrite(pin10, LOW);
|
||||
digitalWrite(pin2, LOW);
|
||||
evse_permission = false;
|
||||
|
@ -919,9 +855,7 @@ void ChademoBattery::handle_chademo_sequence() {
|
|||
|
||||
break;
|
||||
default:
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("UNHANDLED CHADEMO_STATE, setting FAULT");
|
||||
#endif
|
||||
CHADEMO_Status = CHADEMO_FAULT;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -15,8 +15,8 @@ static int16_t current_dA = 0;
|
|||
static uint16_t voltage_dV = 0;
|
||||
static uint32_t remaining_capacity_mAh = 0;
|
||||
static uint16_t cellvoltages_mV[48] = {0};
|
||||
static uint16_t cellvoltage_min_mV = 0;
|
||||
static uint16_t cellvoltage_max_mV = 0;
|
||||
static uint16_t cellvoltage_min_mV = 3700;
|
||||
static uint16_t cellvoltage_max_mV = 3700;
|
||||
static uint16_t cell_count = 0;
|
||||
static uint16_t SOC = 0;
|
||||
static bool has_fault = false;
|
||||
|
@ -108,18 +108,16 @@ uint32_t decode_uint32be(uint8_t data[8], uint8_t offset) {
|
|||
((uint32_t)data[offset + 3]);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_VIA_USB
|
||||
void dump_buff(const char* msg, uint8_t* buff, uint8_t len) {
|
||||
Serial.print("[DALY-BMS] ");
|
||||
Serial.print(msg);
|
||||
logging.print("[DALY-BMS] ");
|
||||
logging.print(msg);
|
||||
for (int i = 0; i < len; i++) {
|
||||
Serial.print(buff[i] >> 4, HEX);
|
||||
Serial.print(buff[i] & 0xf, HEX);
|
||||
Serial.print(" ");
|
||||
logging.print(buff[i] >> 4, HEX);
|
||||
logging.print(buff[i] & 0xf, HEX);
|
||||
logging.print(" ");
|
||||
}
|
||||
Serial.println();
|
||||
logging.println();
|
||||
}
|
||||
#endif
|
||||
|
||||
void decode_packet(uint8_t command, uint8_t data[8]) {
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
|
@ -180,9 +178,7 @@ void DalyBms::transmit_rs485(unsigned long currentMillis) {
|
|||
tx_buff[2] = nextCommand;
|
||||
tx_buff[3] = 8;
|
||||
tx_buff[12] = calculate_checksum(tx_buff);
|
||||
#ifdef DEBUG_VIA_USB
|
||||
dump_buff("transmitting: ", tx_buff, 13);
|
||||
#endif
|
||||
Serial2.write(tx_buff, 13);
|
||||
nextCommand++;
|
||||
if (nextCommand > 0x98)
|
||||
|
@ -202,17 +198,12 @@ void DalyBms::receive() {
|
|||
if (recv_len > 0 && recv_buff[0] != 0xA5 || recv_len > 1 && recv_buff[1] != 0x01 ||
|
||||
recv_len > 2 && (recv_buff[2] < 0x90 || recv_buff[2] > 0x98) || recv_len > 3 && recv_buff[3] != 8 ||
|
||||
recv_len > 12 && recv_buff[12] != calculate_checksum(recv_buff)) {
|
||||
|
||||
#ifdef DEBUG_VIA_USB
|
||||
dump_buff("dropping partial rx: ", recv_buff, recv_len);
|
||||
#endif
|
||||
recv_len = 0;
|
||||
}
|
||||
|
||||
if (recv_len > 12) {
|
||||
#ifdef DEBUG_VIA_USB
|
||||
dump_buff("decoding successfull rx: ", recv_buff, recv_len);
|
||||
#endif
|
||||
decode_packet(recv_buff[2], &recv_buff[4]);
|
||||
recv_len = 0;
|
||||
lastPacket = millis();
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#include "GEELY-GEOMETRY-C-BATTERY.h"
|
||||
#include "../communication/can/comm_can.h"
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../datalayer/datalayer_extended.h" //For "More battery info" webpage
|
||||
#include "../datalayer/datalayer_extended.h"
|
||||
#include "../devboard/utils/events.h"
|
||||
|
||||
/* TODO
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#ifndef GEELY_GEOMETRY_C_BATTERY_H
|
||||
#define GEELY_GEOMETRY_C_BATTERY_H
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../datalayer/datalayer_extended.h"
|
||||
#include "CanBattery.h"
|
||||
#include "GEELY-GEOMETRY-C-HTML.h"
|
||||
|
||||
|
@ -11,6 +10,20 @@
|
|||
|
||||
class GeelyGeometryCBattery : public CanBattery {
|
||||
public:
|
||||
// Use this constructor for the second battery.
|
||||
GeelyGeometryCBattery(DATALAYER_BATTERY_TYPE* datalayer_ptr, DATALAYER_INFO_GEELY_GEOMETRY_C* extended,
|
||||
CAN_Interface targetCan)
|
||||
: CanBattery(targetCan) {
|
||||
datalayer_battery = datalayer_ptr;
|
||||
|
||||
battery_voltage = 0;
|
||||
}
|
||||
// Use the default constructor to create the first or single battery.
|
||||
GeelyGeometryCBattery() {
|
||||
datalayer_battery = &datalayer.battery;
|
||||
datalayer_geometryc = &datalayer_extended.geometryC;
|
||||
}
|
||||
|
||||
virtual void setup(void);
|
||||
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
|
||||
virtual void update_values();
|
||||
|
@ -20,6 +33,11 @@ class GeelyGeometryCBattery : public CanBattery {
|
|||
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
|
||||
|
||||
private:
|
||||
GeelyGeometryCHtmlRenderer renderer;
|
||||
|
||||
DATALAYER_BATTERY_TYPE* datalayer_battery;
|
||||
DATALAYER_INFO_GEELY_GEOMETRY_C* datalayer_geometryc;
|
||||
|
||||
static const int POLL_SOC = 0x4B35;
|
||||
static const int POLL_CC2_VOLTAGE = 0x4BCF;
|
||||
static const int POLL_CELL_MAX_VOLTAGE_NUMBER = 0x4B1E;
|
||||
|
@ -41,8 +59,6 @@ class GeelyGeometryCBattery : public CanBattery {
|
|||
static const int POLL_MULTI_HARDWARE_VERSION = 0x4B6B;
|
||||
static const int POLL_MULTI_SOFTWARE_VERSION = 0x4B6C;
|
||||
|
||||
GeelyGeometryCHtmlRenderer renderer;
|
||||
|
||||
static const int MAX_PACK_VOLTAGE_70_DV = 4420; //70kWh
|
||||
static const int MIN_PACK_VOLTAGE_70_DV = 2860;
|
||||
static const int MAX_PACK_VOLTAGE_53_DV = 4160; //53kWh
|
||||
|
@ -51,9 +67,6 @@ class GeelyGeometryCBattery : public CanBattery {
|
|||
static const int MAX_CELL_VOLTAGE_MV = 4250; //Battery is put into emergency stop if one cell goes over this value
|
||||
static const int MIN_CELL_VOLTAGE_MV = 2700; //Battery is put into emergency stop if one cell goes below this value
|
||||
|
||||
DATALAYER_BATTERY_TYPE* datalayer_battery;
|
||||
DATALAYER_INFO_GEELY_GEOMETRY_C* datalayer_geometryc;
|
||||
|
||||
CAN_frame GEELY_191 = {.FD = false, //PAS_APA_Status , 10ms
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
#ifndef _GEELY_GEOMETRY_C_HTML_H
|
||||
#define _GEELY_GEOMETRY_C_HTML_H
|
||||
|
||||
#include <cstring>
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../datalayer/datalayer_extended.h"
|
||||
#include "../devboard/webserver/BatteryHtmlRenderer.h"
|
||||
|
||||
|
@ -10,7 +8,6 @@ class GeelyGeometryCHtmlRenderer : public BatteryHtmlRenderer {
|
|||
public:
|
||||
String get_status_html() {
|
||||
String content;
|
||||
|
||||
char readableSerialNumber[29]; // One extra space for null terminator
|
||||
memcpy(readableSerialNumber, datalayer_extended.geometryC.BatterySerialNumber,
|
||||
sizeof(datalayer_extended.geometryC.BatterySerialNumber));
|
||||
|
@ -52,7 +49,6 @@ class GeelyGeometryCHtmlRenderer : public BatteryHtmlRenderer {
|
|||
"<h4>Module 5 temperature: " + String(datalayer_extended.geometryC.ModuleTemperatures[4]) + " °C</h4>";
|
||||
content +=
|
||||
"<h4>Module 6 temperature: " + String(datalayer_extended.geometryC.ModuleTemperatures[5]) + " °C</h4>";
|
||||
|
||||
return content;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -12,7 +12,8 @@
|
|||
|
||||
class HyundaiIoniq28Battery : public CanBattery {
|
||||
public:
|
||||
HyundaiIoniq28Battery() : renderer(*this) {}
|
||||
// Use the default constructor to create the first or single battery.
|
||||
HyundaiIoniq28Battery() : renderer(*this) { datalayer_battery = &datalayer.battery; }
|
||||
|
||||
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
|
||||
|
||||
|
|
|
@ -75,12 +75,9 @@ void ImievCZeroIonBattery::
|
|||
}
|
||||
|
||||
if (!BMU_Detected) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("BMU not detected, check wiring!");
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Battery Values");
|
||||
logging.print("BMU SOC: ");
|
||||
logging.print(BMU_SOC);
|
||||
|
@ -90,15 +87,6 @@ void ImievCZeroIonBattery::
|
|||
logging.print(BMU_PackVoltage);
|
||||
logging.print(" BMU_Power: ");
|
||||
logging.print(BMU_Power);
|
||||
logging.print(" Cell max voltage: ");
|
||||
logging.print(max_volt_cel);
|
||||
logging.print(" Cell min voltage: ");
|
||||
logging.print(min_volt_cel);
|
||||
logging.print(" Cell max temp: ");
|
||||
logging.print(max_temp_cel);
|
||||
logging.print(" Cell min temp: ");
|
||||
logging.println(min_temp_cel);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ImievCZeroIonBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
||||
|
|
|
@ -102,21 +102,6 @@ void JaguarIpaceBattery::update_values() {
|
|||
} else {
|
||||
clear_event(EVENT_BATTERY_ISOLATION);
|
||||
}
|
||||
|
||||
/*Finally print out values to serial if configured to do so*/
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Values going to inverter");
|
||||
print_units("SOH%: ", (datalayer.battery.status.soh_pptt * 0.01), "% ");
|
||||
print_units(", SOC%: ", (datalayer.battery.status.reported_soc * 0.01), "% ");
|
||||
print_units(", Voltage: ", (datalayer.battery.status.voltage_dV * 0.1), "V ");
|
||||
print_units(", Max discharge power: ", datalayer.battery.status.max_discharge_power_W, "W ");
|
||||
print_units(", Max charge power: ", datalayer.battery.status.max_charge_power_W, "W ");
|
||||
print_units(", Max temp: ", (datalayer.battery.status.temperature_max_dC * 0.1), "°C ");
|
||||
print_units(", Min temp: ", (datalayer.battery.status.temperature_min_dC * 0.1), "°C ");
|
||||
print_units(", Max cell voltage: ", datalayer.battery.status.cell_max_voltage_mV, "mV ");
|
||||
print_units(", Min cell voltage: ", datalayer.battery.status.cell_min_voltage_mV, "mV ");
|
||||
logging.println("");
|
||||
#endif
|
||||
}
|
||||
|
||||
void JaguarIpaceBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
||||
|
|
|
@ -55,7 +55,6 @@ uint16_t KiaEGmpBattery::estimateSOC(uint16_t packVoltage, uint16_t cellCount, i
|
|||
// Calculate average cell voltage in millivolts
|
||||
uint16_t avgCellVoltage = compensatedPackVoltageMv / cellCount;
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
logging.print("Pack: ");
|
||||
logging.print(packVoltage / 10.0);
|
||||
logging.print("V, Current: ");
|
||||
|
@ -67,7 +66,6 @@ uint16_t KiaEGmpBattery::estimateSOC(uint16_t packVoltage, uint16_t cellCount, i
|
|||
logging.print("V, Avg Cell: ");
|
||||
logging.print(avgCellVoltage);
|
||||
logging.println("mV");
|
||||
#endif
|
||||
|
||||
// Use the cell voltage lookup table to estimate SOC
|
||||
return estimateSOCFromCell(avgCellVoltage);
|
||||
|
@ -187,7 +185,6 @@ void KiaEGmpBattery::update_values() {
|
|||
|
||||
/* Safeties verified. Perform USB serial printout if configured to do so */
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println(); //sepatator
|
||||
logging.println("Values from battery: ");
|
||||
logging.print("SOC BMS: ");
|
||||
|
@ -244,7 +241,6 @@ void KiaEGmpBattery::update_values() {
|
|||
logging.print(" | Inverter ");
|
||||
logging.print(inverterVoltage);
|
||||
logging.println(" Volts");
|
||||
#endif
|
||||
}
|
||||
|
||||
// Getter implementations for HTML renderer
|
||||
|
|
|
@ -171,9 +171,7 @@ uint8_t vw_crc_calc(uint8_t* inputBytes, uint8_t length, uint32_t address) {
|
|||
magicByte = MB16A954A6[counter];
|
||||
break;
|
||||
default: // this won't lead to correct CRC checksums
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Checksum request unknown");
|
||||
#endif
|
||||
magicByte = 0x00;
|
||||
break;
|
||||
}
|
||||
|
@ -202,15 +200,15 @@ uint8_t vw_crc_calc(uint8_t* inputBytes, uint8_t length, uint32_t address) {
|
|||
void MebBattery::
|
||||
update_values() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
|
||||
|
||||
datalayer.battery.status.real_soc = battery_SOC * 5; //*0.05*100
|
||||
datalayer_battery->status.real_soc = battery_SOC * 5; //*0.05*100
|
||||
|
||||
datalayer.battery.status.voltage_dV = BMS_voltage * 2.5; // *0.25*10
|
||||
datalayer_battery->status.voltage_dV = BMS_voltage * 2.5; // *0.25*10
|
||||
|
||||
datalayer.battery.status.current_dA = (BMS_current - 16300); // 0.1 * 10
|
||||
datalayer_battery->status.current_dA = (BMS_current - 16300); // 0.1 * 10
|
||||
|
||||
if (nof_cells_determined) {
|
||||
datalayer.battery.info.total_capacity_Wh =
|
||||
((float)datalayer.battery.info.number_of_cells) * 3.67 * ((float)BMS_capacity_ah) * 0.2 * 1.02564;
|
||||
datalayer_battery->info.total_capacity_Wh =
|
||||
((float)datalayer_battery->info.number_of_cells) * 3.67 * ((float)BMS_capacity_ah) * 0.2 * 1.02564;
|
||||
// The factor 1.02564 = 1/0.975 is to correct for bottom 2.5% which is reported by the remaining_capacity_Wh,
|
||||
// but which is not actually usable, but if we do not include it, the remaining_capacity_Wh can be larger than
|
||||
// the total_capacity_Wh.
|
||||
|
@ -218,32 +216,32 @@ void MebBattery::
|
|||
// total_capacity_Wh calculated above.
|
||||
|
||||
int Wh_max = 61832 * 0.935; // 108 cells
|
||||
if (datalayer.battery.info.number_of_cells <= 84)
|
||||
if (datalayer_battery->info.number_of_cells <= 84)
|
||||
Wh_max = 48091 * 0.9025;
|
||||
else if (datalayer.battery.info.number_of_cells <= 96)
|
||||
else if (datalayer_battery->info.number_of_cells <= 96)
|
||||
Wh_max = 82442 * 0.9025;
|
||||
if (BMS_capacity_ah > 0)
|
||||
datalayer.battery.status.soh_pptt = 10000 * datalayer.battery.info.total_capacity_Wh / (Wh_max * 1.02564);
|
||||
datalayer_battery->status.soh_pptt = 10000 * datalayer_battery->info.total_capacity_Wh / (Wh_max * 1.02564);
|
||||
}
|
||||
|
||||
datalayer.battery.status.remaining_capacity_Wh = usable_energy_amount_Wh * 5;
|
||||
datalayer_battery->status.remaining_capacity_Wh = usable_energy_amount_Wh * 5;
|
||||
|
||||
datalayer.battery.status.max_charge_power_W = (max_charge_power_watt * 100);
|
||||
datalayer_battery->status.max_charge_power_W = (max_charge_power_watt * 100);
|
||||
|
||||
datalayer.battery.status.max_discharge_power_W = (max_discharge_power_watt * 100);
|
||||
datalayer_battery->status.max_discharge_power_W = (max_discharge_power_watt * 100);
|
||||
|
||||
//Power in watts, Negative = charging batt
|
||||
datalayer.battery.status.active_power_W =
|
||||
((datalayer.battery.status.voltage_dV * datalayer.battery.status.current_dA) / 100);
|
||||
datalayer_battery->status.active_power_W =
|
||||
((datalayer_battery->status.voltage_dV * datalayer_battery->status.current_dA) / 100);
|
||||
|
||||
// datalayer.battery.status.temperature_min_dC = actual_temperature_lowest_C*5 -400; // We use the value below, because it has better accuracy
|
||||
datalayer.battery.status.temperature_min_dC = (battery_min_temp * 10) / 64;
|
||||
datalayer_battery->status.temperature_min_dC = (battery_min_temp * 10) / 64;
|
||||
|
||||
// datalayer.battery.status.temperature_max_dC = actual_temperature_highest_C*5 -400; // We use the value below, because it has better accuracy
|
||||
datalayer.battery.status.temperature_max_dC = (battery_max_temp * 10) / 64;
|
||||
datalayer_battery->status.temperature_max_dC = (battery_max_temp * 10) / 64;
|
||||
|
||||
//Map all cell voltages to the global array
|
||||
memcpy(datalayer.battery.status.cell_voltages_mV, cellvoltages_polled, 108 * sizeof(uint16_t));
|
||||
memcpy(datalayer_battery->status.cell_voltages_mV, cellvoltages_polled, 108 * sizeof(uint16_t));
|
||||
|
||||
if (service_disconnect_switch_missing) {
|
||||
set_event(EVENT_HVIL_FAILURE, 1);
|
||||
|
@ -310,9 +308,7 @@ void MebBattery::
|
|||
void MebBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
||||
last_can_msg_timestamp = millis();
|
||||
if (first_can_msg == 0) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.printf("MEB: First CAN msg received\n");
|
||||
#endif
|
||||
first_can_msg = last_can_msg_timestamp;
|
||||
}
|
||||
|
||||
|
@ -326,9 +322,7 @@ void MebBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
if (rx_frame.data.u8[0] !=
|
||||
vw_crc_calc(rx_frame.data.u8, rx_frame.DLC, rx_frame.ID)) { //If CRC does not match calc
|
||||
datalayer.battery.status.CAN_error_counter++;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.printf("MEB: Msg 0x%04X CRC error\n", rx_frame.ID);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
default:
|
||||
|
@ -700,29 +694,23 @@ void MebBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
case 3: // EXTERN CHARGING
|
||||
case 4: // AC_CHARGING
|
||||
case 6: // DC_CHARGING
|
||||
#ifdef DEBUG_LOG
|
||||
if (!datalayer.system.status.battery_allows_contactor_closing)
|
||||
logging.printf("MEB: Contactors closed\n");
|
||||
#endif
|
||||
if (datalayer.battery.status.real_bms_status != BMS_FAULT)
|
||||
datalayer.battery.status.real_bms_status = BMS_ACTIVE;
|
||||
datalayer.system.status.battery_allows_contactor_closing = true;
|
||||
hv_requested = false;
|
||||
break;
|
||||
case 5: // Error
|
||||
#ifdef DEBUG_LOG
|
||||
if (datalayer.system.status.battery_allows_contactor_closing)
|
||||
logging.printf("MEB: Contactors opened\n");
|
||||
#endif
|
||||
datalayer.battery.status.real_bms_status = BMS_FAULT;
|
||||
datalayer.system.status.battery_allows_contactor_closing = false;
|
||||
hv_requested = false;
|
||||
break;
|
||||
case 7: // Init
|
||||
#ifdef DEBUG_LOG
|
||||
if (datalayer.system.status.battery_allows_contactor_closing)
|
||||
logging.printf("MEB: Contactors opened\n");
|
||||
#endif
|
||||
if (datalayer.battery.status.real_bms_status != BMS_FAULT)
|
||||
datalayer.battery.status.real_bms_status = BMS_STANDBY;
|
||||
datalayer.system.status.battery_allows_contactor_closing = false;
|
||||
|
@ -730,10 +718,8 @@ void MebBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
break;
|
||||
case 2: // BALANCING
|
||||
default:
|
||||
#ifdef DEBUG_LOG
|
||||
if (datalayer.system.status.battery_allows_contactor_closing)
|
||||
logging.printf("MEB: Contactors opened\n");
|
||||
#endif
|
||||
if (datalayer.battery.status.real_bms_status != BMS_FAULT)
|
||||
datalayer.battery.status.real_bms_status = BMS_STANDBY;
|
||||
datalayer.system.status.battery_allows_contactor_closing = false;
|
||||
|
@ -1276,10 +1262,8 @@ void MebBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
handle_obd_frame(rx_frame);
|
||||
break;
|
||||
default:
|
||||
#ifdef DEBUG_LOG
|
||||
logging.printf("Unknown CAN frame received:\n");
|
||||
dump_can_frame(rx_frame, MSG_RX);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
|
@ -1292,10 +1276,8 @@ void MebBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
void MebBattery::transmit_can(unsigned long currentMillis) {
|
||||
|
||||
if (currentMillis - last_can_msg_timestamp > 500) {
|
||||
#ifdef DEBUG_LOG
|
||||
if (first_can_msg)
|
||||
logging.printf("MEB: No CAN msg received for 500ms\n");
|
||||
#endif
|
||||
can_msg_received = RX_DEFAULT;
|
||||
first_can_msg = 0;
|
||||
if (datalayer.battery.status.real_bms_status != BMS_FAULT) {
|
||||
|
@ -1373,7 +1355,6 @@ void MebBattery::transmit_can(unsigned long currentMillis) {
|
|||
((int32_t)datalayer_extended.meb.BMS_voltage_intermediate_dV)) < 200))))) {
|
||||
hv_requested = true;
|
||||
datalayer.system.settings.start_precharging = false;
|
||||
#ifdef DEBUG_LOG
|
||||
if (MEB_503.data.u8[3] == BMS_TARGET_HV_OFF) {
|
||||
logging.printf("MEB: Requesting HV\n");
|
||||
}
|
||||
|
@ -1385,7 +1366,6 @@ void MebBattery::transmit_can(unsigned long currentMillis) {
|
|||
logging.printf("MEB: Precharge bit set to inactive\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
MEB_503.data.u8[1] =
|
||||
0x30 | (datalayer.system.status.precharge_status == AUTO_PRECHARGE_PRECHARGING ? 0x80 : 0x00);
|
||||
MEB_503.data.u8[3] = BMS_TARGET_AC_CHARGING;
|
||||
|
@ -1399,7 +1379,6 @@ void MebBattery::transmit_can(unsigned long currentMillis) {
|
|||
datalayer.system.settings.start_precharging = true;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
if (MEB_503.data.u8[3] != BMS_TARGET_HV_OFF) {
|
||||
logging.printf("MEB: Requesting HV_OFF\n");
|
||||
}
|
||||
|
@ -1411,7 +1390,6 @@ void MebBattery::transmit_can(unsigned long currentMillis) {
|
|||
logging.printf("MEB: Precharge bit set to inactive\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
MEB_503.data.u8[1] =
|
||||
0x10 | (datalayer.system.status.precharge_status == AUTO_PRECHARGE_PRECHARGING ? 0x80 : 0x00);
|
||||
MEB_503.data.u8[3] = BMS_TARGET_HV_OFF;
|
||||
|
|
|
@ -9,6 +9,19 @@
|
|||
|
||||
class MebBattery : public CanBattery {
|
||||
public:
|
||||
// Use this constructor for the second battery.
|
||||
MebBattery(DATALAYER_BATTERY_TYPE* datalayer_ptr, DATALAYER_INFO_MEB* extended, CAN_Interface targetCan)
|
||||
: CanBattery(targetCan) {
|
||||
datalayer_battery = datalayer_ptr;
|
||||
|
||||
BMS_voltage = 0;
|
||||
}
|
||||
// Use the default constructor to create the first or single battery.
|
||||
MebBattery() {
|
||||
datalayer_battery = &datalayer.battery;
|
||||
datalayer_meb = &datalayer_extended.meb;
|
||||
}
|
||||
|
||||
virtual void setup(void);
|
||||
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
|
||||
virtual void update_values();
|
||||
|
@ -21,6 +34,10 @@ class MebBattery : public CanBattery {
|
|||
|
||||
private:
|
||||
MebHtmlRenderer renderer;
|
||||
|
||||
DATALAYER_BATTERY_TYPE* datalayer_battery;
|
||||
DATALAYER_INFO_MEB* datalayer_meb;
|
||||
|
||||
static const int MAX_PACK_VOLTAGE_84S_DV = 3528; //5000 = 500.0V
|
||||
static const int MIN_PACK_VOLTAGE_84S_DV = 2520;
|
||||
static const int MAX_PACK_VOLTAGE_96S_DV = 4032;
|
||||
|
|
|
@ -147,11 +147,9 @@ void MgHsPHEVBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
// 15 = isolation fault
|
||||
// 0/8 = checking
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
if (rx_frame.data.u8[1] != previousState) {
|
||||
logging.printf("MG_HS_PHEV: Battery status changed to %d (%d)\n", rx_frame.data.u8[1], rx_frame.data.u8[0]);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (rx_frame.data.u8[1] == 0xf && previousState != 0xf) {
|
||||
// Isolation fault, set event
|
||||
|
@ -168,18 +166,14 @@ void MgHsPHEVBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
// A weird 'stuck' state where the battery won't reconnect
|
||||
datalayer.system.status.battery_allows_contactor_closing = false;
|
||||
if (!datalayer.system.status.BMS_startup_in_progress) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.printf("MG_HS_PHEV: Stuck, resetting.\n");
|
||||
#endif
|
||||
start_bms_reset();
|
||||
}
|
||||
} else if (rx_frame.data.u8[1] == 0xf) {
|
||||
// A fault state (likely isolation failure)
|
||||
datalayer.system.status.battery_allows_contactor_closing = false;
|
||||
if (!datalayer.system.status.BMS_startup_in_progress) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.printf("MG_HS_PHEV: Fault, resetting.\n");
|
||||
#endif
|
||||
start_bms_reset();
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -51,39 +51,6 @@ void RenaultKangooBattery::
|
|||
datalayer.battery.status.cell_min_voltage_mV = LB_Cell_Min_Voltage;
|
||||
|
||||
datalayer.battery.status.cell_max_voltage_mV = LB_Cell_Max_Voltage;
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Values going to inverter:");
|
||||
logging.print("SOH%: ");
|
||||
logging.print(datalayer.battery.status.soh_pptt);
|
||||
logging.print(", SOC% scaled: ");
|
||||
logging.print(datalayer.battery.status.reported_soc);
|
||||
logging.print(", Voltage: ");
|
||||
logging.print(datalayer.battery.status.voltage_dV);
|
||||
logging.print(", Max discharge power: ");
|
||||
logging.print(datalayer.battery.status.max_discharge_power_W);
|
||||
logging.print(", Max charge power: ");
|
||||
logging.print(datalayer.battery.status.max_charge_power_W);
|
||||
logging.print(", Max temp: ");
|
||||
logging.print(datalayer.battery.status.temperature_max_dC);
|
||||
logging.print(", Min temp: ");
|
||||
logging.print(datalayer.battery.status.temperature_min_dC);
|
||||
logging.print(", BMS Status (3=OK): ");
|
||||
logging.print(datalayer.battery.status.bms_status);
|
||||
|
||||
logging.println("Battery values: ");
|
||||
logging.print("Real SOC: ");
|
||||
logging.print(LB_SOC);
|
||||
logging.print(", Current: ");
|
||||
logging.print(LB_Current);
|
||||
logging.print(", kWh remain: ");
|
||||
logging.print(LB_kWh_Remaining);
|
||||
logging.print(", max mV: ");
|
||||
logging.print(LB_Cell_Max_Voltage);
|
||||
logging.print(", min mV: ");
|
||||
logging.print(LB_Cell_Min_Voltage);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void RenaultKangooBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
||||
|
|
|
@ -403,7 +403,7 @@ void RenaultZoeGen2Battery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
battery_balancing_shunts[94] = (rx_frame.data.u8[1] & 0x02) >> 1;
|
||||
battery_balancing_shunts[95] = (rx_frame.data.u8[1] & 0x01);
|
||||
|
||||
memcpy(datalayer.battery.status.cell_balancing_status, battery_balancing_shunts, 96 * sizeof(bool));
|
||||
memcpy(datalayer_battery->status.cell_balancing_status, battery_balancing_shunts, 96 * sizeof(bool));
|
||||
}
|
||||
break;
|
||||
case POLL_ENERGY_COMPLETE:
|
||||
|
|
|
@ -720,13 +720,9 @@ void TeslaBattery::
|
|||
//Start the BMS ECU reset statemachine, only if contactors are OPEN and BMS ECU allows it
|
||||
stateMachineBMSReset = 0;
|
||||
datalayer.battery.settings.user_requests_tesla_bms_reset = false;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("BMS reset requested");
|
||||
#endif //DEBUG_LOG
|
||||
} else {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("ERROR: BMS reset failed due to contactors not being open, or BMS ECU not allowing it");
|
||||
#endif //DEBUG_LOG
|
||||
stateMachineBMSReset = 0xFF;
|
||||
datalayer.battery.settings.user_requests_tesla_bms_reset = false;
|
||||
}
|
||||
|
@ -736,13 +732,9 @@ void TeslaBattery::
|
|||
//Start the SOC reset statemachine, only if SOC < 15% or > 90%
|
||||
stateMachineSOCReset = 0;
|
||||
datalayer.battery.settings.user_requests_tesla_soc_reset = false;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("SOC reset requested");
|
||||
#endif //DEBUG_LOG
|
||||
} else {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("ERROR: SOC reset failed due to SOC not being less than 15 or greater than 90");
|
||||
#endif //DEBUG_LOG
|
||||
stateMachineSOCReset = 0xFF;
|
||||
datalayer.battery.settings.user_requests_tesla_soc_reset = false;
|
||||
}
|
||||
|
@ -1002,8 +994,6 @@ void TeslaBattery::
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
|
||||
printFaultCodesIfActive();
|
||||
logging.print("BMS Contactors State: ");
|
||||
logging.print(getBMSContactorState(battery_contactor)); // Display what state the BMS thinks the contactors are in
|
||||
|
@ -1061,7 +1051,6 @@ void TeslaBattery::
|
|||
logging.printf("PCS_ambientTemp: %.2f°C, DCDC_Temp: %.2f°C, ChgPhA: %.2f°C, ChgPhB: %.2f°C, ChgPhC: %.2f°C.\n",
|
||||
PCS_ambientTemp * 0.1 + 40, PCS_dcdcTemp * 0.1 + 40, PCS_chgPhATemp * 0.1 + 40,
|
||||
PCS_chgPhBTemp * 0.1 + 40, PCS_chgPhCTemp * 0.1 + 40);
|
||||
#endif //DEBUG_LOG
|
||||
}
|
||||
|
||||
void TeslaBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
||||
|
@ -1911,9 +1900,7 @@ void TeslaBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
if (stateMachineBMSQuery != 0xFF && stateMachineBMSReset == 0xFF) {
|
||||
if (memcmp(rx_frame.data.u8, "\x02\x50\x03\xAA\xAA\xAA\xAA\xAA", 8) == 0) {
|
||||
//Received initial response, proceed to actual query
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("CAN UDS: Received BMS query initial handshake reply");
|
||||
#endif //DEBUG_LOG
|
||||
stateMachineBMSQuery = 1;
|
||||
break;
|
||||
}
|
||||
|
@ -1922,9 +1909,7 @@ void TeslaBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
battery_partNumber[0] = rx_frame.data.u8[5];
|
||||
battery_partNumber[1] = rx_frame.data.u8[6];
|
||||
battery_partNumber[2] = rx_frame.data.u8[7];
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("CAN UDS: Received BMS query data frame");
|
||||
#endif //DEBUG_LOG
|
||||
stateMachineBMSQuery = 2;
|
||||
break;
|
||||
}
|
||||
|
@ -1937,32 +1922,25 @@ void TeslaBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
battery_partNumber[7] = rx_frame.data.u8[5];
|
||||
battery_partNumber[8] = rx_frame.data.u8[6];
|
||||
battery_partNumber[9] = rx_frame.data.u8[7];
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("CAN UDS: Received BMS query second data frame");
|
||||
#endif //DEBUG_LOG
|
||||
break;
|
||||
}
|
||||
if (memcmp(&rx_frame.data.u8[0], "\x22", 1) == 0) {
|
||||
//Final part of part number
|
||||
battery_partNumber[10] = rx_frame.data.u8[1];
|
||||
battery_partNumber[11] = rx_frame.data.u8[2];
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("CAN UDS: Received BMS query final data frame");
|
||||
#endif //DEBUG_LOG
|
||||
break;
|
||||
}
|
||||
if (memcmp(rx_frame.data.u8, "\x23\x00\x00\x00\xAA\xAA\xAA\xAA", 8) == 0) {
|
||||
//Received final frame
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("CAN UDS: Received BMS query termination frame");
|
||||
#endif //DEBUG_LOG
|
||||
parsed_battery_partNumber = true;
|
||||
stateMachineBMSQuery = 0xFF;
|
||||
break;
|
||||
}
|
||||
}
|
||||
//BMS Reset
|
||||
#ifdef DEBUG_LOG
|
||||
if (stateMachineBMSQuery == 0xFF) { // Make sure this is reset request not query
|
||||
if (memcmp(rx_frame.data.u8, "\x02\x67\x06\xAA\xAA\xAA\xAA\xAA", 8) == 0) {
|
||||
logging.println("CAN UDS: ECU unlocked");
|
||||
|
@ -1972,7 +1950,6 @@ void TeslaBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
logging.println("CAN UDS: ECU reset positive response, 1 second downtime");
|
||||
}
|
||||
}
|
||||
#endif //DEBUG_LOG
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -2375,25 +2352,19 @@ void TeslaBattery::transmit_can(unsigned long currentMillis) {
|
|||
switch (stateMachineBMSQuery) {
|
||||
case 0:
|
||||
//Initial request
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("CAN UDS: Sending BMS query initial handshake");
|
||||
#endif //DEBUG_LOG
|
||||
TESLA_602.data = {0x02, 0x10, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
transmit_can_frame(&TESLA_602);
|
||||
break;
|
||||
case 1:
|
||||
//Send query
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("CAN UDS: Sending BMS query for pack part number");
|
||||
#endif //DEBUG_LOG
|
||||
TESLA_602.data = {0x03, 0x22, 0xF0, 0x14, 0x00, 0x00, 0x00, 0x00};
|
||||
transmit_can_frame(&TESLA_602);
|
||||
break;
|
||||
case 2:
|
||||
//Flow control
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("CAN UDS: Sending BMS query flow control");
|
||||
#endif //DEBUG_LOG
|
||||
TESLA_602.data = {0x30, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
transmit_can_frame(&TESLA_602);
|
||||
break;
|
||||
|
|
|
@ -42,21 +42,6 @@ void TestFakeBattery::
|
|||
|
||||
//Fake that we get CAN messages
|
||||
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
|
||||
/*Finally print out values to serial if configured to do so*/
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("FAKE Values going to inverter");
|
||||
print_units("SOH%: ", (datalayer_battery->status.soh_pptt * 0.01), "% ");
|
||||
print_units(", SOC%: ", (datalayer_battery->status.reported_soc * 0.01), "% ");
|
||||
print_units(", Voltage: ", (datalayer_battery->status.voltage_dV * 0.1), "V ");
|
||||
print_units(", Max discharge power: ", datalayer_battery->status.max_discharge_power_W, "W ");
|
||||
print_units(", Max charge power: ", datalayer_battery->status.max_charge_power_W, "W ");
|
||||
print_units(", Max temp: ", (datalayer_battery->status.temperature_max_dC * 0.1), "°C ");
|
||||
print_units(", Min temp: ", (datalayer_battery->status.temperature_min_dC * 0.1), "°C ");
|
||||
print_units(", Max cell voltage: ", datalayer_battery->status.cell_max_voltage_mV, "mV ");
|
||||
print_units(", Min cell voltage: ", datalayer_battery->status.cell_min_voltage_mV, "mV ");
|
||||
logging.println("");
|
||||
#endif
|
||||
}
|
||||
|
||||
void TestFakeBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
||||
|
|
|
@ -85,51 +85,6 @@ void VolvoSpaBattery::
|
|||
datalayer.battery.info.total_capacity_Wh = 69511;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
uint8_t cnt = 0;
|
||||
logging.print("BMS reported SOC%: ");
|
||||
logging.println(SOC_BMS);
|
||||
logging.print("Calculated SOC%: ");
|
||||
logging.println(SOC_CALC);
|
||||
logging.print("Rescaled SOC%: ");
|
||||
logging.println(datalayer.battery.status.reported_soc / 100);
|
||||
logging.print("Battery current: ");
|
||||
logging.println(BATT_I);
|
||||
logging.print("Battery voltage: ");
|
||||
logging.println(BATT_U);
|
||||
logging.print("Battery maximum voltage limit: ");
|
||||
logging.println(MAX_U);
|
||||
logging.print("Battery minimum voltage limit: ");
|
||||
logging.println(MIN_U);
|
||||
logging.print("Discharge limit: ");
|
||||
logging.println(HvBattPwrLimDchaSoft);
|
||||
logging.print("Battery Error Indication: ");
|
||||
logging.println(BATT_ERR_INDICATION);
|
||||
logging.print("Maximum battery temperature: ");
|
||||
logging.println(BATT_T_MAX / 10);
|
||||
logging.print("Minimum battery temperature: ");
|
||||
logging.println(BATT_T_MIN / 10);
|
||||
logging.print("Average battery temperature: ");
|
||||
logging.println(BATT_T_AVG / 10);
|
||||
logging.print("BMS Highest cell voltage: ");
|
||||
logging.println(CELL_U_MAX * 10);
|
||||
logging.print("BMS Lowest cell voltage: ");
|
||||
logging.println(CELL_U_MIN * 10);
|
||||
logging.print("BMS Highest cell nr: ");
|
||||
logging.println(CELL_ID_U_MAX);
|
||||
logging.print("Highest cell voltage: ");
|
||||
logging.println(min_max_voltage[1]);
|
||||
logging.print("Lowest cell voltage: ");
|
||||
logging.println(min_max_voltage[0]);
|
||||
logging.print("Cell voltage,");
|
||||
while (cnt < 108) {
|
||||
logging.print(cell_voltages[cnt++]);
|
||||
logging.print(",");
|
||||
}
|
||||
cnt = 0;
|
||||
logging.println(";");
|
||||
#endif
|
||||
}
|
||||
|
||||
void VolvoSpaBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
||||
|
@ -140,9 +95,7 @@ void VolvoSpaBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
BATT_I = (0 - ((((rx_frame.data.u8[6] & 0x7F) * 256.0 + rx_frame.data.u8[7]) * 0.1) - 1638));
|
||||
else {
|
||||
BATT_I = 0;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("BATT_I not valid");
|
||||
#endif
|
||||
}
|
||||
|
||||
if ((rx_frame.data.u8[2] & 0x08) == 0x08)
|
||||
|
@ -195,9 +148,7 @@ void VolvoSpaBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
BATT_ERR_INDICATION = ((rx_frame.data.u8[0] & 0x40) >> 6);
|
||||
else {
|
||||
BATT_ERR_INDICATION = 0;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("BATT_ERR_INDICATION not valid");
|
||||
#endif
|
||||
}
|
||||
if ((rx_frame.data.u8[0] & 0x20) == 0x20) {
|
||||
BATT_T_MAX = ((rx_frame.data.u8[2] & 0x1F) * 256.0 + rx_frame.data.u8[3]);
|
||||
|
@ -207,9 +158,7 @@ void VolvoSpaBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
BATT_T_MAX = 0;
|
||||
BATT_T_MIN = 0;
|
||||
BATT_T_AVG = 0;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("BATT_T not valid");
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case 0x369:
|
||||
|
@ -217,9 +166,7 @@ void VolvoSpaBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
HvBattPwrLimDchaSoft = (((rx_frame.data.u8[6] & 0x03) * 256 + rx_frame.data.u8[6]) >> 2);
|
||||
} else {
|
||||
HvBattPwrLimDchaSoft = 0;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("HvBattPwrLimDchaSoft not valid");
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case 0x175:
|
||||
|
@ -246,9 +193,7 @@ void VolvoSpaBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
SOC_BMS = ((rx_frame.data.u8[6] & 0x03) * 256 + rx_frame.data.u8[7]);
|
||||
} else {
|
||||
SOC_BMS = 0;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("SOC_BMS not valid");
|
||||
#endif
|
||||
}
|
||||
|
||||
if ((rx_frame.data.u8[0] & 0x04) == 0x04) {
|
||||
|
|
|
@ -70,51 +70,6 @@ void VolvoSpaHybridBattery::
|
|||
for (int i = 0; i < 102; ++i) {
|
||||
datalayer.battery.status.cell_voltages_mV[i] = cell_voltages[i];
|
||||
}
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
logging.print("BMS reported SOC%: ");
|
||||
logging.println(SOC_BMS);
|
||||
logging.print("Calculated SOC%: ");
|
||||
logging.println(SOC_CALC);
|
||||
logging.print("Rescaled SOC%: ");
|
||||
logging.println(datalayer.battery.status.reported_soc / 100);
|
||||
logging.print("Battery current: ");
|
||||
logging.println(BATT_I);
|
||||
logging.print("Battery voltage: ");
|
||||
logging.println(BATT_U);
|
||||
logging.print("Battery maximum voltage limit: ");
|
||||
logging.println(MAX_U);
|
||||
logging.print("Battery minimum voltage limit: ");
|
||||
logging.println(MIN_U);
|
||||
logging.print("Remaining Energy: ");
|
||||
logging.println(remaining_capacity);
|
||||
logging.print("Discharge limit: ");
|
||||
logging.println(HvBattPwrLimDchaSoft);
|
||||
logging.print("Battery Error Indication: ");
|
||||
logging.println(BATT_ERR_INDICATION);
|
||||
logging.print("Maximum battery temperature: ");
|
||||
logging.println(BATT_T_MAX / 10);
|
||||
logging.print("Minimum battery temperature: ");
|
||||
logging.println(BATT_T_MIN / 10);
|
||||
logging.print("Average battery temperature: ");
|
||||
logging.println(BATT_T_AVG / 10);
|
||||
logging.print("BMS Highest cell voltage: ");
|
||||
logging.println(CELL_U_MAX);
|
||||
logging.print("BMS Lowest cell voltage: ");
|
||||
logging.println(CELL_U_MIN);
|
||||
logging.print("BMS Highest cell nr: ");
|
||||
logging.println(CELL_ID_U_MAX);
|
||||
logging.print("Highest cell voltage: ");
|
||||
logging.println(min_max_voltage[1]);
|
||||
logging.print("Lowest cell voltage: ");
|
||||
logging.println(min_max_voltage[0]);
|
||||
logging.print("Cell voltage,");
|
||||
while (cnt < 102) {
|
||||
logging.print(cell_voltages[cnt++]);
|
||||
logging.print(",");
|
||||
}
|
||||
logging.println(";");
|
||||
#endif
|
||||
}
|
||||
|
||||
void VolvoSpaHybridBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
||||
|
@ -125,9 +80,7 @@ void VolvoSpaHybridBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
BATT_I = (0 - ((((rx_frame.data.u8[6] & 0x7F) * 256.0 + rx_frame.data.u8[7]) * 0.1) - 1638));
|
||||
else {
|
||||
BATT_I = 0;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("BATT_I not valid");
|
||||
#endif
|
||||
}
|
||||
|
||||
if ((rx_frame.data.u8[2] & 0x08) == 0x08)
|
||||
|
@ -148,9 +101,7 @@ void VolvoSpaHybridBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
BATT_U = (((rx_frame.data.u8[0] & 0x07) * 256.0 + rx_frame.data.u8[1]) * 0.25);
|
||||
else {
|
||||
BATT_U = 0;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("BATT_U not valid");
|
||||
#endif
|
||||
}
|
||||
|
||||
if ((rx_frame.data.u8[0] & 0x40) == 0x40)
|
||||
|
@ -185,9 +136,7 @@ void VolvoSpaHybridBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
BATT_ERR_INDICATION = ((rx_frame.data.u8[0] & 0x40) >> 6);
|
||||
else {
|
||||
BATT_ERR_INDICATION = 0;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("BATT_ERR_INDICATION not valid");
|
||||
#endif
|
||||
}
|
||||
if ((rx_frame.data.u8[0] & 0x20) == 0x20) {
|
||||
BATT_T_MAX = ((rx_frame.data.u8[2] & 0x1F) * 256.0 + rx_frame.data.u8[3]);
|
||||
|
@ -197,9 +146,7 @@ void VolvoSpaHybridBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
BATT_T_MAX = 0;
|
||||
BATT_T_MIN = 0;
|
||||
BATT_T_AVG = 0;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("BATT_T not valid");
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case 0x369:
|
||||
|
@ -207,9 +154,7 @@ void VolvoSpaHybridBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
HvBattPwrLimDchaSoft = (((rx_frame.data.u8[6] & 0x03) * 256 + rx_frame.data.u8[6]) >> 2);
|
||||
} else {
|
||||
HvBattPwrLimDchaSoft = 0;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("HvBattPwrLimDchaSoft not valid");
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case 0x175:
|
||||
|
@ -240,9 +185,7 @@ void VolvoSpaHybridBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
SOC_BMS = ((rx_frame.data.u8[6] & 0x03) * 256 + rx_frame.data.u8[7]);
|
||||
} else {
|
||||
SOC_BMS = 0;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("SOC_BMS not valid");
|
||||
#endif
|
||||
}
|
||||
|
||||
if ((rx_frame.data.u8[0] & 0x04) == 0x04)
|
||||
|
@ -251,9 +194,7 @@ void VolvoSpaHybridBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
else {
|
||||
//CELL_U_MAX = 0;
|
||||
;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("CELL_U_MAX not valid");
|
||||
#endif
|
||||
}
|
||||
|
||||
if ((rx_frame.data.u8[0] & 0x02) == 0x02)
|
||||
|
@ -262,9 +203,7 @@ void VolvoSpaHybridBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
else {
|
||||
//CELL_U_MIN = 0;
|
||||
;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("CELL_U_MIN not valid");
|
||||
#endif
|
||||
}
|
||||
|
||||
if ((rx_frame.data.u8[0] & 0x08) == 0x08)
|
||||
|
@ -273,9 +212,7 @@ void VolvoSpaHybridBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
|||
else {
|
||||
//CELL_ID_U_MAX = 0;
|
||||
;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("CELL_ID_U_MAX not valid");
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case 0x635: // Diag request response
|
||||
|
@ -545,9 +482,7 @@ void VolvoSpaHybridBattery::transmit_can(unsigned long currentMillis) {
|
|||
previousMillis60s = currentMillis;
|
||||
if (true) {
|
||||
readCellVoltages();
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Requesting cell voltages");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,9 +71,6 @@ void ChevyVoltCharger::map_can_frame_to_variable(CAN_frame rx_frame) {
|
|||
datalayer.charger.CAN_charger_still_alive = CAN_STILL_ALIVE; // Let system know charger is sending CAN
|
||||
break;
|
||||
default:
|
||||
#ifdef DEBUG_LOG
|
||||
logging.printf("CAN Rcv unknown frame MsgID=%x\n", rx_frame.ID);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -146,7 +143,6 @@ void ChevyVoltCharger::transmit_can(unsigned long currentMillis) {
|
|||
transmit_can_frame(&charger_set_targets);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
/* Serial echo every 5s of charger stats */
|
||||
if (currentMillis - previousMillis5000ms >= INTERVAL_5_S) {
|
||||
previousMillis5000ms = currentMillis;
|
||||
|
@ -156,5 +152,4 @@ void ChevyVoltCharger::transmit_can(unsigned long currentMillis) {
|
|||
logging.printf("Charger mode=%s\n", (charger_mode > MODE_DISABLED) ? "Enabled" : "Disabled");
|
||||
logging.printf("Charger HVset=%uV,%uA finishCurrent=%uA\n", setpoint_HV_VDC, setpoint_HV_IDC, setpoint_HV_IDC_END);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#include "comm_can.h"
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include "../../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
||||
#include "../../lib/pierremolinaro-ACAN2517FD/ACAN2517FD.h"
|
||||
#include "../../lib/pierremolinaro-acan-esp32/ACAN_ESP32.h"
|
||||
#include "../../lib/pierremolinaro-acan2515/ACAN2515.h"
|
||||
#include "CanReceiver.h"
|
||||
#include "USER_SETTINGS.h"
|
||||
|
@ -19,8 +19,6 @@ struct CanReceiverRegistration {
|
|||
|
||||
static std::multimap<CAN_Interface, CanReceiverRegistration> can_receivers;
|
||||
|
||||
// Parameters
|
||||
CAN_device_t CAN_cfg; // CAN Config
|
||||
const uint8_t rx_queue_size = 10; // Receive Queue size
|
||||
volatile bool send_ok_native = 0;
|
||||
volatile bool send_ok_2515 = 0;
|
||||
|
@ -41,7 +39,10 @@ void register_can_receiver(CanReceiver* receiver, CAN_Interface interface, CAN_S
|
|||
DEBUG_PRINTF("CAN receiver registered, total: %d\n", can_receivers.size());
|
||||
}
|
||||
|
||||
static const uint32_t QUARTZ_FREQUENCY = CRYSTAL_FREQUENCY_MHZ * 1000000UL; //MHZ configured in USER_SETTINGS.h
|
||||
ACAN_ESP32_Settings* settingsespcan;
|
||||
|
||||
uint8_t user_selected_can_addon_crystal_frequency_mhz = 0;
|
||||
static uint32_t QUARTZ_FREQUENCY;
|
||||
SPIClass SPI2515;
|
||||
|
||||
ACAN2515* can2515;
|
||||
|
@ -59,6 +60,12 @@ bool native_can_initialized = false;
|
|||
|
||||
bool init_CAN() {
|
||||
|
||||
if (user_selected_can_addon_crystal_frequency_mhz > 0) {
|
||||
QUARTZ_FREQUENCY = user_selected_can_addon_crystal_frequency_mhz * 1000000UL;
|
||||
} else {
|
||||
QUARTZ_FREQUENCY = CRYSTAL_FREQUENCY_MHZ * 1000000UL;
|
||||
}
|
||||
|
||||
auto nativeIt = can_receivers.find(CAN_NATIVE);
|
||||
if (nativeIt != can_receivers.end()) {
|
||||
auto se_pin = esp32hal->CAN_SE_PIN();
|
||||
|
@ -73,18 +80,42 @@ bool init_CAN() {
|
|||
digitalWrite(se_pin, LOW);
|
||||
}
|
||||
|
||||
CAN_cfg.speed = (CAN_speed_t)nativeIt->second.speed;
|
||||
|
||||
if (!esp32hal->alloc_pins("CAN", tx_pin, rx_pin)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CAN_cfg.tx_pin_id = tx_pin;
|
||||
CAN_cfg.rx_pin_id = rx_pin;
|
||||
CAN_cfg.rx_queue = xQueueCreate(rx_queue_size, sizeof(CAN_frame_t));
|
||||
// Init CAN Module
|
||||
ESP32Can.CANInit();
|
||||
native_can_initialized = true;
|
||||
settingsespcan = new ACAN_ESP32_Settings((int)nativeIt->second.speed * 1000UL);
|
||||
settingsespcan->mRequestedCANMode = ACAN_ESP32_Settings::NormalMode;
|
||||
settingsespcan->mTxPin = tx_pin;
|
||||
settingsespcan->mRxPin = rx_pin;
|
||||
|
||||
const uint32_t errorCode = ACAN_ESP32::can.begin(*settingsespcan);
|
||||
if (errorCode == 0) {
|
||||
native_can_initialized = true;
|
||||
logging.println("Native Can ok");
|
||||
logging.print("Bit Rate prescaler: ");
|
||||
logging.println(settingsespcan->mBitRatePrescaler);
|
||||
logging.print("Time Segment 1: ");
|
||||
logging.println(settingsespcan->mTimeSegment1);
|
||||
logging.print("Time Segment 2: ");
|
||||
logging.println(settingsespcan->mTimeSegment2);
|
||||
logging.print("RJW: ");
|
||||
logging.println(settingsespcan->mRJW);
|
||||
logging.print("Triple Sampling: ");
|
||||
logging.println(settingsespcan->mTripleSampling ? "yes" : "no");
|
||||
logging.print("Actual bit rate: ");
|
||||
logging.print(settingsespcan->actualBitRate());
|
||||
logging.println(" bit/s");
|
||||
logging.print("Exact bit rate ? ");
|
||||
logging.println(settingsespcan->exactBitRate() ? "yes" : "no");
|
||||
logging.print("Sample point: ");
|
||||
logging.print(settingsespcan->samplePointFromBitStart());
|
||||
logging.println("%");
|
||||
} else {
|
||||
logging.print("Error Native Can: 0x");
|
||||
logging.println(errorCode, HEX);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
auto addonIt = can_receivers.find(CAN_ADDON_MCP2515);
|
||||
|
@ -94,16 +125,25 @@ bool init_CAN() {
|
|||
auto sck_pin = esp32hal->MCP2515_SCK();
|
||||
auto miso_pin = esp32hal->MCP2515_MISO();
|
||||
auto mosi_pin = esp32hal->MCP2515_MOSI();
|
||||
auto rst_pin = esp32hal->MCP2515_RST();
|
||||
|
||||
if (!esp32hal->alloc_pins("CAN", cs_pin, int_pin, sck_pin, miso_pin, mosi_pin)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Dual CAN Bus (ESP32+MCP2515) selected");
|
||||
#endif // DEBUG_LOG
|
||||
gBuffer.initWithSize(25);
|
||||
|
||||
if (rst_pin != GPIO_NUM_NC) {
|
||||
pinMode(rst_pin, OUTPUT);
|
||||
digitalWrite(rst_pin, HIGH);
|
||||
delay(100);
|
||||
digitalWrite(rst_pin, LOW);
|
||||
delay(100);
|
||||
digitalWrite(rst_pin, HIGH);
|
||||
delay(100);
|
||||
}
|
||||
|
||||
can2515 = new ACAN2515(cs_pin, SPI2515, int_pin);
|
||||
|
||||
SPI2515.begin(sck_pin, miso_pin, mosi_pin);
|
||||
|
@ -115,14 +155,10 @@ bool init_CAN() {
|
|||
settings2515->mRequestedMode = ACAN2515Settings::NormalMode;
|
||||
const uint16_t errorCode2515 = can2515->begin(*settings2515, [] { can2515->isr(); });
|
||||
if (errorCode2515 == 0) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Can ok");
|
||||
#endif // DEBUG_LOG
|
||||
} else {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.print("Error Can: 0x");
|
||||
logging.println(errorCode2515, HEX);
|
||||
#endif // DEBUG_LOG
|
||||
set_event(EVENT_CANMCP2515_INIT_FAILURE, (uint8_t)errorCode2515);
|
||||
return false;
|
||||
}
|
||||
|
@ -147,9 +183,7 @@ bool init_CAN() {
|
|||
|
||||
canfd = new ACAN2517FD(cs_pin, SPI2517, int_pin);
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("CAN FD add-on (ESP32+MCP2517) selected");
|
||||
#endif // DEBUG_LOG
|
||||
SPI2517.begin(sck_pin, sdo_pin, sdi_pin);
|
||||
auto bitRate = (int)speed * 1000UL;
|
||||
settings2517 = new ACAN2517FDSettings(
|
||||
|
@ -162,7 +196,6 @@ bool init_CAN() {
|
|||
const uint32_t errorCode2517 = canfd->begin(*settings2517, [] { canfd->isr(); });
|
||||
canfd->poll();
|
||||
if (errorCode2517 == 0) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.print("Bit Rate prescaler: ");
|
||||
logging.println(settings2517->mBitRatePrescaler);
|
||||
logging.print("Arbitration Phase segment 1: ");
|
||||
|
@ -179,12 +212,9 @@ bool init_CAN() {
|
|||
logging.print("Arbitration Sample point: ");
|
||||
logging.print(settings2517->arbitrationSamplePointFromBitStart());
|
||||
logging.println("%");
|
||||
#endif // DEBUG_LOG
|
||||
} else {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.print("CAN-FD Configuration error 0x");
|
||||
logging.println(errorCode2517, HEX);
|
||||
#endif // DEBUG_LOG
|
||||
set_event(EVENT_CANMCP2517FD_INIT_FAILURE, (uint8_t)errorCode2517);
|
||||
return false;
|
||||
}
|
||||
|
@ -199,31 +229,31 @@ void transmit_can_frame_to_interface(const CAN_frame* tx_frame, int interface) {
|
|||
}
|
||||
print_can_frame(*tx_frame, frameDirection(MSG_TX));
|
||||
|
||||
#ifdef LOG_CAN_TO_SD
|
||||
add_can_frame_to_buffer(*tx_frame, frameDirection(MSG_TX));
|
||||
#endif
|
||||
if (datalayer.system.info.CAN_SD_logging_active) {
|
||||
add_can_frame_to_buffer(*tx_frame, frameDirection(MSG_TX));
|
||||
}
|
||||
|
||||
switch (interface) {
|
||||
case CAN_NATIVE:
|
||||
case CAN_NATIVE: {
|
||||
|
||||
CAN_frame_t frame;
|
||||
frame.MsgID = tx_frame->ID;
|
||||
frame.FIR.B.FF = tx_frame->ext_ID ? CAN_frame_ext : CAN_frame_std;
|
||||
frame.FIR.B.DLC = tx_frame->DLC;
|
||||
frame.FIR.B.RTR = CAN_no_RTR;
|
||||
for (uint8_t i = 0; i < tx_frame->DLC; i++) {
|
||||
frame.data.u8[i] = tx_frame->data.u8[i];
|
||||
CANMessage frame;
|
||||
frame.id = tx_frame->ID;
|
||||
frame.ext = tx_frame->ext_ID;
|
||||
frame.len = tx_frame->DLC;
|
||||
for (uint8_t i = 0; i < frame.len; i++) {
|
||||
frame.data[i] = tx_frame->data.u8[i];
|
||||
}
|
||||
send_ok_native = ESP32Can.CANWriteFrame(&frame);
|
||||
send_ok_native = ACAN_ESP32::can.tryToSend(frame);
|
||||
|
||||
if (!send_ok_native) {
|
||||
datalayer.system.info.can_native_send_fail = true;
|
||||
}
|
||||
break;
|
||||
} break;
|
||||
case CAN_ADDON_MCP2515: {
|
||||
//Struct with ACAN2515 library format, needed to use the MCP2515 library for CAN2
|
||||
CANMessage MCP2515Frame;
|
||||
MCP2515Frame.id = tx_frame->ID;
|
||||
MCP2515Frame.ext = tx_frame->ext_ID ? CAN_frame_ext : CAN_frame_std;
|
||||
MCP2515Frame.ext = tx_frame->ext_ID;
|
||||
MCP2515Frame.len = tx_frame->DLC;
|
||||
MCP2515Frame.rtr = false;
|
||||
for (uint8_t i = 0; i < MCP2515Frame.len; i++) {
|
||||
|
@ -244,7 +274,7 @@ void transmit_can_frame_to_interface(const CAN_frame* tx_frame, int interface) {
|
|||
MCP2518Frame.type = CANFDMessage::CAN_DATA;
|
||||
}
|
||||
MCP2518Frame.id = tx_frame->ID;
|
||||
MCP2518Frame.ext = tx_frame->ext_ID ? CAN_frame_ext : CAN_frame_std;
|
||||
MCP2518Frame.ext = tx_frame->ext_ID;
|
||||
MCP2518Frame.len = tx_frame->DLC;
|
||||
for (uint8_t i = 0; i < MCP2518Frame.len; i++) {
|
||||
MCP2518Frame.data[i] = tx_frame->data.u8[i];
|
||||
|
@ -276,21 +306,22 @@ void receive_can() {
|
|||
}
|
||||
|
||||
void receive_frame_can_native() { // This section checks if we have a complete CAN message incoming on native CAN port
|
||||
CAN_frame_t rx_frame_native;
|
||||
if (xQueueReceive(CAN_cfg.rx_queue, &rx_frame_native, 0) == pdTRUE) {
|
||||
CAN_frame rx_frame;
|
||||
rx_frame.ID = rx_frame_native.MsgID;
|
||||
if (rx_frame_native.FIR.B.FF == CAN_frame_std) {
|
||||
rx_frame.ext_ID = false;
|
||||
} else { //CAN_frame_ext == 1
|
||||
rx_frame.ext_ID = true;
|
||||
CANMessage frame;
|
||||
|
||||
if (ACAN_ESP32::can.available()) {
|
||||
if (ACAN_ESP32::can.receive(frame)) {
|
||||
|
||||
CAN_frame rx_frame;
|
||||
rx_frame.ID = frame.id;
|
||||
rx_frame.ext_ID = frame.ext;
|
||||
rx_frame.DLC = frame.len;
|
||||
for (uint8_t i = 0; i < frame.len && i < 8; i++) {
|
||||
rx_frame.data.u8[i] = frame.data[i];
|
||||
}
|
||||
|
||||
//message incoming, pass it on to the handler
|
||||
map_can_frame_to_variable(&rx_frame, CAN_NATIVE);
|
||||
}
|
||||
rx_frame.DLC = rx_frame_native.FIR.B.DLC;
|
||||
for (uint8_t i = 0; i < rx_frame.DLC && i < 8; i++) {
|
||||
rx_frame.data.u8[i] = rx_frame_native.data.u8[i];
|
||||
}
|
||||
//message incoming, pass it on to the handler
|
||||
map_can_frame_to_variable(&rx_frame, CAN_NATIVE);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -302,7 +333,7 @@ void receive_frame_can_addon() { // This section checks if we have a complete C
|
|||
can2515->receive(MCP2515frame);
|
||||
|
||||
rx_frame.ID = MCP2515frame.id;
|
||||
rx_frame.ext_ID = MCP2515frame.ext ? CAN_frame_ext : CAN_frame_std;
|
||||
rx_frame.ext_ID = MCP2515frame.ext;
|
||||
rx_frame.DLC = MCP2515frame.len;
|
||||
for (uint8_t i = 0; i < MCP2515frame.len && i < 8; i++) {
|
||||
rx_frame.data.u8[i] = MCP2515frame.data[i];
|
||||
|
@ -332,23 +363,24 @@ void receive_frame_canfd_addon() { // This section checks if we have a complete
|
|||
|
||||
// Support functions
|
||||
void print_can_frame(CAN_frame frame, frameDirection msgDir) {
|
||||
#ifdef DEBUG_CAN_DATA // If enabled in user settings, print out the CAN messages via USB
|
||||
uint8_t i = 0;
|
||||
Serial.print("(");
|
||||
Serial.print(millis() / 1000.0);
|
||||
(msgDir == MSG_RX) ? Serial.print(") RX0 ") : Serial.print(") TX1 ");
|
||||
Serial.print(frame.ID, HEX);
|
||||
Serial.print(" [");
|
||||
Serial.print(frame.DLC);
|
||||
Serial.print("] ");
|
||||
for (i = 0; i < frame.DLC; i++) {
|
||||
Serial.print(frame.data.u8[i] < 16 ? "0" : "");
|
||||
Serial.print(frame.data.u8[i], HEX);
|
||||
if (i < frame.DLC - 1)
|
||||
Serial.print(" ");
|
||||
|
||||
if (datalayer.system.info.CAN_usb_logging_active) {
|
||||
uint8_t i = 0;
|
||||
Serial.print("(");
|
||||
Serial.print(millis() / 1000.0);
|
||||
(msgDir == MSG_RX) ? Serial.print(") RX0 ") : Serial.print(") TX1 ");
|
||||
Serial.print(frame.ID, HEX);
|
||||
Serial.print(" [");
|
||||
Serial.print(frame.DLC);
|
||||
Serial.print("] ");
|
||||
for (i = 0; i < frame.DLC; i++) {
|
||||
Serial.print(frame.data.u8[i] < 16 ? "0" : "");
|
||||
Serial.print(frame.data.u8[i], HEX);
|
||||
if (i < frame.DLC - 1)
|
||||
Serial.print(" ");
|
||||
}
|
||||
Serial.println("");
|
||||
}
|
||||
Serial.println("");
|
||||
#endif // DEBUG_CAN_DATA
|
||||
|
||||
if (datalayer.system.info.can_logging_active) { // If user clicked on CAN Logging page in webserver, start recording
|
||||
dump_can_frame(frame, msgDir);
|
||||
|
@ -362,13 +394,13 @@ void map_can_frame_to_variable(CAN_frame* rx_frame, CAN_Interface interface) {
|
|||
print_can_frame(*rx_frame, frameDirection(MSG_RX));
|
||||
}
|
||||
|
||||
#ifdef LOG_CAN_TO_SD
|
||||
if (interface !=
|
||||
CANFD_NATIVE) { //Avoid printing twice due to receive_frame_canfd_addon sending to both FD interfaces
|
||||
//TODO: This check can be removed later when refactored to use inline functions for logging
|
||||
add_can_frame_to_buffer(*rx_frame, frameDirection(MSG_RX));
|
||||
if (datalayer.system.info.CAN_SD_logging_active) {
|
||||
if (interface !=
|
||||
CANFD_NATIVE) { //Avoid printing twice due to receive_frame_canfd_addon sending to both FD interfaces
|
||||
//TODO: This check can be removed later when refactored to use inline functions for logging
|
||||
add_can_frame_to_buffer(*rx_frame, frameDirection(MSG_RX));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Send the frame to all the receivers registered for this interface.
|
||||
auto receivers = can_receivers.equal_range(interface);
|
||||
|
@ -415,7 +447,7 @@ void dump_can_frame(CAN_frame& frame, frameDirection msgDir) {
|
|||
|
||||
void stop_can() {
|
||||
if (can_receivers.find(CAN_NATIVE) != can_receivers.end()) {
|
||||
ESP32Can.CANStop();
|
||||
ACAN_ESP32::can.end();
|
||||
}
|
||||
|
||||
if (can2515) {
|
||||
|
@ -431,7 +463,7 @@ void stop_can() {
|
|||
|
||||
void restart_can() {
|
||||
if (can_receivers.find(CAN_NATIVE) != can_receivers.end()) {
|
||||
ESP32Can.CANInit();
|
||||
ACAN_ESP32::can.begin(*settingsespcan);
|
||||
}
|
||||
|
||||
if (can2515) {
|
||||
|
@ -446,11 +478,10 @@ void restart_can() {
|
|||
}
|
||||
|
||||
CAN_Speed change_can_speed(CAN_Interface interface, CAN_Speed speed) {
|
||||
auto oldSpeed = (CAN_Speed)CAN_cfg.speed;
|
||||
auto oldSpeed = (CAN_Speed)settingsespcan->mDesiredBitRate;
|
||||
if (interface == CAN_Interface::CAN_NATIVE) {
|
||||
CAN_cfg.speed = (CAN_speed_t)speed;
|
||||
// ReInit native CAN module at new speed
|
||||
ESP32Can.CANInit();
|
||||
settingsespcan->mDesiredBitRate = (int)speed;
|
||||
ACAN_ESP32::can.begin(*settingsespcan);
|
||||
}
|
||||
return oldSpeed;
|
||||
return speed;
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "../../devboard/utils/types.h"
|
||||
|
||||
extern bool use_canfd_as_can;
|
||||
extern uint8_t user_selected_can_addon_crystal_frequency_mhz;
|
||||
|
||||
void dump_can_frame(CAN_frame& frame, frameDirection msgDir);
|
||||
void transmit_can_frame_to_interface(const CAN_frame* tx_frame, int interface);
|
||||
|
|
|
@ -24,7 +24,6 @@ void show_dtc(uint8_t byte0, uint8_t byte1) {
|
|||
}
|
||||
|
||||
void handle_obd_frame(CAN_frame& rx_frame) {
|
||||
#ifdef DEBUG_LOG
|
||||
if (rx_frame.data.u8[1] == 0x7F) {
|
||||
const char* error_str = "?";
|
||||
switch (rx_frame.data.u8[3]) { // See https://automotive.wiki/index.php/ISO_14229
|
||||
|
@ -107,7 +106,6 @@ void handle_obd_frame(CAN_frame& rx_frame) {
|
|||
}
|
||||
}
|
||||
dump_can_frame(rx_frame, MSG_RX);
|
||||
#endif
|
||||
}
|
||||
|
||||
void transmit_obd_can_frame(unsigned int address, int interface, bool canFD) {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#ifndef _OBD_H_
|
||||
#define _OBD_H_
|
||||
|
||||
#include "../../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
||||
#include "comm_can.h"
|
||||
|
||||
void handle_obd_frame(CAN_frame& rx_frame);
|
||||
|
|
|
@ -147,12 +147,10 @@ bool init_contactors() {
|
|||
}
|
||||
|
||||
static void dbg_contactors(const char* state) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.print("[");
|
||||
logging.print(millis());
|
||||
logging.print(" ms] contactors control: ");
|
||||
logging.println(state);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Main functions of the handle_contactors include checking if inverter allows for closing, checking battery 2, checking BMS power output, and actual contactor closing/precharge via GPIO
|
||||
|
|
|
@ -33,6 +33,8 @@ void init_stored_settings() {
|
|||
settings.putBool("EQUIPMENT_STOP", datalayer.system.settings.equipment_stop_active);
|
||||
#endif // LOAD_SAVED_SETTINGS_ON_BOOT
|
||||
|
||||
esp32hal->set_default_configuration_values();
|
||||
|
||||
char tempSSIDstring[63]; // Allocate buffer with sufficient size
|
||||
size_t lengthSSID = settings.getString("SSID", tempSSIDstring, sizeof(tempSSIDstring));
|
||||
if (lengthSSID > 0) { // Successfully read the string from memory. Set it to SSID!
|
||||
|
@ -103,6 +105,7 @@ void init_stored_settings() {
|
|||
user_selected_inverter_ah_capacity = settings.getUInt("INVAHCAPACITY", 0);
|
||||
user_selected_inverter_battery_type = settings.getUInt("INVBTYPE", 0);
|
||||
user_selected_inverter_ignore_contactors = settings.getBool("INVICNT", false);
|
||||
user_selected_can_addon_crystal_frequency_mhz = settings.getUInt("CANFREQ", 8);
|
||||
user_selected_tesla_digital_HVIL = settings.getBool("DIGITALHVIL", false);
|
||||
user_selected_tesla_GTW_country = settings.getUInt("GTWCOUNTRY", 0);
|
||||
user_selected_tesla_GTW_rightHandDrive = settings.getBool("GTWRHD", false);
|
||||
|
@ -141,6 +144,13 @@ void init_stored_settings() {
|
|||
remote_bms_reset = settings.getBool("REMBMSRESET", false);
|
||||
use_canfd_as_can = settings.getBool("CANFDASCAN", false);
|
||||
|
||||
datalayer.system.info.CAN_usb_logging_active = settings.getBool("CANLOGUSB", false);
|
||||
datalayer.system.info.usb_logging_active = settings.getBool("USBENABLED", false);
|
||||
datalayer.system.info.web_logging_active = settings.getBool("WEBENABLED", false);
|
||||
datalayer.system.info.CAN_SD_logging_active = settings.getBool("CANLOGSD", false);
|
||||
datalayer.system.info.SD_logging_active = settings.getBool("SDLOGENABLED", false);
|
||||
datalayer.battery.status.led_mode = (led_mode_enum)settings.getUInt("LEDMODE", false);
|
||||
|
||||
// WIFI AP is enabled by default unless disabled in the settings
|
||||
wifiap_enabled = settings.getBool("WIFIAPENABLED", true);
|
||||
passwordAP = settings.getString("APPASSWORD", "123456789").c_str();
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#define _COMM_NVM_H_
|
||||
|
||||
#include <Preferences.h>
|
||||
#include <WString.h>
|
||||
#include <limits>
|
||||
#include "../../datalayer/datalayer.h"
|
||||
#include "../../devboard/utils/events.h"
|
||||
|
|
|
@ -40,9 +40,7 @@ bool init_precharge_control() {
|
|||
}
|
||||
|
||||
// Setup PWM Channel Frequency and Resolution
|
||||
#ifdef DEBUG_LOG
|
||||
logging.printf("Precharge control initialised\n");
|
||||
#endif
|
||||
|
||||
auto hia4v1_pin = esp32hal->HIA4V1_PIN();
|
||||
auto inverter_disconnect_contactor_pin = esp32hal->INVERTER_DISCONNECT_CONTACTOR_PIN();
|
||||
|
@ -87,9 +85,7 @@ void handle_precharge_control(unsigned long currentMillis) {
|
|||
ledcWriteTone(hia4v1_pin, freq); // Set frequency and set dutycycle to 50%
|
||||
prechargeStartTime = currentMillis;
|
||||
datalayer.system.status.precharge_status = AUTO_PRECHARGE_PRECHARGING;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.printf("Precharge: Starting sequence\n");
|
||||
#endif
|
||||
digitalWrite(inverter_disconnect_contactor_pin, OFF);
|
||||
break;
|
||||
|
||||
|
@ -114,10 +110,8 @@ void handle_precharge_control(unsigned long currentMillis) {
|
|||
freq = Precharge_max_PWM_Freq;
|
||||
if (freq < Precharge_min_PWM_Freq)
|
||||
freq = Precharge_min_PWM_Freq;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.printf("Precharge: Target: %d V Extern: %d V Frequency: %u\n", target_voltage / 10,
|
||||
external_voltage / 10, freq);
|
||||
#endif
|
||||
ledcWriteTone(hia4v1_pin, freq);
|
||||
}
|
||||
|
||||
|
@ -128,40 +122,31 @@ void handle_precharge_control(unsigned long currentMillis) {
|
|||
digitalWrite(hia4v1_pin, LOW);
|
||||
digitalWrite(inverter_disconnect_contactor_pin, ON);
|
||||
datalayer.system.status.precharge_status = AUTO_PRECHARGE_IDLE;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.printf("Precharge: Disabling Precharge bms not standby/active or equipment stop\n");
|
||||
#endif
|
||||
} else if (currentMillis - prechargeStartTime >= MAX_PRECHARGE_TIME_MS ||
|
||||
datalayer.battery.status.real_bms_status == BMS_FAULT) {
|
||||
pinMode(hia4v1_pin, OUTPUT);
|
||||
digitalWrite(hia4v1_pin, LOW);
|
||||
digitalWrite(inverter_disconnect_contactor_pin, ON);
|
||||
datalayer.system.status.precharge_status = AUTO_PRECHARGE_FAILURE;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.printf("Precharge: CRITICAL FAILURE (timeout/BMS fault) -> REQUIRES REBOOT\n");
|
||||
#endif
|
||||
set_event(EVENT_AUTOMATIC_PRECHARGE_FAILURE, 0);
|
||||
// Force stop any further precharge attempts
|
||||
datalayer.system.settings.start_precharging = false;
|
||||
|
||||
// Add event
|
||||
} else if (datalayer.system.status.battery_allows_contactor_closing) {
|
||||
pinMode(hia4v1_pin, OUTPUT);
|
||||
digitalWrite(hia4v1_pin, LOW);
|
||||
digitalWrite(inverter_disconnect_contactor_pin, ON);
|
||||
datalayer.system.status.precharge_status = AUTO_PRECHARGE_COMPLETED;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.printf("Precharge: Disabled (contacts closed) -> COMPLETED\n");
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
case AUTO_PRECHARGE_COMPLETED:
|
||||
if (datalayer.system.settings.equipment_stop_active || datalayer.battery.status.bms_status != ACTIVE) {
|
||||
datalayer.system.status.precharge_status = AUTO_PRECHARGE_IDLE;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.printf("Precharge: equipment stop activated -> IDLE\n");
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -172,9 +157,7 @@ void handle_precharge_control(unsigned long currentMillis) {
|
|||
datalayer.system.status.precharge_status = AUTO_PRECHARGE_IDLE;
|
||||
pinMode(hia4v1_pin, OUTPUT);
|
||||
digitalWrite(hia4v1_pin, LOW);
|
||||
#ifdef DEBUG_LOG
|
||||
logging.printf("Precharge: equipment stop activated -> IDLE\n");
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
@ -110,7 +110,7 @@ struct DATALAYER_BATTERY_STATUS_TYPE {
|
|||
real_bms_status_enum real_bms_status = BMS_DISCONNECTED;
|
||||
|
||||
/** LED mode, customizable by user */
|
||||
led_mode_enum led_mode = LED_MODE;
|
||||
led_mode_enum led_mode = CLASSIC;
|
||||
};
|
||||
|
||||
struct DATALAYER_BATTERY_SETTINGS_TYPE {
|
||||
|
@ -241,6 +241,16 @@ struct DATALAYER_SYSTEM_INFO_TYPE {
|
|||
size_t logged_can_messages_offset = 0;
|
||||
/** bool, determines if CAN messages should be logged for webserver */
|
||||
bool can_logging_active = false;
|
||||
/** bool, determines if USB serial logging should occur */
|
||||
bool CAN_usb_logging_active = false;
|
||||
/** bool, determines if USB serial logging should occur */
|
||||
bool CAN_SD_logging_active = false;
|
||||
/** bool, determines if USB serial logging should occur */
|
||||
bool usb_logging_active = false;
|
||||
/** bool, determines if general logging should be active for webserver */
|
||||
bool web_logging_active = false;
|
||||
/** bool, determines if general logging to SD card should be active */
|
||||
bool SD_logging_active = false;
|
||||
/** uint8_t, enumeration which CAN interface should be used for log playback */
|
||||
uint8_t can_replay_interface = CAN_NATIVE;
|
||||
/** bool, determines if CAN replay should loop or not */
|
||||
|
|
|
@ -3,21 +3,24 @@
|
|||
#include "../../../USER_SETTINGS.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "hw_3LB.h"
|
||||
#include "hw_devkit.h"
|
||||
#include "hw_lilygo.h"
|
||||
#include "hw_stark.h"
|
||||
|
||||
Esp32Hal* esp32hal = nullptr;
|
||||
|
||||
void init_hal() {
|
||||
#if defined(HW_LILYGO)
|
||||
#include "hw_lilygo.h"
|
||||
esp32hal = new LilyGoHal();
|
||||
#elif defined(HW_LILYGO2CAN)
|
||||
#include "hw_lilygo2can.h"
|
||||
esp32hal = new LilyGo2CANHal();
|
||||
#elif defined(HW_STARK)
|
||||
#include "hw_stark.h"
|
||||
esp32hal = new StarkHal();
|
||||
#elif defined(HW_3LB)
|
||||
#include "hw_3LB.h"
|
||||
esp32hal = new ThreeLBHal();
|
||||
#elif defined(HW_DEVKIT)
|
||||
#include "hw_devkit.h"
|
||||
esp32hal = new DevKitHal();
|
||||
#else
|
||||
#error "No HW defined."
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <soc/gpio_num.h>
|
||||
#include <chrono>
|
||||
#include <unordered_map>
|
||||
#include "../../../src/communication/nvm/comm_nvm.h"
|
||||
#include "../../../src/devboard/utils/events.h"
|
||||
#include "../../../src/devboard/utils/logging.h"
|
||||
#include "../../../src/devboard/utils/types.h"
|
||||
|
@ -24,6 +25,8 @@ class Esp32Hal {
|
|||
virtual int MODBUS_CORE() { return 0; }
|
||||
virtual int WIFICORE() { return 0; }
|
||||
|
||||
virtual void set_default_configuration_values() {}
|
||||
|
||||
template <typename... Pins>
|
||||
bool alloc_pins(const char* name, Pins... pins) {
|
||||
std::vector<gpio_num_t> requested_pins = {static_cast<gpio_num_t>(pins)...};
|
||||
|
@ -109,6 +112,8 @@ class Esp32Hal {
|
|||
virtual gpio_num_t MCP2515_CS() { return GPIO_NUM_NC; }
|
||||
// INT output of MCP2515
|
||||
virtual gpio_num_t MCP2515_INT() { return GPIO_NUM_NC; }
|
||||
// Reset pin for MCP2515
|
||||
virtual gpio_num_t MCP2515_RST() { return GPIO_NUM_NC; }
|
||||
|
||||
// CANFD_ADDON defines for MCP2517
|
||||
virtual gpio_num_t MCP2517_SCK() { return GPIO_NUM_NC; }
|
||||
|
|
75
Software/src/devboard/hal/hw_lilygo2can.h
Normal file
75
Software/src/devboard/hal/hw_lilygo2can.h
Normal file
|
@ -0,0 +1,75 @@
|
|||
#ifndef __HW_LILYGO2CAN_H__
|
||||
#define __HW_LILYGO2CAN_H__
|
||||
|
||||
#include "hal.h"
|
||||
|
||||
class LilyGo2CANHal : public Esp32Hal {
|
||||
public:
|
||||
const char* name() { return "LilyGo T_2CAN"; }
|
||||
|
||||
virtual void set_default_configuration_values() {
|
||||
BatteryEmulatorSettingsStore settings;
|
||||
if (!settings.settingExists("CANFREQ")) {
|
||||
settings.saveUInt("CANFREQ", 16);
|
||||
}
|
||||
}
|
||||
|
||||
virtual gpio_num_t CAN_TX_PIN() { return GPIO_NUM_7; }
|
||||
virtual gpio_num_t CAN_RX_PIN() { return GPIO_NUM_6; }
|
||||
|
||||
// Built In MCP2515 CAN_ADDON
|
||||
virtual gpio_num_t MCP2515_SCK() { return GPIO_NUM_12; }
|
||||
virtual gpio_num_t MCP2515_MOSI() { return GPIO_NUM_11; }
|
||||
virtual gpio_num_t MCP2515_MISO() { return GPIO_NUM_13; }
|
||||
virtual gpio_num_t MCP2515_CS() { return GPIO_NUM_10; }
|
||||
virtual gpio_num_t MCP2515_INT() { return GPIO_NUM_8; }
|
||||
virtual gpio_num_t MCP2515_RST() { return GPIO_NUM_9; }
|
||||
|
||||
// CANFD_ADDON defines for MCP2517
|
||||
virtual gpio_num_t MCP2517_SCK() { return GPIO_NUM_17; }
|
||||
virtual gpio_num_t MCP2517_SDI() { return GPIO_NUM_16; }
|
||||
virtual gpio_num_t MCP2517_SDO() { return GPIO_NUM_18; }
|
||||
virtual gpio_num_t MCP2517_CS() { return GPIO_NUM_15; }
|
||||
virtual gpio_num_t MCP2517_INT() { return GPIO_NUM_14; }
|
||||
|
||||
// Contactor handling
|
||||
virtual gpio_num_t POSITIVE_CONTACTOR_PIN() { return GPIO_NUM_38; }
|
||||
virtual gpio_num_t NEGATIVE_CONTACTOR_PIN() { return GPIO_NUM_39; }
|
||||
virtual gpio_num_t PRECHARGE_PIN() { return GPIO_NUM_40; }
|
||||
virtual gpio_num_t SECOND_BATTERY_CONTACTORS_PIN() { return GPIO_NUM_41; }
|
||||
|
||||
// Automatic precharging
|
||||
virtual gpio_num_t HIA4V1_PIN() { return GPIO_NUM_41; }
|
||||
virtual gpio_num_t INVERTER_DISCONNECT_CONTACTOR_PIN() { return GPIO_NUM_40; }
|
||||
|
||||
// SMA CAN contactor pins
|
||||
virtual gpio_num_t INVERTER_CONTACTOR_ENABLE_PIN() { return GPIO_NUM_14; }
|
||||
|
||||
virtual gpio_num_t INVERTER_CONTACTOR_ENABLE_LED_PIN() { return GPIO_NUM_5; }
|
||||
|
||||
// LED
|
||||
virtual gpio_num_t LED_PIN() { return GPIO_NUM_4; }
|
||||
virtual uint8_t LED_MAX_BRIGHTNESS() { return 40; }
|
||||
|
||||
// Equipment stop pin
|
||||
virtual gpio_num_t EQUIPMENT_STOP_PIN() { return GPIO_NUM_5; }
|
||||
|
||||
// Battery wake up pins
|
||||
virtual gpio_num_t WUP_PIN1() { return GPIO_NUM_40; }
|
||||
virtual gpio_num_t WUP_PIN2() { return GPIO_NUM_38; }
|
||||
|
||||
std::vector<comm_interface> available_interfaces() {
|
||||
return {comm_interface::CanNative, comm_interface::CanAddonMcp2515};
|
||||
}
|
||||
};
|
||||
|
||||
#define HalClass LilyGo2CANHal
|
||||
|
||||
/* ----- Error checks below, don't change (can't be moved to separate file) ----- */
|
||||
#ifndef HW_CONFIGURED
|
||||
#define HW_CONFIGURED
|
||||
#else
|
||||
#error Multiple HW defined! Please select a single HW
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -319,9 +319,7 @@ static bool publish_common_info(void) {
|
|||
|
||||
serializeJson(doc, mqtt_msg);
|
||||
if (mqtt_publish(state_topic.c_str(), mqtt_msg, false) == false) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Common info MQTT msg could not be sent");
|
||||
#endif // DEBUG_LOG
|
||||
return false;
|
||||
}
|
||||
doc.clear();
|
||||
|
@ -392,9 +390,7 @@ static bool publish_cell_voltages(void) {
|
|||
serializeJson(doc, mqtt_msg, sizeof(mqtt_msg));
|
||||
|
||||
if (!mqtt_publish(state_topic.c_str(), mqtt_msg, false)) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Cell voltage MQTT msg could not be sent");
|
||||
#endif // DEBUG_LOG
|
||||
return false;
|
||||
}
|
||||
doc.clear();
|
||||
|
@ -413,9 +409,7 @@ static bool publish_cell_voltages(void) {
|
|||
serializeJson(doc, mqtt_msg, sizeof(mqtt_msg));
|
||||
|
||||
if (!mqtt_publish(state_topic_2.c_str(), mqtt_msg, false)) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Cell voltage MQTT msg could not be sent");
|
||||
#endif // DEBUG_LOG
|
||||
return false;
|
||||
}
|
||||
doc.clear();
|
||||
|
@ -440,9 +434,7 @@ static bool publish_cell_balancing(void) {
|
|||
serializeJson(doc, mqtt_msg, sizeof(mqtt_msg));
|
||||
|
||||
if (!mqtt_publish(state_topic.c_str(), mqtt_msg, false)) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Cell balancing MQTT msg could not be sent");
|
||||
#endif // DEBUG_LOG
|
||||
return false;
|
||||
}
|
||||
doc.clear();
|
||||
|
@ -460,9 +452,7 @@ static bool publish_cell_balancing(void) {
|
|||
serializeJson(doc, mqtt_msg, sizeof(mqtt_msg));
|
||||
|
||||
if (!mqtt_publish(state_topic_2.c_str(), mqtt_msg, false)) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Cell balancing MQTT msg could not be sent");
|
||||
#endif // DEBUG_LOG
|
||||
return false;
|
||||
}
|
||||
doc.clear();
|
||||
|
@ -523,9 +513,7 @@ bool publish_events() {
|
|||
|
||||
serializeJson(doc, mqtt_msg);
|
||||
if (!mqtt_publish(state_topic.c_str(), mqtt_msg, false)) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Common info MQTT msg could not be sent");
|
||||
#endif // DEBUG_LOG
|
||||
return false;
|
||||
} else {
|
||||
set_event_MQTTpublished(event_handle);
|
||||
|
@ -541,9 +529,7 @@ bool publish_events() {
|
|||
static bool publish_buttons_discovery(void) {
|
||||
if (ha_autodiscovery_enabled) {
|
||||
if (ha_buttons_published == false) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Publishing buttons discovery");
|
||||
#endif // DEBUG_LOG
|
||||
|
||||
static JsonDocument doc;
|
||||
for (int i = 0; i < sizeof(buttonConfigs) / sizeof(buttonConfigs[0]); i++) {
|
||||
|
@ -573,16 +559,12 @@ void mqtt_message_received(char* topic_raw, int topic_len, char* data, int data_
|
|||
|
||||
char* topic = strndup(topic_raw, topic_len);
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
logging.printf("MQTT message arrived: [%.*s]\n", topic_len, topic);
|
||||
#endif // DEBUG_LOG
|
||||
|
||||
#ifdef REMOTE_BMS_RESET
|
||||
const char* bmsreset_topic = generateButtonTopic("BMSRESET").c_str();
|
||||
if (strcmp(topic, bmsreset_topic) == 0) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Triggering BMS reset");
|
||||
#endif // DEBUG_LOG
|
||||
start_bms_reset();
|
||||
}
|
||||
#endif // REMOTE_BMS_RESET
|
||||
|
@ -616,21 +598,16 @@ static void mqtt_event_handler(void* handler_args, esp_event_base_t base, int32_
|
|||
|
||||
publish_buttons_discovery();
|
||||
subscribe();
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("MQTT connected");
|
||||
#endif // DEBUG_LOG
|
||||
break;
|
||||
case MQTT_EVENT_DISCONNECTED:
|
||||
set_event(EVENT_MQTT_DISCONNECT, 0);
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("MQTT disconnected!");
|
||||
#endif // DEBUG_LOG
|
||||
break;
|
||||
case MQTT_EVENT_DATA:
|
||||
mqtt_message_received(event->topic, event->topic_len, event->data, event->data_len);
|
||||
break;
|
||||
case MQTT_EVENT_ERROR:
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("MQTT_ERROR");
|
||||
logging.print("reported from esp-tls");
|
||||
logging.println(event->error_handle->esp_tls_last_esp_err);
|
||||
|
@ -638,7 +615,6 @@ static void mqtt_event_handler(void* handler_args, esp_event_base_t base, int32_
|
|||
logging.println(event->error_handle->esp_tls_stack_err);
|
||||
logging.print("captured as transport's socket errno");
|
||||
logging.println(strerror(event->error_handle->esp_transport_sock_errno));
|
||||
#endif // DEBUG_LOG
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -723,9 +699,7 @@ void mqtt_loop(void) {
|
|||
if (client_started == false) {
|
||||
esp_mqtt_client_start(client);
|
||||
client_started = true;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("MQTT initialized");
|
||||
#endif // DEBUG_LOG
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -63,9 +63,7 @@ void add_can_frame_to_buffer(CAN_frame frame, frameDirection msgDir) {
|
|||
currentTime / 1000, currentTime % 1000, (msgDir == MSG_RX ? "RX0" : "TX1"), frame.ID, frame.DLC);
|
||||
|
||||
if (xRingbufferSend(can_bufferHandle, &messagestr_buffer, size, pdMS_TO_TICKS(2)) != pdTRUE) {
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Failed to send message to can ring buffer!");
|
||||
#endif // DEBUG_VIA_USB
|
||||
logging.println("Failed to send message to can ring buffer!");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -77,9 +75,7 @@ void add_can_frame_to_buffer(CAN_frame frame, frameDirection msgDir) {
|
|||
size = snprintf(messagestr_buffer, sizeof(messagestr_buffer), "%02X\n", frame.data.u8[i]);
|
||||
|
||||
if (xRingbufferSend(can_bufferHandle, &messagestr_buffer, size, pdMS_TO_TICKS(2)) != pdTRUE) {
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Failed to send message to can ring buffer!");
|
||||
#endif // DEBUG_VIA_USB
|
||||
logging.println("Failed to send message to can ring buffer!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -127,9 +123,7 @@ void add_log_to_buffer(const uint8_t* buffer, size_t size) {
|
|||
return;
|
||||
|
||||
if (xRingbufferSend(log_bufferHandle, buffer, size, pdMS_TO_TICKS(1)) != pdTRUE) {
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Failed to send message to log ring buffer!");
|
||||
#endif // DEBUG_VIA_USB
|
||||
logging.println("Failed to send message to log ring buffer!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -161,25 +155,22 @@ void write_log_to_sdcard() {
|
|||
}
|
||||
|
||||
void init_logging_buffers() {
|
||||
#if defined(LOG_CAN_TO_SD)
|
||||
can_bufferHandle = xRingbufferCreate(32 * 1024, RINGBUF_TYPE_BYTEBUF);
|
||||
if (can_bufferHandle == NULL) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Failed to create CAN ring buffer!");
|
||||
#endif // DEBUG_LOG
|
||||
return;
|
||||
}
|
||||
#endif // defined(LOG_CAN_TO_SD)
|
||||
|
||||
#if defined(LOG_TO_SD)
|
||||
log_bufferHandle = xRingbufferCreate(1024, RINGBUF_TYPE_BYTEBUF);
|
||||
if (log_bufferHandle == NULL) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Failed to create log ring buffer!");
|
||||
#endif // DEBUG_LOG
|
||||
return;
|
||||
if (datalayer.system.info.CAN_SD_logging_active) {
|
||||
can_bufferHandle = xRingbufferCreate(32 * 1024, RINGBUF_TYPE_BYTEBUF);
|
||||
if (can_bufferHandle == NULL) {
|
||||
logging.println("Failed to create CAN ring buffer!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (datalayer.system.info.SD_logging_active) {
|
||||
log_bufferHandle = xRingbufferCreate(1024, RINGBUF_TYPE_BYTEBUF);
|
||||
if (log_bufferHandle == NULL) {
|
||||
logging.println("Failed to create log ring buffer!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif // defined(LOG_TO_SD)
|
||||
}
|
||||
|
||||
bool init_sdcard() {
|
||||
|
@ -196,22 +187,16 @@ bool init_sdcard() {
|
|||
SD_MMC.setPins(sclk_pin, mosi_pin, miso_pin);
|
||||
if (!SD_MMC.begin("/root", true, true, SDMMC_FREQ_HIGHSPEED)) {
|
||||
set_event_latched(EVENT_SD_INIT_FAILED, 0);
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("SD Card initialization failed!");
|
||||
#endif // DEBUG_LOG
|
||||
return false;
|
||||
}
|
||||
|
||||
clear_event(EVENT_SD_INIT_FAILED);
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("SD Card initialization successful.");
|
||||
#endif // DEBUG_LOG
|
||||
|
||||
sd_card_active = true;
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
log_sdcard_details();
|
||||
#endif // DEBUG_LOG
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -159,9 +159,6 @@ void reset_all_events() {
|
|||
}
|
||||
events.level = EVENT_LEVEL_INFO;
|
||||
update_bms_status();
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("All events have been cleared.");
|
||||
#endif
|
||||
}
|
||||
|
||||
void set_event_MQTTpublished(EVENTS_ENUM_TYPE event) {
|
||||
|
|
|
@ -25,6 +25,7 @@ class LED {
|
|||
void heartbeat_run(void);
|
||||
|
||||
uint8_t up_down(float middle_point_f);
|
||||
int LED_PERIOD_MS = 3000;
|
||||
};
|
||||
|
||||
bool led_init(void);
|
||||
|
|
|
@ -3,19 +3,17 @@
|
|||
#include "../../datalayer/datalayer.h"
|
||||
#include "../sdcard/sdcard.h"
|
||||
|
||||
#if defined(LOG_CAN_TO_SD) || defined(LOG_TO_SD)
|
||||
#if !defined(HW_LILYGO)
|
||||
#error The SD card logging feature is only available on LilyGo hardware
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define MAX_LINE_LENGTH_PRINTF 128
|
||||
#define MAX_LENGTH_TIME_STR 14
|
||||
|
||||
bool previous_message_was_newline = true;
|
||||
|
||||
void Logging::add_timestamp(size_t size) {
|
||||
#ifdef DEBUG_LOG
|
||||
// Check if any logging is enabled at runtime
|
||||
if (!datalayer.system.info.web_logging_active && !datalayer.system.info.usb_logging_active) {
|
||||
return;
|
||||
}
|
||||
|
||||
char* message_string = datalayer.system.info.logged_can_messages;
|
||||
int offset = datalayer.system.info.logged_can_messages_offset; // Keeps track of the current position in the buffer
|
||||
size_t message_string_size = sizeof(datalayer.system.info.logged_can_messages);
|
||||
|
@ -23,43 +21,44 @@ void Logging::add_timestamp(size_t size) {
|
|||
char* timestr;
|
||||
static char timestr_buffer[MAX_LENGTH_TIME_STR];
|
||||
|
||||
#ifdef DEBUG_VIA_WEB
|
||||
if (!datalayer.system.info.can_logging_active) {
|
||||
/* If web debug is active and can logging is inactive,
|
||||
* we use the debug logging memory directly for writing the timestring */
|
||||
if (offset + size + MAX_LENGTH_TIME_STR > message_string_size) {
|
||||
offset = 0;
|
||||
if (datalayer.system.info.web_logging_active) {
|
||||
if (!datalayer.system.info.can_logging_active) {
|
||||
/* If web debug is active and can logging is inactive,
|
||||
* we use the debug logging memory directly for writing the timestring */
|
||||
if (offset + size + MAX_LENGTH_TIME_STR > message_string_size) {
|
||||
offset = 0;
|
||||
}
|
||||
timestr = datalayer.system.info.logged_can_messages + offset;
|
||||
} else {
|
||||
timestr = timestr_buffer;
|
||||
}
|
||||
timestr = datalayer.system.info.logged_can_messages + offset;
|
||||
} else {
|
||||
timestr = timestr_buffer;
|
||||
}
|
||||
#else
|
||||
timestr = timestr_buffer;
|
||||
#endif // DEBUG_VIA_WEB
|
||||
|
||||
offset += min(MAX_LENGTH_TIME_STR - 1,
|
||||
snprintf(timestr, MAX_LENGTH_TIME_STR, "%8lu.%03lu ", currentTime / 1000, currentTime % 1000));
|
||||
|
||||
#ifdef DEBUG_VIA_WEB
|
||||
if (!datalayer.system.info.can_logging_active) {
|
||||
if (datalayer.system.info.web_logging_active && !datalayer.system.info.can_logging_active) {
|
||||
datalayer.system.info.logged_can_messages_offset = offset; // Update offset in buffer
|
||||
}
|
||||
#endif // DEBUG_VIA_WEB
|
||||
|
||||
// LOG_TO_SD remains as compile-time option for now
|
||||
#ifdef LOG_TO_SD
|
||||
add_log_to_buffer((uint8_t*)timestr, MAX_LENGTH_TIME_STR);
|
||||
#endif // LOG_TO_SD
|
||||
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.write(timestr);
|
||||
#endif // DEBUG_VIA_USB
|
||||
|
||||
#endif // DEBUG_LOG
|
||||
if (datalayer.system.info.usb_logging_active) {
|
||||
Serial.write(timestr);
|
||||
}
|
||||
}
|
||||
|
||||
size_t Logging::write(const uint8_t* buffer, size_t size) {
|
||||
#ifdef DEBUG_LOG
|
||||
// Check if any logging is enabled at runtime
|
||||
if (!datalayer.system.info.web_logging_active && !datalayer.system.info.usb_logging_active) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (previous_message_was_newline) {
|
||||
add_timestamp(size);
|
||||
}
|
||||
|
@ -67,11 +66,12 @@ size_t Logging::write(const uint8_t* buffer, size_t size) {
|
|||
#ifdef LOG_TO_SD
|
||||
add_log_to_buffer(buffer, size);
|
||||
#endif
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.write(buffer, size);
|
||||
#endif
|
||||
#ifdef DEBUG_VIA_WEB
|
||||
if (!datalayer.system.info.can_logging_active) {
|
||||
|
||||
if (datalayer.system.info.usb_logging_active) {
|
||||
Serial.write(buffer, size);
|
||||
}
|
||||
|
||||
if (datalayer.system.info.web_logging_active && !datalayer.system.info.can_logging_active) {
|
||||
char* message_string = datalayer.system.info.logged_can_messages;
|
||||
int offset = datalayer.system.info.logged_can_messages_offset; // Keeps track of the current position in the buffer
|
||||
size_t message_string_size = sizeof(datalayer.system.info.logged_can_messages);
|
||||
|
@ -82,16 +82,17 @@ size_t Logging::write(const uint8_t* buffer, size_t size) {
|
|||
memcpy(message_string + offset, buffer, size);
|
||||
datalayer.system.info.logged_can_messages_offset = offset + size; // Update offset in buffer
|
||||
}
|
||||
#endif // DEBUG_VIA_WEB
|
||||
|
||||
previous_message_was_newline = buffer[size - 1] == '\n';
|
||||
return size;
|
||||
#endif // DEBUG_LOG
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Logging::printf(const char* fmt, ...) {
|
||||
#ifdef DEBUG_LOG
|
||||
// Check if any logging is enabled at runtime
|
||||
if (!datalayer.system.info.web_logging_active && !datalayer.system.info.usb_logging_active) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (previous_message_was_newline) {
|
||||
add_timestamp(MAX_LINE_LENGTH_PRINTF);
|
||||
}
|
||||
|
@ -101,21 +102,22 @@ void Logging::printf(const char* fmt, ...) {
|
|||
int offset = datalayer.system.info.logged_can_messages_offset; // Keeps track of the current position in the buffer
|
||||
static char buffer[MAX_LINE_LENGTH_PRINTF];
|
||||
char* message_buffer;
|
||||
#ifdef DEBUG_VIA_WEB
|
||||
if (!datalayer.system.info.can_logging_active) {
|
||||
/* If web debug is active and can logging is inactive,
|
||||
* we use the debug logging memory directly for writing the output */
|
||||
if (offset + MAX_LINE_LENGTH_PRINTF > message_string_size) {
|
||||
// Not enough space, reset and start from the beginning
|
||||
offset = 0;
|
||||
|
||||
if (datalayer.system.info.web_logging_active) {
|
||||
if (!datalayer.system.info.can_logging_active) {
|
||||
/* If web debug is active and can logging is inactive,
|
||||
* we use the debug logging memory directly for writing the output */
|
||||
if (offset + MAX_LINE_LENGTH_PRINTF > message_string_size) {
|
||||
// Not enough space, reset and start from the beginning
|
||||
offset = 0;
|
||||
}
|
||||
message_buffer = message_string + offset;
|
||||
} else {
|
||||
message_buffer = buffer;
|
||||
}
|
||||
message_buffer = message_string + offset;
|
||||
} else {
|
||||
message_buffer = buffer;
|
||||
}
|
||||
#else
|
||||
message_buffer = buffer;
|
||||
#endif // DEBUG_VIA_WEB
|
||||
|
||||
va_list(args);
|
||||
va_start(args, fmt);
|
||||
|
@ -126,18 +128,15 @@ void Logging::printf(const char* fmt, ...) {
|
|||
add_log_to_buffer((uint8_t*)message_buffer, size);
|
||||
#endif // LOG_TO_SD
|
||||
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.write(message_buffer, size);
|
||||
#endif // DEBUG_VIA_USB
|
||||
if (datalayer.system.info.usb_logging_active) {
|
||||
Serial.write(message_buffer, size);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_VIA_WEB
|
||||
if (!datalayer.system.info.can_logging_active) {
|
||||
if (datalayer.system.info.web_logging_active && !datalayer.system.info.can_logging_active) {
|
||||
// Data was already added to buffer, just move offset
|
||||
datalayer.system.info.logged_can_messages_offset =
|
||||
offset + size; // Keeps track of the current position in the buffer
|
||||
}
|
||||
#endif // DEBUG_VIA_WEB
|
||||
|
||||
previous_message_was_newline = message_buffer[size - 1] == '\n';
|
||||
#endif // DEBUG_LOG
|
||||
}
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
#ifndef __LOGGING_H__
|
||||
#define __LOGGING_H__
|
||||
|
||||
#include <Print.h>
|
||||
#include <inttypes.h>
|
||||
#include "../../../USER_SETTINGS.h"
|
||||
#include "../../datalayer/datalayer.h"
|
||||
#include "types.h"
|
||||
|
||||
#ifndef UNIT_TEST
|
||||
// Real implementation for production
|
||||
#include <Print.h>
|
||||
|
||||
class Logging : public Print {
|
||||
void add_timestamp(size_t size);
|
||||
|
||||
|
@ -13,18 +17,51 @@ class Logging : public Print {
|
|||
virtual size_t write(const uint8_t* buffer, size_t size);
|
||||
virtual size_t write(uint8_t) { return 0; }
|
||||
void printf(const char* fmt, ...);
|
||||
void log_bms_status(real_bms_status_enum bms_status);
|
||||
Logging() {}
|
||||
};
|
||||
|
||||
extern Logging logging;
|
||||
// Production macros
|
||||
#define DEBUG_PRINTF(fmt, ...) \
|
||||
do { \
|
||||
if (datalayer.system.info.web_logging_active || datalayer.system.info.usb_logging_active) { \
|
||||
logging.printf(fmt, ##__VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define DEBUG_PRINTLN(str) \
|
||||
do { \
|
||||
if (datalayer.system.info.web_logging_active || datalayer.system.info.usb_logging_active) { \
|
||||
logging.println(str); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
#define DEBUG_PRINTF(fmt, ...) logging.printf(fmt, ##__VA_ARGS__)
|
||||
#define DEBUG_PRINTLN(str) logging.println(str)
|
||||
#else
|
||||
// Mock implementation for tests
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
|
||||
class Logging {
|
||||
public:
|
||||
// Mock methods that do nothing
|
||||
size_t write(const uint8_t* buffer, size_t size) { return size; }
|
||||
size_t write(uint8_t) { return 0; }
|
||||
|
||||
static void printf(const char* fmt, ...) {
|
||||
// Empty implementation - silence unused parameter warnings
|
||||
(void)fmt;
|
||||
}
|
||||
|
||||
static void println(const char* str) { (void)str; }
|
||||
|
||||
Logging() {}
|
||||
};
|
||||
|
||||
// Test macros - empty implementations
|
||||
#define DEBUG_PRINTF(fmt, ...) ((void)0)
|
||||
#define DEBUG_PRINTLN(str) ((void)0)
|
||||
|
||||
#endif
|
||||
|
||||
extern Logging logging;
|
||||
|
||||
#endif // __LOGGING_H__
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
#include "../../datalayer/datalayer.h"
|
||||
#include "index_html.h"
|
||||
|
||||
#if defined(DEBUG_VIA_WEB) || defined(LOG_TO_SD)
|
||||
char* strnchr(const char* s, int c, size_t n) {
|
||||
// Like strchr, but only searches the first 'n' bytes of the string.
|
||||
|
||||
|
@ -54,13 +53,13 @@ String debug_logger_processor(void) {
|
|||
".can-message { background-color: #404E57; margin-bottom: 5px; padding: 10px; border-radius: 5px; font-family: "
|
||||
"monospace; }";
|
||||
content += "</style>";
|
||||
#ifdef DEBUG_VIA_WEB
|
||||
content += "<button onclick='refreshPage()'>Refresh data</button> ";
|
||||
#endif
|
||||
if (datalayer.system.info.web_logging_active) {
|
||||
content += "<button onclick='refreshPage()'>Refresh data</button> ";
|
||||
}
|
||||
content += "<button onclick='exportLog()'>Export to .txt</button> ";
|
||||
#ifdef LOG_TO_SD
|
||||
content += "<button onclick='deleteLog()'>Delete log file</button> ";
|
||||
#endif
|
||||
if (datalayer.system.info.SD_logging_active) {
|
||||
content += "<button onclick='deleteLog()'>Delete log file</button> ";
|
||||
}
|
||||
content += "<button onclick='goToMainPage()'>Back to main page</button>";
|
||||
|
||||
// Start a new block for the debug log messages
|
||||
|
@ -99,12 +98,11 @@ String debug_logger_processor(void) {
|
|||
content += "<script>";
|
||||
content += "function refreshPage(){ location.reload(true); }";
|
||||
content += "function exportLog() { window.location.href = '/export_log'; }";
|
||||
#ifdef LOG_TO_SD
|
||||
content += "function deleteLog() { window.location.href = '/delete_log'; }";
|
||||
#endif
|
||||
if (datalayer.system.info.SD_logging_active) {
|
||||
content += "function deleteLog() { window.location.href = '/delete_log'; }";
|
||||
}
|
||||
content += "function goToMainPage() { window.location.href = '/'; }";
|
||||
content += "</script>";
|
||||
content += index_html_footer;
|
||||
return content;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -46,12 +46,6 @@ String events_processor(const String& var) {
|
|||
EVENTS_ENUM_TYPE event_handle = event.event_handle;
|
||||
event_pointer = event.event_pointer;
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Showing Event: " + String(get_event_enum_string(event_handle)) +
|
||||
" count: " + String(event_pointer->occurences) + " seconds: " + String(event_pointer->timestamp) +
|
||||
" data: " + String(event_pointer->data) +
|
||||
" level: " + String(get_event_level_string(event_handle)));
|
||||
#endif
|
||||
content.concat("<div class='event'>");
|
||||
content.concat("<div>" + String(get_event_enum_string(event_handle)) + "</div>");
|
||||
content.concat("<div>" + String(get_event_level_string(event_handle)) + "</div>");
|
||||
|
|
|
@ -93,6 +93,8 @@ String options_from_map(int selected, const TMap& value_name_map) {
|
|||
return options;
|
||||
}
|
||||
|
||||
static const std::map<int, String> led_modes = {{0, "Classic"}, {1, "Energy Flow"}, {2, "Heartbeat"}};
|
||||
|
||||
static const std::map<int, String> tesla_countries = {
|
||||
{21843, "US (USA)"}, {17217, "CA (Canada)"}, {18242, "GB (UK & N Ireland)"},
|
||||
{17483, "DK (Denmark)"}, {17477, "DE (Germany)"}, {16725, "AU (Australia)"}};
|
||||
|
@ -281,6 +283,26 @@ String settings_processor(const String& var, BatteryEmulatorSettingsStore& setti
|
|||
return settings.getBool("WIFIAPENABLED", wifiap_enabled) ? "checked" : "";
|
||||
}
|
||||
|
||||
if (var == "CANLOGUSB") {
|
||||
return settings.getBool("CANLOGUSB") ? "checked" : "";
|
||||
}
|
||||
|
||||
if (var == "USBENABLED") {
|
||||
return settings.getBool("USBENABLED") ? "checked" : "";
|
||||
}
|
||||
|
||||
if (var == "WEBENABLED") {
|
||||
return settings.getBool("WEBENABLED") ? "checked" : "";
|
||||
}
|
||||
|
||||
if (var == "CANLOGSD") {
|
||||
return settings.getBool("CANLOGSD") ? "checked" : "";
|
||||
}
|
||||
|
||||
if (var == "SDLOGENABLED") {
|
||||
return settings.getBool("SDLOGENABLED") ? "checked" : "";
|
||||
}
|
||||
|
||||
if (var == "MQTTENABLED") {
|
||||
return settings.getBool("MQTTENABLED") ? "checked" : "";
|
||||
}
|
||||
|
@ -527,6 +549,10 @@ String settings_processor(const String& var, BatteryEmulatorSettingsStore& setti
|
|||
return settings.getBool("INVICNT") ? "checked" : "";
|
||||
}
|
||||
|
||||
if (var == "CANFREQ") {
|
||||
return String(settings.getUInt("CANFREQ", 8));
|
||||
}
|
||||
|
||||
if (var == "DIGITALHVIL") {
|
||||
return settings.getBool("DIGITALHVIL") ? "checked" : "";
|
||||
}
|
||||
|
@ -551,6 +577,10 @@ String settings_processor(const String& var, BatteryEmulatorSettingsStore& setti
|
|||
return options_from_map(settings.getUInt("GTWPACK", 0), tesla_pack);
|
||||
}
|
||||
|
||||
if (var == "LEDMODE") {
|
||||
return options_from_map(settings.getUInt("LEDMODE", 0), led_modes);
|
||||
}
|
||||
|
||||
return String();
|
||||
}
|
||||
|
||||
|
@ -898,6 +928,9 @@ const char* getCANInterfaceName(CAN_Interface interface) {
|
|||
</select>
|
||||
</div>
|
||||
|
||||
<label>Can Addon Frequency: </label>
|
||||
<input name='CANFREQ' type='text' value="%CANFREQ%" pattern="^[0-9]+$" />
|
||||
|
||||
<label>Equipment stop button: </label><select name='EQSTOP'>
|
||||
%EQSTOP%
|
||||
</select>
|
||||
|
@ -937,6 +970,25 @@ const char* getCANInterfaceName(CAN_Interface interface) {
|
|||
<label>Custom hostname: </label>
|
||||
<input type='text' name='HOSTNAME' value="%HOSTNAME%" />
|
||||
|
||||
<label>Enable CAN logging via USB serial: </label>
|
||||
<input type='checkbox' name='CANLOGUSB' value='on' style='margin-left: 0;' %CANLOGUSB% />
|
||||
|
||||
<label>Enable logging via USB serial: </label>
|
||||
<input type='checkbox' name='USBENABLED' value='on' style='margin-left: 0;' %USBENABLED% />
|
||||
|
||||
<label>Enable logging via Webserver: </label>
|
||||
<input type='checkbox' name='WEBENABLED' value='on' style='margin-left: 0;' %WEBENABLED% />
|
||||
|
||||
<label>Enable CAN logging via SD card: </label>
|
||||
<input type='checkbox' name='CANLOGSD' value='on' style='margin-left: 0;' %CANLOGSD% />
|
||||
|
||||
<label>Enable logging via SD card: </label>
|
||||
<input type='checkbox' name='SDLOGENABLED' value='on' style='margin-left: 0;' %SDLOGENABLED% />
|
||||
|
||||
<label for='LEDMODE'>Status LED pattern: </label><select name='LEDMODE' id='LEDMODE'>
|
||||
%LEDMODE%
|
||||
</select>
|
||||
|
||||
<label>Enable MQTT: </label>
|
||||
<input type='checkbox' name='MQTTENABLED' value='on' style='margin-left: 0;' %MQTTENABLED% />
|
||||
|
||||
|
|
|
@ -267,13 +267,13 @@ void init_webserver() {
|
|||
}
|
||||
});
|
||||
|
||||
#if defined(DEBUG_VIA_WEB) || defined(LOG_TO_SD)
|
||||
// Route for going to debug logging web page
|
||||
server.on("/log", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
AsyncWebServerResponse* response = request->beginResponse(200, "text/html", debug_logger_processor());
|
||||
request->send(response);
|
||||
});
|
||||
#endif // DEBUG_VIA_WEB
|
||||
if (datalayer.system.info.web_logging_active || datalayer.system.info.SD_logging_active) {
|
||||
// Route for going to debug logging web page
|
||||
server.on("/log", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
AsyncWebServerResponse* response = request->beginResponse(200, "text/html", debug_logger_processor());
|
||||
request->send(response);
|
||||
});
|
||||
}
|
||||
|
||||
// Define the handler to stop can logging
|
||||
server.on("/stop_can_logging", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
|
@ -289,93 +289,89 @@ void init_webserver() {
|
|||
},
|
||||
handleFileUpload);
|
||||
|
||||
#ifndef LOG_CAN_TO_SD
|
||||
// Define the handler to export can log
|
||||
server.on("/export_can_log", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
String logs = String(datalayer.system.info.logged_can_messages);
|
||||
if (logs.length() == 0) {
|
||||
logs = "No logs available.";
|
||||
}
|
||||
if (datalayer.system.info.CAN_SD_logging_active) {
|
||||
// Define the handler to export can log
|
||||
server.on("/export_can_log", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
pause_can_writing();
|
||||
request->send(SD_MMC, CAN_LOG_FILE, String(), true);
|
||||
resume_can_writing();
|
||||
});
|
||||
|
||||
// Get the current time
|
||||
time_t now = time(nullptr);
|
||||
struct tm timeinfo;
|
||||
localtime_r(&now, &timeinfo);
|
||||
// Define the handler to delete can log
|
||||
server.on("/delete_can_log", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
delete_can_log();
|
||||
request->send(200, "text/plain", "Log file deleted");
|
||||
});
|
||||
} else {
|
||||
// Define the handler to export can log
|
||||
server.on("/export_can_log", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
String logs = String(datalayer.system.info.logged_can_messages);
|
||||
if (logs.length() == 0) {
|
||||
logs = "No logs available.";
|
||||
}
|
||||
|
||||
// Ensure time retrieval was successful
|
||||
char filename[32];
|
||||
if (strftime(filename, sizeof(filename), "canlog_%H-%M-%S.txt", &timeinfo)) {
|
||||
// Valid filename created
|
||||
} else {
|
||||
// Fallback filename if automatic timestamping failed
|
||||
strcpy(filename, "battery_emulator_can_log.txt");
|
||||
}
|
||||
// Get the current time
|
||||
time_t now = time(nullptr);
|
||||
struct tm timeinfo;
|
||||
localtime_r(&now, &timeinfo);
|
||||
|
||||
// Use request->send with dynamic headers
|
||||
AsyncWebServerResponse* response = request->beginResponse(200, "text/plain", logs);
|
||||
response->addHeader("Content-Disposition", String("attachment; filename=\"") + String(filename) + "\"");
|
||||
request->send(response);
|
||||
});
|
||||
#endif
|
||||
// Ensure time retrieval was successful
|
||||
char filename[32];
|
||||
if (strftime(filename, sizeof(filename), "canlog_%H-%M-%S.txt", &timeinfo)) {
|
||||
// Valid filename created
|
||||
} else {
|
||||
// Fallback filename if automatic timestamping failed
|
||||
strcpy(filename, "battery_emulator_can_log.txt");
|
||||
}
|
||||
|
||||
#ifdef LOG_CAN_TO_SD
|
||||
// Define the handler to export can log
|
||||
server.on("/export_can_log", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
pause_can_writing();
|
||||
request->send(SD_MMC, CAN_LOG_FILE, String(), true);
|
||||
resume_can_writing();
|
||||
});
|
||||
// Use request->send with dynamic headers
|
||||
AsyncWebServerResponse* response = request->beginResponse(200, "text/plain", logs);
|
||||
response->addHeader("Content-Disposition", String("attachment; filename=\"") + String(filename) + "\"");
|
||||
request->send(response);
|
||||
});
|
||||
}
|
||||
|
||||
// Define the handler to delete can log
|
||||
server.on("/delete_can_log", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
delete_can_log();
|
||||
request->send(200, "text/plain", "Log file deleted");
|
||||
});
|
||||
#endif
|
||||
if (datalayer.system.info.SD_logging_active) {
|
||||
// Define the handler to delete log file
|
||||
server.on("/delete_log", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
delete_log();
|
||||
request->send(200, "text/plain", "Log file deleted");
|
||||
});
|
||||
|
||||
#ifdef LOG_TO_SD
|
||||
// Define the handler to delete log file
|
||||
server.on("/delete_log", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
delete_log();
|
||||
request->send(200, "text/plain", "Log file deleted");
|
||||
});
|
||||
// Define the handler to export debug log
|
||||
server.on("/export_log", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
pause_log_writing();
|
||||
request->send(SD_MMC, LOG_FILE, String(), true);
|
||||
resume_log_writing();
|
||||
});
|
||||
} else {
|
||||
// Define the handler to export debug log
|
||||
server.on("/export_log", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
String logs = String(datalayer.system.info.logged_can_messages);
|
||||
if (logs.length() == 0) {
|
||||
logs = "No logs available.";
|
||||
}
|
||||
|
||||
// Define the handler to export debug log
|
||||
server.on("/export_log", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
pause_log_writing();
|
||||
request->send(SD_MMC, LOG_FILE, String(), true);
|
||||
resume_log_writing();
|
||||
});
|
||||
#endif
|
||||
// Get the current time
|
||||
time_t now = time(nullptr);
|
||||
struct tm timeinfo;
|
||||
localtime_r(&now, &timeinfo);
|
||||
|
||||
#ifndef LOG_TO_SD
|
||||
// Define the handler to export debug log
|
||||
server.on("/export_log", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
String logs = String(datalayer.system.info.logged_can_messages);
|
||||
if (logs.length() == 0) {
|
||||
logs = "No logs available.";
|
||||
}
|
||||
// Ensure time retrieval was successful
|
||||
char filename[32];
|
||||
if (strftime(filename, sizeof(filename), "log_%H-%M-%S.txt", &timeinfo)) {
|
||||
// Valid filename created
|
||||
} else {
|
||||
// Fallback filename if automatic timestamping failed
|
||||
strcpy(filename, "battery_emulator_log.txt");
|
||||
}
|
||||
|
||||
// Get the current time
|
||||
time_t now = time(nullptr);
|
||||
struct tm timeinfo;
|
||||
localtime_r(&now, &timeinfo);
|
||||
|
||||
// Ensure time retrieval was successful
|
||||
char filename[32];
|
||||
if (strftime(filename, sizeof(filename), "log_%H-%M-%S.txt", &timeinfo)) {
|
||||
// Valid filename created
|
||||
} else {
|
||||
// Fallback filename if automatic timestamping failed
|
||||
strcpy(filename, "battery_emulator_log.txt");
|
||||
}
|
||||
|
||||
// Use request->send with dynamic headers
|
||||
AsyncWebServerResponse* response = request->beginResponse(200, "text/plain", logs);
|
||||
response->addHeader("Content-Disposition", String("attachment; filename=\"") + String(filename) + "\"");
|
||||
request->send(response);
|
||||
});
|
||||
#endif
|
||||
// Use request->send with dynamic headers
|
||||
AsyncWebServerResponse* response = request->beginResponse(200, "text/plain", logs);
|
||||
response->addHeader("Content-Disposition", String("attachment; filename=\"") + String(filename) + "\"");
|
||||
request->send(response);
|
||||
});
|
||||
}
|
||||
|
||||
// Route for going to cellmonitor web page
|
||||
def_route_with_auth("/cellmonitor", server, HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
|
@ -413,8 +409,9 @@ void init_webserver() {
|
|||
};
|
||||
|
||||
const char* boolSettingNames[] = {
|
||||
"DBLBTR", "CNTCTRL", "CNTCTRLDBL", "PWMCNTCTRL", "PERBMSRESET", "REMBMSRESET", "CANFDASCAN",
|
||||
"WIFIAPENABLED", "MQTTENABLED", "HADISC", "MQTTTOPICS", "INVICNT", "GTWRHD", "DIGITALHVIL",
|
||||
"DBLBTR", "CNTCTRL", "CNTCTRLDBL", "PWMCNTCTRL", "PERBMSRESET", "SDLOGENABLED", "REMBMSRESET",
|
||||
"USBENABLED", "CANLOGUSB", "WEBENABLED", "CANFDASCAN", "CANLOGSD", "WIFIAPENABLED", "MQTTENABLED",
|
||||
"HADISC", "MQTTTOPICS", "INVICNT", "GTWRHD", "DIGITALHVIL",
|
||||
};
|
||||
|
||||
// Handles the form POST from UI to save settings of the common image
|
||||
|
@ -515,6 +512,9 @@ void init_webserver() {
|
|||
} else if (p->name() == "INVBTYPE") {
|
||||
auto type = atoi(p->value().c_str());
|
||||
settings.saveUInt("INVBTYPE", (int)type);
|
||||
} else if (p->name() == "CANFREQ") {
|
||||
auto type = atoi(p->value().c_str());
|
||||
settings.saveUInt("CANFREQ", type);
|
||||
} else if (p->name() == "GTWCOUNTRY") {
|
||||
auto type = atoi(p->value().c_str());
|
||||
settings.saveUInt("GTWCOUNTRY", type);
|
||||
|
@ -527,6 +527,9 @@ void init_webserver() {
|
|||
} else if (p->name() == "GTWPACK") {
|
||||
auto type = atoi(p->value().c_str());
|
||||
settings.saveUInt("GTWPACK", type);
|
||||
} else if (p->name() == "LEDMODE") {
|
||||
auto type = atoi(p->value().c_str());
|
||||
settings.saveUInt("LEDMODE", type);
|
||||
}
|
||||
|
||||
for (auto& boolSetting : boolSettings) {
|
||||
|
@ -903,6 +906,9 @@ String processor(const String& var) {
|
|||
#ifdef HW_LILYGO
|
||||
content += " Hardware: LilyGo T-CAN485";
|
||||
#endif // HW_LILYGO
|
||||
#ifdef HW_LILYGO2CAN
|
||||
content += " Hardware: LilyGo T_2CAN";
|
||||
#endif // HW_LILYGO2CAN
|
||||
#ifdef HW_STARK
|
||||
content += " Hardware: Stark CMR Module";
|
||||
#endif // HW_STARK
|
||||
|
@ -1400,9 +1406,9 @@ String processor(const String& var) {
|
|||
content += "<button onclick='Advanced()'>More Battery Info</button> ";
|
||||
content += "<button onclick='CANlog()'>CAN logger</button> ";
|
||||
content += "<button onclick='CANreplay()'>CAN replay</button> ";
|
||||
#if defined(DEBUG_VIA_WEB) || defined(LOG_TO_SD)
|
||||
content += "<button onclick='Log()'>Log</button> ";
|
||||
#endif // DEBUG_VIA_WEB
|
||||
if (datalayer.system.info.web_logging_active || datalayer.system.info.SD_logging_active) {
|
||||
content += "<button onclick='Log()'>Log</button> ";
|
||||
}
|
||||
content += "<button onclick='Cellmon()'>Cellmonitor</button> ";
|
||||
content += "<button onclick='Events()'>Events</button> ";
|
||||
content += "<button onclick='askReboot()'>Reboot Emulator</button>";
|
||||
|
@ -1482,9 +1488,7 @@ void onOTAProgress(size_t current, size_t final) {
|
|||
// Log every 1 second
|
||||
if (millis() - ota_progress_millis > 1000) {
|
||||
ota_progress_millis = millis();
|
||||
#ifdef DEBUG_LOG
|
||||
logging.printf("OTA Progress Current: %u bytes, Final: %u bytes\n", current, final);
|
||||
#endif // DEBUG_LOG
|
||||
// Reset the "watchdog"
|
||||
ota_timeout_timer.reset();
|
||||
}
|
||||
|
@ -1501,13 +1505,9 @@ void onOTAEnd(bool success) {
|
|||
// Max Charge/Discharge = 0; CAN = stop; contactors = open
|
||||
setBatteryPause(true, true, true, false);
|
||||
// a reboot will be done by the OTA library. no need to do anything here
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("OTA update finished successfully!");
|
||||
#endif // DEBUG_LOG
|
||||
} else {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("There was an error during OTA update!");
|
||||
#endif // DEBUG_LOG
|
||||
//try to Resume the battery pause and CAN communication
|
||||
setBatteryPause(false, false);
|
||||
}
|
||||
|
|
|
@ -130,33 +130,22 @@ void wifi_monitor() {
|
|||
// Try WiFi.reconnect() if it was successfully connected at least once
|
||||
if (hasConnectedBefore) {
|
||||
lastReconnectAttempt = currentMillis; // Reset reconnection attempt timer
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Wi-Fi reconnect attempt...");
|
||||
#endif
|
||||
if (WiFi.reconnect()) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Wi-Fi reconnect attempt sucess...");
|
||||
#endif
|
||||
reconnectAttempts = 0; // Reset the attempt counter on successful reconnect
|
||||
} else {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Wi-Fi reconnect attempt error...");
|
||||
#endif
|
||||
reconnectAttempts++;
|
||||
if (reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Failed to reconnect multiple times, forcing a full connection attempt...");
|
||||
#endif
|
||||
FullReconnectToWiFi();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If no previous connection, force a full connection attempt
|
||||
if (currentMillis - lastReconnectAttempt > current_full_reconnect_interval) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("No previous OK connection, force a full connection attempt...");
|
||||
#endif
|
||||
|
||||
wifiap_enabled = true;
|
||||
WiFi.mode(WIFI_AP_STA);
|
||||
init_WiFi_AP();
|
||||
|
@ -188,17 +177,13 @@ void connectToWiFi() {
|
|||
|
||||
if (WiFi.status() != WL_CONNECTED) {
|
||||
lastReconnectAttempt = millis(); // Reset the reconnect attempt timer
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Connecting to Wi-Fi...");
|
||||
#endif
|
||||
|
||||
DEBUG_PRINTF("Connecting to Wi-Fi SSID: %s, password: %s, Channel: %d\n", ssid.c_str(), password.c_str(),
|
||||
wifi_channel);
|
||||
WiFi.begin(ssid.c_str(), password.c_str(), wifi_channel);
|
||||
} else {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Wi-Fi already connected.");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -220,11 +205,9 @@ void onWifiConnect(WiFiEvent_t event, WiFiEventInfo_t info) {
|
|||
void onWifiGotIP(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
//clear disconnects events if we got a IP
|
||||
clear_event(EVENT_WIFI_DISCONNECT);
|
||||
#ifdef DEBUG_LOG
|
||||
logging.print("Wi-Fi Got IP. ");
|
||||
logging.print("IP address: ");
|
||||
logging.println(WiFi.localIP().toString());
|
||||
#endif
|
||||
}
|
||||
|
||||
// Event handler for Wi-Fi disconnection
|
||||
|
@ -233,9 +216,7 @@ void onWifiDisconnect(WiFiEvent_t event, WiFiEventInfo_t info) {
|
|||
if (connected_once) {
|
||||
set_event(EVENT_WIFI_DISCONNECT, 0);
|
||||
}
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Wi-Fi disconnected.");
|
||||
#endif
|
||||
//we dont do anything here, the reconnect will be handled by the monitor
|
||||
//too many events received when the connection is lost
|
||||
//normal reconnect retry start at first 2 seconds
|
||||
|
@ -254,9 +235,7 @@ void init_mDNS() {
|
|||
|
||||
// Initialize mDNS .local resolution
|
||||
if (!MDNS.begin(mdnsHost)) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Error setting up MDNS responder!");
|
||||
#endif
|
||||
} else {
|
||||
// Advertise via bonjour the service so we can auto discover these battery emulators on the local network.
|
||||
MDNS.addService(mdnsHost, "tcp", 80);
|
||||
|
|
|
@ -523,40 +523,23 @@ void FoxessCanInverter::map_can_frame_to_variable(CAN_frame rx_frame) {
|
|||
if (rx_frame.ID == 0x1871) {
|
||||
datalayer.system.status.CAN_inverter_still_alive = CAN_STILL_ALIVE;
|
||||
if (rx_frame.data.u8[0] == 0x03) { //0x1871 [0x03, 0x06, 0x17, 0x05, 0x09, 0x09, 0x28, 0x22]
|
||||
//This message is sent by the inverter every '6' seconds (0.5s after the pack serial numbers)
|
||||
//and contains a timestamp in bytes 2-7 i.e. <YY>,<MM>,<DD>,<HH>,<mm>,<ss>
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Inverter sends current time and date");
|
||||
#endif
|
||||
//This message is sent by the inverter every '6' seconds (0.5s after the pack serial numbers)
|
||||
//and contains a timestamp in bytes 2-7 i.e. <YY>,<MM>,<DD>,<HH>,<mm>,<ss>
|
||||
} else if (rx_frame.data.u8[0] == 0x01) {
|
||||
if (rx_frame.data.u8[4] == 0x00) {
|
||||
// Inverter wants to know bms info (every 1s)
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Inverter requests 1s BMS info, we reply");
|
||||
#endif
|
||||
// Inverter wants to know bms info (every 1s)
|
||||
send_bms_info = true;
|
||||
} else if (rx_frame.data.u8[4] == 0x01) { // b4 0x01 , 0x1871 [0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00]
|
||||
//Inverter wants to know all individual cellvoltages (occurs 6 seconds after valid BMS reply)
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Inverter requests individual battery pack status, we reply");
|
||||
#endif
|
||||
send_individual_pack_status = true;
|
||||
} else if (rx_frame.data.u8[4] == 0x04) { // b4 0x01 , 0x1871 [0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00]
|
||||
//Inverter wants to know all individual cellvoltages (occurs 6 seconds after valid BMS reply)
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Inverter requests cellvoltages and temps, we reply");
|
||||
#endif
|
||||
send_cellvoltages = true;
|
||||
}
|
||||
} else if (rx_frame.data.u8[0] == 0x02) { //0x1871 [0x02, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00]
|
||||
// Ack message
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Inverter acks, no reply needed");
|
||||
#endif
|
||||
// Ack message
|
||||
} else if (rx_frame.data.u8[0] == 0x05) { //0x1871 [0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00]
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Inverter wants to know serial numbers, we reply");
|
||||
#endif
|
||||
// Inverter wants to know serial numbers, we reply
|
||||
send_serial_numbers = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,16 +14,13 @@ void KostalInverterProtocol::float2frame(uint8_t* arr, float value, uint8_t fram
|
|||
}
|
||||
|
||||
static void dbg_timestamp(void) {
|
||||
#ifdef DEBUG_KOSTAL_RS485_DATA
|
||||
logging.print("[");
|
||||
logging.print(millis());
|
||||
logging.print(" ms] ");
|
||||
#endif
|
||||
}
|
||||
|
||||
static void dbg_frame(uint8_t* frame, int len, const char* prefix) {
|
||||
dbg_timestamp();
|
||||
#ifdef DEBUG_KOSTAL_RS485_DATA
|
||||
logging.print(prefix);
|
||||
logging.print(": ");
|
||||
for (uint8_t i = 0; i < len; i++) {
|
||||
|
@ -34,26 +31,11 @@ static void dbg_frame(uint8_t* frame, int len, const char* prefix) {
|
|||
logging.print(" ");
|
||||
}
|
||||
logging.println("");
|
||||
#endif
|
||||
#ifdef DEBUG_KOSTAL_RS485_DATA_USB
|
||||
Serial.print(prefix);
|
||||
Serial.print(": ");
|
||||
for (uint8_t i = 0; i < len; i++) {
|
||||
if (frame[i] < 0x10) {
|
||||
Serial.print("0");
|
||||
}
|
||||
Serial.print(frame[i], HEX);
|
||||
Serial.print(" ");
|
||||
}
|
||||
Serial.println("");
|
||||
#endif
|
||||
}
|
||||
|
||||
static void dbg_message(const char* msg) {
|
||||
dbg_timestamp();
|
||||
#ifdef DEBUG_KOSTAL_RS485_DATA
|
||||
logging.println(msg);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing#Encoding_examples */
|
||||
|
@ -154,24 +136,18 @@ void KostalInverterProtocol::update_values() {
|
|||
float2frame(CYCLIC_DATA, (float)datalayer.battery.status.current_dA / 10, 18); // Last current
|
||||
float2frame(CYCLIC_DATA, (float)datalayer.battery.status.current_dA / 10, 22); // Should be Avg current(1s)
|
||||
|
||||
// Close contactors after 7 battery info frames requested
|
||||
if (f2_startup_count > 7) {
|
||||
// Close contactors after 20 battery info frames requested
|
||||
if (f2_startup_count > 20) {
|
||||
datalayer.system.status.inverter_allows_contactor_closing = true;
|
||||
dbg_message("inverter_allows_contactor_closing -> true");
|
||||
dbg_message("inverter_allows_contactor_closing -> true (info frame)");
|
||||
}
|
||||
|
||||
// On startup, byte 56 seems to be always 0x00 couple of frames,.
|
||||
if (f2_startup_count < 9) {
|
||||
CYCLIC_DATA[56] = 0x00;
|
||||
} else {
|
||||
if (datalayer.system.status.inverter_allows_contactor_closing) {
|
||||
CYCLIC_DATA[56] = 0x01;
|
||||
}
|
||||
|
||||
// On startup, byte 59 seems to be always 0x02 couple of frames,.
|
||||
if (f2_startup_count < 14) {
|
||||
CYCLIC_DATA[59] = 0x02;
|
||||
} else {
|
||||
CYCLIC_DATA[59] = 0x00;
|
||||
} else {
|
||||
CYCLIC_DATA[56] = 0x00;
|
||||
CYCLIC_DATA[59] = 0x02;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -214,6 +190,12 @@ void KostalInverterProtocol::receive() // Runs as fast as possible to handle th
|
|||
{
|
||||
currentMillis = millis();
|
||||
|
||||
// Auto-reset contactor_test_active after 5 seconds
|
||||
if (contactortestTimerActive && (millis() - contactortestTimerStart >= 5000)) {
|
||||
datalayer.system.status.inverter_allows_contactor_closing = true;
|
||||
dbg_message("inverter_allows_contactor_closing -> true (Contactor test ended)");
|
||||
contactortestTimerActive = false;
|
||||
}
|
||||
if (datalayer.system.status.battery_allows_contactor_closing & !contactorMillis) {
|
||||
contactorMillis = currentMillis;
|
||||
}
|
||||
|
@ -240,9 +222,17 @@ void KostalInverterProtocol::receive() // Runs as fast as possible to handle th
|
|||
if (RS485_RXFRAME[6] == 0x5E) {
|
||||
// Set State function
|
||||
if (RS485_RXFRAME[7] == 0x00) {
|
||||
// Allow contactor closing
|
||||
datalayer.system.status.inverter_allows_contactor_closing = true;
|
||||
dbg_message("inverter_allows_contactor_closing -> true (5E 02)");
|
||||
send_kostal(ACK_FRAME, 8); // ACK
|
||||
} else if (RS485_RXFRAME[7] == 0x04) {
|
||||
// contactor test STATE, ACK sent
|
||||
datalayer.system.status.inverter_allows_contactor_closing = false;
|
||||
dbg_message("inverter_allows_contactor_closing -> false (Contactor test start)");
|
||||
send_kostal(ACK_FRAME, 8); // ACK
|
||||
contactortestTimerStart = currentMillis;
|
||||
contactortestTimerActive = true;
|
||||
} else if (RS485_RXFRAME[7] == 0xFF) {
|
||||
// no ACK sent
|
||||
} else {
|
||||
|
@ -279,6 +269,8 @@ void KostalInverterProtocol::receive() // Runs as fast as possible to handle th
|
|||
tmpframe[38] = calculate_kostal_crc(tmpframe, 38);
|
||||
null_stuffer(tmpframe, 40);
|
||||
send_kostal(tmpframe, 40);
|
||||
datalayer.system.status.inverter_allows_contactor_closing = false;
|
||||
dbg_message("inverter_allows_contactor_closing -> false (battery info sent)");
|
||||
info_sent = true;
|
||||
if (!startupMillis) {
|
||||
startupMillis = currentMillis;
|
||||
|
|
|
@ -8,13 +8,6 @@
|
|||
#define SELECTED_INVERTER_CLASS KostalInverterProtocol
|
||||
#endif
|
||||
|
||||
//#define DEBUG_KOSTAL_RS485_DATA // Enable this line to get TX / RX printed out via logging
|
||||
//#define DEBUG_KOSTAL_RS485_DATA_USB // Enable this line to get TX / RX printed out via USB
|
||||
|
||||
#if defined(DEBUG_KOSTAL_RS485_DATA) && !defined(DEBUG_LOG)
|
||||
#error "enable LOG_TO_SD, DEBUG_VIA_USB or DEBUG_VIA_WEB in order to use DEBUG_KOSTAL_RS485_DATA"
|
||||
#endif
|
||||
|
||||
class KostalInverterProtocol : public Rs485InverterProtocol {
|
||||
public:
|
||||
const char* name() override { return Name; }
|
||||
|
@ -43,6 +36,8 @@ class KostalInverterProtocol : public Rs485InverterProtocol {
|
|||
unsigned long currentMillis;
|
||||
unsigned long startupMillis = 0;
|
||||
unsigned long contactorMillis = 0;
|
||||
unsigned long contactortestTimerStart = 0;
|
||||
bool contactortestTimerActive = false;
|
||||
|
||||
uint16_t rx_index = 0;
|
||||
bool RX_allow = false;
|
||||
|
|
|
@ -35,9 +35,7 @@ ModbusMessage ModbusInverterProtocol::FC03(ModbusMessage request) {
|
|||
if ((addr + words) > MBPV_MAX) {
|
||||
// Yes - send respective error response
|
||||
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_ADDRESS);
|
||||
#ifdef DEBUG_LOG
|
||||
logging.printf("Modbus FC03 error: illegal request addr=%d words=%d\n", addr, words);
|
||||
#endif
|
||||
return response;
|
||||
}
|
||||
|
||||
|
@ -48,10 +46,6 @@ ModbusMessage ModbusInverterProtocol::FC03(ModbusMessage request) {
|
|||
response.add((uint16_t)(mbPV[addr + i]));
|
||||
}
|
||||
|
||||
// #ifdef DEBUG_LOG
|
||||
// logging.printf("Modbus FC03 response: %d %d\n", addr, mbPV[addr]);
|
||||
// #endif
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
|
@ -67,9 +61,7 @@ ModbusMessage ModbusInverterProtocol::FC06(ModbusMessage request) {
|
|||
if ((addr) > MBPV_MAX) {
|
||||
// Yes - send respective error response
|
||||
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_ADDRESS);
|
||||
#ifdef DEBUG_LOG
|
||||
logging.printf("Modbus FC06 error: illegal request addr=%d val=%d\n", addr, val);
|
||||
#endif
|
||||
return response;
|
||||
}
|
||||
|
||||
|
@ -97,18 +89,14 @@ ModbusMessage ModbusInverterProtocol::FC16(ModbusMessage request) {
|
|||
|| (words > 123)) // can't support more than this in request packet
|
||||
{ // Yes - send respective error response
|
||||
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_VALUE);
|
||||
#ifdef DEBUG_LOG
|
||||
logging.printf("Modbus FC16 error: bad registers addr=%d words=%d bytes=%d\n", addr, words, bytes);
|
||||
#endif
|
||||
return response;
|
||||
}
|
||||
// Address overflow?
|
||||
if ((addr + words) > MBPV_MAX) {
|
||||
// Yes - send respective error response
|
||||
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_ADDRESS);
|
||||
#ifdef DEBUG_LOG
|
||||
logging.printf("Modbus FC16 error: overflow addr=%d words=%d\n", addr, words);
|
||||
#endif
|
||||
return response;
|
||||
}
|
||||
|
||||
|
@ -145,20 +133,16 @@ ModbusMessage ModbusInverterProtocol::FC23(ModbusMessage request) {
|
|||
|| (read_words > 125)) // can't fit more than this in the response packet
|
||||
{ // Yes - send respective error response
|
||||
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_VALUE);
|
||||
#ifdef DEBUG_LOG
|
||||
logging.printf("Modbus FC23 error: bad registers write_addr=%d write_words=%d write_bytes=%d read_words=%d\n",
|
||||
write_addr, write_words, write_bytes, read_words);
|
||||
#endif
|
||||
return response;
|
||||
}
|
||||
// Address overflow?
|
||||
if (((write_addr + write_words) > MBPV_MAX) ||
|
||||
((read_addr + read_words) > MBPV_MAX)) { // Yes - send respective error response
|
||||
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_ADDRESS);
|
||||
#ifdef DEBUG_LOG
|
||||
logging.printf("Modbus FC23 error: overflow write_addr=%d write_words=%d read_addr=%d read_words=%d\n", write_addr,
|
||||
write_words, read_addr, read_words);
|
||||
#endif
|
||||
return response;
|
||||
}
|
||||
|
||||
|
|
|
@ -107,34 +107,11 @@ void PylonLvInverter::map_can_frame_to_variable(CAN_frame rx_frame) {
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_VIA_USB
|
||||
void dump_frame(CAN_frame* frame) {
|
||||
Serial.print("[PYLON-LV] sending CAN frame ");
|
||||
Serial.print(frame->ID, HEX);
|
||||
Serial.print(": ");
|
||||
for (int i = 0; i < 8; i++) {
|
||||
Serial.print(frame->data.u8[i] >> 4, HEX);
|
||||
Serial.print(frame->data.u8[i] & 0xf, HEX);
|
||||
Serial.print(" ");
|
||||
}
|
||||
Serial.println();
|
||||
}
|
||||
#endif
|
||||
|
||||
void PylonLvInverter::transmit_can(unsigned long currentMillis) {
|
||||
|
||||
if (currentMillis - previousMillis1000ms >= 1000) {
|
||||
previousMillis1000ms = currentMillis;
|
||||
|
||||
#ifdef DEBUG_VIA_USB
|
||||
dump_frame(&PYLON_351);
|
||||
dump_frame(&PYLON_355);
|
||||
dump_frame(&PYLON_356);
|
||||
dump_frame(&PYLON_359);
|
||||
dump_frame(&PYLON_35C);
|
||||
dump_frame(&PYLON_35E);
|
||||
#endif
|
||||
|
||||
transmit_can_frame(&PYLON_351);
|
||||
transmit_can_frame(&PYLON_355);
|
||||
transmit_can_frame(&PYLON_356);
|
||||
|
|
|
@ -227,9 +227,7 @@ void SmaBydHInverter::map_can_frame_to_variable(CAN_frame rx_frame) {
|
|||
break;
|
||||
case 0x5E7: //Message originating from SMA inverter - Pairing request
|
||||
case 0x660: //Message originating from SMA inverter - Pairing request
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Received SMA pairing request");
|
||||
#endif // DEBUG_LOG
|
||||
pairing_events++;
|
||||
set_event(EVENT_SMA_PAIRING, pairing_events);
|
||||
datalayer.system.status.CAN_inverter_still_alive = CAN_STILL_ALIVE;
|
||||
|
|
|
@ -218,9 +218,7 @@ void SmaBydHvsInverter::map_can_frame_to_variable(CAN_frame rx_frame) {
|
|||
break;
|
||||
case 0x5E7: //Message originating from SMA inverter - Pairing request
|
||||
case 0x660: //Message originating from SMA inverter - Pairing request
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Received SMA pairing request");
|
||||
#endif // DEBUG_LOG
|
||||
pairing_events++;
|
||||
set_event(EVENT_SMA_PAIRING, pairing_events);
|
||||
datalayer.system.status.CAN_inverter_still_alive = CAN_STILL_ALIVE;
|
||||
|
|
|
@ -126,9 +126,7 @@ void SmaTripowerInverter::map_can_frame_to_variable(CAN_frame rx_frame) {
|
|||
break;
|
||||
case 0x5E7: //Message originating from SMA inverter - Pairing request
|
||||
case 0x660: //Message originating from SMA inverter - Pairing request
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Received SMA pairing request");
|
||||
#endif // DEBUG_LOG
|
||||
pairing_events++;
|
||||
set_event(EVENT_SMA_PAIRING, pairing_events);
|
||||
datalayer.system.status.CAN_inverter_still_alive = CAN_STILL_ALIVE;
|
||||
|
|
|
@ -147,9 +147,7 @@ void SolaxInverter::map_can_frame_to_variable(CAN_frame rx_frame) {
|
|||
|
||||
switch (STATE) {
|
||||
case (BATTERY_ANNOUNCE):
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Solax Battery State: Announce");
|
||||
#endif
|
||||
datalayer.system.status.inverter_allows_contactor_closing = false;
|
||||
SOLAX_1875.data.u8[4] = (0x00); // Inform Inverter: Contactor 0=off, 1=on.
|
||||
for (uint8_t i = 0; i < number_of_batteries; i++) {
|
||||
|
@ -183,9 +181,7 @@ void SolaxInverter::map_can_frame_to_variable(CAN_frame rx_frame) {
|
|||
transmit_can_frame(&SOLAX_1878);
|
||||
transmit_can_frame(&SOLAX_1801); // Announce that the battery will be connected
|
||||
STATE = CONTACTOR_CLOSED; // Jump to Contactor Closed State
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Solax Battery State: Contactor Closed");
|
||||
#endif
|
||||
break;
|
||||
|
||||
case (CONTACTOR_CLOSED):
|
||||
|
@ -213,14 +209,10 @@ void SolaxInverter::map_can_frame_to_variable(CAN_frame rx_frame) {
|
|||
if (rx_frame.ID == 0x1871 && rx_frame.data.u64 == __builtin_bswap64(0x0500010000000000)) {
|
||||
transmit_can_frame(&SOLAX_1881);
|
||||
transmit_can_frame(&SOLAX_1882);
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("1871 05-frame received from inverter");
|
||||
#endif
|
||||
}
|
||||
if (rx_frame.ID == 0x1871 && rx_frame.data.u8[0] == (0x03)) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("1871 03-frame received from inverter");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,298 +0,0 @@
|
|||
/**
|
||||
* @section License
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2017, Thomas Barth, barth-dev.de
|
||||
* 2017, Jaime Breva, jbreva@nayarsystems.com
|
||||
* 2018, Michael Wagner, mw@iot-make.de
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "CAN.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/queue.h"
|
||||
|
||||
#include "esp_intr_alloc.h" // Renamed when migrating ESP32 2.x -> 3.x
|
||||
#include "soc/dport_reg.h"
|
||||
#include "soc/gpio_sig_map.h"
|
||||
#include <math.h>
|
||||
|
||||
#include "driver/gpio.h"
|
||||
|
||||
#include "can_regdef.h"
|
||||
#include "CAN_config.h"
|
||||
|
||||
// CAN Filter - no acceptance filter
|
||||
static CAN_filter_t __filter = { Dual_Mode, 0, 0, 0, 0, 0Xff, 0Xff, 0Xff, 0Xff };
|
||||
|
||||
static void CAN_read_frame_phy();
|
||||
static void CAN_isr(void *arg_p);
|
||||
static int CAN_write_frame_phy(const CAN_frame_t *p_frame);
|
||||
static SemaphoreHandle_t sem_tx_complete;
|
||||
|
||||
static void CAN_isr(void *arg_p) {
|
||||
|
||||
// Interrupt flag buffer
|
||||
__CAN_IRQ_t interrupt;
|
||||
BaseType_t higherPriorityTaskWoken = pdFALSE;
|
||||
|
||||
// Read interrupt status and clear flags
|
||||
interrupt = MODULE_CAN->IR.U;
|
||||
|
||||
// Handle RX frame available interrupt
|
||||
if ((interrupt & __CAN_IRQ_RX) != 0)
|
||||
CAN_read_frame_phy(&higherPriorityTaskWoken);
|
||||
|
||||
// Handle TX complete interrupt
|
||||
// Handle error interrupts.
|
||||
if ((interrupt & (__CAN_IRQ_TX | __CAN_IRQ_ERR //0x4
|
||||
| __CAN_IRQ_DATA_OVERRUN // 0x8
|
||||
| __CAN_IRQ_WAKEUP // 0x10
|
||||
| __CAN_IRQ_ERR_PASSIVE // 0x20
|
||||
| __CAN_IRQ_ARB_LOST // 0x40
|
||||
| __CAN_IRQ_BUS_ERR // 0x80
|
||||
)) != 0) {
|
||||
xSemaphoreGiveFromISR(sem_tx_complete, &higherPriorityTaskWoken);
|
||||
}
|
||||
|
||||
// check if any higher priority task has been woken by any handler
|
||||
if (higherPriorityTaskWoken)
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
|
||||
static void CAN_read_frame_phy(BaseType_t *higherPriorityTaskWoken) {
|
||||
|
||||
// byte iterator
|
||||
uint8_t __byte_i;
|
||||
|
||||
// frame read buffer
|
||||
CAN_frame_t __frame;
|
||||
|
||||
// check if we have a queue. If not, operation is aborted.
|
||||
if (CAN_cfg.rx_queue == NULL) {
|
||||
// Let the hardware know the frame has been read.
|
||||
MODULE_CAN->CMR.B.RRB = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
// get FIR
|
||||
__frame.FIR.U = MODULE_CAN->MBX_CTRL.FCTRL.FIR.U;
|
||||
|
||||
// check if this is a standard or extended CAN frame
|
||||
// standard frame
|
||||
if (__frame.FIR.B.FF == CAN_frame_std) {
|
||||
|
||||
// Get Message ID
|
||||
__frame.MsgID = _CAN_GET_STD_ID;
|
||||
|
||||
// deep copy data bytes
|
||||
for (__byte_i = 0; __byte_i < __frame.FIR.B.DLC; __byte_i++)
|
||||
__frame.data.u8[__byte_i] = MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.STD.data[__byte_i];
|
||||
}
|
||||
// extended frame
|
||||
else {
|
||||
|
||||
// Get Message ID
|
||||
__frame.MsgID = _CAN_GET_EXT_ID;
|
||||
|
||||
// deep copy data bytes
|
||||
for (__byte_i = 0; __byte_i < __frame.FIR.B.DLC; __byte_i++)
|
||||
__frame.data.u8[__byte_i] = MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.data[__byte_i];
|
||||
}
|
||||
|
||||
// send frame to input queue
|
||||
xQueueSendToBackFromISR(CAN_cfg.rx_queue, &__frame, higherPriorityTaskWoken);
|
||||
|
||||
// Let the hardware know the frame has been read.
|
||||
MODULE_CAN->CMR.B.RRB = 1;
|
||||
}
|
||||
|
||||
static int CAN_write_frame_phy(const CAN_frame_t *p_frame) {
|
||||
|
||||
// byte iterator
|
||||
uint8_t __byte_i;
|
||||
|
||||
// copy frame information record
|
||||
MODULE_CAN->MBX_CTRL.FCTRL.FIR.U = p_frame->FIR.U;
|
||||
|
||||
// standard frame
|
||||
if (p_frame->FIR.B.FF == CAN_frame_std) {
|
||||
|
||||
// Write message ID
|
||||
_CAN_SET_STD_ID(p_frame->MsgID);
|
||||
|
||||
// Copy the frame data to the hardware
|
||||
for (__byte_i = 0; __byte_i < p_frame->FIR.B.DLC; __byte_i++)
|
||||
MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.STD.data[__byte_i] = p_frame->data.u8[__byte_i];
|
||||
|
||||
}
|
||||
// extended frame
|
||||
else {
|
||||
|
||||
// Write message ID
|
||||
_CAN_SET_EXT_ID(p_frame->MsgID);
|
||||
|
||||
// Copy the frame data to the hardware
|
||||
for (__byte_i = 0; __byte_i < p_frame->FIR.B.DLC; __byte_i++)
|
||||
MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.data[__byte_i] = p_frame->data.u8[__byte_i];
|
||||
}
|
||||
|
||||
// Transmit frame
|
||||
MODULE_CAN->CMR.B.TR = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CAN_init() {
|
||||
|
||||
// Time quantum
|
||||
double __tq;
|
||||
|
||||
// enable module
|
||||
DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_CAN_CLK_EN);
|
||||
DPORT_SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_CAN_RST); //Added https://github.com/miwagner/ESP32-Arduino-CAN/pull/37/commits/feccb722866fbdcc7628b941efe9f79295b0cf81
|
||||
DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_CAN_RST);
|
||||
|
||||
// configure TX pin
|
||||
gpio_set_level(CAN_cfg.tx_pin_id, 1);
|
||||
gpio_set_direction(CAN_cfg.tx_pin_id, GPIO_MODE_OUTPUT);
|
||||
gpio_matrix_out(CAN_cfg.tx_pin_id, TWAI_TX_IDX, 0, 0);
|
||||
gpio_pad_select_gpio(CAN_cfg.tx_pin_id);
|
||||
|
||||
// configure RX pin
|
||||
gpio_set_direction(CAN_cfg.rx_pin_id, GPIO_MODE_INPUT);
|
||||
gpio_matrix_in(CAN_cfg.rx_pin_id, TWAI_RX_IDX, 0);
|
||||
gpio_pad_select_gpio(CAN_cfg.rx_pin_id);
|
||||
|
||||
// set to PELICAN mode
|
||||
MODULE_CAN->CDR.B.CAN_M = 0x1;
|
||||
|
||||
// synchronization jump width is the same for all baud rates
|
||||
MODULE_CAN->BTR0.B.SJW = 0x1;
|
||||
|
||||
// TSEG2 is the same for all baud rates
|
||||
MODULE_CAN->BTR1.B.TSEG2 = 0x1;
|
||||
|
||||
// select time quantum and set TSEG1
|
||||
switch (CAN_cfg.speed) {
|
||||
case CAN_SPEED_1000KBPS:
|
||||
MODULE_CAN->BTR1.B.TSEG1 = 0x4;
|
||||
__tq = 0.125;
|
||||
break;
|
||||
|
||||
case CAN_SPEED_800KBPS:
|
||||
MODULE_CAN->BTR1.B.TSEG1 = 0x6;
|
||||
__tq = 0.125;
|
||||
break;
|
||||
|
||||
case CAN_SPEED_200KBPS:
|
||||
MODULE_CAN->BTR1.B.TSEG1 = 0xc;
|
||||
MODULE_CAN->BTR1.B.TSEG2 = 0x5;
|
||||
__tq = 0.25;
|
||||
break;
|
||||
|
||||
default:
|
||||
MODULE_CAN->BTR1.B.TSEG1 = 0xc;
|
||||
__tq = ((float) 1000 / CAN_cfg.speed) / 16;
|
||||
}
|
||||
|
||||
// set baud rate prescaler
|
||||
MODULE_CAN->BTR0.B.BRP = (uint8_t) round((((APB_CLK_FREQ * __tq) / 2) - 1) / 1000000) - 1;
|
||||
|
||||
/* Set sampling
|
||||
* 1 -> triple; the bus is sampled three times; recommended for low/medium speed buses (class A and B) where
|
||||
* filtering spikes on the bus line is beneficial 0 -> single; the bus is sampled once; recommended for high speed
|
||||
* buses (SAE class C)*/
|
||||
MODULE_CAN->BTR1.B.SAM = 0x0;
|
||||
|
||||
// enable all interrupts
|
||||
MODULE_CAN->IER.U = 0xef; //ESP32 V3 0XEF ESP32 NOT V3 0XFF
|
||||
|
||||
// Set acceptance filter
|
||||
MODULE_CAN->MOD.B.AFM = __filter.FM;
|
||||
MODULE_CAN->MBX_CTRL.ACC.CODE[0] = __filter.ACR0;
|
||||
MODULE_CAN->MBX_CTRL.ACC.CODE[1] = __filter.ACR1;
|
||||
MODULE_CAN->MBX_CTRL.ACC.CODE[2] = __filter.ACR2;
|
||||
MODULE_CAN->MBX_CTRL.ACC.CODE[3] = __filter.ACR3;
|
||||
MODULE_CAN->MBX_CTRL.ACC.MASK[0] = __filter.AMR0;
|
||||
MODULE_CAN->MBX_CTRL.ACC.MASK[1] = __filter.AMR1;
|
||||
MODULE_CAN->MBX_CTRL.ACC.MASK[2] = __filter.AMR2;
|
||||
MODULE_CAN->MBX_CTRL.ACC.MASK[3] = __filter.AMR3;
|
||||
|
||||
// set to normal mode
|
||||
MODULE_CAN->OCR.B.OCMODE = __CAN_OC_NOM;
|
||||
|
||||
// clear error counters
|
||||
MODULE_CAN->TXERR.U = 0;
|
||||
MODULE_CAN->RXERR.U = 0;
|
||||
(void) MODULE_CAN->ECC;
|
||||
|
||||
// clear interrupt flags
|
||||
(void) MODULE_CAN->IR.U;
|
||||
|
||||
// install CAN ISR
|
||||
esp_intr_alloc(ETS_CAN_INTR_SOURCE, 0, CAN_isr, NULL, NULL);
|
||||
|
||||
// allocate the tx complete semaphore
|
||||
sem_tx_complete = xSemaphoreCreateBinary();
|
||||
|
||||
// Showtime. Release Reset Mode.
|
||||
MODULE_CAN->MOD.B.RM = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool CAN_write_frame(const CAN_frame_t *p_frame) {
|
||||
if (sem_tx_complete == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write the frame to the controller
|
||||
CAN_write_frame_phy(p_frame);
|
||||
|
||||
return xSemaphoreTake(sem_tx_complete, 20) == pdTRUE ? true : false;
|
||||
}
|
||||
|
||||
int CAN_stop() {
|
||||
// enter reset mode
|
||||
MODULE_CAN->MOD.B.RM = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CAN_config_filter(const CAN_filter_t* p_filter) {
|
||||
|
||||
__filter.FM = p_filter->FM;
|
||||
__filter.ACR0 = p_filter->ACR0;
|
||||
__filter.ACR1 = p_filter->ACR1;
|
||||
__filter.ACR2 = p_filter->ACR2;
|
||||
__filter.ACR3 = p_filter->ACR3;
|
||||
__filter.AMR0 = p_filter->AMR0;
|
||||
__filter.AMR1 = p_filter->AMR1;
|
||||
__filter.AMR2 = p_filter->AMR2;
|
||||
__filter.AMR3 = p_filter->AMR3;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,135 +0,0 @@
|
|||
/**
|
||||
* @section License
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2017, Thomas Barth, barth-dev.de
|
||||
* 2018, Michael Wagner, mw@iot-make.de
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __DRIVERS_CAN_H__
|
||||
#define __DRIVERS_CAN_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include "CAN_config.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief CAN frame type (standard/extended)
|
||||
*/
|
||||
typedef enum {
|
||||
CAN_frame_std = 0, /**< Standard frame, using 11 bit identifer. */
|
||||
CAN_frame_ext = 1 /**< Extended frame, using 29 bit identifer. */
|
||||
} CAN_frame_format_t;
|
||||
|
||||
/**
|
||||
* \brief CAN RTR
|
||||
*/
|
||||
typedef enum {
|
||||
CAN_no_RTR = 0, /**< No RTR frame. */
|
||||
CAN_RTR = 1 /**< RTR frame. */
|
||||
} CAN_RTR_t;
|
||||
|
||||
/** \brief Frame information record type */
|
||||
typedef union {
|
||||
uint32_t U; /**< \brief Unsigned access */
|
||||
struct {
|
||||
uint8_t DLC : 4; /**< \brief [3:0] DLC, Data length container */
|
||||
unsigned int unknown_2 : 2; /**< \brief \internal unknown */
|
||||
CAN_RTR_t RTR : 1; /**< \brief [6:6] RTR, Remote Transmission Request */
|
||||
CAN_frame_format_t FF : 1; /**< \brief [7:7] Frame Format, see# CAN_frame_format_t*/
|
||||
unsigned int reserved_24 : 24; /**< \brief \internal Reserved */
|
||||
} B;
|
||||
} CAN_FIR_t;
|
||||
|
||||
/** \brief CAN Frame structure */
|
||||
typedef struct {
|
||||
CAN_FIR_t FIR; /**< \brief Frame information record*/
|
||||
uint32_t MsgID; /**< \brief Message ID */
|
||||
union {
|
||||
uint8_t u8[8]; /**< \brief Payload byte access*/
|
||||
uint32_t u32[2]; /**< \brief Payload u32 access*/
|
||||
uint64_t u64; /**< \brief Payload u64 access*/
|
||||
} data;
|
||||
} CAN_frame_t;
|
||||
|
||||
typedef enum {
|
||||
Dual_Mode=0, /**< \brief The dual acceptance filter option is enabled (two filters, each with the length of 16 bit are active) */
|
||||
Single_Mode=1 /**< \brief The single acceptance filter option is enabled (one filter with the length of 32 bit is active) */
|
||||
} CAN_filter_mode_t;
|
||||
|
||||
/** \brief CAN Filter structure */
|
||||
typedef struct {
|
||||
CAN_filter_mode_t FM:1; /**< \brief [0:0] Filter Mode */
|
||||
uint8_t ACR0; /**< \brief Acceptance Code Register ACR0 */
|
||||
uint8_t ACR1; /**< \brief Acceptance Code Register ACR1 */
|
||||
uint8_t ACR2; /**< \brief Acceptance Code Register ACR2 */
|
||||
uint8_t ACR3; /**< \brief Acceptance Code Register ACR3 */
|
||||
uint8_t AMR0; /**< \brief Acceptance Mask Register AMR0 */
|
||||
uint8_t AMR1; /**< \brief Acceptance Mask Register AMR1 */
|
||||
uint8_t AMR2; /**< \brief Acceptance Mask Register AMR2 */
|
||||
uint8_t AMR3; /**< \brief Acceptance Mask Register AMR3 */
|
||||
} CAN_filter_t;
|
||||
|
||||
extern void gpio_matrix_in(uint32_t gpio, uint32_t signal_idx, bool inv);
|
||||
extern void gpio_matrix_out(uint32_t gpio, uint32_t signal_idx, bool out_inv, bool oen_inv);
|
||||
extern void gpio_pad_select_gpio(uint8_t gpio_num);
|
||||
|
||||
/**
|
||||
* \brief Initialize the CAN Module
|
||||
*
|
||||
* \return 0 CAN Module had been initialized
|
||||
*/
|
||||
int CAN_init(void);
|
||||
|
||||
/**
|
||||
* \brief Send a can frame
|
||||
*
|
||||
* \param p_frame Pointer to the frame to be send, see #CAN_frame_t
|
||||
* \return 1 Frame has been written to the module
|
||||
*/
|
||||
bool CAN_write_frame(const CAN_frame_t *p_frame);
|
||||
|
||||
/**
|
||||
* \brief Stops the CAN Module
|
||||
*
|
||||
* \return 0 CAN Module was stopped
|
||||
*/
|
||||
int CAN_stop(void);
|
||||
|
||||
/**
|
||||
* \brief Config CAN Filter, must call before CANInit()
|
||||
*
|
||||
* \param p_filter Pointer to the filter, see #CAN_filter_t
|
||||
* \return 0 CAN Filter had been initialized
|
||||
*/
|
||||
int CAN_config_filter(const CAN_filter_t* p_filter);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -1,71 +0,0 @@
|
|||
/**
|
||||
* @section License
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2017, Thomas Barth, barth-dev.de
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __DRIVERS_CAN_CFG_H__
|
||||
#define __DRIVERS_CAN_CFG_H__
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** \brief CAN Node Bus speed */
|
||||
typedef enum {
|
||||
CAN_SPEED_100KBPS = 100, /**< \brief CAN Node runs at 100kBit/s. */
|
||||
CAN_SPEED_125KBPS = 125, /**< \brief CAN Node runs at 125kBit/s. */
|
||||
CAN_SPEED_200KBPS = 200, /**< \brief CAN Node runs at 250kBit/s. */
|
||||
CAN_SPEED_250KBPS = 250, /**< \brief CAN Node runs at 250kBit/s. */
|
||||
CAN_SPEED_500KBPS = 500, /**< \brief CAN Node runs at 500kBit/s. */
|
||||
CAN_SPEED_800KBPS = 800, /**< \brief CAN Node runs at 800kBit/s. */
|
||||
CAN_SPEED_1000KBPS = 1000 /**< \brief CAN Node runs at 1000kBit/s. */
|
||||
} CAN_speed_t;
|
||||
|
||||
/** \brief CAN configuration structure */
|
||||
typedef struct {
|
||||
CAN_speed_t speed; /**< \brief CAN speed. */
|
||||
gpio_num_t tx_pin_id; /**< \brief TX pin. */
|
||||
gpio_num_t rx_pin_id; /**< \brief RX pin. */
|
||||
QueueHandle_t rx_queue; /**< \brief Handler to FreeRTOS RX queue. */
|
||||
QueueHandle_t tx_queue; /**< \brief Handler to FreeRTOS TX queue. */
|
||||
TaskHandle_t tx_handle; /**< \brief Handler to FreeRTOS TX task. */
|
||||
TaskHandle_t rx_handle; /**< \brief Handler to FreeRTOS RX task. */
|
||||
} CAN_device_t;
|
||||
|
||||
/** \brief CAN configuration reference */
|
||||
extern CAN_device_t CAN_cfg;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __DRIVERS_CAN_CFG_H__ */
|
|
@ -1,19 +0,0 @@
|
|||
#include "ESP32CAN.h"
|
||||
#include <Arduino.h>
|
||||
#include "../../devboard/utils/events.h"
|
||||
|
||||
int ESP32CAN::CANInit() {
|
||||
return CAN_init();
|
||||
}
|
||||
bool ESP32CAN::CANWriteFrame(const CAN_frame_t* p_frame) {
|
||||
return CAN_write_frame(p_frame);
|
||||
}
|
||||
|
||||
int ESP32CAN::CANStop() {
|
||||
return CAN_stop();
|
||||
}
|
||||
int ESP32CAN::CANConfigFilter(const CAN_filter_t* p_filter) {
|
||||
return CAN_config_filter(p_filter);
|
||||
}
|
||||
|
||||
ESP32CAN ESP32Can;
|
|
@ -1,18 +0,0 @@
|
|||
#ifndef ESP32CAN_H
|
||||
#define ESP32CAN_H
|
||||
|
||||
#include "../../lib/miwagner-ESP32-Arduino-CAN/CAN.h"
|
||||
#include "../../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
|
||||
|
||||
class ESP32CAN {
|
||||
public:
|
||||
bool tx_ok = true;
|
||||
int CANInit();
|
||||
int CANConfigFilter(const CAN_filter_t* p_filter);
|
||||
bool CANWriteFrame(const CAN_frame_t* p_frame);
|
||||
int CANStop();
|
||||
void CANSetCfg(CAN_device_t* can_cfg);
|
||||
};
|
||||
|
||||
extern ESP32CAN ESP32Can;
|
||||
#endif
|
|
@ -1,279 +0,0 @@
|
|||
/**
|
||||
* @section License
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2017, Thomas Barth, barth-dev.de
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __DRIVERS_CAN_REGDEF_H_
|
||||
#define __DRIVERS_CAN_REGDEF_H_
|
||||
|
||||
#include "CAN.h" //CAN_FIR_t
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** \brief Start address of CAN registers */
|
||||
#define MODULE_CAN ((volatile CAN_Module_t *) 0x3ff6b000)
|
||||
|
||||
/** \brief Get standard message ID */
|
||||
#define _CAN_GET_STD_ID \
|
||||
(((uint32_t) MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.STD.ID[0] << 3) | (MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.STD.ID[1] >> 5))
|
||||
|
||||
/** \brief Get extended message ID */
|
||||
#define _CAN_GET_EXT_ID \
|
||||
(((uint32_t) MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[0] << 21) | \
|
||||
(MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[1] << 13) | (MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[2] << 5) | \
|
||||
(MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[3] >> 3))
|
||||
|
||||
/** \brief Set standard message ID */
|
||||
#define _CAN_SET_STD_ID(x) \
|
||||
MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.STD.ID[0] = ((x) >> 3); \
|
||||
MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.STD.ID[1] = ((x) << 5);
|
||||
|
||||
/** \brief Set extended message ID */
|
||||
#define _CAN_SET_EXT_ID(x) \
|
||||
MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[0] = ((x) >> 21); \
|
||||
MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[1] = ((x) >> 13); \
|
||||
MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[2] = ((x) >> 5); \
|
||||
MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[3] = ((x) << 3);
|
||||
|
||||
/** \brief Interrupt status register */
|
||||
typedef enum {
|
||||
__CAN_IRQ_RX = BIT(0), /**< \brief RX Interrupt */
|
||||
__CAN_IRQ_TX = BIT(1), /**< \brief TX Interrupt */
|
||||
__CAN_IRQ_ERR = BIT(2), /**< \brief Error Interrupt */
|
||||
__CAN_IRQ_DATA_OVERRUN = BIT(3), /**< \brief Data Overrun Interrupt */
|
||||
__CAN_IRQ_WAKEUP = BIT(4), /**< \brief Wakeup Interrupt */
|
||||
__CAN_IRQ_ERR_PASSIVE = BIT(5), /**< \brief Passive Error Interrupt */
|
||||
__CAN_IRQ_ARB_LOST = BIT(6), /**< \brief Arbitration lost interrupt */
|
||||
__CAN_IRQ_BUS_ERR = BIT(7), /**< \brief Bus error Interrupt */
|
||||
} __CAN_IRQ_t;
|
||||
|
||||
/** \brief OCMODE options. */
|
||||
typedef enum {
|
||||
__CAN_OC_BOM = 0b00, /**< \brief bi-phase output mode */
|
||||
__CAN_OC_TOM = 0b01, /**< \brief test output mode */
|
||||
__CAN_OC_NOM = 0b10, /**< \brief normal output mode */
|
||||
__CAN_OC_COM = 0b11, /**< \brief clock output mode */
|
||||
} __CAN_OCMODE_t;
|
||||
|
||||
/**
|
||||
* CAN controller (SJA1000).
|
||||
*/
|
||||
typedef struct {
|
||||
union {
|
||||
uint32_t U; /**< \brief Unsigned access */
|
||||
struct {
|
||||
unsigned int RM : 1; /**< \brief MOD.0 Reset Mode */
|
||||
unsigned int LOM : 1; /**< \brief MOD.1 Listen Only Mode */
|
||||
unsigned int STM : 1; /**< \brief MOD.2 Self Test Mode */
|
||||
unsigned int AFM : 1; /**< \brief MOD.3 Acceptance Filter Mode */
|
||||
unsigned int SM : 1; /**< \brief MOD.4 Sleep Mode */
|
||||
unsigned int reserved_27 : 27; /**< \brief \internal Reserved */
|
||||
} B;
|
||||
} MOD;
|
||||
union {
|
||||
uint32_t U; /**< \brief Unsigned access */
|
||||
struct {
|
||||
unsigned int TR : 1; /**< \brief CMR.0 Transmission Request */
|
||||
unsigned int AT : 1; /**< \brief CMR.1 Abort Transmission */
|
||||
unsigned int RRB : 1; /**< \brief CMR.2 Release Receive Buffer */
|
||||
unsigned int CDO : 1; /**< \brief CMR.3 Clear Data Overrun */
|
||||
unsigned int GTS : 1; /**< \brief CMR.4 Go To Sleep */
|
||||
unsigned int reserved_27 : 27; /**< \brief \internal Reserved */
|
||||
} B;
|
||||
} CMR;
|
||||
union {
|
||||
uint32_t U; /**< \brief Unsigned access */
|
||||
struct {
|
||||
unsigned int RBS : 1; /**< \brief SR.0 Receive Buffer Status */
|
||||
unsigned int DOS : 1; /**< \brief SR.1 Data Overrun Status */
|
||||
unsigned int TBS : 1; /**< \brief SR.2 Transmit Buffer Status */
|
||||
unsigned int TCS : 1; /**< \brief SR.3 Transmission Complete Status */
|
||||
unsigned int RS : 1; /**< \brief SR.4 Receive Status */
|
||||
unsigned int TS : 1; /**< \brief SR.5 Transmit Status */
|
||||
unsigned int ES : 1; /**< \brief SR.6 Error Status */
|
||||
unsigned int BS : 1; /**< \brief SR.7 Bus Status */
|
||||
unsigned int reserved_24 : 24; /**< \brief \internal Reserved */
|
||||
} B;
|
||||
} SR;
|
||||
union {
|
||||
uint32_t U; /**< \brief Unsigned access */
|
||||
struct {
|
||||
unsigned int RI : 1; /**< \brief IR.0 Receive Interrupt */
|
||||
unsigned int TI : 1; /**< \brief IR.1 Transmit Interrupt */
|
||||
unsigned int EI : 1; /**< \brief IR.2 Error Interrupt */
|
||||
unsigned int DOI : 1; /**< \brief IR.3 Data Overrun Interrupt */
|
||||
unsigned int WUI : 1; /**< \brief IR.4 Wake-Up Interrupt */
|
||||
unsigned int EPI : 1; /**< \brief IR.5 Error Passive Interrupt */
|
||||
unsigned int ALI : 1; /**< \brief IR.6 Arbitration Lost Interrupt */
|
||||
unsigned int BEI : 1; /**< \brief IR.7 Bus Error Interrupt */
|
||||
unsigned int reserved_24 : 24; /**< \brief \internal Reserved */
|
||||
} B;
|
||||
} IR;
|
||||
union {
|
||||
uint32_t U; /**< \brief Unsigned access */
|
||||
struct {
|
||||
unsigned int RIE : 1; /**< \brief IER.0 Receive Interrupt Enable */
|
||||
unsigned int TIE : 1; /**< \brief IER.1 Transmit Interrupt Enable */
|
||||
unsigned int EIE : 1; /**< \brief IER.2 Error Interrupt Enable */
|
||||
unsigned int DOIE : 1; /**< \brief IER.3 Data Overrun Interrupt Enable */
|
||||
unsigned int WUIE : 1; /**< \brief IER.4 Wake-Up Interrupt Enable */
|
||||
unsigned int EPIE : 1; /**< \brief IER.5 Error Passive Interrupt Enable */
|
||||
unsigned int ALIE : 1; /**< \brief IER.6 Arbitration Lost Interrupt Enable */
|
||||
unsigned int BEIE : 1; /**< \brief IER.7 Bus Error Interrupt Enable */
|
||||
unsigned int reserved_24 : 24; /**< \brief \internal Reserved */
|
||||
} B;
|
||||
} IER;
|
||||
uint32_t RESERVED0;
|
||||
union {
|
||||
uint32_t U; /**< \brief Unsigned access */
|
||||
struct {
|
||||
unsigned int BRP : 6; /**< \brief BTR0[5:0] Baud Rate Prescaler */
|
||||
unsigned int SJW : 2; /**< \brief BTR0[7:6] Synchronization Jump Width*/
|
||||
unsigned int reserved_24 : 24; /**< \brief \internal Reserved */
|
||||
} B;
|
||||
} BTR0;
|
||||
union {
|
||||
uint32_t U; /**< \brief Unsigned access */
|
||||
struct {
|
||||
unsigned int TSEG1 : 4; /**< \brief BTR1[3:0] Timing Segment 1 */
|
||||
unsigned int TSEG2 : 3; /**< \brief BTR1[6:4] Timing Segment 2*/
|
||||
unsigned int SAM : 1; /**< \brief BTR1.7 Sampling*/
|
||||
unsigned int reserved_24 : 24; /**< \brief \internal Reserved */
|
||||
} B;
|
||||
} BTR1;
|
||||
union {
|
||||
uint32_t U; /**< \brief Unsigned access */
|
||||
struct {
|
||||
unsigned int OCMODE : 2; /**< \brief OCR[1:0] Output Control Mode, see # */
|
||||
unsigned int OCPOL0 : 1; /**< \brief OCR.2 Output Control Polarity 0 */
|
||||
unsigned int OCTN0 : 1; /**< \brief OCR.3 Output Control Transistor N0 */
|
||||
unsigned int OCTP0 : 1; /**< \brief OCR.4 Output Control Transistor P0 */
|
||||
unsigned int OCPOL1 : 1; /**< \brief OCR.5 Output Control Polarity 1 */
|
||||
unsigned int OCTN1 : 1; /**< \brief OCR.6 Output Control Transistor N1 */
|
||||
unsigned int OCTP1 : 1; /**< \brief OCR.7 Output Control Transistor P1 */
|
||||
unsigned int reserved_24 : 24; /**< \brief \internal Reserved */
|
||||
} B;
|
||||
} OCR;
|
||||
uint32_t RESERVED1[2];
|
||||
union {
|
||||
uint32_t U; /**< \brief Unsigned access */
|
||||
struct {
|
||||
unsigned int ALC : 8; /**< \brief ALC[7:0] Arbitration Lost Capture */
|
||||
unsigned int reserved_24 : 24; /**< \brief \internal Reserved */
|
||||
} B;
|
||||
} ALC;
|
||||
union {
|
||||
uint32_t U; /**< \brief Unsigned access */
|
||||
struct {
|
||||
unsigned int ECC : 8; /**< \brief ECC[7:0] Error Code Capture */
|
||||
unsigned int reserved_24 : 24; /**< \brief \internal Reserved */
|
||||
} B;
|
||||
} ECC;
|
||||
union {
|
||||
uint32_t U; /**< \brief Unsigned access */
|
||||
struct {
|
||||
unsigned int EWLR : 8; /**< \brief EWLR[7:0] Error Warning Limit */
|
||||
unsigned int reserved_24 : 24; /**< \brief \internal Reserved */
|
||||
} B;
|
||||
} EWLR;
|
||||
union {
|
||||
uint32_t U; /**< \brief Unsigned access */
|
||||
struct {
|
||||
unsigned int RXERR : 8; /**< \brief RXERR[7:0] Receive Error Counter */
|
||||
unsigned int reserved_24 : 24; /**< \brief \internal Reserved */
|
||||
} B;
|
||||
} RXERR;
|
||||
union {
|
||||
uint32_t U; /**< \brief Unsigned access */
|
||||
struct {
|
||||
unsigned int TXERR : 8; /**< \brief TXERR[7:0] Transmit Error Counter */
|
||||
unsigned int reserved_24 : 24; /**< \brief \internal Reserved */
|
||||
} B;
|
||||
} TXERR;
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint32_t CODE[4]; /**< \brief Acceptance Message ID */
|
||||
uint32_t MASK[4]; /**< \brief Acceptance Mask */
|
||||
uint32_t RESERVED2[5];
|
||||
} ACC; /**< \brief Acceptance filtering */
|
||||
struct {
|
||||
CAN_FIR_t FIR; /**< \brief Frame information record */
|
||||
union {
|
||||
struct {
|
||||
uint32_t ID[2]; /**< \brief Standard frame message-ID*/
|
||||
uint32_t data[8]; /**< \brief Standard frame payload */
|
||||
uint32_t reserved[2];
|
||||
} STD; /**< \brief Standard frame format */
|
||||
struct {
|
||||
uint32_t ID[4]; /**< \brief Extended frame message-ID*/
|
||||
uint32_t data[8]; /**< \brief Extended frame payload */
|
||||
} EXT; /**< \brief Extended frame format */
|
||||
} TX_RX; /**< \brief RX/TX interface */
|
||||
} FCTRL; /**< \brief Function control regs */
|
||||
} MBX_CTRL; /**< \brief Mailbox control */
|
||||
union {
|
||||
uint32_t U; /**< \brief Unsigned access */
|
||||
struct {
|
||||
unsigned int RMC : 8; /**< \brief RMC[7:0] RX Message Counter */
|
||||
unsigned int reserved_24 : 24; /**< \brief \internal Reserved Enable */
|
||||
} B;
|
||||
} RMC;
|
||||
union {
|
||||
uint32_t U; /**< \brief Unsigned access */
|
||||
struct {
|
||||
unsigned int RBSA : 8; /**< \brief RBSA[7:0] RX Buffer Start Address */
|
||||
unsigned int reserved_24 : 24; /**< \brief \internal Reserved Enable */
|
||||
} B;
|
||||
} RBSA;
|
||||
union {
|
||||
uint32_t U; /**< \brief Unsigned access */
|
||||
struct {
|
||||
unsigned int COD : 3; /**< \brief CDR[2:0] CLKOUT frequency selector based of fOSC*/
|
||||
unsigned int COFF : 1; /**< \brief CDR.3 CLKOUT off*/
|
||||
unsigned int reserved_1 : 1; /**< \brief \internal Reserved */
|
||||
unsigned int
|
||||
RXINTEN : 1; /**< \brief CDR.5 This bit allows the TX1 output to be used as a dedicated receive interrupt
|
||||
output*/
|
||||
unsigned int
|
||||
CBP : 1; /**< \brief CDR.6 allows to bypass the CAN input comparator and is only possible in reset mode.*/
|
||||
unsigned int
|
||||
CAN_M : 1; /**< \brief CDR.7 If CDR.7 is at logic 0 the CAN controller operates in BasicCAN mode. If set to
|
||||
logic 1 the CAN controller operates in PeliCAN mode. Write access is only possible in reset
|
||||
mode*/
|
||||
unsigned int reserved_24 : 24; /**< \brief \internal Reserved */
|
||||
} B;
|
||||
} CDR;
|
||||
uint32_t IRAM[2];
|
||||
} CAN_Module_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __DRIVERS_CAN_REGDEF_H_ */
|
|
@ -1,20 +1,20 @@
|
|||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// A CAN driver for MCP2517FD, CANFD mode
|
||||
// by Pierre Molinaro
|
||||
// https://github.com/pierremolinaro/acan2517FD
|
||||
//
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#include "ACAN2517FD.h"
|
||||
#include "../../system_settings.h" //Contains task priority
|
||||
#include "../../system_settings.h" //Contains task priority //Modded by Battery-Emulator
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static const uint8_t TXBWS = 0 ;
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// Note about ESP32
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// It appears that Arduino ESP32 interrupts are managed in a completely different way from "usual" Arduino:
|
||||
// - SPI.usingInterrupt is not implemented;
|
||||
|
@ -43,7 +43,7 @@ static const uint8_t TXBWS = 0 ;
|
|||
// #endif
|
||||
// mSPI.endTransaction () ;
|
||||
//
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
static void myESP32Task (void * pData) {
|
||||
|
@ -55,9 +55,9 @@ static const uint8_t TXBWS = 0 ;
|
|||
}
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// ACAN2517FD register addresses
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static const uint16_t CON_REGISTER = 0x000 ;
|
||||
static const uint16_t NBTCFG_REGISTER = 0x004 ;
|
||||
|
@ -68,85 +68,85 @@ static const uint16_t TREC_REGISTER = 0x034 ;
|
|||
static const uint16_t BDIAG0_REGISTER = 0x038 ;
|
||||
static const uint16_t BDIAG1_REGISTER = 0x03C ;
|
||||
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
// TXQ REGISTERS
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static const uint16_t TXQCON_REGISTER = 0x050 ;
|
||||
static const uint16_t TXQSTA_REGISTER = 0x054 ;
|
||||
static const uint16_t TXQUA_REGISTER = 0x058 ;
|
||||
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
// INTERRUPT REGISTERS
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static const uint16_t INT_REGISTER = 0x01C ;
|
||||
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
// FIFO REGISTERS
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static uint16_t FIFOCON_REGISTER (const uint16_t inFIFOIndex) { // 1 ... 31
|
||||
return 0x05C + 12 * (inFIFOIndex - 1) ;
|
||||
}
|
||||
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static uint16_t FIFOSTA_REGISTER (const uint16_t inFIFOIndex) { // 1 ... 31
|
||||
return 0x060 + 12 * (inFIFOIndex - 1) ;
|
||||
}
|
||||
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static uint16_t FIFOUA_REGISTER (const uint16_t inFIFOIndex) { // 1 ... 31
|
||||
return 0x064 + 12 * (inFIFOIndex - 1) ;
|
||||
}
|
||||
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
// FILTER REGISTERS
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static uint16_t FLTCON_REGISTER (const uint16_t inFilterIndex) { // 0 ... 31 (DS20005688B, page 58)
|
||||
return 0x1D0 + inFilterIndex ;
|
||||
}
|
||||
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static uint16_t FLTOBJ_REGISTER (const uint16_t inFilterIndex) { // 0 ... 31 (DS20005688B, page 60)
|
||||
return 0x1F0 + 8 * inFilterIndex ;
|
||||
}
|
||||
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static uint16_t MASK_REGISTER (const uint16_t inFilterIndex) { // 0 ... 31 (DS20005688B, page 61)
|
||||
return 0x1F4 + 8 * inFilterIndex ;
|
||||
}
|
||||
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
// OSCILLATOR REGISTER
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static const uint16_t OSC_REGISTER = 0xE00 ;
|
||||
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
// INPUT / OUPUT CONTROL REGISTER
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static const uint16_t IOCON_REGISTER_00_07 = 0xE04 ;
|
||||
static const uint16_t IOCON_REGISTER_08_15 = 0xE05 ;
|
||||
static const uint16_t IOCON_REGISTER_16_23 = 0xE06 ;
|
||||
static const uint16_t IOCON_REGISTER_24_31 = 0xE07 ;
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// RECEIVE FIFO INDEX
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static const uint8_t RECEIVE_FIFO_INDEX = 1 ;
|
||||
static const uint8_t TRANSMIT_FIFO_INDEX = 2 ;
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// BYTE BUFFER UTILITY FUNCTIONS
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static void enterU32InBufferAtIndex (const uint32_t inValue, uint8_t ioBuffer [], const uint8_t inIndex) {
|
||||
ioBuffer [inIndex + 0] = (uint8_t) inValue ;
|
||||
|
@ -155,7 +155,7 @@ static void enterU32InBufferAtIndex (const uint32_t inValue, uint8_t ioBuffer []
|
|||
ioBuffer [inIndex + 3] = (uint8_t) (inValue >> 24) ;
|
||||
}
|
||||
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static uint32_t u32FromBufferAtIndex (uint8_t ioBuffer [], const uint8_t inIndex) {
|
||||
uint32_t result = (uint32_t) ioBuffer [inIndex + 0] ;
|
||||
|
@ -165,7 +165,7 @@ static uint32_t u32FromBufferAtIndex (uint8_t ioBuffer [], const uint8_t inIndex
|
|||
return result ;
|
||||
}
|
||||
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static uint16_t u16FromBufferAtIndex (uint8_t ioBuffer [], const uint8_t inIndex) {
|
||||
uint16_t result = (uint16_t) ioBuffer [inIndex + 0] ;
|
||||
|
@ -173,8 +173,7 @@ static uint16_t u16FromBufferAtIndex (uint8_t ioBuffer [], const uint8_t inIndex
|
|||
return result ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static inline void turnOffInterrupts () {
|
||||
#ifndef DISABLEMCP2517FDCOMPAT
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
|
@ -209,7 +208,6 @@ mINT (inINT),
|
|||
mUsesTXQ (false),
|
||||
mHardwareTxFIFOFull (false),
|
||||
mRxInterruptEnabled (true),
|
||||
mHasDataBitRate (false),
|
||||
mTransmitFIFOPayload (0),
|
||||
mTXQBufferPayload (0),
|
||||
mReceiveFIFOPayload (0),
|
||||
|
@ -223,7 +221,7 @@ mDriverTransmitBuffer ()
|
|||
{
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
uint32_t ACAN2517FD::begin (const ACAN2517FDSettings & inSettings,
|
||||
void (* inInterruptServiceRoutine) (void)) {
|
||||
|
@ -234,7 +232,7 @@ uint32_t ACAN2517FD::begin (const ACAN2517FDSettings & inSettings,
|
|||
return begin (inSettings, inInterruptServiceRoutine, filters) ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
uint32_t ACAN2517FD::begin (const ACAN2517FDSettings & inSettings,
|
||||
void (* inInterruptServiceRoutine) (void),
|
||||
|
@ -503,17 +501,14 @@ uint32_t ACAN2517FD::begin (const ACAN2517FDSettings & inSettings,
|
|||
// bits 11-8: TSEG2 - 1
|
||||
// bits 7-4: unused
|
||||
// bits 3-0: SJW - 1
|
||||
mHasDataBitRate = inSettings.mDataBitRateFactor != ::DataBitRateFactor::x1 ;
|
||||
if (mHasDataBitRate) {
|
||||
data = inSettings.mBitRatePrescaler - 1 ;
|
||||
data <<= 8 ;
|
||||
data |= inSettings.mDataPhaseSegment1 - 1 ;
|
||||
data <<= 8 ;
|
||||
data |= inSettings.mDataPhaseSegment2 - 1 ;
|
||||
data <<= 8 ;
|
||||
data |= inSettings.mDataSJW - 1 ;
|
||||
writeRegister32 (DBTCFG_REGISTER, data) ;
|
||||
}
|
||||
data = inSettings.mBitRatePrescaler - 1 ;
|
||||
data <<= 8 ;
|
||||
data |= inSettings.mDataPhaseSegment1 - 1 ;
|
||||
data <<= 8 ;
|
||||
data |= inSettings.mDataPhaseSegment2 - 1 ;
|
||||
data <<= 8 ;
|
||||
data |= inSettings.mDataSJW - 1 ;
|
||||
writeRegister32 (DBTCFG_REGISTER, data) ;
|
||||
//----------------------------------- Request mode (CON_REGISTER + 3, DS20005688B, page 24)
|
||||
// bits 7-4: Transmit Bandwith Sharing Bits ---> 0
|
||||
// bit 3: Abort All Pending Transmissions bit --> 0
|
||||
|
@ -531,14 +526,14 @@ uint32_t ACAN2517FD::begin (const ACAN2517FDSettings & inSettings,
|
|||
}
|
||||
}
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
xTaskCreate (myESP32Task, "ACAN2517Handler", 1024, this, TASK_ACAN2517FD_PRIORITY, &mESP32TaskHandle) ;
|
||||
xTaskCreate (myESP32Task, "ACAN2517Handler", 1024, this, TASK_ACAN2517FD_PRIORITY, &mESP32TaskHandle) ; //Modded by Battery-Emulator
|
||||
#endif
|
||||
if (mINT != 255) { // 255 means interrupt is not used
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
attachInterrupt (itPin, inInterruptServiceRoutine, FALLING) ;
|
||||
#else
|
||||
attachInterrupt (itPin, inInterruptServiceRoutine, LOW) ; // Thank to Flole998
|
||||
mSPI.usingInterrupt (itPin) ; // usingInterrupt is not implemented in Arduino ESP32
|
||||
attachInterrupt (itPin, inInterruptServiceRoutine, LOW) ; // Thank to Flole998
|
||||
#endif
|
||||
}
|
||||
// If you begin() multiple times without constructor,
|
||||
|
@ -550,13 +545,13 @@ uint32_t ACAN2517FD::begin (const ACAN2517FDSettings & inSettings,
|
|||
return errorCode ;
|
||||
}
|
||||
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
// end method (resets the MCP2517FD, deallocate buffers, and detach interrupt pin)
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool ACAN2517FD::end (void) {
|
||||
mSPI.beginTransaction (mSPISettings) ;
|
||||
turnOffInterrupts () ;
|
||||
turnOffInterrupts () ;
|
||||
//--- Detach interrupt pin
|
||||
if (mINT != 255) { // 255 means interrupt is not used
|
||||
const int8_t itPin = digitalPinToInterrupt (mINT) ;
|
||||
|
@ -597,15 +592,19 @@ bool ACAN2517FD::end (void) {
|
|||
return ok ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// SEND FRAME
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool ACAN2517FD::tryToSend (const CANFDMessage & inMessage) {
|
||||
bool ok = inMessage.isValid () ;
|
||||
if (ok) {
|
||||
mSPI.beginTransaction (mSPISettings) ;
|
||||
turnOffInterrupts () ;
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
taskDISABLE_INTERRUPTS () ;
|
||||
#else
|
||||
noInterrupts () ;
|
||||
#endif
|
||||
if (inMessage.idx == 0) {
|
||||
ok = inMessage.len <= mTransmitFIFOPayload ;
|
||||
if (ok) {
|
||||
|
@ -617,13 +616,13 @@ bool ACAN2517FD::tryToSend (const CANFDMessage & inMessage) {
|
|||
ok = sendViaTXQ (inMessage) ;
|
||||
}
|
||||
}
|
||||
turnOnInterrupts();
|
||||
turnOnInterrupts () ;
|
||||
mSPI.endTransaction () ;
|
||||
}
|
||||
return ok ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool ACAN2517FD::enterInTransmitBuffer (const CANFDMessage & inMessage) {
|
||||
bool result ;
|
||||
|
@ -645,7 +644,7 @@ bool ACAN2517FD::enterInTransmitBuffer (const CANFDMessage & inMessage) {
|
|||
return result ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static uint32_t lengthCodeForLength (const uint8_t inLength) {
|
||||
uint32_t result = inLength & 0x0F ;
|
||||
|
@ -661,7 +660,7 @@ static uint32_t lengthCodeForLength (const uint8_t inLength) {
|
|||
return result ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void ACAN2517FD::appendInControllerTxFIFO (const CANFDMessage & inMessage) {
|
||||
const uint16_t ramAddr = uint16_t (0x400 + readRegister32Assume_SPI_transaction (FIFOUA_REGISTER (TRANSMIT_FIFO_INDEX))) ;
|
||||
|
@ -686,9 +685,7 @@ void ACAN2517FD::appendInControllerTxFIFO (const CANFDMessage & inMessage) {
|
|||
break ;
|
||||
case CANFDMessage::CANFD_WITH_BIT_RATE_SWITCH :
|
||||
flags |= 1 << 7 ; // Set FDF bit
|
||||
if (mHasDataBitRate) {
|
||||
flags |= 1 << 6 ; // Set BRS bit
|
||||
}
|
||||
flags |= 1 << 6 ; // Set BRS bit
|
||||
break ;
|
||||
}
|
||||
//--- Word count
|
||||
|
@ -714,7 +711,7 @@ void ACAN2517FD::appendInControllerTxFIFO (const CANFDMessage & inMessage) {
|
|||
writeRegister8Assume_SPI_transaction (FIFOCON_REGISTER (TRANSMIT_FIFO_INDEX) + 1, data8);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool ACAN2517FD::sendViaTXQ (const CANFDMessage & inMessage) {
|
||||
bool ok = mUsesTXQ ;
|
||||
|
@ -751,9 +748,7 @@ bool ACAN2517FD::sendViaTXQ (const CANFDMessage & inMessage) {
|
|||
break ;
|
||||
case CANFDMessage::CANFD_WITH_BIT_RATE_SWITCH :
|
||||
flags |= 1 << 7 ; // Set FDF bit
|
||||
if (mHasDataBitRate) {
|
||||
flags |= 1 << 6 ; // Set BRS bit
|
||||
}
|
||||
flags |= 1 << 6 ; // Set BRS bit
|
||||
break ;
|
||||
}
|
||||
//--- Word count
|
||||
|
@ -782,41 +777,48 @@ bool ACAN2517FD::sendViaTXQ (const CANFDMessage & inMessage) {
|
|||
return ok ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// RECEIVE FRAME
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool ACAN2517FD::available (void) {
|
||||
mSPI.beginTransaction (mSPISettings) ;
|
||||
turnOffInterrupts () ;
|
||||
turnOffInterrupts () ;
|
||||
const bool hasReceivedMessage = mDriverReceiveBuffer.count () > 0 ;
|
||||
turnOnInterrupts();
|
||||
turnOnInterrupts () ;
|
||||
mSPI.endTransaction () ;
|
||||
return hasReceivedMessage ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool ACAN2517FD::receive (CANFDMessage & outMessage) {
|
||||
mSPI.beginTransaction (mSPISettings) ;
|
||||
turnOffInterrupts () ;
|
||||
const bool hasReceivedMessage = mDriverReceiveBuffer.remove (outMessage) ;
|
||||
//--- If receive interrupt is disabled, enable it (added in release 2.17)
|
||||
if (mINT == 255) { // No interrupt pin
|
||||
mRxInterruptEnabled = true ;
|
||||
isr_poll_core () ; // Perform polling
|
||||
}else if (!mRxInterruptEnabled) {
|
||||
|
||||
mSPI.beginTransaction (mSPISettings) ;
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
taskDISABLE_INTERRUPTS () ;
|
||||
#else
|
||||
noInterrupts () ;
|
||||
#endif
|
||||
|
||||
mRxInterruptEnabled = true ;
|
||||
uint8_t data8 = readRegister8Assume_SPI_transaction (INT_REGISTER + 2) ;
|
||||
data8 |= (1 << 1) ; // Receive FIFO Interrupt Enable
|
||||
writeRegister8Assume_SPI_transaction (INT_REGISTER + 2, data8) ;
|
||||
|
||||
turnOnInterrupts () ;
|
||||
mSPI.endTransaction () ;
|
||||
}
|
||||
turnOnInterrupts();
|
||||
mSPI.endTransaction () ;
|
||||
return hasReceivedMessage ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool ACAN2517FD::dispatchReceivedMessage (const tFilterMatchCallBack inFilterMatchCallBack) {
|
||||
CANFDMessage receivedMessage ;
|
||||
|
@ -834,9 +836,9 @@ bool ACAN2517FD::dispatchReceivedMessage (const tFilterMatchCallBack inFilterMat
|
|||
return hasReceived ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// POLLING (ESP32)
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
void ACAN2517FD::poll (void) {
|
||||
|
@ -846,22 +848,22 @@ bool ACAN2517FD::dispatchReceivedMessage (const tFilterMatchCallBack inFilterMat
|
|||
}
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// POLLING (other than ESP32)
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#ifndef ARDUINO_ARCH_ESP32
|
||||
void ACAN2517FD::poll (void) {
|
||||
turnOffInterrupts () ;
|
||||
noInterrupts () ;
|
||||
isr_poll_core () ;
|
||||
turnOnInterrupts () ;
|
||||
interrupts () ;
|
||||
}
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// INTERRUPT SERVICE ROUTINE (ESP32)
|
||||
// https://stackoverflow.com/questions/51750377/how-to-disable-interrupt-watchdog-in-esp32-or-increase-isr-time-limit
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
void ACAN2517FD::isr (void) {
|
||||
|
@ -871,9 +873,9 @@ bool ACAN2517FD::dispatchReceivedMessage (const tFilterMatchCallBack inFilterMat
|
|||
}
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// INTERRUPT SERVICE ROUTINE (other than ESP32)
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#ifndef ARDUINO_ARCH_ESP32
|
||||
void ACAN2517FD::isr (void) {
|
||||
|
@ -881,9 +883,9 @@ bool ACAN2517FD::dispatchReceivedMessage (const tFilterMatchCallBack inFilterMat
|
|||
}
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// INTERRUPT SERVICE ROUTINES (common)
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void ACAN2517FD::isr_poll_core (void) {
|
||||
mSPI.beginTransaction (mSPISettings) ;
|
||||
|
@ -933,7 +935,7 @@ void ACAN2517FD::isr_poll_core (void) {
|
|||
mSPI.endTransaction () ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void ACAN2517FD::transmitInterrupt (void) { // Generated if hardware transmit FIFO is not full
|
||||
CANFDMessage message ;
|
||||
|
@ -948,7 +950,7 @@ void ACAN2517FD::transmitInterrupt (void) { // Generated if hardware transmit FI
|
|||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void ACAN2517FD::receiveInterrupt (void) {
|
||||
const uint16_t ramAddress = uint16_t (0x400 + readRegister32Assume_SPI_transaction (FIFOUA_REGISTER (RECEIVE_FIFO_INDEX))) ;
|
||||
|
@ -1007,9 +1009,9 @@ void ACAN2517FD::receiveInterrupt (void) {
|
|||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// MCP2517FD REGISTER ACCESS, SECOND LEVEL FUNCTIONS (HANDLE CS, ASSUME WITHIN SPI TRANSACTION)
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void ACAN2517FD::writeRegister32Assume_SPI_transaction (const uint16_t inRegisterAddress,
|
||||
const uint32_t inValue) {
|
||||
|
@ -1027,7 +1029,7 @@ void ACAN2517FD::writeRegister32Assume_SPI_transaction (const uint16_t inRegiste
|
|||
deassertCS () ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void ACAN2517FD::writeRegister8Assume_SPI_transaction (const uint16_t inRegisterAddress,
|
||||
const uint8_t inValue) {
|
||||
|
@ -1042,7 +1044,7 @@ void ACAN2517FD::writeRegister8Assume_SPI_transaction (const uint16_t inRegister
|
|||
deassertCS () ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
uint32_t ACAN2517FD::readRegister32Assume_SPI_transaction (const uint16_t inRegisterAddress) {
|
||||
//--- Read word register via 6-byte buffer (speed enhancement, thanks to thomasfla)
|
||||
|
@ -1061,7 +1063,7 @@ uint32_t ACAN2517FD::readRegister32Assume_SPI_transaction (const uint16_t inRegi
|
|||
return result ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
uint16_t ACAN2517FD::readRegister16Assume_SPI_transaction (const uint16_t inRegisterAddress) {
|
||||
//--- Read half-word register via 4-byte buffer (speed enhancement, thanks to thomasfla)
|
||||
|
@ -1080,7 +1082,7 @@ uint16_t ACAN2517FD::readRegister16Assume_SPI_transaction (const uint16_t inRegi
|
|||
return result ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
uint8_t ACAN2517FD::readRegister8Assume_SPI_transaction (const uint16_t inRegisterAddress) {
|
||||
//--- Read byte register via 3-byte buffer (speed enhancement, thanks to thomasfla)
|
||||
|
@ -1094,71 +1096,71 @@ uint8_t ACAN2517FD::readRegister8Assume_SPI_transaction (const uint16_t inRegist
|
|||
return buffer [2] ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// MCP2517FD REGISTER ACCESS, THIRD LEVEL FUNCTIONS (HANDLE CS AND SPI TRANSACTION)
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void ACAN2517FD::writeRegister8 (const uint16_t inRegisterAddress, const uint8_t inValue) {
|
||||
mSPI.beginTransaction (mSPISettings) ;
|
||||
turnOffInterrupts () ;
|
||||
writeRegister8Assume_SPI_transaction (inRegisterAddress, inValue) ;
|
||||
turnOnInterrupts () ;
|
||||
turnOnInterrupts () ;
|
||||
mSPI.endTransaction () ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
uint8_t ACAN2517FD::readRegister8 (const uint16_t inRegisterAddress) {
|
||||
mSPI.beginTransaction (mSPISettings) ;
|
||||
turnOffInterrupts () ;
|
||||
const uint8_t result = readRegister8Assume_SPI_transaction (inRegisterAddress) ;
|
||||
turnOnInterrupts () ;
|
||||
turnOnInterrupts () ;
|
||||
mSPI.endTransaction () ;
|
||||
return result ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
uint16_t ACAN2517FD::readRegister16 (const uint16_t inRegisterAddress) {
|
||||
mSPI.beginTransaction (mSPISettings) ;
|
||||
turnOffInterrupts () ;
|
||||
const uint16_t result = readRegister16Assume_SPI_transaction (inRegisterAddress) ;
|
||||
turnOnInterrupts () ;
|
||||
turnOnInterrupts () ;
|
||||
mSPI.endTransaction () ;
|
||||
return result ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void ACAN2517FD::writeRegister32 (const uint16_t inRegisterAddress, const uint32_t inValue) {
|
||||
mSPI.beginTransaction (mSPISettings) ;
|
||||
turnOffInterrupts () ;
|
||||
writeRegister32Assume_SPI_transaction (inRegisterAddress, inValue) ;
|
||||
turnOnInterrupts () ;
|
||||
turnOnInterrupts () ;
|
||||
mSPI.endTransaction () ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
uint32_t ACAN2517FD::readRegister32 (const uint16_t inRegisterAddress) {
|
||||
mSPI.beginTransaction (mSPISettings) ;
|
||||
turnOffInterrupts () ;
|
||||
const uint32_t result = readRegister32Assume_SPI_transaction (inRegisterAddress) ;
|
||||
turnOnInterrupts () ;
|
||||
turnOnInterrupts () ;
|
||||
mSPI.endTransaction () ;
|
||||
return result ;
|
||||
}
|
||||
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
// Current MCP2517FD Operation Mode
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
ACAN2517FDSettings::OperationMode ACAN2517FD::currentOperationMode (void) {
|
||||
const uint8_t mode = readRegister8 (CON_REGISTER + 2) >> 5 ;
|
||||
return ACAN2517FDSettings::OperationMode (mode) ;
|
||||
}
|
||||
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool ACAN2517FD::recoverFromRestrictedOperationMode (void) {
|
||||
bool recoveryDone = false ;
|
||||
|
@ -1182,9 +1184,9 @@ bool ACAN2517FD::recoverFromRestrictedOperationMode (void) {
|
|||
return recoveryDone ;
|
||||
}
|
||||
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
// Set MCP2517FD Operation Mode
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void ACAN2517FD::setOperationMode (const ACAN2517FDSettings::OperationMode inOperationMode) {
|
||||
// bits 7-4: Transmit Bandwith Sharing Bits ---> 0
|
||||
|
@ -1192,7 +1194,7 @@ void ACAN2517FD::setOperationMode (const ACAN2517FDSettings::OperationMode inOpe
|
|||
writeRegister8 (CON_REGISTER + 3, uint8_t (inOperationMode));
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void ACAN2517FD::reset2517FD (void) {
|
||||
mSPI.beginTransaction (mSPISettings) ; // Check RESET is performed with 800 kHz clock
|
||||
|
@ -1200,14 +1202,14 @@ void ACAN2517FD::reset2517FD (void) {
|
|||
assertCS () ;
|
||||
mSPI.transfer16 (0x00) ; // Reset instruction: 0x0000
|
||||
deassertCS () ;
|
||||
turnOnInterrupts () ;
|
||||
turnOnInterrupts () ;
|
||||
mSPI.endTransaction () ;
|
||||
}
|
||||
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
// Sleep Mode to Configuration Mode
|
||||
// (returns true if MCP2517FD was in sleep mode)
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
// The device exits Sleep mode due to a dominant edge on RXCAN or by enabling the oscillator (clearing OSC.OSCDIS).
|
||||
// The module will transition automatically to Configuration mode.
|
||||
|
||||
|
@ -1226,21 +1228,21 @@ bool ACAN2517FD::performSleepModeToConfigurationMode (void) {
|
|||
return inSleepMode ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
uint32_t ACAN2517FD::errorCounters (void) {
|
||||
return readRegister32 (TREC_REGISTER) ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
uint32_t ACAN2517FD::diagInfos (const int inIndex) { // thanks to Flole998 and turmary
|
||||
return readRegister32 (inIndex ? BDIAG1_REGISTER: BDIAG0_REGISTER) ;
|
||||
}
|
||||
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
// GPIO
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void ACAN2517FD::gpioSetMode (const uint8_t inPin, const uint8_t inMode) {
|
||||
if (inPin <= 1) {
|
||||
|
@ -1260,7 +1262,7 @@ void ACAN2517FD::gpioSetMode (const uint8_t inPin, const uint8_t inMode) {
|
|||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void ACAN2517FD::gpioWrite (const uint8_t inPin, const uint8_t inLevel) {
|
||||
if (inPin <= 1) {
|
||||
|
@ -1274,14 +1276,14 @@ void ACAN2517FD::gpioWrite (const uint8_t inPin, const uint8_t inLevel) {
|
|||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool ACAN2517FD::gpioRead (const uint8_t inPin) {
|
||||
const uint8_t value = readRegister8 (IOCON_REGISTER_16_23) ;
|
||||
return (value >> inPin) & 1 ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void ACAN2517FD::configureGPIO0AsXSTBY (void) {
|
||||
uint8_t value = readRegister8 (IOCON_REGISTER_00_07) ;
|
||||
|
@ -1289,4 +1291,4 @@ void ACAN2517FD::configureGPIO0AsXSTBY (void) {
|
|||
writeRegister8 (IOCON_REGISTER_00_07, value) ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// A CAN driver for MCP2517FD, CANFD mode
|
||||
// by Pierre Molinaro
|
||||
// https://github.com/pierremolinaro/acan2517FD
|
||||
//
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#include "ACAN2517FDSettings.h"
|
||||
#include "ACANFDBuffer.h"
|
||||
#include "CANMessage.h"
|
||||
#include "ACAN2517FD_ACANFDBuffer.h"
|
||||
#include "ACAN2517FD_CANMessage.h"
|
||||
#include "ACAN2517FDFilters.h"
|
||||
#include <SPI.h>
|
||||
|
||||
|
@ -25,23 +24,23 @@
|
|||
//
|
||||
#define DISABLEMCP2517FDCOMPAT
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// ACAN2517FD class
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class ACAN2517FD {
|
||||
|
||||
//······················································································································
|
||||
// CONSTRUCTOR
|
||||
//······················································································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// CONSTRUCTOR
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: ACAN2517FD (const uint8_t inCS, // CS input of MCP2517FD
|
||||
SPIClass & inSPI, // Hardware SPI object
|
||||
const uint8_t inINT) ; // INT output of MCP2517FD
|
||||
|
||||
//······················································································································
|
||||
// begin method (returns 0 if no error)
|
||||
//······················································································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// begin method (returns 0 if no error)
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: uint32_t begin (const ACAN2517FDSettings & inSettings,
|
||||
void (* inInterruptServiceRoutine) (void)) ;
|
||||
|
@ -73,22 +72,22 @@ class ACAN2517FD {
|
|||
public: static const uint32_t kISRNotNullAndNoIntPin = uint32_t (1) << 19 ;
|
||||
public: static const uint32_t kInvalidTDCO = uint32_t (1) << 20 ;
|
||||
|
||||
//······················································································································
|
||||
// end method (resets the MCP2517FD, deallocate buffers, and detach interrupt pin)
|
||||
// Return true if end method succeeds, and false otherwise
|
||||
//······················································································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// end method (resets the MCP2517FD, deallocate buffers, and detach interrupt pin)
|
||||
// Return true if end method succeeds, and false otherwise
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: bool end (void) ;
|
||||
|
||||
//······················································································································
|
||||
// Send a message
|
||||
//······················································································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Send a message
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: bool tryToSend (const CANFDMessage & inMessage) ;
|
||||
|
||||
//······················································································································
|
||||
// Receive a message
|
||||
//······················································································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Receive a message
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: bool receive (CANFDMessage & outMessage) ;
|
||||
public: bool available (void) ;
|
||||
|
@ -98,44 +97,44 @@ class ACAN2517FD {
|
|||
//--- Call back function array
|
||||
private: ACANFDCallBackRoutine * mCallBackFunctionArray = NULL ;
|
||||
|
||||
//······················································································································
|
||||
// Get error counters
|
||||
//······················································································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Get error counters
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: uint32_t errorCounters (void) ;
|
||||
|
||||
//······················································································································
|
||||
// Get diagnostic information (thanks to Flole998 and turmary)
|
||||
// inIndex == 0 returns BDIAG0_REGISTER
|
||||
// inIndex != 0 returns BDIAG1_REGISTER
|
||||
//······················································································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Get diagnostic information (thanks to Flole998 and turmary)
|
||||
// inIndex == 0 returns BDIAG0_REGISTER
|
||||
// inIndex != 0 returns BDIAG1_REGISTER
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: uint32_t diagInfos (const int inIndex = 1) ;
|
||||
|
||||
//······················································································································
|
||||
// Operation Mode
|
||||
//······················································································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Operation Mode
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: ACAN2517FDSettings::OperationMode currentOperationMode (void) ;
|
||||
|
||||
public: void setOperationMode (const ACAN2517FDSettings::OperationMode inMode) ;
|
||||
|
||||
//······················································································································
|
||||
// Recovery from Restricted Operation Mode
|
||||
//······················································································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Recovery from Restricted Operation Mode
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: bool recoverFromRestrictedOperationMode (void) ;
|
||||
|
||||
//······················································································································
|
||||
// Sleep Mode to Configuration Mode
|
||||
// (returns true if MCP2517FD was in sleep mode)
|
||||
//······················································································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Sleep Mode to Configuration Mode
|
||||
// (returns true if MCP2517FD was in sleep mode)
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: bool performSleepModeToConfigurationMode (void) ;
|
||||
|
||||
//······················································································································
|
||||
// Private properties
|
||||
//······················································································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Private properties
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
private: TaskHandle_t mESP32TaskHandle = nullptr ;
|
||||
|
@ -147,40 +146,51 @@ class ACAN2517FD {
|
|||
private: bool mUsesTXQ ;
|
||||
private: bool mHardwareTxFIFOFull ;
|
||||
private: bool mRxInterruptEnabled ; // Added in 2.1.7
|
||||
private: bool mHasDataBitRate ;
|
||||
private: uint8_t mTransmitFIFOPayload ; // in byte count
|
||||
private: uint8_t mTXQBufferPayload ; // in byte count
|
||||
private: uint8_t mReceiveFIFOPayload ; // in byte count
|
||||
private: uint8_t mTXBWS_RequestedMode ;
|
||||
private: uint8_t mHardwareReceiveBufferOverflowCount ;
|
||||
|
||||
//······················································································································
|
||||
// Receive buffer
|
||||
//······················································································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Receive buffer
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
private: ACANFDBuffer mDriverReceiveBuffer ;
|
||||
|
||||
public: uint32_t driverReceiveBufferPeakCount (void) const { return mDriverReceiveBuffer.peakCount () ; }
|
||||
public: uint32_t driverReceiveBufferPeakCount (void) const {
|
||||
return mDriverReceiveBuffer.peakCount () ;
|
||||
}
|
||||
|
||||
public: uint8_t hardwareReceiveBufferOverflowCount (void) const { return mHardwareReceiveBufferOverflowCount ; }
|
||||
public: uint8_t hardwareReceiveBufferOverflowCount (void) const {
|
||||
return mHardwareReceiveBufferOverflowCount ;
|
||||
}
|
||||
|
||||
public: void resetHardwareReceiveBufferOverflowCount (void) { mHardwareReceiveBufferOverflowCount = 0 ; }
|
||||
public: void resetHardwareReceiveBufferOverflowCount (void) {
|
||||
mHardwareReceiveBufferOverflowCount = 0 ;
|
||||
}
|
||||
|
||||
//······················································································································
|
||||
// Transmit buffer
|
||||
//······················································································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Transmit buffer
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
private: ACANFDBuffer mDriverTransmitBuffer ;
|
||||
|
||||
public: uint32_t driverTransmitBufferSize (void) const { return mDriverTransmitBuffer.size () ; }
|
||||
public: uint32_t driverTransmitBufferSize (void) const {
|
||||
return mDriverTransmitBuffer.size () ;
|
||||
}
|
||||
|
||||
public: uint32_t driverTransmitBufferCount (void) const { return mDriverTransmitBuffer.count () ; }
|
||||
public: uint32_t driverTransmitBufferCount (void) const {
|
||||
return mDriverTransmitBuffer.count () ;
|
||||
}
|
||||
|
||||
public: uint32_t driverTransmitBufferPeakCount (void) const { return mDriverTransmitBuffer.peakCount () ; }
|
||||
public: uint32_t driverTransmitBufferPeakCount (void) const {
|
||||
return mDriverTransmitBuffer.peakCount () ;
|
||||
}
|
||||
|
||||
//······················································································································
|
||||
// Private methods
|
||||
//······················································································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Private methods
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
private: void writeRegister32Assume_SPI_transaction (const uint16_t inRegisterAddress, const uint32_t inValue) ;
|
||||
private: void writeRegister8Assume_SPI_transaction (const uint16_t inRegisterAddress, const uint8_t inValue) ;
|
||||
|
@ -202,15 +212,15 @@ class ACAN2517FD {
|
|||
private: bool enterInTransmitBuffer (const CANFDMessage & inMessage) ;
|
||||
private: void appendInControllerTxFIFO (const CANFDMessage & inMessage) ;
|
||||
|
||||
//······················································································································
|
||||
// Polling
|
||||
//······················································································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Polling
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: void poll (void) ;
|
||||
|
||||
//······················································································································
|
||||
// Interrupt service routine
|
||||
//······················································································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Interrupt service routine
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: void isr (void) ;
|
||||
public: void isr_poll_core (void) ;
|
||||
|
@ -220,9 +230,9 @@ class ACAN2517FD {
|
|||
public: SemaphoreHandle_t mISRSemaphore ;
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
// Optimized CS handling (thanks to Flole998)
|
||||
//······················································································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Optimized CS handling (thanks to Flole998)
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
#if defined(__AVR__)
|
||||
private: volatile uint8_t *cs_pin_reg;
|
||||
|
@ -293,10 +303,8 @@ class ACAN2517FD {
|
|||
*(cs_pin_reg+8+2) = cs_pin_mask;
|
||||
}
|
||||
#elif defined(ARDUINO_ARCH_ESP8266)
|
||||
// private: volatile uint32_t *cs_pin_reg;
|
||||
private: uint32_t cs_pin_mask;
|
||||
private: inline void initCS () {
|
||||
// cs_pin_reg = (volatile uint32_t*)GPO;
|
||||
cs_pin_mask = 1 << mCS;
|
||||
pinMode(mCS, OUTPUT);
|
||||
}
|
||||
|
@ -333,9 +341,9 @@ class ACAN2517FD {
|
|||
}
|
||||
#endif
|
||||
|
||||
//······················································································································
|
||||
// GPIO
|
||||
//······················································································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// GPIO
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: void gpioSetMode (const uint8_t inPin, const uint8_t inMode) ;
|
||||
|
||||
|
@ -345,16 +353,16 @@ class ACAN2517FD {
|
|||
|
||||
public: void configureGPIO0AsXSTBY (void) ;
|
||||
|
||||
//······················································································································
|
||||
// No copy
|
||||
//······················································································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// No copy
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
private: ACAN2517FD (const ACAN2517FD &) = delete ;
|
||||
private: ACAN2517FD & operator = (const ACAN2517FD &) = delete ;
|
||||
|
||||
//······················································································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
} ;
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
|
|
@ -1,27 +1,27 @@
|
|||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// An utility class for:
|
||||
// - ACAN2517FD CAN driver for MCP2517FD (CANFD mode)
|
||||
// by Pierre Molinaro
|
||||
// https://github.com/pierremolinaro/acan2517FD
|
||||
//
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#ifndef ACAN2517FD_FILTERS_CLASS_DEFINED
|
||||
#define ACAN2517FD_FILTERS_CLASS_DEFINED
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#include "CANFDMessage.h"
|
||||
#include "ACAN2517FD_CANFDMessage.h"
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// ACAN2517FDFilters class
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class ACAN2517FDFilters {
|
||||
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
// EMBEDDED CLASS
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
private: class Filter {
|
||||
public: Filter * mNextFilter ;
|
||||
|
@ -43,9 +43,9 @@ class ACAN2517FDFilters {
|
|||
private: Filter & operator = (const Filter &) ;
|
||||
} ;
|
||||
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
// ENUMERATED TYPE
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
public: typedef enum {
|
||||
kFiltersOk,
|
||||
|
@ -58,15 +58,15 @@ class ACAN2517FDFilters {
|
|||
kInconsistencyBetweenMaskAndAcceptance
|
||||
} FilterStatus ;
|
||||
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
// CONSTRUCTOR
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
public: ACAN2517FDFilters (void) {}
|
||||
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
// DESTRUCTOR
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
public: ~ ACAN2517FDFilters (void) {
|
||||
while (mFirstFilter != NULL) {
|
||||
|
@ -76,9 +76,9 @@ class ACAN2517FDFilters {
|
|||
}
|
||||
}
|
||||
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
// RECEIVE FILTERS
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
public: void appendPassAllFilter (const ACANFDCallBackRoutine inCallBackRoutine) { // Accept any frame
|
||||
Filter * f = new Filter (0, 0, inCallBackRoutine) ;
|
||||
|
@ -91,7 +91,7 @@ class ACAN2517FDFilters {
|
|||
mFilterCount += 1 ;
|
||||
}
|
||||
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
public: void appendFormatFilter (const tFrameFormat inFormat, // Accept any identifier
|
||||
const ACANFDCallBackRoutine inCallBackRoutine) {
|
||||
|
@ -107,7 +107,7 @@ class ACAN2517FDFilters {
|
|||
mFilterCount += 1 ;
|
||||
}
|
||||
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
public: void appendFrameFilter (const tFrameFormat inFormat,
|
||||
const uint32_t inIdentifier,
|
||||
|
@ -141,7 +141,7 @@ class ACAN2517FDFilters {
|
|||
mFilterCount += 1 ;
|
||||
}
|
||||
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
public: void appendFilter (const tFrameFormat inFormat,
|
||||
const uint32_t inMask,
|
||||
|
@ -196,9 +196,9 @@ class ACAN2517FDFilters {
|
|||
mFilterCount += 1 ;
|
||||
}
|
||||
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
// ACCESSORS
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
public: FilterStatus filterStatus (void) const { return mFilterStatus ; }
|
||||
|
||||
|
@ -206,9 +206,9 @@ class ACAN2517FDFilters {
|
|||
|
||||
public: uint8_t filterCount (void) const { return mFilterCount ; }
|
||||
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
// PRIVATE PROPERTIES
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
private: uint8_t mFilterCount = 0 ;
|
||||
private: Filter * mFirstFilter = NULL ;
|
||||
|
@ -216,23 +216,23 @@ class ACAN2517FDFilters {
|
|||
private: FilterStatus mFilterStatus = kFiltersOk ;
|
||||
private: uint8_t mFilterErrorIndex = 0 ;
|
||||
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
// NO COPY
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
private: ACAN2517FDFilters (const ACAN2517FDFilters &) = delete ;
|
||||
private: ACAN2517FDFilters & operator = (const ACAN2517FDFilters &) = delete ;
|
||||
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
// Friend
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
friend class ACAN2517FD ;
|
||||
|
||||
//······················································································································
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} ;
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// A CAN driver for MCP2517FD (CANFD mode)
|
||||
// by Pierre Molinaro
|
||||
// https://github.com/pierremolinaro/acan2517FD
|
||||
//
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#include "ACAN2517FDSettings.h"
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#pragma GCC diagnostic error "-Wswitch-enum"
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// sysClock
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
uint32_t ACAN2517FDSettings::sysClock (const Oscillator inOscillator) {
|
||||
uint32_t sysClock = 40UL * 1000 * 1000 ;
|
||||
|
@ -39,9 +39,9 @@ uint32_t ACAN2517FDSettings::sysClock (const Oscillator inOscillator) {
|
|||
return sysClock ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// CONSTRUCTOR
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
ACAN2517FDSettings::ACAN2517FDSettings (const Oscillator inOscillator,
|
||||
const uint32_t inDesiredArbitrationBitRate,
|
||||
|
@ -51,148 +51,93 @@ mOscillator (inOscillator),
|
|||
mSysClock (sysClock (inOscillator)),
|
||||
mDesiredArbitrationBitRate (inDesiredArbitrationBitRate),
|
||||
mDataBitRateFactor (inDataBitRateFactor) {
|
||||
if (inDataBitRateFactor == DataBitRateFactor::x1) { // Single bit rate
|
||||
const uint32_t maxTQCount = MAX_ARBITRATION_PHASE_SEGMENT_1 + MAX_ARBITRATION_PHASE_SEGMENT_2 + 1 ; // Setting for slowest bit rate
|
||||
uint32_t BRP = MAX_BRP ;
|
||||
uint32_t smallestError = UINT32_MAX ;
|
||||
uint32_t bestBRP = 1 ; // Setting for highest bit rate
|
||||
uint32_t bestTQCount = 4 ; // Setting for highest bit rate
|
||||
uint32_t TQCount = mSysClock / inDesiredArbitrationBitRate / BRP ;
|
||||
//--- Loop for finding best BRP and best TQCount
|
||||
while ((TQCount <= (MAX_ARBITRATION_PHASE_SEGMENT_1 + MAX_ARBITRATION_PHASE_SEGMENT_2 + 1)) && (BRP > 0)) {
|
||||
//--- Compute error using TQCount
|
||||
if ((TQCount >= 4) && (TQCount <= maxTQCount)) {
|
||||
const uint32_t error = mSysClock - inDesiredArbitrationBitRate * TQCount * BRP ; // error is always >= 0
|
||||
if (error <= smallestError) {
|
||||
smallestError = error ;
|
||||
bestBRP = BRP ;
|
||||
bestTQCount = TQCount ;
|
||||
}
|
||||
// First compute data bit rate
|
||||
const uint32_t maxDataTQCount = MAX_DATA_PHASE_SEGMENT_1 + MAX_DATA_PHASE_SEGMENT_2 ; // Setting for slowest bit rate
|
||||
const uint32_t desiredDataBitRate = inDesiredArbitrationBitRate * uint8_t (inDataBitRateFactor) ;
|
||||
uint32_t smallestError = UINT32_MAX ;
|
||||
uint32_t bestBRP = MAX_BRP ; // Setting for lowest bit rate
|
||||
uint32_t bestDataTQCount = maxDataTQCount ; // Setting for lowest bit rate
|
||||
uint32_t dataTQCount = 4 ;
|
||||
uint32_t brp = mSysClock / desiredDataBitRate / dataTQCount ;
|
||||
//--- Loop for finding best BRP and best TQCount
|
||||
while ((dataTQCount <= maxDataTQCount) && (brp > 0)) {
|
||||
//--- Compute error using brp
|
||||
if (brp <= MAX_BRP) {
|
||||
const uint32_t error = mSysClock - desiredDataBitRate * dataTQCount * brp ; // error is always >= 0
|
||||
if (error <= smallestError) {
|
||||
smallestError = error ;
|
||||
bestBRP = brp ;
|
||||
bestDataTQCount = dataTQCount ;
|
||||
}
|
||||
//--- Compute error using TQCount+1
|
||||
if ((TQCount >= 3) && (TQCount < maxTQCount)) {
|
||||
const uint32_t error = inDesiredArbitrationBitRate * (TQCount + 1) * BRP - mSysClock ; // error is always >= 0
|
||||
if (error <= smallestError) {
|
||||
smallestError = error ;
|
||||
bestBRP = BRP ;
|
||||
bestTQCount = TQCount + 1 ;
|
||||
}
|
||||
}
|
||||
//--- Compute error using brp+1
|
||||
if (brp < MAX_BRP) {
|
||||
const uint32_t error = desiredDataBitRate * dataTQCount * (brp + 1) - mSysClock ; // error is always >= 0
|
||||
if (error <= smallestError) {
|
||||
smallestError = error ;
|
||||
bestBRP = brp + 1 ;
|
||||
bestDataTQCount = dataTQCount ;
|
||||
}
|
||||
//--- Continue with next value of BRP
|
||||
BRP -- ;
|
||||
TQCount = (BRP == 0) ? (maxTQCount + 1) : (mSysClock / inDesiredArbitrationBitRate / BRP) ;
|
||||
}
|
||||
//--- Compute PS2 (1 <= PS2 <= 128)
|
||||
uint32_t PS2 = bestTQCount / 5 ; // For sampling point at 80%
|
||||
if (PS2 == 0) {
|
||||
PS2 = 1 ;
|
||||
}else if (PS2 > MAX_ARBITRATION_PHASE_SEGMENT_2) {
|
||||
PS2 = MAX_ARBITRATION_PHASE_SEGMENT_2 ;
|
||||
}
|
||||
//--- Compute PS1 (1 <= PS1 <= 256)
|
||||
uint32_t PS1 = bestTQCount - PS2 - 1 /* Sync Seg */ ;
|
||||
if (PS1 > MAX_ARBITRATION_PHASE_SEGMENT_1) {
|
||||
PS2 += PS1 - MAX_ARBITRATION_PHASE_SEGMENT_1 ;
|
||||
PS1 = MAX_ARBITRATION_PHASE_SEGMENT_1 ;
|
||||
}
|
||||
//---
|
||||
mBitRatePrescaler = (uint16_t) bestBRP ;
|
||||
mArbitrationPhaseSegment1 = (uint16_t) PS1 ;
|
||||
mArbitrationPhaseSegment2 = (uint8_t) PS2 ;
|
||||
mArbitrationSJW = mArbitrationPhaseSegment2 ; // Always 1 <= SJW <= 128, and SJW <= mArbitrationPhaseSegment2
|
||||
//--- Final check of the nominal configuration
|
||||
const uint32_t W = bestTQCount * mDesiredArbitrationBitRate * bestBRP ;
|
||||
const uint64_t diff = (mSysClock > W) ? (mSysClock - W) : (W - mSysClock) ;
|
||||
const uint64_t ppm = (uint64_t) (1000UL * 1000UL) ; // UL suffix is required for Arduino Uno
|
||||
mArbitrationBitRateClosedToDesiredRate = (diff * ppm) <= (((uint64_t) W) * inTolerancePPM) ;
|
||||
}else{ // Dual bit rate, first compute data bit rate
|
||||
const uint32_t maxDataTQCount = MAX_DATA_PHASE_SEGMENT_1 + MAX_DATA_PHASE_SEGMENT_2 ; // Setting for slowest bit rate
|
||||
const uint32_t desiredDataBitRate = inDesiredArbitrationBitRate * uint8_t (inDataBitRateFactor) ;
|
||||
uint32_t smallestError = UINT32_MAX ;
|
||||
uint32_t bestBRP = MAX_BRP ; // Setting for lowest bit rate
|
||||
uint32_t bestDataTQCount = maxDataTQCount ; // Setting for lowest bit rate
|
||||
uint32_t dataTQCount = 4 ;
|
||||
uint32_t brp = mSysClock / desiredDataBitRate / dataTQCount ;
|
||||
//--- Loop for finding best BRP and best TQCount
|
||||
while ((dataTQCount <= maxDataTQCount) && (brp > 0)) {
|
||||
//--- Compute error using brp
|
||||
if (brp <= MAX_BRP) {
|
||||
const uint32_t error = mSysClock - desiredDataBitRate * dataTQCount * brp ; // error is always >= 0
|
||||
if (error <= smallestError) {
|
||||
smallestError = error ;
|
||||
bestBRP = brp ;
|
||||
bestDataTQCount = dataTQCount ;
|
||||
}
|
||||
}
|
||||
//--- Compute error using brp+1
|
||||
if (brp < MAX_BRP) {
|
||||
const uint32_t error = desiredDataBitRate * dataTQCount * (brp + 1) - mSysClock ; // error is always >= 0
|
||||
if (error <= smallestError) {
|
||||
smallestError = error ;
|
||||
bestBRP = brp + 1 ;
|
||||
bestDataTQCount = dataTQCount ;
|
||||
}
|
||||
}
|
||||
//--- Continue with next value of BRP
|
||||
dataTQCount += 1 ;
|
||||
brp = mSysClock / desiredDataBitRate / dataTQCount ;
|
||||
}
|
||||
//--- Compute data PS2 (1 <= PS2 <= 16)
|
||||
uint32_t dataPS2 = bestDataTQCount / 5 ; // For sampling point at 80%
|
||||
if (dataPS2 == 0) {
|
||||
dataPS2 = 1 ;
|
||||
}
|
||||
//--- Compute data PS1 (1 <= PS1 <= 32)
|
||||
uint32_t dataPS1 = bestDataTQCount - dataPS2 - 1 /* Sync Seg */ ;
|
||||
if (dataPS1 > MAX_DATA_PHASE_SEGMENT_1) {
|
||||
dataPS2 += dataPS1 - MAX_DATA_PHASE_SEGMENT_1 ;
|
||||
dataPS1 = MAX_DATA_PHASE_SEGMENT_1 ;
|
||||
}
|
||||
//---
|
||||
if ((mDesiredArbitrationBitRate * uint32_t (inDataBitRateFactor)) <= (1000UL * 1000)) {
|
||||
mTDCO = 0 ;
|
||||
}else{
|
||||
const int TDCO = bestBRP * dataPS1 ; // According to DS20005678D, §3.4.8 Page 20
|
||||
mTDCO = (TDCO > 63) ? 63 : (int8_t) TDCO ;
|
||||
}
|
||||
mDataPhaseSegment1 = (uint8_t) dataPS1 ;
|
||||
mDataPhaseSegment2 = (uint8_t) dataPS2 ;
|
||||
mDataSJW = mDataPhaseSegment2 ;
|
||||
const uint32_t arbitrationTQCount = bestDataTQCount * uint8_t (mDataBitRateFactor) ;
|
||||
//--- Compute arbiration PS2 (1 <= PS2 <= 128)
|
||||
uint32_t arbitrationPS2 = arbitrationTQCount / 5 ; // For sampling point at 80%
|
||||
if (arbitrationPS2 == 0) {
|
||||
arbitrationPS2 = 1 ;
|
||||
}
|
||||
//--- Compute PS1 (1 <= PS1 <= 256)
|
||||
uint32_t arbitrationPS1 = arbitrationTQCount - arbitrationPS2 - 1 /* Sync Seg */ ;
|
||||
if (arbitrationPS1 > MAX_ARBITRATION_PHASE_SEGMENT_1) {
|
||||
arbitrationPS2 += arbitrationPS1 - MAX_ARBITRATION_PHASE_SEGMENT_1 ;
|
||||
arbitrationPS1 = MAX_ARBITRATION_PHASE_SEGMENT_1 ;
|
||||
}
|
||||
//---
|
||||
mBitRatePrescaler = (uint16_t) bestBRP ;
|
||||
mArbitrationPhaseSegment1 = (uint16_t) arbitrationPS1 ;
|
||||
mArbitrationPhaseSegment2 = (uint8_t) arbitrationPS2 ;
|
||||
mArbitrationSJW = mArbitrationPhaseSegment2 ; // Always 1 <= SJW <= 128, and SJW <= mArbitrationPhaseSegment2
|
||||
//--- Final check of the nominal configuration
|
||||
const uint32_t W = arbitrationTQCount * mDesiredArbitrationBitRate * bestBRP ;
|
||||
const uint64_t diff = (mSysClock > W) ? (mSysClock - W) : (W - mSysClock) ;
|
||||
const uint64_t ppm = (uint64_t) (1000UL * 1000UL) ; // UL suffix is required for Arduino Uno
|
||||
mArbitrationBitRateClosedToDesiredRate = (diff * ppm) <= (((uint64_t) W) * inTolerancePPM) ;
|
||||
//--- Continue with next value of BRP
|
||||
dataTQCount += 1 ;
|
||||
brp = mSysClock / desiredDataBitRate / dataTQCount ;
|
||||
}
|
||||
//--- Compute data PS2 (1 <= PS2 <= 16)
|
||||
uint32_t dataPS2 = bestDataTQCount / 5 ; // For sampling point at 80%
|
||||
if (dataPS2 == 0) {
|
||||
dataPS2 = 1 ;
|
||||
}
|
||||
//--- Compute data PS1 (1 <= PS1 <= 32)
|
||||
uint32_t dataPS1 = bestDataTQCount - dataPS2 - 1 /* Sync Seg */ ;
|
||||
if (dataPS1 > MAX_DATA_PHASE_SEGMENT_1) {
|
||||
dataPS2 += dataPS1 - MAX_DATA_PHASE_SEGMENT_1 ;
|
||||
dataPS1 = MAX_DATA_PHASE_SEGMENT_1 ;
|
||||
}
|
||||
//---
|
||||
if ((mDesiredArbitrationBitRate * uint32_t (inDataBitRateFactor)) <= (1000UL * 1000)) {
|
||||
mTDCO = 0 ;
|
||||
}else{
|
||||
const int TDCO = bestBRP * dataPS1 ; // According to DS20005678D, §3.4.8 Page 20
|
||||
mTDCO = (TDCO > 63) ? 63 : (int8_t) TDCO ;
|
||||
}
|
||||
mDataPhaseSegment1 = (uint8_t) dataPS1 ;
|
||||
mDataPhaseSegment2 = (uint8_t) dataPS2 ;
|
||||
mDataSJW = mDataPhaseSegment2 ;
|
||||
const uint32_t arbitrationTQCount = bestDataTQCount * uint8_t (mDataBitRateFactor) ;
|
||||
//--- Compute arbitration PS2 (1 <= PS2 <= 128)
|
||||
uint32_t arbitrationPS2 = arbitrationTQCount / 5 ; // For sampling point at 80%
|
||||
if (arbitrationPS2 == 0) {
|
||||
arbitrationPS2 = 1 ;
|
||||
}
|
||||
//--- Compute PS1 (1 <= PS1 <= 256)
|
||||
uint32_t arbitrationPS1 = arbitrationTQCount - arbitrationPS2 - 1 /* Sync Seg */ ;
|
||||
if (arbitrationPS1 > MAX_ARBITRATION_PHASE_SEGMENT_1) {
|
||||
arbitrationPS2 += arbitrationPS1 - MAX_ARBITRATION_PHASE_SEGMENT_1 ;
|
||||
arbitrationPS1 = MAX_ARBITRATION_PHASE_SEGMENT_1 ;
|
||||
}
|
||||
//---
|
||||
mBitRatePrescaler = (uint16_t) bestBRP ;
|
||||
mArbitrationPhaseSegment1 = (uint16_t) arbitrationPS1 ;
|
||||
mArbitrationPhaseSegment2 = (uint8_t) arbitrationPS2 ;
|
||||
mArbitrationSJW = mArbitrationPhaseSegment2 ; // Always 1 <= SJW <= 128, and SJW <= mArbitrationPhaseSegment2
|
||||
//--- Final check of the nominal configuration
|
||||
const uint32_t W = arbitrationTQCount * mDesiredArbitrationBitRate * bestBRP ;
|
||||
const uint64_t diff = (mSysClock > W) ? (mSysClock - W) : (W - mSysClock) ;
|
||||
const uint64_t ppm = (uint64_t) (1000UL * 1000UL) ; // UL suffix is required for Arduino Uno
|
||||
mArbitrationBitRateClosedToDesiredRate = (diff * ppm) <= (((uint64_t) W) * inTolerancePPM) ;
|
||||
} ;
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// ACCESSORS
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
uint32_t ACAN2517FDSettings::actualArbitrationBitRate (void) const {
|
||||
const uint32_t arbitrationTQCount = 1 /* Sync Seg */ + mArbitrationPhaseSegment1 + mArbitrationPhaseSegment2 ;
|
||||
return mSysClock / mBitRatePrescaler / arbitrationTQCount ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
uint32_t ACAN2517FDSettings::actualDataBitRate (void) const {
|
||||
if (mDataBitRateFactor == DataBitRateFactor::x1) {
|
||||
|
@ -203,14 +148,14 @@ uint32_t ACAN2517FDSettings::actualDataBitRate (void) const {
|
|||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool ACAN2517FDSettings::exactArbitrationBitRate (void) const {
|
||||
const uint32_t TQCount = 1 /* Sync Seg */ + mArbitrationPhaseSegment1 + mArbitrationPhaseSegment2 ;
|
||||
return mSysClock == (mBitRatePrescaler * mDesiredArbitrationBitRate * TQCount) ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool ACAN2517FDSettings::exactDataBitRate (void) const {
|
||||
if (mDataBitRateFactor == DataBitRateFactor::x1) {
|
||||
|
@ -221,7 +166,7 @@ bool ACAN2517FDSettings::exactDataBitRate (void) const {
|
|||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool ACAN2517FDSettings::dataBitRateIsAMultipleOfArbitrationBitRate (void) const {
|
||||
bool result = mDataBitRateFactor == DataBitRateFactor::x1 ;
|
||||
|
@ -233,7 +178,7 @@ bool ACAN2517FDSettings::dataBitRateIsAMultipleOfArbitrationBitRate (void) const
|
|||
return result ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
uint32_t ACAN2517FDSettings::ppmFromDesiredArbitrationBitRate (void) const {
|
||||
const uint32_t TQCount = 1 /* Sync Seg */ + mArbitrationPhaseSegment1 + mArbitrationPhaseSegment2 ;
|
||||
|
@ -243,7 +188,7 @@ uint32_t ACAN2517FDSettings::ppmFromDesiredArbitrationBitRate (void) const {
|
|||
return (uint32_t) ((diff * ppm) / W) ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
uint32_t ACAN2517FDSettings::arbitrationSamplePointFromBitStart (void) const {
|
||||
const uint32_t nominalTQCount = 1 /* Sync Seg */ + mArbitrationPhaseSegment1 + mArbitrationPhaseSegment2 ;
|
||||
|
@ -252,7 +197,7 @@ uint32_t ACAN2517FDSettings::arbitrationSamplePointFromBitStart (void) const {
|
|||
return (samplePoint * partPerCent) / nominalTQCount ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
uint32_t ACAN2517FDSettings::dataSamplePointFromBitStart (void) const {
|
||||
const uint32_t nominalTQCount = 1 /* Sync Seg */ + mDataPhaseSegment1 + mDataPhaseSegment2 ;
|
||||
|
@ -261,7 +206,7 @@ uint32_t ACAN2517FDSettings::dataSamplePointFromBitStart (void) const {
|
|||
return (samplePoint * partPerCent) / nominalTQCount ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
uint32_t ACAN2517FDSettings::CANBitSettingConsistency (void) const {
|
||||
uint32_t errorCode = 0 ; // Means no error
|
||||
|
@ -329,9 +274,9 @@ uint32_t ACAN2517FDSettings::CANBitSettingConsistency (void) const {
|
|||
return errorCode ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// RAM USAGE
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
uint32_t ACAN2517FDSettings::ramUsage (void) const {
|
||||
uint32_t result = 0 ;
|
||||
|
@ -345,11 +290,11 @@ uint32_t ACAN2517FDSettings::ramUsage (void) const {
|
|||
return result ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
uint32_t ACAN2517FDSettings::objectSizeForPayload (const PayloadSize inPayload) {
|
||||
static const uint8_t kPayload [8] = {16, 20, 24, 28, 32, 40, 56, 72} ;
|
||||
return kPayload [inPayload] ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
|
|
@ -1,25 +1,25 @@
|
|||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// A CAN driver for MCP2517FD (CANFD mode)
|
||||
// by Pierre Molinaro
|
||||
// https://github.com/pierremolinaro/acan2517FD
|
||||
//
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#include "ACANFD_DataBitRateFactor.h"
|
||||
#include "ACAN2517FD_DataBitRateFactor.h"
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// ACAN2517FDSettings class
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class ACAN2517FDSettings {
|
||||
|
||||
//······················································································································
|
||||
// ENUMERATED TYPES
|
||||
//······················································································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// ENUMERATED TYPES
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: typedef enum : uint8_t {
|
||||
OSC_4MHz,
|
||||
|
@ -64,9 +64,9 @@ class ACAN2517FDSettings {
|
|||
PAYLOAD_64 = 7
|
||||
} PayloadSize ;
|
||||
|
||||
//······················································································································
|
||||
// Deprecated enumeration (now use DataBitRateFactor declared in ACANFD_DataBitRateFactor.h)
|
||||
//······················································································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Deprecated enumeration (now use DataBitRateFactor declared in ACANFD_DataBitRateFactor.h)
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public : typedef enum : uint8_t {
|
||||
DATA_BITRATE_x1 = 1,
|
||||
|
@ -81,18 +81,18 @@ class ACAN2517FDSettings {
|
|||
DATA_BITRATE_x10 = 10
|
||||
} DataBitRateFactor_Deprecated ;
|
||||
|
||||
//······················································································································
|
||||
// CONSTRUCTOR
|
||||
//······················································································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// CONSTRUCTOR
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: ACAN2517FDSettings (const Oscillator inOscillator,
|
||||
const uint32_t inDesiredArbitrationBitRate,
|
||||
const DataBitRateFactor inDataBitRateFactor,
|
||||
const uint32_t inTolerancePPM = 1000) ;
|
||||
|
||||
//······················································································································
|
||||
// DEPRECATED CONSTRUCTOR (for compatibility with version < 2.1.0)
|
||||
//······················································································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// DEPRECATED CONSTRUCTOR (for compatibility with version < 2.1.0)
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: ACAN2517FDSettings (const Oscillator inOscillator,
|
||||
const uint32_t inDesiredArbitrationBitRate,
|
||||
|
@ -101,9 +101,9 @@ class ACAN2517FDSettings {
|
|||
ACAN2517FDSettings (inOscillator, inDesiredArbitrationBitRate, DataBitRateFactor (inDataBitRateFactor), inTolerancePPM) {
|
||||
}
|
||||
|
||||
//······················································································································
|
||||
// CAN BIT TIMING
|
||||
//······················································································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// CAN BIT TIMING
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
private: Oscillator mOscillator ;
|
||||
private: uint32_t mSysClock ; // In Hz
|
||||
|
@ -123,44 +123,44 @@ class ACAN2517FDSettings {
|
|||
//--- Transmitter Delay Compensation Offset
|
||||
public: int8_t mTDCO = 0 ; // -64 ... +63
|
||||
|
||||
//······················································································································
|
||||
// MCP2517FD TXCAN pin is Open Drain ?
|
||||
//······················································································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// MCP2517FD TXCAN pin is Open Drain ?
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: bool mTXCANIsOpenDrain = false ; // false --> Push/Pull Output, true --> Open Drain Output
|
||||
|
||||
//······················································································································
|
||||
// MCP2517FD INT pin is Open Drain ?
|
||||
//······················································································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// MCP2517FD INT pin is Open Drain ?
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: bool mINTIsOpenDrain = false ; // false --> Push/Pull Output, true --> Open Drain Output
|
||||
|
||||
//······················································································································
|
||||
// ISO CRC Enable
|
||||
//······················································································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// ISO CRC Enable
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
// false --> Do NOT include Stuff Bit Count in CRC Field and use CRC Initialization Vector with all zeros
|
||||
// true --> Include Stuff Bit Count in CRC Field and use Non-Zero CRC Initialization Vector according to ISO 11898-1:2015
|
||||
public: bool mISOCRCEnabled = true ;
|
||||
|
||||
//······················································································································
|
||||
// CLKO pin function (default value is MCP2517FD power on setting)
|
||||
//······················································································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// CLKO pin function (default value is MCP2517FD power on setting)
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: CLKOpin mCLKOPin = CLKO_DIVIDED_BY_10 ;
|
||||
|
||||
//······················································································································
|
||||
// Requested mode
|
||||
//······················································································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Requested mode
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: OperationMode mRequestedMode = NormalFD ;
|
||||
|
||||
//······················································································································
|
||||
// TRANSMIT FIFO
|
||||
//······················································································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// TRANSMIT FIFO
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
//--- Driver transmit buffer size
|
||||
public: uint16_t mDriverTransmitFIFOSize = 22 ; // >= 0
|
||||
public: uint16_t mDriverTransmitFIFOSize = 16 ; // >= 0
|
||||
|
||||
//--- Controller transmit FIFO size
|
||||
public: uint8_t mControllerTransmitFIFOSize = 1 ; // 1 ... 32
|
||||
|
@ -174,9 +174,9 @@ class ACAN2517FDSettings {
|
|||
//--- Controller transmit FIFO retransmission attempts
|
||||
public: RetransmissionAttempts mControllerTransmitFIFORetransmissionAttempts = UnlimitedNumber ;
|
||||
|
||||
//······················································································································
|
||||
// TXQ BUFFER
|
||||
//······················································································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// TXQ BUFFER
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
//--- TXQ buffer size (0 --> TXQ disabled)
|
||||
public: uint8_t mControllerTXQSize = 0 ; // 0 ... 32
|
||||
|
@ -191,9 +191,9 @@ class ACAN2517FDSettings {
|
|||
public: RetransmissionAttempts mControllerTXQBufferRetransmissionAttempts = UnlimitedNumber ;
|
||||
|
||||
|
||||
//······················································································································
|
||||
// RECEIVE FIFO
|
||||
//······················································································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// RECEIVE FIFO
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
//--- Driver receive buffer size
|
||||
public: uint16_t mDriverReceiveFIFOSize = 32 ; // > 0
|
||||
|
@ -204,15 +204,15 @@ class ACAN2517FDSettings {
|
|||
//--- Controller receive FIFO size
|
||||
public: uint8_t mControllerReceiveFIFOSize = 27 ; // 1 ... 32
|
||||
|
||||
//······················································································································
|
||||
// SYSCLOCK frequency computation
|
||||
//······················································································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// SYSCLOCK frequency computation
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: static uint32_t sysClock (const Oscillator inOscillator) ;
|
||||
|
||||
//······················································································································
|
||||
// Accessors
|
||||
//······················································································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Accessors
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: Oscillator oscillator (void) const { return mOscillator ; }
|
||||
public: uint32_t sysClock (void) const { return mSysClock ; }
|
||||
|
@ -222,61 +222,61 @@ class ACAN2517FDSettings {
|
|||
public: bool exactDataBitRate (void) const ;
|
||||
public: bool dataBitRateIsAMultipleOfArbitrationBitRate (void) const ;
|
||||
|
||||
//······················································································································
|
||||
// RAM USAGE
|
||||
//······················································································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// RAM USAGE
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: uint32_t ramUsage (void) const ;
|
||||
|
||||
public: static uint32_t objectSizeForPayload (const PayloadSize inPayload) ;
|
||||
|
||||
//······················································································································
|
||||
// Distance between actual bit rate and requested bit rate (in ppm, part-per-million)
|
||||
//······················································································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Distance between actual bit rate and requested bit rate (in ppm, part-per-million)
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: uint32_t ppmFromDesiredArbitrationBitRate (void) const ;
|
||||
public: uint32_t ppmFromDesiredDataBitRate (void) const ;
|
||||
|
||||
//······················································································································
|
||||
// Distance of sample point from bit start (in ppc, part-per-cent, denoted by %)
|
||||
//······················································································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Distance of sample point from bit start (in ppc, part-per-cent, denoted by %)
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: uint32_t arbitrationSamplePointFromBitStart (void) const ;
|
||||
public: uint32_t dataSamplePointFromBitStart (void) const ;
|
||||
|
||||
//······················································································································
|
||||
// Bit settings are consistent ? (returns 0 if ok)
|
||||
//······················································································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Bit settings are consistent ? (returns 0 if ok)
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: uint32_t CANBitSettingConsistency (void) const ;
|
||||
|
||||
//······················································································································
|
||||
// Constants returned by CANBitSettingConsistency
|
||||
//······················································································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Constants returned by CANBitSettingConsistency
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: static const uint32_t kBitRatePrescalerIsZero = ((uint32_t) 1) << 0 ;
|
||||
public: static const uint32_t kBitRatePrescalerIsGreaterThan256 = ((uint32_t) 1) << 1 ;
|
||||
public: static const uint32_t kArbitrationPhaseSegment1IsLowerThan2 = ((uint32_t) 1) << 2 ;
|
||||
public: static const uint32_t kArbitrationPhaseSegment1IsGreaterThan256 = ((uint32_t) 1) << 3 ;
|
||||
public: static const uint32_t kArbitrationPhaseSegment2IsZero = ((uint32_t) 1) << 4 ;
|
||||
public: static const uint32_t kArbitrationPhaseSegment2IsGreaterThan128 = ((uint32_t) 1) << 5 ;
|
||||
public: static const uint32_t kArbitrationSJWIsZero = ((uint32_t) 1) << 6 ;
|
||||
public: static const uint32_t kArbitrationSJWIsGreaterThan128 = ((uint32_t) 1) << 7 ;
|
||||
public: static const uint32_t kArbitrationSJWIsGreaterThanPhaseSegment1 = ((uint32_t) 1) << 8 ;
|
||||
public: static const uint32_t kArbitrationSJWIsGreaterThanPhaseSegment2 = ((uint32_t) 1) << 9 ;
|
||||
public: static const uint32_t kArbitrationTQCountNotDivisibleByDataBitRateFactor = ((uint32_t) 1) << 10 ;
|
||||
public: static const uint32_t kDataPhaseSegment1IsLowerThan2 = ((uint32_t) 1) << 11 ;
|
||||
public: static const uint32_t kDataPhaseSegment1IsGreaterThan32 = ((uint32_t) 1) << 12 ;
|
||||
public: static const uint32_t kDataPhaseSegment2IsZero = ((uint32_t) 1) << 13 ;
|
||||
public: static const uint32_t kDataPhaseSegment2IsGreaterThan16 = ((uint32_t) 1) << 14 ;
|
||||
public: static const uint32_t kDataSJWIsZero = ((uint32_t) 1) << 15 ;
|
||||
public: static const uint32_t kDataSJWIsGreaterThan16 = ((uint32_t) 1) << 16 ;
|
||||
public: static const uint32_t kDataSJWIsGreaterThanPhaseSegment1 = ((uint32_t) 1) << 17 ;
|
||||
public: static const uint32_t kDataSJWIsGreaterThanPhaseSegment2 = ((uint32_t) 1) << 18 ;
|
||||
public: static const uint32_t kBitRatePrescalerIsZero = uint32_t (1) << 0 ;
|
||||
public: static const uint32_t kBitRatePrescalerIsGreaterThan256 = uint32_t (1) << 1 ;
|
||||
public: static const uint32_t kArbitrationPhaseSegment1IsLowerThan2 = uint32_t (1) << 2 ;
|
||||
public: static const uint32_t kArbitrationPhaseSegment1IsGreaterThan256 = uint32_t (1) << 3 ;
|
||||
public: static const uint32_t kArbitrationPhaseSegment2IsZero = uint32_t (1) << 4 ;
|
||||
public: static const uint32_t kArbitrationPhaseSegment2IsGreaterThan128 = uint32_t (1) << 5 ;
|
||||
public: static const uint32_t kArbitrationSJWIsZero = uint32_t (1) << 6 ;
|
||||
public: static const uint32_t kArbitrationSJWIsGreaterThan128 = uint32_t (1) << 7 ;
|
||||
public: static const uint32_t kArbitrationSJWIsGreaterThanPhaseSegment1 = uint32_t (1) << 8 ;
|
||||
public: static const uint32_t kArbitrationSJWIsGreaterThanPhaseSegment2 = uint32_t (1) << 9 ;
|
||||
public: static const uint32_t kArbitrationTQCountNotDivisibleByDataBitRateFactor = uint32_t (1) << 10 ;
|
||||
public: static const uint32_t kDataPhaseSegment1IsLowerThan2 = uint32_t (1) << 11 ;
|
||||
public: static const uint32_t kDataPhaseSegment1IsGreaterThan32 = uint32_t (1) << 12 ;
|
||||
public: static const uint32_t kDataPhaseSegment2IsZero = uint32_t (1) << 13 ;
|
||||
public: static const uint32_t kDataPhaseSegment2IsGreaterThan16 = uint32_t (1) << 14 ;
|
||||
public: static const uint32_t kDataSJWIsZero = uint32_t (1) << 15 ;
|
||||
public: static const uint32_t kDataSJWIsGreaterThan16 = uint32_t (1) << 16 ;
|
||||
public: static const uint32_t kDataSJWIsGreaterThanPhaseSegment1 = uint32_t (1) << 17 ;
|
||||
public: static const uint32_t kDataSJWIsGreaterThanPhaseSegment2 = uint32_t (1) << 18 ;
|
||||
|
||||
//······················································································································
|
||||
// Max values
|
||||
//······················································································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Max values
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: static const uint16_t MAX_BRP = 256 ;
|
||||
|
||||
|
@ -288,9 +288,9 @@ class ACAN2517FDSettings {
|
|||
public: static const uint8_t MAX_DATA_PHASE_SEGMENT_2 = 16 ;
|
||||
public: static const uint8_t MAX_DATA_SJW = 16 ;
|
||||
|
||||
//······················································································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
} ;
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// A CAN driver for MCP2517FD CAN Controller in CANFD mode
|
||||
// by Pierre Molinaro
|
||||
// https://github.com/pierremolinaro/acan2517FD
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#ifndef ACANFD_BUFFER_CLASS_DEFINED
|
||||
#define ACANFD_BUFFER_CLASS_DEFINED
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#include "ACAN2517FD_CANFDMessage.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class ACANFDBuffer {
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Default constructor
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: ACANFDBuffer (void) :
|
||||
mBuffer (NULL),
|
||||
mSize (0),
|
||||
mReadIndex (0),
|
||||
mCount (0),
|
||||
mPeakCount (0) {
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Destructor
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: ~ ACANFDBuffer (void) {
|
||||
delete [] mBuffer ;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Private properties
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
private: CANFDMessage * mBuffer ;
|
||||
private: uint32_t mSize ;
|
||||
private: uint32_t mReadIndex ;
|
||||
private: uint32_t mCount ;
|
||||
private: uint32_t mPeakCount ; // > mSize if overflow did occur
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Accessors
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: inline uint32_t size (void) const { return mSize ; }
|
||||
public: inline uint32_t count (void) const { return mCount ; }
|
||||
public: inline bool isFull (void) const { return mCount == mSize ; } // Added in release 2.17 (thanks to Flole998)
|
||||
public: inline uint32_t peakCount (void) const { return mPeakCount ; }
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// initWithSize
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: void initWithSize (const uint32_t inSize) {
|
||||
delete [] mBuffer ; mBuffer = new CANFDMessage [inSize] ;
|
||||
mSize = inSize ;
|
||||
mReadIndex = 0 ;
|
||||
mCount = 0 ;
|
||||
mPeakCount = 0 ;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// append
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: bool append (const CANFDMessage & inMessage) {
|
||||
const bool ok = mCount < mSize ;
|
||||
if (ok) {
|
||||
uint32_t writeIndex = mReadIndex + mCount ;
|
||||
if (writeIndex >= mSize) {
|
||||
writeIndex -= mSize ;
|
||||
}
|
||||
mBuffer [writeIndex] = inMessage ;
|
||||
mCount += 1 ;
|
||||
if (mPeakCount < mCount) {
|
||||
mPeakCount = mCount ;
|
||||
}
|
||||
}else{
|
||||
mPeakCount = mSize + 1 ;
|
||||
}
|
||||
return ok ;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Remove
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: bool remove (CANFDMessage & outMessage) {
|
||||
const bool ok = mCount > 0 ;
|
||||
if (ok) {
|
||||
outMessage = mBuffer [mReadIndex] ;
|
||||
mCount -= 1 ;
|
||||
mReadIndex += 1 ;
|
||||
if (mReadIndex == mSize) {
|
||||
mReadIndex = 0 ;
|
||||
}
|
||||
}
|
||||
return ok ;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// No copy
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
private: ACANFDBuffer (const ACANFDBuffer &) = delete ;
|
||||
private: ACANFDBuffer & operator = (const ACANFDBuffer &) = delete ;
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
} ;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endif
|
|
@ -1,21 +1,21 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// Generic CANFD Message
|
||||
// by Pierre Molinaro
|
||||
//
|
||||
// https://github.com/pierremolinaro/acan2517FD
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#ifndef GENERIC_CANFD_MESSAGE_DEFINED
|
||||
#define GENERIC_CANFD_MESSAGE_DEFINED
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#include "CANMessage.h"
|
||||
#include "ACAN2517FD_CANMessage.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// CANFDMessage class
|
||||
//-----------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// Note that "len" field contains the actual length, not its encoding in CANFD frames
|
||||
// Valid values are: 0, 1, ..., 8, 12, 16, 20, 24, 32, 48, 64.
|
||||
// Having other values is an error that prevents frame to be sent by tryToSend
|
||||
|
@ -23,9 +23,9 @@
|
|||
|
||||
class CANFDMessage {
|
||||
|
||||
//·············································································
|
||||
// Constructors
|
||||
//·············································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Constructors
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public : CANFDMessage (void) :
|
||||
id (0), // Frame identifier
|
||||
|
@ -36,7 +36,7 @@ class CANFDMessage {
|
|||
data () {
|
||||
}
|
||||
|
||||
//·············································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public : CANFDMessage (const CANMessage & inMessage) :
|
||||
id (inMessage.id), // Frame identifier
|
||||
|
@ -48,9 +48,9 @@ class CANFDMessage {
|
|||
data64 [0] = inMessage.data64 ;
|
||||
}
|
||||
|
||||
//·············································································
|
||||
// Enumerated Type
|
||||
//·············································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Enumerated Type
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: typedef enum : uint8_t {
|
||||
CAN_REMOTE,
|
||||
|
@ -59,9 +59,9 @@ class CANFDMessage {
|
|||
CANFD_WITH_BIT_RATE_SWITCH
|
||||
} Type ;
|
||||
|
||||
//·············································································
|
||||
// Properties
|
||||
//·············································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Properties
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public : uint32_t id ; // Frame identifier
|
||||
public : bool ext ; // false -> base frame, true -> extended frame
|
||||
|
@ -80,9 +80,9 @@ class CANFDMessage {
|
|||
uint8_t data [64] ;
|
||||
} ;
|
||||
|
||||
//·············································································
|
||||
// Methods
|
||||
//·············································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Methods
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: void pad (void) {
|
||||
uint8_t paddedLength = len ;
|
||||
|
@ -107,7 +107,7 @@ class CANFDMessage {
|
|||
}
|
||||
}
|
||||
|
||||
//·············································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: bool isValid (void) const {
|
||||
if ((type == CAN_REMOTE) || (type == CAN_DATA)) { // Remote frame
|
||||
|
@ -121,14 +121,14 @@ class CANFDMessage {
|
|||
}
|
||||
}
|
||||
|
||||
//·············································································
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
} ;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
typedef void (*ACANFDCallBackRoutine) (const CANFDMessage & inMessage) ;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endif
|
|
@ -1,4 +1,4 @@
|
|||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// Generic CAN Message
|
||||
// by Pierre Molinaro
|
||||
//
|
||||
|
@ -8,16 +8,16 @@
|
|||
// https://github.com/pierremolinaro/acan2517
|
||||
// https://github.com/pierremolinaro/acan2517FD
|
||||
//
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#ifndef GENERIC_CAN_MESSAGE_DEFINED
|
||||
#define GENERIC_CAN_MESSAGE_DEFINED
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class CANMessage {
|
||||
public : uint32_t id = 0 ; // Frame identifier
|
||||
|
@ -38,12 +38,12 @@ class CANMessage {
|
|||
} ;
|
||||
} ;
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
typedef enum {kStandard, kExtended} tFrameFormat ;
|
||||
typedef enum {kData, kRemote} tFrameKind ;
|
||||
typedef void (*ACANCallBackRoutine) (const CANMessage & inMessage) ;
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endif
|
|
@ -1,4 +1,4 @@
|
|||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// A CANFD driver
|
||||
// by Pierre Molinaro
|
||||
|
||||
|
@ -6,16 +6,16 @@
|
|||
// https://github.com/pierremolinaro/ACAN_T4
|
||||
// https://github.com/pierremolinaro/ACAN2517FD
|
||||
//
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#ifndef ACANFD_DATA_BIT_RATE_FACTOR_DEFINED
|
||||
#define ACANFD_DATA_BIT_RATE_FACTOR_DEFINED
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
enum class DataBitRateFactor : uint8_t {
|
||||
x1 = 1,
|
||||
|
@ -30,6 +30,6 @@ enum class DataBitRateFactor : uint8_t {
|
|||
x10 = 10
|
||||
} ;
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endif
|
|
@ -1,119 +0,0 @@
|
|||
//----------------------------------------------------------------------------------------------------------------------
|
||||
// A CAN driver for MCP2517FD CAN Controller in CANFD mode
|
||||
// by Pierre Molinaro
|
||||
// https://github.com/pierremolinaro/acan2517FD
|
||||
//
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
#ifndef ACANFD_BUFFER_CLASS_DEFINED
|
||||
#define ACANFD_BUFFER_CLASS_DEFINED
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
#include "CANFDMessage.h"
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
class ACANFDBuffer {
|
||||
|
||||
//······················································································································
|
||||
// Default constructor
|
||||
//······················································································································
|
||||
|
||||
public: ACANFDBuffer (void) :
|
||||
mBuffer (NULL),
|
||||
mSize (0),
|
||||
mReadIndex (0),
|
||||
mCount (0),
|
||||
mPeakCount (0) {
|
||||
}
|
||||
|
||||
//······················································································································
|
||||
// Destructor
|
||||
//······················································································································
|
||||
|
||||
public: ~ ACANFDBuffer (void) {
|
||||
delete [] mBuffer ;
|
||||
}
|
||||
|
||||
//······················································································································
|
||||
// Private properties
|
||||
//······················································································································
|
||||
|
||||
private: CANFDMessage * mBuffer ;
|
||||
private: uint32_t mSize ;
|
||||
private: uint32_t mReadIndex ;
|
||||
private: uint32_t mCount ;
|
||||
private: uint32_t mPeakCount ; // > mSize if overflow did occur
|
||||
|
||||
//······················································································································
|
||||
// Accessors
|
||||
//······················································································································
|
||||
|
||||
public: inline uint32_t size (void) const { return mSize ; }
|
||||
public: inline uint32_t count (void) const { return mCount ; }
|
||||
public: inline bool isFull (void) const { return mCount == mSize ; } // Added in release 2.17 (thanks to Flole998)
|
||||
public: inline uint32_t peakCount (void) const { return mPeakCount ; }
|
||||
|
||||
//······················································································································
|
||||
// initWithSize
|
||||
//······················································································································
|
||||
|
||||
public: void initWithSize (const uint32_t inSize) {
|
||||
delete [] mBuffer ; mBuffer = new CANFDMessage [inSize] ;
|
||||
mSize = inSize ;
|
||||
mReadIndex = 0 ;
|
||||
mCount = 0 ;
|
||||
mPeakCount = 0 ;
|
||||
}
|
||||
|
||||
//······················································································································
|
||||
// append
|
||||
//······················································································································
|
||||
|
||||
public: bool append (const CANFDMessage & inMessage) {
|
||||
const bool ok = mCount < mSize ;
|
||||
if (ok) {
|
||||
uint32_t writeIndex = mReadIndex + mCount ;
|
||||
if (writeIndex >= mSize) {
|
||||
writeIndex -= mSize ;
|
||||
}
|
||||
mBuffer [writeIndex] = inMessage ;
|
||||
mCount += 1 ;
|
||||
if (mPeakCount < mCount) {
|
||||
mPeakCount = mCount ;
|
||||
}
|
||||
}else{
|
||||
mPeakCount = mSize + 1 ;
|
||||
}
|
||||
return ok ;
|
||||
}
|
||||
|
||||
//······················································································································
|
||||
// Remove
|
||||
//······················································································································
|
||||
|
||||
public: bool remove (CANFDMessage & outMessage) {
|
||||
const bool ok = mCount > 0 ;
|
||||
if (ok) {
|
||||
outMessage = mBuffer [mReadIndex] ;
|
||||
mCount -= 1 ;
|
||||
mReadIndex += 1 ;
|
||||
if (mReadIndex == mSize) {
|
||||
mReadIndex = 0 ;
|
||||
}
|
||||
}
|
||||
return ok ;
|
||||
}
|
||||
|
||||
//······················································································································
|
||||
// No copy
|
||||
//······················································································································
|
||||
|
||||
private: ACANFDBuffer (const ACANFDBuffer &) = delete ;
|
||||
private: ACANFDBuffer & operator = (const ACANFDBuffer &) = delete ;
|
||||
} ;
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
#endif
|
0
Software/src/lib/pierremolinaro-ACAN2517FD/README.md
Normal file → Executable file
0
Software/src/lib/pierremolinaro-ACAN2517FD/README.md
Normal file → Executable file
|
@ -0,0 +1,9 @@
|
|||
name=ACAN2517FD
|
||||
version=2.1.16
|
||||
author=Pierre Molinaro
|
||||
maintainer=Pierre Molinaro <Pierre.Molinaro@pcmolinaro.name>
|
||||
sentence=Driver for MCP2517FD and MCP2518FD CAN Controller (CAN FD mode)
|
||||
paragraph=This library is an Arduino CAN network driver for the MCP2517FD, the MCP2518FD and the MCP251863 CAN Controller, in CAN FD mode. Compatible with ACAN, ACAN2515, ACAN2517 libraries, with ACAN_T4 library from version 2.1.0. Default configuration sends and receives any frame – no default filter to provide. Reception filters (up to 32) can be easily defined. Compatible with ESP32 from version 1.1.0.
|
||||
category=Communication
|
||||
url=https://github.com/pierremolinaro/acan2517FD
|
||||
architectures=*
|
526
Software/src/lib/pierremolinaro-acan-esp32/ACAN_ESP32.cpp
Normal file
526
Software/src/lib/pierremolinaro-acan-esp32/ACAN_ESP32.cpp
Normal file
|
@ -0,0 +1,526 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// Include files
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#include "ACAN_ESP32.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//--- Header for periph_module_enable
|
||||
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
|
||||
#include <esp_private/periph_ctrl.h>
|
||||
#else
|
||||
#include <driver/periph_ctrl.h>
|
||||
#endif
|
||||
|
||||
#include <hal/clk_gate_ll.h> // For ESP32 board manager
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// ESP32 Critical Section
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// taskENTER_CRITICAL() of FREE-RTOS is deprecated as portENTER_CRITICAL() in ESP32
|
||||
//--- https://esp32.com/viewtopic.php?t=1703
|
||||
static portMUX_TYPE portMux = portMUX_INITIALIZER_UNLOCKED ;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// CONSTRUCTOR for ESP32C6 (2 TWAI controllers)
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32C6
|
||||
ACAN_ESP32::ACAN_ESP32 (const uint32_t inBaseAddress,
|
||||
const uint32_t inTxPinIndexSelector,
|
||||
const uint32_t inRxPinIndexSelector,
|
||||
const periph_module_t inPeriphModule,
|
||||
const periph_interrupt_t inInterruptSource,
|
||||
const uint32_t inClockEnableAddress) :
|
||||
twaiBaseAddress (inBaseAddress),
|
||||
twaiTxPinSelector (inTxPinIndexSelector),
|
||||
twaiRxPinSelector (inRxPinIndexSelector),
|
||||
twaiPeriphModule (inPeriphModule),
|
||||
twaiInterruptSource (inInterruptSource),
|
||||
twaiClockEnableAddress (inClockEnableAddress),
|
||||
mAcceptedFrameFormat (ACAN_ESP32_Filter::standardAndExtended),
|
||||
mDriverReceiveBuffer (),
|
||||
mDriverTransmitBuffer (),
|
||||
mDriverIsSending (false) {
|
||||
}
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// CONSTRUCTOR for others (1 TWAI controller)
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C6
|
||||
ACAN_ESP32::ACAN_ESP32 (void) :
|
||||
mAcceptedFrameFormat (ACAN_ESP32_Filter::standardAndExtended),
|
||||
mDriverReceiveBuffer (),
|
||||
mDriverTransmitBuffer (),
|
||||
mDriverIsSending (false) {
|
||||
}
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Set the GPIO pins
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void ACAN_ESP32::setGPIOPins (const gpio_num_t inTXPin,
|
||||
const gpio_num_t inRXPin) {
|
||||
//--- Set TX pin
|
||||
pinMode (inTXPin, OUTPUT) ;
|
||||
pinMatrixOutAttach (inTXPin, twaiTxPinSelector, false, false) ;
|
||||
//--- Set RX pin
|
||||
pinMode (inRXPin, INPUT) ;
|
||||
pinMatrixInAttach (inRXPin, twaiRxPinSelector, false) ;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Set the Requested Mode
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void ACAN_ESP32::setRequestedCANMode (const ACAN_ESP32_Settings & inSettings,
|
||||
const ACAN_ESP32_Filter & inFilter) {
|
||||
// ESP32 CAN Operation Mode Configuration
|
||||
//
|
||||
// Supported Mode MODE Registers
|
||||
// - Normal Mode - Reset -> bit(0)
|
||||
// - No ACK - ListenOnly -> bit(1)
|
||||
// - Acceptance Filter - SelfTest -> bit(2)
|
||||
// - Acceptance Filter -> bit(3)
|
||||
|
||||
uint8_t requestedMode = 0 ;
|
||||
switch (inSettings.mRequestedCANMode) {
|
||||
case ACAN_ESP32_Settings::NormalMode :
|
||||
break ;
|
||||
case ACAN_ESP32_Settings::ListenOnlyMode :
|
||||
requestedMode = TWAI_LISTEN_ONLY_MODE ;
|
||||
break ;
|
||||
case ACAN_ESP32_Settings::LoopBackMode :
|
||||
requestedMode = TWAI_SELF_TEST_MODE ;
|
||||
break ;
|
||||
}
|
||||
|
||||
if (inFilter.mAMFSingle) {
|
||||
requestedMode |= TWAI_RX_FILTER_MODE ;
|
||||
}
|
||||
|
||||
TWAI_MODE_REG () = requestedMode | TWAI_RESET_MODE ;
|
||||
uint32_t unusedResult __attribute__((unused)) = TWAI_MODE_REG () ;
|
||||
|
||||
do{
|
||||
TWAI_MODE_REG () = requestedMode ;
|
||||
}while ((TWAI_MODE_REG () & TWAI_RESET_MODE) != 0) ;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Set the Bus timing Registers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
inline void ACAN_ESP32::setBitTimingSettings (const ACAN_ESP32_Settings & inSettings) {
|
||||
// BUS TIMING Configuration of ESP32 CAN
|
||||
// ACAN_ESP32_Settings calculates the best values for the desired bit Rate.
|
||||
|
||||
//--- Caution! TWAI_BUS_TIMING_0_REG is specific
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32
|
||||
// BTR0 : bit (0 - 5) -> Baud Rate Prescaller (BRP)
|
||||
// bit (6 - 7) -> Resynchronization Jump Width (RJW)
|
||||
TWAI_BUS_TIMING_0_REG () =
|
||||
((inSettings.mRJW - 1) << 6) | // SJW
|
||||
((inSettings.mBitRatePrescaler - 1) << 0) // BRP
|
||||
;
|
||||
#elif defined (CONFIG_IDF_TARGET_ESP32S3)
|
||||
// BTR0 : bit (00 - 13) -> Baud Rate Prescaller (BRP)
|
||||
// bit (14 - 15) -> Resynchronization Jump Width (RJW)
|
||||
TWAI_BUS_TIMING_0_REG () =
|
||||
((inSettings.mRJW - 1) << 14) | // SJW
|
||||
((inSettings.mBitRatePrescaler - 1) << 0) // BRP
|
||||
;
|
||||
#elif defined (CONFIG_IDF_TARGET_ESP32S2)
|
||||
// BTR0 : bit (00 - 13) -> Baud Rate Prescaller (BRP)
|
||||
// bit (14 - 15) -> Resynchronization Jump Width (RJW)
|
||||
TWAI_BUS_TIMING_0_REG () =
|
||||
((inSettings.mRJW - 1) << 14) | // SJW
|
||||
((inSettings.mBitRatePrescaler - 1) << 0) // BRP
|
||||
;
|
||||
#elif defined (CONFIG_IDF_TARGET_ESP32C3)
|
||||
// BTR0 : bit (00 - 13) -> Baud Rate Prescaller (BRP)
|
||||
// bit (14 - 15) -> Resynchronization Jump Width (RJW)
|
||||
TWAI_BUS_TIMING_0_REG () =
|
||||
((inSettings.mRJW - 1) << 14) | // SJW
|
||||
((inSettings.mBitRatePrescaler - 1) << 0) // BRP
|
||||
;
|
||||
#elif defined (CONFIG_IDF_TARGET_ESP32C6)
|
||||
// BTR0 : bit (00 - 13) -> Baud Rate Prescaller (BRP)
|
||||
// bit (14 - 15) -> Resynchronization Jump Width (RJW)
|
||||
TWAI_BUS_TIMING_0_REG () =
|
||||
((inSettings.mRJW - 1) << 14) | // SJW
|
||||
((inSettings.mBitRatePrescaler - 1) << 0) // BRP
|
||||
;
|
||||
#else
|
||||
#error "Unknown board"
|
||||
#endif
|
||||
|
||||
//--- BTR1: bit (0 - 3) -> TimeSegment 1 (Tseg1)
|
||||
// bit (4 - 6) -> TimeSegment 2 (Tseg2)
|
||||
// bit (7) -> TripleSampling? (SAM)
|
||||
TWAI_BUS_TIMING_1_REG () =
|
||||
((inSettings.mTripleSampling) << 7) | // Sampling
|
||||
((inSettings.mTimeSegment2 - 1) << 4) | // Tseg2
|
||||
((inSettings.mTimeSegment1 - 1) << 0) // Tseg1
|
||||
;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void ACAN_ESP32::setAcceptanceFilter (const ACAN_ESP32_Filter & inFilter) {
|
||||
//--- Write the Code and Mask Registers with Acceptance Filter Settings
|
||||
if (inFilter.mAMFSingle) {
|
||||
TWAI_MODE_REG () |= TWAI_RX_FILTER_MODE ;
|
||||
}
|
||||
mAcceptedFrameFormat = inFilter.mFormat ;
|
||||
|
||||
TWAI_ACC_CODE_FILTER (0) = inFilter.mACR0 ;
|
||||
TWAI_ACC_CODE_FILTER (1) = inFilter.mACR1 ;
|
||||
TWAI_ACC_CODE_FILTER (2) = inFilter.mACR2 ;
|
||||
TWAI_ACC_CODE_FILTER (3) = inFilter.mACR3 ;
|
||||
|
||||
TWAI_ACC_MASK_FILTER (0) = inFilter.mAMR0 ;
|
||||
TWAI_ACC_MASK_FILTER (1) = inFilter.mAMR1 ;
|
||||
TWAI_ACC_MASK_FILTER (2) = inFilter.mAMR2 ;
|
||||
TWAI_ACC_MASK_FILTER (3) = inFilter.mAMR3 ;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// BEGIN
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
uint32_t ACAN_ESP32::begin (const ACAN_ESP32_Settings & inSettings,
|
||||
const ACAN_ESP32_Filter & inFilterSettings) {
|
||||
// Serial.println (twaiBaseAddress, HEX) ;
|
||||
uint32_t errorCode = 0 ; // Ok by default
|
||||
//--------------------------------- Enable CAN module clock (only for ESP32C6)
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32C6
|
||||
#define ACAN_CLOCK_ENABLE_REG (*((volatile uint32_t *) twaiClockEnableAddress))
|
||||
ACAN_CLOCK_ENABLE_REG = 1 << 22 ;
|
||||
//--- Display enable settings (for twai0)
|
||||
// #define ACAN_PCR_TWAI0_CONF_REG (*((volatile uint32_t *) (0x60096000 + 0x05C)))
|
||||
// #define ACAN_PCR_TWAI0_FUNC_CLK_CONF_REG (*((volatile uint32_t *) (0x60096000 + 0x060)))
|
||||
// Serial.print ("Reset reg (should be 1): 0x") ;
|
||||
// Serial.println (ACAN_PCR_TWAI0_CONF_REG, HEX) ;
|
||||
// Serial.print ("Clock select reg (should be 0x400000): 0x") ;
|
||||
// Serial.println (ACAN_PCR_TWAI0_FUNC_CLK_CONF_REG, HEX) ;
|
||||
#endif
|
||||
//--------------------------------- Enable CAN module
|
||||
periph_module_enable (twaiPeriphModule) ;
|
||||
//--------------------------------- Set GPIO pins
|
||||
setGPIOPins (inSettings.mTxPin, inSettings.mRxPin);
|
||||
//--------------------------------- Required: It is must to enter RESET Mode to write the Configuration Registers
|
||||
TWAI_CMD_REG () = TWAI_ABORT_TX ;
|
||||
TWAI_MODE_REG () = TWAI_RESET_MODE ;
|
||||
while ((TWAI_MODE_REG () & TWAI_RESET_MODE) == 0) {
|
||||
TWAI_MODE_REG () = TWAI_RESET_MODE ;
|
||||
}
|
||||
if ((TWAI_MODE_REG () & TWAI_RESET_MODE) == 0) {
|
||||
errorCode = kNotInResetModeInConfiguration ;
|
||||
}
|
||||
//--------------------------------- Disable Interupts
|
||||
TWAI_INT_ENA_REG () = 0 ;
|
||||
if (mInterruptHandler != nullptr) {
|
||||
esp_intr_free (mInterruptHandler) ;
|
||||
mInterruptHandler = nullptr ;
|
||||
}
|
||||
//--------------------------------- Use Pelican Mode
|
||||
TWAI_CLOCK_DIVIDER_REG () = TWAI_EXT_MODE ;
|
||||
//---- Check the Register access and bit timing settings before writing to the Bit Timing Registers
|
||||
TWAI_BUS_TIMING_0_REG () = 0x55 ;
|
||||
bool ok = TWAI_BUS_TIMING_0_REG () == 0x55 ;
|
||||
if (ok) {
|
||||
TWAI_BUS_TIMING_0_REG () = 0xAA ;
|
||||
ok = TWAI_BUS_TIMING_0_REG () == 0xAA ;
|
||||
}
|
||||
if (!ok) {
|
||||
errorCode |= kCANRegistersError ;
|
||||
}
|
||||
//----------------------------------- If ok, check the bit timing settings are correct
|
||||
if (!inSettings.mBitRateClosedToDesiredRate) {
|
||||
errorCode |= kTooFarFromDesiredBitRate;
|
||||
}
|
||||
errorCode |= inSettings.CANBitSettingConsistency ();
|
||||
//----------------------------------- Allocate buffer
|
||||
if (!mDriverReceiveBuffer.initWithSize (inSettings.mDriverReceiveBufferSize)) {
|
||||
errorCode |= kCannotAllocateDriverReceiveBuffer ;
|
||||
}
|
||||
if (!mDriverTransmitBuffer.initWithSize (inSettings.mDriverTransmitBufferSize)) {
|
||||
errorCode |= kCannotAllocateDriverTransmitBuffer ;
|
||||
}
|
||||
//--------------------------------- Set Bus timing Registers
|
||||
if (errorCode == 0) {
|
||||
setBitTimingSettings (inSettings) ;
|
||||
}
|
||||
//--------------------------------- Set the Acceptance Filter
|
||||
setAcceptanceFilter (inFilterSettings) ;
|
||||
//--------------------------------- Set and clear the error counters to default value
|
||||
TWAI_ERR_WARNING_LIMIT_REG () = 96 ;
|
||||
TWAI_RX_ERR_CNT_REG () = 0 ;
|
||||
//--------------------------------- Clear the Interrupt Registers
|
||||
const uint32_t unusedVariable __attribute__((unused)) = TWAI_INT_RAW_REG () ;
|
||||
//--------------------------------- Set Interrupt Service Routine
|
||||
esp_intr_alloc (twaiInterruptSource, 0, isr, this, & mInterruptHandler) ;
|
||||
//--------------------------------- Enable Interupts
|
||||
TWAI_INT_ENA_REG () = TWAI_TX_INT_ENA | TWAI_RX_INT_ENA ;
|
||||
//--------------------------------- Set to Requested Mode
|
||||
setRequestedCANMode (inSettings, inFilterSettings) ;
|
||||
//---
|
||||
return errorCode ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------
|
||||
// Stop CAN controller and uninstall ISR
|
||||
//----------------------------------------------------------------------------------------
|
||||
|
||||
void ACAN_ESP32::end (void) {
|
||||
//--------------------------------- Abort any pending transfer, don't care about resetting
|
||||
TWAI_CMD_REG () = TWAI_ABORT_TX ;
|
||||
|
||||
//--------------------------------- Disable Interupts
|
||||
TWAI_INT_ENA_REG () = 0 ;
|
||||
if (mInterruptHandler != nullptr) {
|
||||
esp_intr_free (mInterruptHandler) ;
|
||||
mInterruptHandler = nullptr ;
|
||||
}
|
||||
|
||||
//--------------------------------- Disable CAN module
|
||||
periph_module_disable (PERIPH_TWAI_MODULE) ;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//--- Status Flags (returns 0 if no error)
|
||||
// Bit 0 : hardware receive FIFO overflow
|
||||
// Bit 1 : driver receive FIFO overflow
|
||||
// Bit 2 : bus off
|
||||
// Bit 3 : reset mode
|
||||
|
||||
uint32_t ACAN_ESP32::statusFlags (void) const {
|
||||
uint32_t result = 0 ; // Ok
|
||||
const uint32_t status = TWAI_STATUS_REG () ;
|
||||
//--- Hardware receive FIFO overflow ?
|
||||
if ((status & TWAI_OVERRUN_ST) != 0) {
|
||||
result |= 1U << 0 ;
|
||||
}
|
||||
//--- Driver receive FIFO overflow ?
|
||||
if (mDriverReceiveBuffer.didOverflow ()) {
|
||||
result |= 1U << 1 ;
|
||||
}
|
||||
//--- Bus off ?
|
||||
if ((status & TWAI_BUS_OFF_ST) != 0) {
|
||||
result |= 1U << 2 ;
|
||||
}
|
||||
//--- Reset mode ?
|
||||
if ((TWAI_MODE_REG () & TWAI_RESET_MODE) != 0) {
|
||||
result |= 1U << 3 ;
|
||||
}
|
||||
//---
|
||||
return result ;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool ACAN_ESP32::recoverFromBusOff (void) const {
|
||||
const bool isBusOff = (TWAI_STATUS_REG () & TWAI_BUS_OFF_ST) != 0 ;
|
||||
const bool inResetMode = (TWAI_MODE_REG () & TWAI_RESET_MODE) != 0 ;
|
||||
const bool recover = isBusOff && inResetMode ;
|
||||
if (recover) {
|
||||
TWAI_MODE_REG () &= ~ TWAI_RESET_MODE ;
|
||||
}
|
||||
return recover ;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Interrupt Handler
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void IRAM_ATTR ACAN_ESP32::isr (void * inUserArgument) {
|
||||
ACAN_ESP32 * myDriver = (ACAN_ESP32 *) inUserArgument ;
|
||||
|
||||
portENTER_CRITICAL (&portMux) ;
|
||||
const uint32_t interrupt = myDriver->TWAI_INT_RAW_REG () ;
|
||||
if ((interrupt & TWAI_RX_INT_ST) != 0) {
|
||||
myDriver->handleRXInterrupt () ;
|
||||
}
|
||||
if ((interrupt & TWAI_TX_INT_ST) != 0) {
|
||||
myDriver->handleTXInterrupt () ;
|
||||
}
|
||||
portEXIT_CRITICAL (&portMux) ;
|
||||
|
||||
portYIELD_FROM_ISR () ;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void ACAN_ESP32::handleTXInterrupt (void) {
|
||||
CANMessage message ;
|
||||
const bool sendmsg = mDriverTransmitBuffer.remove (message) ;
|
||||
if (sendmsg) {
|
||||
internalSendMessage (message) ;
|
||||
}else {
|
||||
mDriverIsSending = false ;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void ACAN_ESP32::handleRXInterrupt (void) {
|
||||
CANMessage frame;
|
||||
getReceivedMessage (frame) ;
|
||||
switch (mAcceptedFrameFormat) {
|
||||
case ACAN_ESP32_Filter::standard :
|
||||
if (!frame.ext) {
|
||||
mDriverReceiveBuffer.append (frame) ;
|
||||
}
|
||||
break ;
|
||||
case ACAN_ESP32_Filter::extended :
|
||||
if (frame.ext) {
|
||||
mDriverReceiveBuffer.append (frame) ;
|
||||
}
|
||||
break ;
|
||||
case ACAN_ESP32_Filter::standardAndExtended :
|
||||
mDriverReceiveBuffer.append (frame) ;
|
||||
break ;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// RECEPTION
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool ACAN_ESP32::available (void) const {
|
||||
const bool hasReceivedMessage = mDriverReceiveBuffer.count () > 0 ;
|
||||
return hasReceivedMessage ;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool ACAN_ESP32::receive (CANMessage & outMessage) {
|
||||
portENTER_CRITICAL (&portMux) ;
|
||||
const bool hasReceivedMessage = mDriverReceiveBuffer.remove (outMessage) ;
|
||||
portEXIT_CRITICAL (&portMux) ;
|
||||
return hasReceivedMessage ;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void ACAN_ESP32::getReceivedMessage (CANMessage & outFrame) {
|
||||
const uint32_t frameInfo = TWAI_FRAME_INFO () ;
|
||||
|
||||
outFrame.len = frameInfo & 0xF;
|
||||
if (outFrame.len > 8) {
|
||||
outFrame.len = 8 ;
|
||||
}
|
||||
outFrame.rtr = (frameInfo & TWAI_RTR) != 0 ;
|
||||
outFrame.ext = (frameInfo & TWAI_FRAME_FORMAT_EFF) != 0 ;
|
||||
|
||||
if (!outFrame.ext) { //--- Standard Frame
|
||||
outFrame.id = uint32_t (TWAI_ID_SFF(0)) << 3 ;
|
||||
outFrame.id |= uint32_t (TWAI_ID_SFF(1)) >> 5 ;
|
||||
|
||||
for (uint8_t i=0 ; i<outFrame.len ; i++) {
|
||||
outFrame.data[i] = uint8_t (TWAI_DATA_SFF (i)) ;
|
||||
}
|
||||
}else{ //--- Extended Frame
|
||||
outFrame.id = uint32_t (TWAI_ID_EFF(0)) << 21 ;
|
||||
outFrame.id |= uint32_t (TWAI_ID_EFF(1)) << 13 ;
|
||||
outFrame.id |= uint32_t (TWAI_ID_EFF(2)) << 5 ;
|
||||
outFrame.id |= uint32_t (TWAI_ID_EFF(3)) >> 3 ;
|
||||
for (uint8_t i=0 ; i<outFrame.len ; i++) {
|
||||
outFrame.data [i] = uint8_t (TWAI_DATA_EFF (i)) ;
|
||||
}
|
||||
}
|
||||
|
||||
TWAI_CMD_REG () = TWAI_RELEASE_BUF ;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// TRANSMISSION
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool ACAN_ESP32::tryToSend (const CANMessage & inMessage) {
|
||||
bool sendMessage ;
|
||||
//--- Bug fixed in 1.0.2 (thanks to DirkMeintjies)
|
||||
portENTER_CRITICAL (&portMux) ;
|
||||
if (mDriverIsSending) {
|
||||
sendMessage = mDriverTransmitBuffer.append (inMessage);
|
||||
}else{
|
||||
internalSendMessage (inMessage) ;
|
||||
mDriverIsSending = true ;
|
||||
sendMessage = true ;
|
||||
}
|
||||
portEXIT_CRITICAL (&portMux) ;
|
||||
return sendMessage ;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void ACAN_ESP32::internalSendMessage (const CANMessage & inFrame) {
|
||||
//--- DLC
|
||||
const uint8_t dlc = (inFrame.len <= 8) ? inFrame.len : 8 ;
|
||||
//--- RTR
|
||||
const uint8_t rtr = inFrame.rtr ? TWAI_RTR : 0 ;
|
||||
//--- Format
|
||||
const uint8_t format = (inFrame.ext) ? TWAI_FRAME_FORMAT_EFF : TWAI_FRAME_FORMAT_SFF ;
|
||||
//--- Set Frame Information
|
||||
TWAI_FRAME_INFO () = format | rtr | dlc ;
|
||||
//--- Identifier and data
|
||||
if (!inFrame.ext) { //--- Standard Frame
|
||||
//--- Set ID
|
||||
TWAI_ID_SFF(0) = (inFrame.id >> 3) & 255 ;
|
||||
TWAI_ID_SFF(1) = (inFrame.id << 5) & 255 ;
|
||||
//--- Set data
|
||||
for (uint8_t i=0 ; i<dlc ; i++) {
|
||||
TWAI_DATA_SFF (i) = inFrame.data [i] ;
|
||||
}
|
||||
}else{ //--- Extended Frame
|
||||
//--- Set ID
|
||||
TWAI_ID_EFF(0) = (inFrame.id >> 21) & 255 ;
|
||||
TWAI_ID_EFF(1) = (inFrame.id >> 13) & 255 ;
|
||||
TWAI_ID_EFF(2) = (inFrame.id >> 5) & 255 ;
|
||||
TWAI_ID_EFF(3) = (inFrame.id << 3) & 255 ;
|
||||
//--- Set data
|
||||
for (uint8_t i=0 ; i<dlc ; i++) {
|
||||
TWAI_DATA_EFF (i) = inFrame.data [i] ;
|
||||
}
|
||||
}
|
||||
//--- Send command
|
||||
TWAI_CMD_REG () = ((TWAI_MODE_REG () & TWAI_SELF_TEST_MODE) != 0)
|
||||
? TWAI_SELF_RX_REQ
|
||||
: TWAI_TX_REQ
|
||||
;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Driver instances for ESP32C6
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32C6
|
||||
ACAN_ESP32 ACAN_ESP32::can (DR_REG_TWAI0_BASE,
|
||||
TWAI0_TX_IDX,
|
||||
TWAI0_RX_IDX,
|
||||
PERIPH_TWAI0_MODULE,
|
||||
ETS_TWAI0_INTR_SOURCE,
|
||||
PCR_TWAI0_FUNC_CLK_CONF_REG) ;
|
||||
ACAN_ESP32 ACAN_ESP32::can1 (DR_REG_TWAI1_BASE,
|
||||
TWAI1_TX_IDX,
|
||||
TWAI1_RX_IDX,
|
||||
PERIPH_TWAI1_MODULE,
|
||||
ETS_TWAI1_INTR_SOURCE,
|
||||
PCR_TWAI1_FUNC_CLK_CONF_REG) ;
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Driver instance for others
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C6
|
||||
ACAN_ESP32 ACAN_ESP32::can ;
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
375
Software/src/lib/pierremolinaro-acan-esp32/ACAN_ESP32.h
Normal file
375
Software/src/lib/pierremolinaro-acan-esp32/ACAN_ESP32.h
Normal file
|
@ -0,0 +1,375 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Include files
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#include "ACAN_ESP32_TWAI_base_address.h"
|
||||
#include "ACAN_ESP32_Settings.h"
|
||||
#include "ACAN_ESP32_CANMessage.h"
|
||||
#include "ACAN_ESP32_Buffer16.h"
|
||||
#include "ACAN_ESP32_AcceptanceFilters.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// ESP32 CAN class
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class ACAN_ESP32 {
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// CONSTRUCTOR for ESP32C6 (2 TWAI controllers)
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32C6
|
||||
private: const uint32_t twaiBaseAddress ;
|
||||
private: const uint32_t twaiTxPinSelector ;
|
||||
private: const uint32_t twaiRxPinSelector ;
|
||||
private: const periph_module_t twaiPeriphModule ;
|
||||
private: const periph_interrupt_t twaiInterruptSource ;
|
||||
private: const uint32_t twaiClockEnableAddress ;
|
||||
private: ACAN_ESP32 (const uint32_t inBaseAddress,
|
||||
const uint32_t inTxPinIndexSelector,
|
||||
const uint32_t inRxPinIndexSelector,
|
||||
const periph_module_t inPeriphModule,
|
||||
const periph_interrupt_t inInterruptSource,
|
||||
const uint32_t inClockEnableAddress) ;
|
||||
#endif
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// CONSTRUCTOR for others (1 TWAI controller)
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C6
|
||||
private: ACAN_ESP32 (void) ;
|
||||
#endif
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Initialisation: returns 0 if ok, otherwise see error codes below
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: uint32_t begin (const ACAN_ESP32_Settings & inSettings,
|
||||
const ACAN_ESP32_Filter & inFilterSettings = ACAN_ESP32_Filter::acceptAll ()) ;
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Deinit: Stop CAN controller and uninstall ISR
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
public: void end (void) ;
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// CAN Configuration Private Methods
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
private: void setGPIOPins (const gpio_num_t inTXPin, const gpio_num_t inRXPin);
|
||||
private: void setBitTimingSettings(const ACAN_ESP32_Settings &inSettings) ;
|
||||
private: void setRequestedCANMode (const ACAN_ESP32_Settings &inSettings,
|
||||
const ACAN_ESP32_Filter & inFilter) ;
|
||||
private: void setAcceptanceFilter (const ACAN_ESP32_Filter & inFilter) ;
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Receiving messages
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: bool available (void) const ;
|
||||
public: bool receive (CANMessage & outMessage) ;
|
||||
public: void getReceivedMessage (CANMessage & outFrame) ;
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Receive buffer
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
private: ACAN_ESP32_Filter::Format mAcceptedFrameFormat ;
|
||||
|
||||
private: ACAN_ESP32_Buffer16 mDriverReceiveBuffer ;
|
||||
|
||||
public: inline uint16_t driverReceiveBufferSize (void) const { return mDriverReceiveBuffer.size () ; }
|
||||
public: inline uint16_t driverReceiveBufferCount (void) const { return mDriverReceiveBuffer.count() ; }
|
||||
public: inline uint16_t driverReceiveBufferPeakCount (void) const { return mDriverReceiveBuffer.peakCount () ; }
|
||||
|
||||
public: inline void resetDriverReceiveBufferPeakCount (void) { mDriverReceiveBuffer.resetPeakCount () ; }
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Transmitting messages
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: bool tryToSend (const CANMessage & inMessage) ;
|
||||
private: void internalSendMessage (const CANMessage & inFrame) ;
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Transmit buffer
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
private: ACAN_ESP32_Buffer16 mDriverTransmitBuffer ;
|
||||
private: bool mDriverIsSending ;
|
||||
|
||||
public: inline uint16_t driverTransmitBufferSize (void) const { return mDriverTransmitBuffer.size () ; }
|
||||
public: inline uint16_t driverTransmitBufferCount (void) const { return mDriverTransmitBuffer.count () ; }
|
||||
public: inline uint16_t driverTransmitBufferPeakCount (void) const { return mDriverTransmitBuffer.peakCount () ; }
|
||||
|
||||
public: inline void resetDriverTransmitBufferPeakCount (void) { mDriverTransmitBuffer.resetPeakCount () ; }
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Error codes returned by begin
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: static const uint32_t kNotInResetModeInConfiguration = 1 << 16 ;
|
||||
public: static const uint32_t kCANRegistersError = 1 << 17 ;
|
||||
public: static const uint32_t kTooFarFromDesiredBitRate = 1 << 18 ;
|
||||
public: static const uint32_t kInconsistentBitRateSettings = 1 << 19 ;
|
||||
public: static const uint32_t kCannotAllocateDriverReceiveBuffer = 1 << 20 ;
|
||||
public: static const uint32_t kCannotAllocateDriverTransmitBuffer = 1 << 21 ;
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Interrupt Handler
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: static void IRAM_ATTR isr (void * inUserArgument) ;
|
||||
private: intr_handle_t mInterruptHandler ;
|
||||
|
||||
public: void handleTXInterrupt (void) ;
|
||||
public: void handleRXInterrupt (void) ;
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// STATUS FLAGS
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
//--- Status Flags (returns 0 if no error)
|
||||
// Bit 0 : hardware receive FIFO overflow
|
||||
// Bit 1 : driver receive FIFO overflow
|
||||
// Bit 2 : bus off
|
||||
// Bit 3 : reset mode
|
||||
|
||||
public: uint32_t statusFlags (void) const ;
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Recover from Bus-Off
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: bool recoverFromBusOff (void) const ;
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// No Copy
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
private: ACAN_ESP32 (const ACAN_ESP32 &) = delete ;
|
||||
private: ACAN_ESP32 & operator = (const ACAN_ESP32 &) = delete ;
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Driver instances
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: static ACAN_ESP32 can ;
|
||||
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32C6
|
||||
public: static ACAN_ESP32 can1 ;
|
||||
#endif
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Register Access
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: inline volatile uint32_t & TWAI_MODE_REG (void) const {
|
||||
return * ((volatile uint32_t *) (twaiBaseAddress + 0x000)) ;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: inline volatile uint32_t & TWAI_CMD_REG (void) const {
|
||||
return * ((volatile uint32_t *) (twaiBaseAddress + 0x004)) ;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: inline volatile uint32_t & TWAI_STATUS_REG (void) const {
|
||||
return * ((volatile uint32_t *) (twaiBaseAddress + 0x008)) ;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: inline volatile uint32_t & TWAI_INT_RAW_REG (void) const {
|
||||
return * ((volatile uint32_t *) (twaiBaseAddress + 0x00C)) ;
|
||||
}
|
||||
|
||||
public: inline volatile uint32_t & TWAI_INT_ENA_REG (void) const {
|
||||
return * ((volatile uint32_t *) (twaiBaseAddress + 0x010)) ;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: inline volatile uint32_t & TWAI_BUS_TIMING_0_REG (void) const {
|
||||
return * ((volatile uint32_t *) (twaiBaseAddress + 0x018)) ;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: inline volatile uint32_t & TWAI_BUS_TIMING_1_REG (void) const {
|
||||
return * ((volatile uint32_t *) (twaiBaseAddress + 0x01C)) ;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: inline volatile uint32_t & TWAI_ARB_LOST_CAP_REG (void) const {
|
||||
return * ((volatile uint32_t *) (twaiBaseAddress + 0x02C)) ;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: inline volatile uint32_t & TWAI_ERR_CODE_CAP_REG (void) const {
|
||||
return * ((volatile uint32_t *) (twaiBaseAddress + 0x030)) ;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: inline volatile uint32_t & TWAI_ERR_WARNING_LIMIT_REG (void) const {
|
||||
return * ((volatile uint32_t *) (twaiBaseAddress + 0x034)) ;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: inline volatile uint32_t & TWAI_RX_ERR_CNT_REG (void) const {
|
||||
return * ((volatile uint32_t *) (twaiBaseAddress + 0x038)) ;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: inline volatile uint32_t & TWAI_TX_ERR_CNT_REG (void) const {
|
||||
return * ((volatile uint32_t *) (twaiBaseAddress + 0x03C)) ;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: inline volatile uint32_t & TWAI_FRAME_INFO (void) const {
|
||||
return * ((volatile uint32_t *) (twaiBaseAddress + 0x040)) ;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
//----- SFF : Standard Frame Format - array size: 2
|
||||
public: inline volatile uint32_t & TWAI_ID_SFF (const uint32_t inIndex) const {
|
||||
return * ((volatile uint32_t *) (twaiBaseAddress + 0x044 + 4 * inIndex)) ;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
//----- EFF : Extended Frame Format - array size: 4
|
||||
public: inline volatile uint32_t & TWAI_ID_EFF (const uint32_t inIndex) const {
|
||||
return * ((volatile uint32_t *)(twaiBaseAddress + 0x044 + 4 * inIndex)) ;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
//----- DATA array size: 8
|
||||
public: inline volatile uint32_t & TWAI_DATA_SFF (const uint32_t inIndex) const {
|
||||
return * ((volatile uint32_t *) (twaiBaseAddress + 0x04C + 4 * inIndex)) ;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
//----- DATA array size: 8
|
||||
public: inline volatile uint32_t & TWAI_DATA_EFF (const uint32_t inIndex) const {
|
||||
return * ((volatile uint32_t *) (twaiBaseAddress + 0x054 + 4 * inIndex)) ;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// CAN Acceptance Filter Registers
|
||||
//----- CODE array size: 4
|
||||
public: inline volatile uint32_t & TWAI_ACC_CODE_FILTER (const uint32_t inIndex) const {
|
||||
return * ((volatile uint32_t *) (twaiBaseAddress + 0x040 + 4 * inIndex)) ;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
//----- MASK array size: 4
|
||||
public: inline volatile uint32_t & TWAI_ACC_MASK_FILTER (const uint32_t inIndex) const {
|
||||
return * ((volatile uint32_t *) (twaiBaseAddress + 0x050 + 4 * inIndex)) ;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: inline volatile uint32_t & TWAI_RX_MESSAGE_COUNTER_REG (void) const {
|
||||
return * ((volatile uint32_t *) (twaiBaseAddress + 0x074)) ;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: inline volatile uint32_t & TWAI_CLOCK_DIVIDER_REG (void) const {
|
||||
return * ((volatile uint32_t *) (twaiBaseAddress + 0x07C)) ;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
} ;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// TWAI_MODE_REG bit definitions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static const uint32_t TWAI_RESET_MODE = 0x01 ;
|
||||
static const uint32_t TWAI_LISTEN_ONLY_MODE = 0x02 ;
|
||||
static const uint32_t TWAI_SELF_TEST_MODE = 0x04 ;
|
||||
static const uint32_t TWAI_RX_FILTER_MODE = 0x08 ;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// TWAI_CMD bit definitions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static const uint32_t TWAI_TX_REQ = 0x01 ;
|
||||
static const uint32_t TWAI_ABORT_TX = 0x02 ;
|
||||
static const uint32_t TWAI_RELEASE_BUF = 0x04 ;
|
||||
static const uint32_t TWAI_CLR_OVERRUN = 0x08 ;
|
||||
static const uint32_t TWAI_SELF_RX_REQ = 0x10 ;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// TWAI_STATUS_REG bit definitions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static const uint32_t TWAI_RX_BUF_ST = 0x01 ;
|
||||
static const uint32_t TWAI_OVERRUN_ST = 0x02 ;
|
||||
static const uint32_t TWAI_TX_BUF_ST = 0x04 ;
|
||||
static const uint32_t TWAI_TX_COMPLETE = 0x08 ;
|
||||
static const uint32_t TWAI_RX_ST = 0x10 ;
|
||||
static const uint32_t TWAI_TX_ST = 0x20 ;
|
||||
static const uint32_t TWAI_ERR_ST = 0x40 ;
|
||||
static const uint32_t TWAI_BUS_OFF_ST = 0x80 ;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// TWAI_INT_RAW_REG bit definitins
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static const uint32_t TWAI_RX_INT_ST = 0x01;
|
||||
static const uint32_t TWAI_TX_INT_ST = 0x02;
|
||||
static const uint32_t TWAI_ERR_WARN_INT_ST = 0x04;
|
||||
static const uint32_t TWAI_OVERRUN_INT_ST = 0x08;
|
||||
static const uint32_t TWAI_ERR_PASSIVE_INT_ST = 0x20;
|
||||
static const uint32_t TWAI_ARB_LOST_INT_ST = 0x40;
|
||||
static const uint32_t TWAI_BUS_ERR_INT_ST = 0x80;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// TWAI_INT_ENA_REG bit definitions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static const uint32_t TWAI_RX_INT_ENA = 0x01 ;
|
||||
static const uint32_t TWAI_TX_INT_ENA = 0x02 ;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// TWAI_FRAME_INFO bit definitions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static const uint32_t TWAI_FRAME_FORMAT_SFF = 0x00 ;
|
||||
static const uint32_t TWAI_FRAME_FORMAT_EFF = 0x80 ;
|
||||
static const uint32_t TWAI_RTR = 0x40 ;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// CAN FRAME REGISTERS definitions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static const uint32_t TWAI_MSG_STD_ID = 0x7FF ;
|
||||
static const uint32_t TWAI_MSG_EXT_ID = 0x1FFFFFFF ;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// TWAI_CLOCK_DIVIDER_REG bit definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static const uint32_t TWAI_EXT_MODE = 0x80 ;
|
||||
|
||||
//------------------------------------------------------------------------------
|
|
@ -0,0 +1,214 @@
|
|||
#pragma once
|
||||
|
||||
//----------------------------------------------------------------------------------------
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
//----------------------------------------------------------------------------------------
|
||||
|
||||
class ACAN_ESP32_Filter {
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: typedef enum : uint8_t { standard, extended, standardAndExtended } Format ;
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: typedef enum : uint8_t { data, remote, dataAndRemote } Type ;
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// public properties
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: uint8_t mACR0 ;
|
||||
public: uint8_t mACR1 ;
|
||||
public: uint8_t mACR2 ;
|
||||
public: uint8_t mACR3 ;
|
||||
public: uint8_t mAMR0 ;
|
||||
public: uint8_t mAMR1 ;
|
||||
public: uint8_t mAMR2 ;
|
||||
public: uint8_t mAMR3 ;
|
||||
public: bool mAMFSingle ;
|
||||
public: Format mFormat ;
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Default private constructor
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
private: ACAN_ESP32_Filter (void) :
|
||||
mACR0 (0),
|
||||
mACR1 (0),
|
||||
mACR2 (0),
|
||||
mACR3 (0),
|
||||
mAMR0 (0xFF),
|
||||
mAMR1 (0xFF),
|
||||
mAMR2 (0xFF),
|
||||
mAMR3 (0xFF),
|
||||
mAMFSingle (false),
|
||||
mFormat (standardAndExtended) {
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Accept all filter
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: static inline ACAN_ESP32_Filter acceptAll (void) {
|
||||
return ACAN_ESP32_Filter () ;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Accept only standard frames
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: static inline ACAN_ESP32_Filter acceptStandardFrames (void) {
|
||||
ACAN_ESP32_Filter result ;
|
||||
result.mFormat = standard ;
|
||||
return result ;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Accept only extended frames
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: static inline ACAN_ESP32_Filter acceptExtendedFrames (void) {
|
||||
ACAN_ESP32_Filter result ;
|
||||
result.mFormat = extended ;
|
||||
return result ;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// singleStandardFilter: see SJA100 datasheet, figure 9 page 45 (and figure 10 page 46)
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: static inline ACAN_ESP32_Filter singleStandardFilter (const ACAN_ESP32_Filter::Type inType,
|
||||
const uint16_t inIdentifier,
|
||||
const uint16_t inDontCareMask) {
|
||||
ACAN_ESP32_Filter result ;
|
||||
result.mAMFSingle = true ; // Single Filter
|
||||
result.mFormat = standard ;
|
||||
result.mACR0 = uint8_t (inIdentifier >> 3) ;
|
||||
result.mACR1 = uint8_t (inIdentifier << 5) ;
|
||||
result.mAMR0 = uint8_t (inDontCareMask >> 3) ;
|
||||
result.mAMR1 = uint8_t (inDontCareMask << 5) | 0x0F ;
|
||||
switch (inType) {
|
||||
case data :
|
||||
break ;
|
||||
case remote :
|
||||
result.mACR1 |= 0x10 ;
|
||||
break ;
|
||||
case dataAndRemote :
|
||||
result.mAMR1 |= 0x10 ;
|
||||
break ;
|
||||
}
|
||||
return result ;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// singleExtendedFilter: see SJA100 datasheet, figure 10 page 46 (and figure 9 page 45)
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: static inline ACAN_ESP32_Filter singleExtendedFilter (const ACAN_ESP32_Filter::Type inType,
|
||||
const uint32_t inIdentifier,
|
||||
const uint32_t inDontCareMask) {
|
||||
ACAN_ESP32_Filter result ;
|
||||
result.mAMFSingle = true ; // Single Filter
|
||||
result.mFormat = extended ;
|
||||
result.mACR0 = uint8_t (inIdentifier >> 21) ;
|
||||
result.mACR1 = uint8_t (inIdentifier >> 13) ;
|
||||
result.mACR2 = uint8_t (inIdentifier >> 5) ;
|
||||
result.mACR3 = uint8_t (inIdentifier << 3) ;
|
||||
|
||||
result.mAMR0 = uint8_t (inDontCareMask >> 21) ;
|
||||
result.mAMR1 = uint8_t (inDontCareMask >> 13) ;
|
||||
result.mAMR2 = uint8_t (inDontCareMask >> 5) ;
|
||||
result.mAMR3 = uint8_t (inDontCareMask << 3) ;
|
||||
|
||||
switch (inType) {
|
||||
case data :
|
||||
break ;
|
||||
case remote :
|
||||
result.mACR3 |= 0x04 ;
|
||||
break ;
|
||||
case dataAndRemote :
|
||||
result.mAMR3 |= 0x04 ;
|
||||
break ;
|
||||
}
|
||||
return result ;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// dualStandardFilter: see SJA100 datasheet, figure 11 page 47 (and figure 12 page 48)
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: static inline ACAN_ESP32_Filter dualStandardFilter (const ACAN_ESP32_Filter::Type inType0,
|
||||
const uint16_t inIdentifier0,
|
||||
const uint16_t inDontCareMask0,
|
||||
const ACAN_ESP32_Filter::Type inType1,
|
||||
const uint16_t inIdentifier1,
|
||||
const uint16_t inDontCareMask1) {
|
||||
ACAN_ESP32_Filter result ;
|
||||
result.mAMFSingle = false ; // Dual Filter
|
||||
result.mFormat = standard ;
|
||||
|
||||
result.mACR0 = uint8_t (inIdentifier0 >> 3) ;
|
||||
result.mACR1 = uint8_t (inIdentifier0 << 5) ;
|
||||
result.mAMR0 = uint8_t (inDontCareMask0 >> 3) ;
|
||||
result.mAMR1 = uint8_t (inDontCareMask0 << 5) | 0x0F ;
|
||||
switch (inType0) {
|
||||
case data :
|
||||
break ;
|
||||
case remote :
|
||||
result.mACR1 |= 0x10 ;
|
||||
break ;
|
||||
case dataAndRemote :
|
||||
result.mAMR1 |= 0x10 ;
|
||||
break ;
|
||||
}
|
||||
|
||||
result.mACR2 = uint8_t (inIdentifier1 >> 3) ;
|
||||
result.mACR3 = uint8_t (inIdentifier1 << 5) ;
|
||||
result.mAMR2 = uint8_t (inDontCareMask1 >> 3) ;
|
||||
result.mAMR3 = uint8_t (inDontCareMask1 << 5) | 0x0F ;
|
||||
switch (inType1) {
|
||||
case data :
|
||||
break ;
|
||||
case remote :
|
||||
result.mACR3 |= 0x10 ;
|
||||
break ;
|
||||
case dataAndRemote :
|
||||
result.mAMR3 |= 0x10 ;
|
||||
break ;
|
||||
}
|
||||
return result ;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// dualExtendedFilter: see SJA100 datasheet, figure 12 page 48 (and figure 11 page 47)
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: static inline ACAN_ESP32_Filter dualExtendedFilter (const uint32_t inIdentifier0,
|
||||
const uint32_t inDontCareMask0,
|
||||
const uint32_t inIdentifier1,
|
||||
const uint32_t inDontCareMask1) {
|
||||
ACAN_ESP32_Filter result ;
|
||||
result.mAMFSingle = false ; // Dual Filter
|
||||
result.mFormat = extended ;
|
||||
|
||||
result.mACR0 = uint8_t (inIdentifier0 >> 21) ;
|
||||
result.mACR1 = uint8_t (inIdentifier0 >> 13) ;
|
||||
result.mAMR0 = uint8_t (inDontCareMask0 >> 21) ;
|
||||
result.mAMR1 = uint8_t (inDontCareMask0 >> 13) ;
|
||||
|
||||
result.mACR2 = uint8_t (inIdentifier1 >> 21) ;
|
||||
result.mACR3 = uint8_t (inIdentifier1 << 13) ;
|
||||
result.mAMR2 = uint8_t (inDontCareMask1 >> 21) ;
|
||||
result.mAMR3 = uint8_t (inDontCareMask1 << 13) ;
|
||||
|
||||
return result ;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
} ;
|
||||
|
||||
//----------------------------------------------------------------------------------------
|
135
Software/src/lib/pierremolinaro-acan-esp32/ACAN_ESP32_Buffer16.h
Normal file
135
Software/src/lib/pierremolinaro-acan-esp32/ACAN_ESP32_Buffer16.h
Normal file
|
@ -0,0 +1,135 @@
|
|||
//----------------------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
//----------------------------------------------------------------------------------------
|
||||
|
||||
#include "ACAN_ESP32_CANMessage.h"
|
||||
|
||||
//----------------------------------------------------------------------------------------
|
||||
|
||||
class ACAN_ESP32_Buffer16 {
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Default constructor
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: ACAN_ESP32_Buffer16 (void) :
|
||||
mBuffer (NULL),
|
||||
mSize (0),
|
||||
mReadIndex (0),
|
||||
mCount (0),
|
||||
mPeakCount (0) {
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Destructor
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: ~ ACAN_ESP32_Buffer16 (void) {
|
||||
delete [] mBuffer ;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Private properties
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
private: CANMessage * mBuffer ;
|
||||
private: uint16_t mSize ;
|
||||
private: uint16_t mReadIndex ;
|
||||
private: uint16_t mCount ;
|
||||
private: uint16_t mPeakCount ; // > mSize if overflow did occur
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Accessors
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: inline uint16_t size (void) const { return mSize ; }
|
||||
public: inline uint16_t count (void) const { return mCount ; }
|
||||
public: inline uint16_t peakCount (void) const { return mPeakCount ; }
|
||||
public: inline uint16_t didOverflow (void) const { return mPeakCount > mSize ; }
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// initWithSize
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: bool initWithSize (const uint16_t inSize) {
|
||||
delete [] mBuffer ;
|
||||
mBuffer = new CANMessage [inSize] ;
|
||||
const bool ok = mBuffer != NULL ;
|
||||
mSize = ok ? inSize : 0 ;
|
||||
mReadIndex = 0 ;
|
||||
mCount = 0 ;
|
||||
mPeakCount = 0 ;
|
||||
return ok ;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// append
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: bool append (const CANMessage & inMessage) {
|
||||
const bool ok = mCount < mSize ;
|
||||
if (ok) {
|
||||
uint16_t writeIndex = mReadIndex + mCount ;
|
||||
if (writeIndex >= mSize) {
|
||||
writeIndex -= mSize ;
|
||||
}
|
||||
mBuffer [writeIndex] = inMessage ;
|
||||
mCount += 1 ;
|
||||
if (mPeakCount < mCount) {
|
||||
mPeakCount = mCount ;
|
||||
}
|
||||
}else{
|
||||
mPeakCount = mSize + 1 ; // Overflow
|
||||
}
|
||||
return ok ;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Remove
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: bool remove (CANMessage & outMessage) {
|
||||
const bool ok = mCount > 0 ;
|
||||
if (ok) {
|
||||
outMessage = mBuffer [mReadIndex] ;
|
||||
mCount -= 1 ;
|
||||
mReadIndex += 1 ;
|
||||
if (mReadIndex == mSize) {
|
||||
mReadIndex = 0 ;
|
||||
}
|
||||
}
|
||||
return ok ;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Free
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: void free (void) {
|
||||
delete [] mBuffer ; mBuffer = nullptr ;
|
||||
mSize = 0 ;
|
||||
mReadIndex = 0 ;
|
||||
mCount = 0 ;
|
||||
mPeakCount = 0 ;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Reset Peak Count
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: inline void resetPeakCount (void) { mPeakCount = mCount ; }
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// No copy
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
private: ACAN_ESP32_Buffer16 (const ACAN_ESP32_Buffer16 &) = delete ;
|
||||
private: ACAN_ESP32_Buffer16 & operator = (const ACAN_ESP32_Buffer16 &) = delete ;
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
} ;
|
||||
|
||||
//----------------------------------------------------------------------------------------
|
|
@ -0,0 +1,49 @@
|
|||
//----------------------------------------------------------------------------------------
|
||||
// Generic CAN Message
|
||||
// by Pierre Molinaro
|
||||
//
|
||||
// This file is common to the following libraries
|
||||
// https://github.com/pierremolinaro/acan
|
||||
// https://github.com/pierremolinaro/acan2515
|
||||
// https://github.com/pierremolinaro/acan2517
|
||||
// https://github.com/pierremolinaro/acan2517FD
|
||||
//
|
||||
//----------------------------------------------------------------------------------------
|
||||
|
||||
#ifndef GENERIC_CAN_MESSAGE_DEFINED
|
||||
#define GENERIC_CAN_MESSAGE_DEFINED
|
||||
|
||||
//----------------------------------------------------------------------------------------
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
//----------------------------------------------------------------------------------------
|
||||
|
||||
class CANMessage {
|
||||
public : uint32_t id = 0 ; // Frame identifier
|
||||
public : bool ext = false ; // false -> standard frame, true -> extended frame
|
||||
public : bool rtr = false ; // false -> data frame, true -> remote frame
|
||||
public : uint8_t idx = 0 ; // This field is used by the driver
|
||||
public : uint8_t len = 0 ; // Length of data (0 ... 8)
|
||||
public : union {
|
||||
uint64_t data64 ; // Caution: subject to endianness
|
||||
int64_t data_s64 ; // Caution: subject to endianness
|
||||
uint32_t data32 [2] ; // Caution: subject to endianness
|
||||
int32_t data_s32 [2] ; // Caution: subject to endianness
|
||||
float dataFloat [2] ; // Caution: subject to endianness
|
||||
uint16_t data16 [4] ; // Caution: subject to endianness
|
||||
int16_t data_s16 [4] ; // Caution: subject to endianness
|
||||
int8_t data_s8 [8] ;
|
||||
uint8_t data [8] = {0, 0, 0, 0, 0, 0, 0, 0} ;
|
||||
} ;
|
||||
} ;
|
||||
|
||||
//----------------------------------------------------------------------------------------
|
||||
|
||||
typedef enum {kStandard, kExtended} tFrameFormat ;
|
||||
typedef enum {kData, kRemote} tFrameKind ;
|
||||
typedef void (*ACANCallBackRoutine) (const CANMessage & inMessage) ;
|
||||
|
||||
//----------------------------------------------------------------------------------------
|
||||
|
||||
#endif
|
|
@ -0,0 +1,128 @@
|
|||
//----------------------------------------------------------------------------------------
|
||||
// Include files
|
||||
//----------------------------------------------------------------------------------------
|
||||
|
||||
#include "ACAN_ESP32_Settings.h"
|
||||
|
||||
//----------------------------------------------------------------------------------------
|
||||
// CAN Settings
|
||||
//----------------------------------------------------------------------------------------
|
||||
|
||||
ACAN_ESP32_Settings::ACAN_ESP32_Settings (const uint32_t inDesiredBitRate,
|
||||
const uint32_t inTolerancePPM) :
|
||||
mDesiredBitRate (inDesiredBitRate) {
|
||||
uint32_t TQCount = MAX_TQ ; // TQ: min(3) max(25)
|
||||
uint32_t bestBRP = MAX_BRP ; // Setting for slowest bit rate
|
||||
uint32_t bestTQCount = MAX_TQ ; // Setting for slowest bit rate
|
||||
uint32_t smallestError = UINT32_MAX ;
|
||||
const uint32_t CANClock = CAN_CLOCK () ;
|
||||
uint32_t BRP = CANClock / (inDesiredBitRate * TQCount) ; // BRP: min(2) max(128)
|
||||
//--- Loop for finding best BRP and best TQCount
|
||||
while ((TQCount >= MIN_TQ) && (BRP <= MAX_BRP)) {
|
||||
//--- Compute error using BRP (caution: BRP should be > 0)
|
||||
if (BRP >= MIN_BRP) {
|
||||
const uint32_t error = CANClock - (inDesiredBitRate * TQCount * BRP) ; // error is always >= 0
|
||||
if (error < smallestError) {
|
||||
smallestError = error ;
|
||||
bestBRP = BRP ;
|
||||
bestTQCount = TQCount ;
|
||||
}
|
||||
}
|
||||
//--- Compute error using BRP+1 (caution: BRP+1 should be <= 128)
|
||||
if (BRP < MAX_BRP) {
|
||||
const uint32_t error = (inDesiredBitRate * TQCount * (BRP + 1)) - CANClock ; // error is always >= 0
|
||||
if (error < smallestError) {
|
||||
smallestError = error ;
|
||||
bestBRP = BRP + 1 ;
|
||||
bestTQCount = TQCount ;
|
||||
}
|
||||
}
|
||||
//--- Continue with next value of TQCount
|
||||
TQCount -= 1 ;
|
||||
BRP = CANClock / (inDesiredBitRate * TQCount) ;
|
||||
}
|
||||
//--- Set the BRP
|
||||
mBitRatePrescaler = uint8_t (bestBRP) ;
|
||||
//--- Compute PS2 (2 <= TSeg2 <= 8)
|
||||
const uint32_t PS2 = 2 + 3 * (bestTQCount - 5) / 10 ;
|
||||
mTimeSegment2 = uint8_t (PS2) ;
|
||||
//--- Compute PS1 (1 <= PS1 <= 16)
|
||||
const uint32_t PS1 = bestTQCount - PS2 - SYNC_SEGMENT ;
|
||||
mTimeSegment1 = uint8_t (PS1) ;
|
||||
//--- SJW (1...4) min of Tseg2
|
||||
mRJW = (mTimeSegment2 > 4) ? 4 : mTimeSegment2 ;
|
||||
//--- Triple sampling ?
|
||||
mTripleSampling = (inDesiredBitRate <= 125000) && (mTimeSegment2 > 2) ;
|
||||
//--- Final check of the configuration
|
||||
const uint32_t W = bestTQCount * mDesiredBitRate * mBitRatePrescaler ;
|
||||
const uint64_t diff = (CANClock > W) ? (CANClock - W) : (W - CANClock) ;
|
||||
const uint64_t ppm = uint64_t (1000UL * 1000UL) ;
|
||||
mBitRateClosedToDesiredRate = (diff * ppm) <= (uint64_t (W) * inTolerancePPM) ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------
|
||||
|
||||
uint32_t ACAN_ESP32_Settings::actualBitRate (void) const {
|
||||
const uint32_t TQCount = SYNC_SEGMENT + mTimeSegment1 + mTimeSegment2 ;
|
||||
return CAN_CLOCK () / mBitRatePrescaler / TQCount ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------
|
||||
|
||||
bool ACAN_ESP32_Settings::exactBitRate (void) const {
|
||||
const uint32_t TQCount = SYNC_SEGMENT + mTimeSegment1 + mTimeSegment2 ;
|
||||
return CAN_CLOCK () == (mDesiredBitRate * mBitRatePrescaler * TQCount) ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------
|
||||
|
||||
uint32_t ACAN_ESP32_Settings::ppmFromDesiredBitRate(void) const {
|
||||
const uint32_t TQCount = SYNC_SEGMENT + mTimeSegment1 + mTimeSegment2 ;
|
||||
const uint32_t W = TQCount * mDesiredBitRate * mBitRatePrescaler ;
|
||||
const uint32_t CANClock = CAN_CLOCK () ;
|
||||
const uint64_t diff = (CANClock > W) ? (CANClock - W) : (W - CANClock) ;
|
||||
const uint64_t ppm = (uint64_t)(1000UL * 1000UL) ; // UL suffix is required for Arduino Uno
|
||||
return uint32_t ((diff * ppm) / W) ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------
|
||||
|
||||
uint32_t ACAN_ESP32_Settings::samplePointFromBitStart (void) const {
|
||||
const uint32_t TQCount = SYNC_SEGMENT + mTimeSegment1 + mTimeSegment2 ;
|
||||
const uint32_t samplePoint = SYNC_SEGMENT + mTimeSegment1 - mTripleSampling ;
|
||||
const uint32_t partPerCent = 100 ;
|
||||
return (samplePoint * partPerCent) / TQCount ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------
|
||||
|
||||
uint16_t ACAN_ESP32_Settings::CANBitSettingConsistency (void) const {
|
||||
uint16_t errorCode = 0 ; // No error
|
||||
if (mBitRatePrescaler < MIN_BRP) {
|
||||
errorCode |= kBitRatePrescalerIsZero ;
|
||||
}else if (mBitRatePrescaler > MAX_BRP) {
|
||||
errorCode |= kBitRatePrescalerIsGreaterThan64 ;
|
||||
}
|
||||
if (mTimeSegment1 == 0) {
|
||||
errorCode |= kTimeSegment1IsZero ;
|
||||
}else if ((mTimeSegment2 == 2) && mTripleSampling) {
|
||||
errorCode |= kTimeSegment2Is2AndTripleSampling ;
|
||||
}else if (mTimeSegment1 > MAX_TIME_SEGMENT_1) {
|
||||
errorCode |= kTimeSegment1IsGreaterThan16 ;
|
||||
}
|
||||
if (mTimeSegment2 < 2) {
|
||||
errorCode |= kTimeSegment2IsLowerThan2 ;
|
||||
}else if (mTimeSegment2 > MAX_TIME_SEGMENT_2) {
|
||||
errorCode |= kTimeSegment2IsGreaterThan8 ;
|
||||
}
|
||||
if (mRJW == 0) {
|
||||
errorCode |= kRJWIsZero ;
|
||||
}else if (mRJW > mTimeSegment2) {
|
||||
errorCode |= kRJWIsGreaterThanTimeSegment2 ;
|
||||
}else if (mRJW > MAX_SJW) {
|
||||
errorCode |= kRJWIsGreaterThan4 ;
|
||||
}
|
||||
return errorCode ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------
|
155
Software/src/lib/pierremolinaro-acan-esp32/ACAN_ESP32_Settings.h
Normal file
155
Software/src/lib/pierremolinaro-acan-esp32/ACAN_ESP32_Settings.h
Normal file
|
@ -0,0 +1,155 @@
|
|||
//----------------------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
//----------------------------------------------------------------------------------------
|
||||
// Include files
|
||||
//----------------------------------------------------------------------------------------
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
//--- For getting getApbFrequency function declaration
|
||||
#ifdef ARDUINO
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------------------------------
|
||||
// CAN CLOCK
|
||||
//----------------------------------------------------------------------------------------
|
||||
|
||||
#ifdef ARDUINO
|
||||
inline uint32_t CAN_CLOCK (void) { return getApbFrequency () / 2 ; }// APB_CLK_FREQ: 80 MHz APB CLOCK
|
||||
#else
|
||||
inline uint32_t CAN_CLOCK (void) { return 40 * 1000 * 1000 ; } // 40 MHz
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------------------------------
|
||||
// ESP32 ACANSettings class
|
||||
//----------------------------------------------------------------------------------------
|
||||
|
||||
class ACAN_ESP32_Settings {
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// ENUMERATED TYPE
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
//--- CAN driver operating modes
|
||||
public: typedef enum
|
||||
#ifdef ARDUINO
|
||||
: uint8_t
|
||||
#endif
|
||||
{
|
||||
NormalMode,
|
||||
ListenOnlyMode,
|
||||
LoopBackMode
|
||||
} CANMode ;
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// CONSTRUCTOR
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: ACAN_ESP32_Settings (const uint32_t inDesiredBitRate,
|
||||
const uint32_t inTolerancePPM = 1000) ;
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// CAN PINS
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
#ifdef ARDUINO
|
||||
public: gpio_num_t mTxPin = GPIO_NUM_5 ;
|
||||
public: gpio_num_t mRxPin = GPIO_NUM_4 ;
|
||||
#endif
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// CAN BIT TIMING
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: uint32_t mDesiredBitRate ; // In kb/s
|
||||
public: uint8_t mBitRatePrescaler = 0 ; // 1...64
|
||||
public: uint8_t mTimeSegment1 = 0 ; // 1...16
|
||||
public: uint8_t mTimeSegment2 = 0 ; // 2...8
|
||||
public: uint8_t mRJW = 0 ; // 1...4
|
||||
public: bool mTripleSampling = false ; // true --> triple sampling, false --> single sampling
|
||||
public: bool mBitRateClosedToDesiredRate = false ; // The above configuration is correct
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Max values
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: static const uint8_t SYNC_SEGMENT = 1 ; // Fixed Sync Segment
|
||||
public: static const uint32_t MAX_BRP = 64 ;
|
||||
public: static const uint32_t MIN_BRP = 1 ;
|
||||
public: static const uint32_t MAX_TQ = 25 ;
|
||||
public: static const uint32_t MIN_TQ = 5 ;
|
||||
public: static const uint8_t MAX_TIME_SEGMENT_1 = 16 ;
|
||||
public: static const uint8_t MAX_TIME_SEGMENT_2 = 8 ;
|
||||
public: static const uint8_t MAX_SJW = 4 ;
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Requested mode
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: CANMode mRequestedCANMode = NormalMode ;
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Receive buffer size
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: uint16_t mDriverReceiveBufferSize = 32 ;
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Transmit buffer sizes
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: uint16_t mDriverTransmitBufferSize = 16 ;
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Compute actual bit rate
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: uint32_t actualBitRate (void) const ;
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Exact bit rate ?
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: bool exactBitRate (void) const ;
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Distance between actual bit rate and requested bit rate (in ppm, part-per-million)
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: uint32_t ppmFromDesiredBitRate (void) const;
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Distance of sample point from bit start (in ppc, part-per-cent, denoted by %)
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: uint32_t samplePointFromBitStart (void) const;
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Bit settings are consistent ? (returns 0 if ok)
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: uint16_t CANBitSettingConsistency (void) const ;
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Constants returned by CANBitSettingConsistency
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public: static const uint16_t kBitRatePrescalerIsZero = 1 << 0 ;
|
||||
public: static const uint16_t kBitRatePrescalerIsGreaterThan64 = 1 << 1 ;
|
||||
public: static const uint16_t kTimeSegment1IsZero = 1 << 2 ;
|
||||
public: static const uint16_t kTimeSegment1IsGreaterThan16 = 1 << 3 ;
|
||||
public: static const uint16_t kTimeSegment2IsLowerThan2 = 1 << 4 ;
|
||||
public: static const uint16_t kTimeSegment2IsGreaterThan8 = 1 << 5 ;
|
||||
public: static const uint16_t kTimeSegment2Is2AndTripleSampling = 1 << 6 ;
|
||||
public: static const uint16_t kRJWIsZero = 1 << 7 ;
|
||||
public: static const uint16_t kRJWIsGreaterThan4 = 1 << 8 ;
|
||||
public: static const uint16_t kRJWIsGreaterThanTimeSegment2 = 1 << 9 ;
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
} ;
|
||||
|
||||
//----------------------------------------------------------------------------------------
|
|
@ -0,0 +1,85 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// ESP32 TWAI REGISTER BASE
|
||||
// See sdkconfig.h files for defining CONFIG_IDF_TARGET_xxxx
|
||||
// DR_REG_TWAI_BASE is defined in:
|
||||
// - ~/Library/Arduino15/packages/esp32/hardware/esp32/2.0.11/tools/sdk/esp32c3/include/soc/esp32c3/include/soc/soc.h
|
||||
// - ~//Library/Arduino15/packages/esp32/hardware/esp32/2.0.11/tools/sdk/esp32s3/include/soc/esp32s3/include/soc/soc.h
|
||||
// DR_REG_CAN_BASE is defined in:
|
||||
// - ~//Library/Arduino15/packages/esp32/hardware/esp32/2.0.11/tools/sdk/esp32/include/soc/esp32/include/soc/soc.h
|
||||
// No definition for ESP32-S2 in ~/Library/Arduino15/packages/esp32/hardware/esp32/2.0.11/tools/sdk/esp32s2/include/soc/esp32s2/include/soc/soc.h
|
||||
// However the ~/Library/Arduino15/packages/esp32/hardware/esp32/2.0.11/tools/ide-debug/svd/esp32s2.svd file defines
|
||||
// the TWAI base address: 0x3F42B000
|
||||
// But ESP32S2 reference manual (§3.3.5) gives two addresses:
|
||||
// - 0x3F40_0000 + 0x0002_B000 = 0x3F42_B000 from PeriBus1 (faster)
|
||||
// - 0x6000_0000 + 0x0002_B000 = 0x6002_B000 from PeriBus2
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Include files
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/queue.h>
|
||||
#include <esp_intr_alloc.h>
|
||||
#include <soc/gpio_sig_map.h>
|
||||
#include <soc/periph_defs.h>
|
||||
#include <soc/interrupts.h>
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// In ESP32 2.x board managers, ETS_TWAI_INTR_SOURCE is an int constant.
|
||||
//
|
||||
// In ESP32 3.0.0 board manager, ETS_TWAI_INTR_SOURCE is an periph_interrput_t constant.
|
||||
//
|
||||
// In ESP32 3.3.0-alpha1 board manager, periph_interrput_t has been deprecated
|
||||
// in favor of periph_interrupt_t.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// esp32/hardware/esp32/3.3.0-alpha1/cores/esp32/esp_arduino_version.h
|
||||
#include <esp_arduino_version.h>
|
||||
|
||||
#if ESP_ARDUINO_VERSION < ESP_ARDUINO_VERSION_VAL(3, 0, 0)
|
||||
typedef int periph_interrupt_t ;
|
||||
#elif ESP_ARDUINO_VERSION < ESP_ARDUINO_VERSION_VAL(3, 3, 0)
|
||||
typedef periph_interrput_t periph_interrupt_t ;
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined (CONFIG_IDF_TARGET_ESP32S3)
|
||||
static const uint32_t twaiBaseAddress = DR_REG_TWAI_BASE ; // 0x6002B000
|
||||
static const uint32_t twaiTxPinSelector = TWAI_TX_IDX ;
|
||||
static const uint32_t twaiRxPinSelector = TWAI_RX_IDX ;
|
||||
static const periph_module_t twaiPeriphModule = PERIPH_TWAI_MODULE ;
|
||||
static const periph_interrupt_t twaiInterruptSource = ETS_TWAI_INTR_SOURCE ;
|
||||
#elif defined (CONFIG_IDF_TARGET_ESP32S2)
|
||||
static const uint32_t twaiBaseAddress = 0x3F42B000 ;
|
||||
static const uint32_t twaiTxPinSelector = TWAI_TX_IDX ;
|
||||
static const uint32_t twaiRxPinSelector = TWAI_RX_IDX ;
|
||||
static const periph_module_t twaiPeriphModule = PERIPH_TWAI_MODULE ;
|
||||
static const periph_interrupt_t twaiInterruptSource = ETS_TWAI_INTR_SOURCE ;
|
||||
#elif defined (CONFIG_IDF_TARGET_ESP32C3)
|
||||
static const uint32_t twaiBaseAddress = DR_REG_TWAI_BASE ; // 0x6002B000
|
||||
static const uint32_t twaiTxPinSelector = TWAI_TX_IDX ;
|
||||
static const uint32_t twaiRxPinSelector = TWAI_RX_IDX ;
|
||||
static const periph_module_t twaiPeriphModule = PERIPH_TWAI_MODULE ;
|
||||
static const periph_interrupt_t twaiInterruptSource = ETS_TWAI_INTR_SOURCE ;
|
||||
#elif defined (CONFIG_IDF_TARGET_ESP32C6)
|
||||
// twaiBaseAddress, twaiTxPinSelector, twaiRxPinSelector, twaiPeriphModule
|
||||
// and twaiInterruptSource are defined as instance properties
|
||||
// of ACAN_ESP32 class
|
||||
#elif defined (CONFIG_IDF_TARGET_ESP32)
|
||||
static const uint32_t twaiBaseAddress = DR_REG_CAN_BASE ; // 0x3ff6B000
|
||||
static const uint32_t twaiTxPinSelector = TWAI_TX_IDX ;
|
||||
static const uint32_t twaiRxPinSelector = TWAI_RX_IDX ;
|
||||
static const periph_module_t twaiPeriphModule = PERIPH_TWAI_MODULE ;
|
||||
static const periph_interrupt_t twaiInterruptSource = ETS_TWAI_INTR_SOURCE ;
|
||||
#else
|
||||
#error "ESP32 TWAI (CAN) module not handled for this platform"
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
Loading…
Add table
Add a link
Reference in a new issue