Merge branch 'main' into bugfix/ioniq28-PID

This commit is contained in:
Daniel Öster 2025-08-31 17:19:06 +03:00
commit d750201795
96 changed files with 3069 additions and 2501 deletions

View file

@ -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)

View file

@ -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 weve 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;

View file

@ -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;

View file

@ -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.28Requirements 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;
}

View file

@ -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();

View file

@ -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

View file

@ -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,

View file

@ -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]) + " &deg;C</h4>";
content +=
"<h4>Module 6 temperature: " + String(datalayer_extended.geometryC.ModuleTemperatures[5]) + " &deg;C</h4>";
return content;
}
};

View file

@ -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; }

View file

@ -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) {

View file

@ -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) {

View file

@ -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

View file

@ -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;

View file

@ -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;

View file

@ -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 {

View file

@ -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) {

View file

@ -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:

View file

@ -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;

View file

@ -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) {

View file

@ -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) {

View file

@ -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
}
}
}

View file

@ -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
}

View file

@ -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;
}

View file

@ -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);

View file

@ -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) {

View file

@ -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);

View file

@ -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

View file

@ -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();

View file

@ -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"

View file

@ -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;

View file

@ -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 */

View file

@ -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."

View file

@ -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; }

View 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

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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) {

View file

@ -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);

View file

@ -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
}

View file

@ -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__

View file

@ -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

View file

@ -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>");

View file

@ -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% />

View file

@ -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);
}

View file

@ -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);

View file

@ -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;
}
}

View file

@ -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;

View file

@ -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;

View file

@ -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;
}

View file

@ -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);

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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
}
}

View file

@ -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;
}

View file

@ -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

View file

@ -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__ */

View file

@ -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;

View file

@ -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

View file

@ -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_ */

View file

@ -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) ;
}
//----------------------------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------

View file

@ -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 ;
//······················································································································
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
} ;
//----------------------------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------

View file

@ -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

View file

@ -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] ;
}
//----------------------------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------

View file

@ -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 ;
//······················································································································
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
} ;
//----------------------------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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
View file

View 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=*

View 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
//------------------------------------------------------------------------------

View 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 ;
//------------------------------------------------------------------------------

View file

@ -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 ;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
} ;
//----------------------------------------------------------------------------------------

View 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 ;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
} ;
//----------------------------------------------------------------------------------------

View file

@ -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

View file

@ -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 ;
}
//----------------------------------------------------------------------------------------

View 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 ;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
} ;
//----------------------------------------------------------------------------------------

View file

@ -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
//------------------------------------------------------------------------------