Feature: CAN-FD + EGMP batteries! (#244)

* ACAN2517FD lib addition, integrate support for canfd, and add initial EGMP battery capability
This commit is contained in:
Daniel Öster 2024-04-08 20:45:14 +03:00 committed by GitHub
parent c5118a8a09
commit 63371c8b60
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 3626 additions and 6 deletions

View file

@ -46,6 +46,10 @@ static const uint32_t QUARTZ_FREQUENCY = 8UL * 1000UL * 1000UL; // 8 MHz
ACAN2515 can(MCP2515_CS, SPI, MCP2515_INT);
static ACAN2515_Buffer16 gBuffer;
#endif
#ifdef CAN_FD
#include "src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD.h"
ACAN2517FD canfd(MCP2517_CS, SPI, MCP2517_INT);
#endif
// ModbusRTU parameters
#if defined(BYD_MODBUS) || defined(LUNA2000_MODBUS)
@ -179,8 +183,11 @@ void mainLoop(void* pvParameters) {
// Input
receive_can(); // Receive CAN messages. Runs as fast as possible
#ifdef CAN_FD
receive_canfd(); // Receive CAN-FD messages. Runs as fast as possible
#endif
#ifdef DUAL_CAN
receive_can2();
receive_can2(); // Receive CAN messages on CAN2. Runs as fast as possible
#endif
#if defined(SERIAL_LINK_RECEIVER) || defined(SERIAL_LINK_TRANSMITTER)
runSerialDataLink();
@ -299,9 +306,46 @@ void init_CAN() {
gBuffer.initWithSize(25);
SPI.begin(MCP2515_SCK, MCP2515_MISO, MCP2515_MOSI);
ACAN2515Settings settings(QUARTZ_FREQUENCY, 500UL * 1000UL); // CAN bit rate 500 kb/s
settings.mRequestedMode = ACAN2515Settings::NormalMode; // Select loopback mode
settings.mRequestedMode = ACAN2515Settings::NormalMode;
can.begin(settings, [] { can.isr(); });
#endif
#ifdef CAN_FD
#ifdef DEBUG_VIA_USB
Serial.println("CAN FD add-on (ESP32+MCP2517) selected");
#endif
SPI.begin(MCP2517_SCK, MCP2517_SDO, MCP2517_SDI);
ACAN2517FDSettings settings(ACAN2517FDSettings::OSC_40MHz, 500 * 1000,
DataBitRateFactor::x4); // Arbitration bit rate: 500 kbit/s, data bit rate: 2 Mbit/s
settings.mRequestedMode = ACAN2517FDSettings::NormalFD; // ListenOnly / Normal20B / NormalFD
const uint32_t errorCode = canfd.begin(settings, [] { canfd.isr(); });
if (errorCode == 0) {
#ifdef DEBUG_VIA_USB
Serial.print("Bit Rate prescaler: ");
Serial.println(settings.mBitRatePrescaler);
Serial.print("Arbitration Phase segment 1: ");
Serial.println(settings.mArbitrationPhaseSegment1);
Serial.print("Arbitration Phase segment 2: ");
Serial.println(settings.mArbitrationPhaseSegment2);
Serial.print("Arbitration SJW:");
Serial.println(settings.mArbitrationSJW);
Serial.print("Actual Arbitration Bit Rate: ");
Serial.print(settings.actualArbitrationBitRate());
Serial.println(" bit/s");
Serial.print("Exact Arbitration Bit Rate ? ");
Serial.println(settings.exactArbitrationBitRate() ? "yes" : "no");
Serial.print("Arbitration Sample point: ");
Serial.print(settings.arbitrationSamplePointFromBitStart());
Serial.println("%");
#endif
} else {
#ifdef DEBUG_VIA_USB
Serial.print("CAN-FD Configuration error 0x");
Serial.println(errorCode, HEX);
#endif
set_event(EVENT_CANFD_INIT_FAILURE, (uint8_t)errorCode);
}
#endif
}
void init_LED() {
@ -342,10 +386,6 @@ void init_modbus() {
#ifdef BYD_MODBUS
// Init Static data to the RTU Modbus
handle_static_data_modbus_byd();
#endif
#if defined(SERIAL_LINK_RECEIVER) || defined(SERIAL_LINK_TRANSMITTER)
// Check that Dual LilyGo via RS485 option isn't enabled, this collides with Modbus!
#error MODBUS CANNOT BE USED IN DOUBLE LILYGO SETUPS! CHECK USER SETTINGS!
#endif
// Init Serial2 connected to the RTU Modbus
@ -416,7 +456,17 @@ void init_battery() {
#endif
}
#ifdef CAN_FD
// Functions
void receive_canfd() { // This section checks if we have a complete CAN-FD message incoming
CANFDMessage frame;
if (canfd.available()) {
canfd.receive(frame);
receive_canfd_battery(frame);
}
}
#endif
void receive_can() { // This section checks if we have a complete CAN message incoming
// Depending on which battery/inverter is selected, we forward this to their respective CAN routines
CAN_frame_t rx_frame;
@ -474,6 +524,7 @@ void send_can() {
#endif
// Battery
send_can_battery();
// Charger
#ifdef CHEVYVOLT_CHARGER
send_can_chevyvolt_charger();
#endif

View file

@ -12,6 +12,7 @@
//#define CHADEMO_BATTERY
//#define IMIEV_CZERO_ION_BATTERY
//#define KIA_HYUNDAI_64_BATTERY
//#define KIA_E_GMP_BATTERY
//#define NISSAN_LEAF_BATTERY
//#define RENAULT_KANGOO_BATTERY
//#define RENAULT_ZOE_BATTERY
@ -36,6 +37,7 @@
//#define CONTACTOR_CONTROL //Enable this line to have pins 25,32,33 handle automatic precharge/contactor+/contactor- closing sequence
//#define PWM_CONTACTOR_CONTROL //Enable this line to use PWM logic for contactors, which lower power consumption and heat generation
//#define DUAL_CAN //Enable this line to activate an isolated secondary CAN Bus using add-on MCP2515 controller (Needed for FoxESS inverters)
//#define CAN_FD //Enable this line to activate an isolated secondary CAN-FD bus using add-on MCP2517FD controller (Needed for some batteries)
//#define SERIAL_LINK_RECEIVER //Enable this line to receive battery data over RS485 pins from another Lilygo (This LilyGo interfaces with inverter)
//#define SERIAL_LINK_TRANSMITTER //Enable this line to send battery data over RS485 pins to another Lilygo (This LilyGo interfaces with battery)
#define WEBSERVER //Enable this line to enable WiFi, and to run the webserver. See USER_SETTINGS.cpp for the Wifi settings.

View file

@ -15,6 +15,13 @@
#include "IMIEV-CZERO-ION-BATTERY.h" //See this file for more triplet battery settings
#endif
#ifdef KIA_E_GMP_BATTERY
#include "KIA-E-GMP-BATTERY.h" //See this file for more GMP battery settings
#ifndef CAN_FD
#error KIA HYUNDAI EGMP BATTERIES CANNOT BE USED WITHOUT CAN FD
#endif
#endif
#ifdef KIA_HYUNDAI_64_BATTERY
#include "KIA-HYUNDAI-64-BATTERY.h" //See this file for more 64kWh battery settings
#endif
@ -56,6 +63,9 @@ void receive_can_battery();
#else
void receive_can_battery(CAN_frame_t rx_frame);
#endif
#ifdef CAN_FD
void receive_canfd_battery(CANFDMessage frame);
#endif
void update_values_battery();
void send_can_battery();
void setup_battery(void);

View file

@ -0,0 +1,423 @@
#include "BATTERIES.h"
#ifdef KIA_E_GMP_BATTERY
#include "../devboard/utils/events.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
#include "../lib/pierremolinaro-ACAN2517FD/ACAN2517FD.h"
#include "KIA-E-GMP-BATTERY.h"
/* Do not change code below unless you are sure what you are doing */
static unsigned long previousMillis500ms = 0; // will store last time a 500ms CAN Message was send
static uint8_t CANstillAlive = 12; //counter for checking if CAN is still alive
#define MAX_CELL_VOLTAGE 4250 //Battery is put into emergency stop if one cell goes over this value
#define MIN_CELL_VOLTAGE 2950 //Battery is put into emergency stop if one cell goes below this value
#define MAX_CELL_DEVIATION 150 //LED turns yellow on the board if mv delta exceeds this value
static uint16_t inverterVoltageFrameHigh = 0;
static uint16_t inverterVoltage = 0;
static uint16_t soc_calculated = 0;
static uint16_t SOC_BMS = 0;
static uint16_t SOC_Display = 0;
static uint16_t batterySOH = 1000;
static uint16_t CellVoltMax_mV = 3700;
static uint16_t CellVoltMin_mV = 3700;
static uint16_t cell_deviation_mV = 0;
static uint16_t batteryVoltage = 0;
static int16_t leadAcidBatteryVoltage = 120;
static int16_t batteryAmps = 0;
static int16_t powerWatt = 0;
static int16_t temperatureMax = 0;
static int16_t temperatureMin = 0;
static int16_t allowedDischargePower = 0;
static int16_t allowedChargePower = 0;
static int16_t poll_data_pid = 0;
static uint8_t CellVmaxNo = 0;
static uint8_t CellVminNo = 0;
static uint8_t batteryManagementMode = 0;
static uint8_t BMS_ign = 0;
static uint8_t batteryRelay = 0;
static uint8_t waterleakageSensor = 164;
static uint8_t startedUp = false;
static uint8_t counter_200 = 0;
static uint8_t KIA_7E4_COUNTER = 0x01;
static int8_t temperature_water_inlet = 0;
static int8_t powerRelayTemperature = 0;
static int8_t heatertemp = 0;
CANFDMessage EGMP_7E4;
CANFDMessage EGMP_7E4_ack;
void set_cell_voltages(CANFDMessage frame, int start, int length, int startCell) {
for (size_t i = 0; i < length; i++) {
system_cellvoltages_mV[startCell + i] = (frame.data[start + i] * 20);
}
}
void update_values_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
system_real_SOC_pptt = (SOC_Display * 10); //increase SOC range from 0-100.0 -> 100.00
system_SOH_pptt = (batterySOH * 10); //Increase decimals from 100.0% -> 100.00%
system_battery_voltage_dV = batteryVoltage; //value is *10 (3700 = 370.0)
system_battery_current_dA = batteryAmps; //value is *10 (150 = 15.0)
system_capacity_Wh = BATTERY_WH_MAX;
system_remaining_capacity_Wh = static_cast<int>((static_cast<double>(system_real_SOC_pptt) / 10000) * BATTERY_WH_MAX);
//system_max_charge_power_W = (uint16_t)allowedChargePower * 10; //From kW*100 to Watts
//The allowed charge power is not available. We estimate this value
if (system_scaled_SOC_pptt == 10000) { // When scaled SOC is 100%, set allowed charge power to 0
system_max_charge_power_W = 0;
} else { // No limits, max charging power allowed
system_max_charge_power_W = MAXCHARGEPOWERALLOWED;
}
//system_max_discharge_power_W = (uint16_t)allowedDischargePower * 10; //From kW*100 to Watts
if (system_scaled_SOC_pptt < 100) { // When scaled SOC is <1%, set allowed charge power to 0
system_max_discharge_power_W = 0;
} else { // No limits, max charging power allowed
system_max_discharge_power_W = MAXDISCHARGEPOWERALLOWED;
}
powerWatt = ((batteryVoltage * batteryAmps) / 100);
system_active_power_W = powerWatt; //Power in watts, Negative = charging batt
system_temperature_min_dC = (int8_t)temperatureMin * 10; //Increase decimals, 17C -> 17.0C
system_temperature_max_dC = (int8_t)temperatureMax * 10; //Increase decimals, 18C -> 18.0C
system_cell_max_voltage_mV = CellVoltMax_mV;
system_cell_min_voltage_mV = CellVoltMin_mV;
/* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/
if (!CANstillAlive) {
set_event(EVENT_CANFD_RX_FAILURE, 0);
} else {
CANstillAlive--;
clear_event(EVENT_CANFD_RX_FAILURE);
}
if (waterleakageSensor == 0) {
set_event(EVENT_WATER_INGRESS, 0);
}
if (leadAcidBatteryVoltage < 110) {
set_event(EVENT_12V_LOW, leadAcidBatteryVoltage);
}
// Check if cell voltages are within allowed range
cell_deviation_mV = (system_cell_max_voltage_mV - system_cell_min_voltage_mV);
if (CellVoltMax_mV >= MAX_CELL_VOLTAGE) {
set_event(EVENT_CELL_OVER_VOLTAGE, 0);
}
if (CellVoltMin_mV <= MIN_CELL_VOLTAGE) {
set_event(EVENT_CELL_UNDER_VOLTAGE, 0);
}
if (cell_deviation_mV > MAX_CELL_DEVIATION) {
set_event(EVENT_CELL_DEVIATION_HIGH, 0);
} else {
clear_event(EVENT_CELL_DEVIATION_HIGH);
}
if (system_bms_status == FAULT) { //Incase we enter a critical fault state, zero out the allowed limits
system_max_charge_power_W = 0;
system_max_discharge_power_W = 0;
}
/* Safeties verified. Perform USB serial printout if configured to do so */
#ifdef DEBUG_VIA_USB
Serial.println(); //sepatator
Serial.println("Values from battery: ");
Serial.print("SOC BMS: ");
Serial.print((uint16_t)SOC_BMS / 10.0, 1);
Serial.print("% | SOC Display: ");
Serial.print((uint16_t)SOC_Display / 10.0, 1);
Serial.print("% | SOH ");
Serial.print((uint16_t)batterySOH / 10.0, 1);
Serial.println("%");
Serial.print((int16_t)batteryAmps / 10.0, 1);
Serial.print(" Amps | ");
Serial.print((uint16_t)batteryVoltage / 10.0, 1);
Serial.print(" Volts | ");
Serial.print((int16_t)system_active_power_W);
Serial.println(" Watts");
Serial.print("Allowed Charge ");
Serial.print((uint16_t)allowedChargePower * 10);
Serial.print(" W | Allowed Discharge ");
Serial.print((uint16_t)allowedDischargePower * 10);
Serial.println(" W");
Serial.print("MaxCellVolt ");
Serial.print(CellVoltMax_mV);
Serial.print(" mV No ");
Serial.print(CellVmaxNo);
Serial.print(" | MinCellVolt ");
Serial.print(CellVoltMin_mV);
Serial.print(" mV No ");
Serial.println(CellVminNo);
Serial.print("TempHi ");
Serial.print((int16_t)temperatureMax);
Serial.print("°C TempLo ");
Serial.print((int16_t)temperatureMin);
Serial.print("°C WaterInlet ");
Serial.print((int8_t)temperature_water_inlet);
Serial.print("°C PowerRelay ");
Serial.print((int8_t)powerRelayTemperature * 2);
Serial.println("°C");
Serial.print("Aux12volt: ");
Serial.print((int16_t)leadAcidBatteryVoltage / 10.0, 1);
Serial.println("V | ");
Serial.print("BmsManagementMode ");
Serial.print((uint8_t)batteryManagementMode, BIN);
if (bitRead((uint8_t)BMS_ign, 2) == 1) {
Serial.print(" | BmsIgnition ON");
} else {
Serial.print(" | BmsIgnition OFF");
}
if (bitRead((uint8_t)batteryRelay, 0) == 1) {
Serial.print(" | PowerRelay ON");
} else {
Serial.print(" | PowerRelay OFF");
}
Serial.print(" | Inverter ");
Serial.print(inverterVoltage);
Serial.println(" Volts");
#endif
}
void receive_canfd_battery(CANFDMessage frame) {
CANstillAlive = 12;
switch (frame.id) {
case 0x7EC:
// printFrame(frame);
switch (frame.data[0]) {
case 0x10: //"PID Header"
// Serial.println ("Send ack");
poll_data_pid = frame.data[4];
// if (frame.data[4] == poll_data_pid) {
canfd.tryToSend(EGMP_7E4_ack); //Send ack to BMS if the same frame is sent as polled
// }
break;
case 0x21: //First frame in PID group
if (poll_data_pid == 1) {
allowedChargePower = ((frame.data[3] << 8) + frame.data[4]);
allowedDischargePower = ((frame.data[5] << 8) + frame.data[6]);
SOC_BMS = frame.data[2] * 5; //100% = 200 ( 200 * 5 = 1000 )
} else if (poll_data_pid == 2) {
// set cell voltages data, start bite, data length from start, start cell
set_cell_voltages(frame, 2, 6, 0);
} else if (poll_data_pid == 3) {
set_cell_voltages(frame, 2, 6, 32);
} else if (poll_data_pid == 4) {
set_cell_voltages(frame, 2, 6, 64);
} else if (poll_data_pid == 0x0A) {
set_cell_voltages(frame, 2, 6, 96);
} else if (poll_data_pid == 0x0B) {
set_cell_voltages(frame, 2, 6, 128);
} else if (poll_data_pid == 0x0C) {
set_cell_voltages(frame, 2, 6, 160);
}
break;
case 0x22: //Second datarow in PID group
if (poll_data_pid == 1) {
batteryVoltage = (frame.data[3] << 8) + frame.data[4];
batteryAmps = (frame.data[1] << 8) + frame.data[2];
temperatureMax = frame.data[5];
temperatureMin = frame.data[6];
// temp1 = frame.data[7];
} else if (poll_data_pid == 2) {
set_cell_voltages(frame, 1, 7, 6);
} else if (poll_data_pid == 3) {
set_cell_voltages(frame, 1, 7, 38);
} else if (poll_data_pid == 4) {
set_cell_voltages(frame, 1, 7, 70);
} else if (poll_data_pid == 0x0A) {
set_cell_voltages(frame, 1, 7, 102);
} else if (poll_data_pid == 0x0B) {
set_cell_voltages(frame, 1, 7, 134);
} else if (poll_data_pid == 0x0C) {
set_cell_voltages(frame, 1, 7, 166);
} else if (poll_data_pid == 6) {
batteryManagementMode = frame.data[5];
}
break;
case 0x23: //Third datarow in PID group
if (poll_data_pid == 1) {
temperature_water_inlet = frame.data[6];
CellVoltMax_mV = (frame.data[7] * 20); //(volts *50) *20 =mV
// temp2 = frame.data[1];
// temp3 = frame.data[2];
// temp4 = frame.data[3];
} else if (poll_data_pid == 2) {
set_cell_voltages(frame, 1, 7, 13);
} else if (poll_data_pid == 3) {
set_cell_voltages(frame, 1, 7, 45);
} else if (poll_data_pid == 4) {
set_cell_voltages(frame, 1, 7, 77);
} else if (poll_data_pid == 0x0A) {
set_cell_voltages(frame, 1, 7, 109);
} else if (poll_data_pid == 0x0B) {
set_cell_voltages(frame, 1, 7, 141);
} else if (poll_data_pid == 0x0C) {
set_cell_voltages(frame, 1, 7, 173);
} else if (poll_data_pid == 5) {
// ac = frame.data[3];
// Vdiff = frame.data[4];
// airbag = frame.data[6];
heatertemp = frame.data[7];
}
break;
case 0x24: //Fourth datarow in PID group
if (poll_data_pid == 1) {
CellVmaxNo = frame.data[1];
CellVoltMin_mV = (frame.data[2] * 20); //(volts *50) *20 =mV
CellVminNo = frame.data[3];
// fanMod = frame.data[4];
// fanSpeed = frame.data[5];
leadAcidBatteryVoltage = frame.data[6]; //12v Battery Volts
//cumulative_charge_current[0] = frame.data[7];
} else if (poll_data_pid == 2) {
set_cell_voltages(frame, 1, 7, 20);
} else if (poll_data_pid == 3) {
set_cell_voltages(frame, 1, 7, 52);
} else if (poll_data_pid == 4) {
set_cell_voltages(frame, 1, 7, 84);
} else if (poll_data_pid == 0x0A) {
set_cell_voltages(frame, 1, 7, 116);
} else if (poll_data_pid == 0x0B) {
set_cell_voltages(frame, 1, 7, 148);
} else if (poll_data_pid == 0x0C) {
set_cell_voltages(frame, 1, 7, 180);
} else if (poll_data_pid == 5) {
batterySOH = ((frame.data[2] << 8) + frame.data[3]);
// maxDetCell = frame.data[4];
// minDet = (frame.data[5] << 8) + frame.data[6];
// minDetCell = frame.data[7];
}
break;
case 0x25: //Fifth datarow in PID group
if (poll_data_pid == 1) {
//cumulative_charge_current[1] = frame.data[1];
//cumulative_charge_current[2] = frame.data[2];
//cumulative_charge_current[3] = frame.data[3];
//cumulative_discharge_current[0] = frame.data[4];
//cumulative_discharge_current[1] = frame.data[5];
//cumulative_discharge_current[2] = frame.data[6];
//cumulative_discharge_current[3] = frame.data[7];
//set_cumulative_charge_current();
//set_cumulative_discharge_current();
} else if (poll_data_pid == 2) {
set_cell_voltages(frame, 1, 5, 27);
} else if (poll_data_pid == 3) {
set_cell_voltages(frame, 1, 5, 59);
} else if (poll_data_pid == 4) {
set_cell_voltages(frame, 1, 5, 91);
} else if (poll_data_pid == 0x0A) {
set_cell_voltages(frame, 1, 5, 123);
} else if (poll_data_pid == 0x0B) {
set_cell_voltages(frame, 1, 5, 155);
} else if (poll_data_pid == 0x0C) {
set_cell_voltages(frame, 1, 5, 187);
//set_cell_count();
} else if (poll_data_pid == 5) {
// system_number_of_cells = 98;
SOC_Display = frame.data[1] * 5;
}
break;
case 0x26: //Sixth datarow in PID group
if (poll_data_pid == 1) {
//cumulative_energy_charged[0] = frame.data[1];
// cumulative_energy_charged[1] = frame.data[2];
//cumulative_energy_charged[2] = frame.data[3];
//cumulative_energy_charged[3] = frame.data[4];
//cumulative_energy_discharged[0] = frame.data[5];
//cumulative_energy_discharged[1] = frame.data[6];
//cumulative_energy_discharged[2] = frame.data[7];
// set_cumulative_energy_charged();
}
break;
case 0x27: //Seventh datarow in PID group
if (poll_data_pid == 1) {
//cumulative_energy_discharged[3] = frame.data[1];
//opTimeBytes[0] = frame.data[2];
//opTimeBytes[1] = frame.data[3];
//opTimeBytes[2] = frame.data[4];
//opTimeBytes[3] = frame.data[5];
BMS_ign = frame.data[6];
inverterVoltageFrameHigh = frame.data[7]; // BMS Capacitoir
// set_cumulative_energy_discharged();
// set_opTime();
}
break;
case 0x28: //Eighth datarow in PID group
if (poll_data_pid == 1) {
inverterVoltage = (inverterVoltageFrameHigh << 8) + frame.data[1]; // BMS Capacitoir
}
break;
}
break;
default:
break;
}
}
void receive_can_battery(CAN_frame_t frame) {} // Not used on CAN-FD battery, just included to compile
void send_can_battery() {
unsigned long currentMillis = millis();
//Send 500ms CANFD message
if (currentMillis - previousMillis500ms >= INTERVAL_500_MS) {
// Check if sending of CAN messages has been delayed too much.
if ((currentMillis - previousMillis500ms >= INTERVAL_500_MS_DELAYED) && (currentMillis > BOOTUP_TIME)) {
set_event(EVENT_CAN_OVERRUN, (currentMillis - previousMillis500ms));
}
previousMillis500ms = currentMillis;
EGMP_7E4.data[3] = KIA_7E4_COUNTER;
canfd.tryToSend(EGMP_7E4);
KIA_7E4_COUNTER++;
if (KIA_7E4_COUNTER > 0x0D) { // gets up to 0x010C before repeating
KIA_7E4_COUNTER = 0x01;
}
}
}
void setup_battery(void) { // Performs one time setup at startup
#ifdef DEBUG_VIA_USB
Serial.println("Hyundai E-GMP (Electric Global Modular Platform) battery selected");
#endif
system_number_of_cells = 192; // TODO: will vary depending on battery
system_max_design_voltage_dV =
8064; // TODO: define when battery is known, charging is not possible (goes into forced discharge)
system_min_design_voltage_dV = 4320; // TODO: define when battery is known. discharging further is disabled
EGMP_7E4.id = 0x7E4;
EGMP_7E4.ext = false;
EGMP_7E4.len = 8;
uint8_t dataEGMP_7E4[8] = {0x03, 0x22, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00}; //Poll PID 03 22 01 01
memcpy(EGMP_7E4.data, dataEGMP_7E4, sizeof(dataEGMP_7E4));
EGMP_7E4_ack.id = 0x7E4;
EGMP_7E4_ack.ext = false;
EGMP_7E4_ack.len = 8;
uint8_t dataEGMP_7E4_ack[8] = {0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; //Ack frame, correct PID is returned
memcpy(EGMP_7E4_ack.data, dataEGMP_7E4_ack, sizeof(dataEGMP_7E4_ack));
}
#endif

View file

@ -0,0 +1,41 @@
#ifndef KIA_E_GMP_BATTERY_H
#define KIA_E_GMP_BATTERY_H
#include <Arduino.h>
#include "../../USER_SETTINGS.h"
#include "../devboard/config.h" // Needed for all defines
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
#include "../lib/pierremolinaro-ACAN2517FD/ACAN2517FD.h"
extern ACAN2517FD canfd;
#define BATTERY_SELECTED
#define MAXCHARGEPOWERALLOWED 10000
#define MAXDISCHARGEPOWERALLOWED 10000
// These parameters need to be mapped for the inverter
extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern int16_t system_active_power_W; //W, -32000 to 32000
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t system_max_design_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t system_min_design_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint16_t system_max_discharge_power_W; //W, 0-65000
extern uint16_t system_max_charge_power_W; //W, 0-65000
extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000, Stores the highest cell millivolt value
extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
extern uint16_t system_cellvoltages_mV[MAX_AMOUNT_CELLS]; //Array with all cell voltages in mV
extern uint8_t system_number_of_cells; //Total number of cell voltages, set by each battery
extern uint8_t system_bms_status; //Enum 0-5
extern bool batteryAllowsContactorClosing; //Bool, true/false
extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false
void setup_battery(void);
#endif

View file

@ -20,6 +20,14 @@
#define MCP2515_INT 35 // INT output of MCP2515 | | Pin 35 is input only, without pullup/down resistors
#endif
#ifdef CAN_FD
#define MCP2517_SCK 12 // SCK input of MCP2517
#define MCP2517_SDI 5 // SDI input of MCP2517
#define MCP2517_SDO 34 // SDO output of MCP2517
#define MCP2517_CS 18 // CS input of MCP2517
#define MCP2517_INT 35 // INT output of MCP2517
#endif
#ifdef CONTACTOR_CONTROL
#define POSITIVE_CONTACTOR_PIN 32
#define NEGATIVE_CONTACTOR_PIN 33
@ -75,7 +83,19 @@
#define INTERVAL_20_MS_DELAYED 30
#define INTERVAL_30_MS_DELAYED 40
#define INTERVAL_100_MS_DELAYED 120
#define INTERVAL_500_MS_DELAYED 550
#define BOOTUP_TIME 1000 // Time in ms it takes before system is considered fully started up
#if defined(DUAL_CAN) && defined(CAN_FD)
// Check that user did not try to use dual can and fd-can on same hardware pins
#error CAN-FD AND DUAL-CAN CANNOT BE USED SIMULTANEOUSLY
#endif
#if defined(BYD_MODBUS) || defined(LUNA2000_MODBUS)
#if defined(SERIAL_LINK_RECEIVER) || defined(SERIAL_LINK_TRANSMITTER)
// Check that Dual LilyGo via RS485 option isn't enabled, this collides with Modbus!
#error MODBUS CANNOT BE USED IN DOUBLE LILYGO SETUPS! CHECK USER SETTINGS!
#endif
#endif
#endif

View file

@ -125,8 +125,10 @@ void init_events(void) {
events.entries[i].log = true;
}
events.entries[EVENT_CANFD_INIT_FAILURE].level = EVENT_LEVEL_WARNING;
events.entries[EVENT_CAN_OVERRUN].level = EVENT_LEVEL_INFO;
events.entries[EVENT_CAN_RX_FAILURE].level = EVENT_LEVEL_ERROR;
events.entries[EVENT_CANFD_RX_FAILURE].level = EVENT_LEVEL_ERROR;
events.entries[EVENT_CAN_RX_WARNING].level = EVENT_LEVEL_WARNING;
events.entries[EVENT_CAN_TX_FAILURE].level = EVENT_LEVEL_ERROR;
events.entries[EVENT_WATER_INGRESS].level = EVENT_LEVEL_ERROR;
@ -183,10 +185,14 @@ void clear_event(EVENTS_ENUM_TYPE event) {
const char* get_event_message_string(EVENTS_ENUM_TYPE event) {
switch (event) {
case EVENT_CANFD_INIT_FAILURE:
return "CAN-FD initialization failed. Check hardware or bitrate settings";
case EVENT_CAN_OVERRUN:
return "CAN message failed to send within defined time. Contact developers, CPU load might be too high.";
case EVENT_CAN_RX_FAILURE:
return "No CAN communication detected for 60s. Shutting down battery control.";
case EVENT_CANFD_RX_FAILURE:
return "No CANFD communication detected for 60s. Shutting down battery control.";
case EVENT_CAN_RX_WARNING:
return "ERROR: High amount of corrupted CAN messages detected. Check CAN wire shielding!";
case EVENT_CAN_TX_FAILURE:

View file

@ -28,8 +28,10 @@
*/
#define EVENTS_ENUM_TYPE(XX) \
XX(EVENT_CANFD_INIT_FAILURE) \
XX(EVENT_CAN_OVERRUN) \
XX(EVENT_CAN_RX_FAILURE) \
XX(EVENT_CANFD_RX_FAILURE) \
XX(EVENT_CAN_RX_WARNING) \
XX(EVENT_CAN_TX_FAILURE) \
XX(EVENT_WATER_INGRESS) \

View file

@ -427,6 +427,9 @@ String processor(const String& var) {
#ifdef KIA_HYUNDAI_64_BATTERY
content += "Kia/Hyundai 64kWh";
#endif
#ifdef KIA_E_GMP_BATTERY
content += "Kia/Hyundai EGMP platform";
#endif
#ifdef NISSAN_LEAF_BATTERY
content += "Nissan LEAF";
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,350 @@
//----------------------------------------------------------------------------------------------------------------------
// 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 "ACAN2517FDFilters.h"
#include <SPI.h>
//----------------------------------------------------------------------------------------------------------------------
// ACAN2517FD class
//----------------------------------------------------------------------------------------------------------------------
class ACAN2517FD {
//······················································································································
// 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)
//······················································································································
public: uint32_t begin (const ACAN2517FDSettings & inSettings,
void (* inInterruptServiceRoutine) (void)) ;
public: uint32_t begin (const ACAN2517FDSettings & inSettings,
void (* inInterruptServiceRoutine) (void),
const ACAN2517FDFilters & inFilters) ;
//--- Error code returned by begin
public: static const uint32_t kRequestedConfigurationModeTimeOut = uint32_t (1) << 0 ;
public: static const uint32_t kReadBackErrorWith1MHzSPIClock = uint32_t (1) << 1 ;
public: static const uint32_t kTooFarFromDesiredBitRate = uint32_t (1) << 2 ;
public: static const uint32_t kInconsistentBitRateSettings = uint32_t (1) << 3 ;
public: static const uint32_t kINTPinIsNotAnInterrupt = uint32_t (1) << 4 ;
public: static const uint32_t kISRIsNull = uint32_t (1) << 5 ;
public: static const uint32_t kFilterDefinitionError = uint32_t (1) << 6 ;
public: static const uint32_t kMoreThan32Filters = uint32_t (1) << 7 ;
public: static const uint32_t kControllerReceiveFIFOSizeIsZero = uint32_t (1) << 8 ;
public: static const uint32_t kControllerReceiveFIFOSizeGreaterThan32 = uint32_t (1) << 9 ;
public: static const uint32_t kControllerTransmitFIFOSizeIsZero = uint32_t (1) << 10 ;
public: static const uint32_t kControllerTransmitFIFOSizeGreaterThan32 = uint32_t (1) << 11 ;
public: static const uint32_t kControllerRamUsageGreaterThan2048 = uint32_t (1) << 12 ;
public: static const uint32_t kControllerTXQPriorityGreaterThan31 = uint32_t (1) << 13 ;
public: static const uint32_t kControllerTransmitFIFOPriorityGreaterThan31 = uint32_t (1) << 14 ;
public: static const uint32_t kControllerTXQSizeGreaterThan32 = uint32_t (1) << 15 ;
public: static const uint32_t kRequestedModeTimeOut = uint32_t (1) << 16 ;
public: static const uint32_t kX10PLLNotReadyWithin1MS = uint32_t (1) << 17 ;
public: static const uint32_t kReadBackErrorWithFullSpeedSPIClock = uint32_t (1) << 18 ;
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
//······················································································································
public: bool end (void) ;
//······················································································································
// Send a message
//······················································································································
public: bool tryToSend (const CANFDMessage & inMessage) ;
//······················································································································
// Receive a message
//······················································································································
public: bool receive (CANFDMessage & outMessage) ;
public: bool available (void) ;
public: typedef void (*tFilterMatchCallBack) (const uint32_t inFilterIndex) ;
public: bool dispatchReceivedMessage (const tFilterMatchCallBack inFilterMatchCallBack = NULL) ;
//--- Call back function array
private: ACANFDCallBackRoutine * mCallBackFunctionArray = NULL ;
//······················································································································
// 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
//······················································································································
public: uint32_t diagInfos (const int inIndex = 1) ;
//······················································································································
// Operation Mode
//······················································································································
public: ACAN2517FDSettings::OperationMode currentOperationMode (void) ;
public: void setOperationMode (const ACAN2517FDSettings::OperationMode inMode) ;
//······················································································································
// Recovery from Restricted Operation Mode
//······················································································································
public: bool recoverFromRestrictedOperationMode (void) ;
//······················································································································
// Sleep Mode to Configuration Mode
// (returns true if MCP2517FD was in sleep mode)
//······················································································································
public: bool performSleepModeToConfigurationMode (void) ;
//······················································································································
// Private properties
//······················································································································
#ifdef ARDUINO_ARCH_ESP32
private: TaskHandle_t mESP32TaskHandle = nullptr ;
#endif
private: SPISettings mSPISettings ;
private: SPIClass & mSPI ;
private: const uint8_t mCS ;
private: const uint8_t mINT ;
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
//······················································································································
private: ACANFDBuffer mDriverReceiveBuffer ;
public: uint32_t driverReceiveBufferPeakCount (void) const { return mDriverReceiveBuffer.peakCount () ; }
public: uint8_t hardwareReceiveBufferOverflowCount (void) const { return mHardwareReceiveBufferOverflowCount ; }
public: void resetHardwareReceiveBufferOverflowCount (void) { mHardwareReceiveBufferOverflowCount = 0 ; }
//······················································································································
// Transmit buffer
//······················································································································
private: ACANFDBuffer mDriverTransmitBuffer ;
public: uint32_t driverTransmitBufferSize (void) const { return mDriverTransmitBuffer.size () ; }
public: uint32_t driverTransmitBufferCount (void) const { return mDriverTransmitBuffer.count () ; }
public: uint32_t driverTransmitBufferPeakCount (void) const { return mDriverTransmitBuffer.peakCount () ; }
//······················································································································
// 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) ;
private: uint32_t readRegister32Assume_SPI_transaction (const uint16_t inRegisterAddress) ;
private: uint8_t readRegister8Assume_SPI_transaction (const uint16_t inRegisterAddress) ;
private: uint16_t readRegister16Assume_SPI_transaction (const uint16_t inRegisterAddress) ;
private: void reset2517FD (void) ;
private: void writeRegister8 (const uint16_t inRegisterAddress, const uint8_t inValue) ;
private: void writeRegister32 (const uint16_t inAddress, const uint32_t inValue) ;
private: uint8_t readRegister8 (const uint16_t inAddress) ;
private: uint16_t readRegister16 (const uint16_t inAddress) ;
private: uint32_t readRegister32 (const uint16_t inAddress) ;
private: bool sendViaTXQ (const CANFDMessage & inMessage) ;
private: bool enterInTransmitBuffer (const CANFDMessage & inMessage) ;
private: void appendInControllerTxFIFO (const CANFDMessage & inMessage) ;
//······················································································································
// Polling
//······················································································································
public: void poll (void) ;
//······················································································································
// Interrupt service routine
//······················································································································
public: void isr (void) ;
public: void isr_poll_core (void) ;
private: void receiveInterrupt (void) ;
private: void transmitInterrupt (void) ;
#ifdef ARDUINO_ARCH_ESP32
public: SemaphoreHandle_t mISRSemaphore ;
#endif
//----------------------------------------------------------------------------------------------------------------------
// Optimized CS handling (thanks to Flole998)
//······················································································································
#if defined(__AVR__)
private: volatile uint8_t *cs_pin_reg;
private: uint8_t cs_pin_mask;
private: inline void initCS () {
cs_pin_reg = portOutputRegister(digitalPinToPort(mCS));
cs_pin_mask = digitalPinToBitMask(mCS);
pinMode(mCS, OUTPUT);
}
private: inline void assertCS() {
*(cs_pin_reg) &= ~cs_pin_mask;
}
private: inline void deassertCS() {
*(cs_pin_reg) |= cs_pin_mask;
}
#elif defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK66FX1M0__) || defined(__MK64FX512__)
private: volatile uint8_t *cs_pin_reg;
private: inline void initCS () {
cs_pin_reg = portOutputRegister(mCS);
pinMode(mCS, OUTPUT);
}
private: inline void assertCS() {
*(cs_pin_reg+256) = 1;
}
private: inline void deassertCS() {
*(cs_pin_reg+128) = 1;
}
#elif defined(__MKL26Z64__)
private: volatile uint8_t *cs_pin_reg;
private: uint8_t cs_pin_mask;
private: inline void initCS () {
cs_pin_reg = portOutputRegister(digitalPinToPort(mCS));
cs_pin_mask = digitalPinToBitMask(mCS);
pinMode(mCS, OUTPUT);
}
private: inline void assertCS() {
*(cs_pin_reg+8) = cs_pin_mask;
}
private: inline void deassertCS() {
*(cs_pin_reg+4) = cs_pin_mask;
}
#elif defined(__SAM3X8E__) || defined(__SAM3A8C__) || defined(__SAM3A4C__)
private: volatile uint32_t *cs_pin_reg;
private: uint32_t cs_pin_mask;
private: inline void initCS () {
cs_pin_reg = &(digitalPinToPort(mCS)->PIO_PER);
cs_pin_mask = digitalPinToBitMask(mCS);
pinMode(mCS, OUTPUT);
}
private: inline void assertCS() {
*(cs_pin_reg+13) = cs_pin_mask;
}
private: inline void deassertCS() {
*(cs_pin_reg+12) = cs_pin_mask;
}
#elif defined(__PIC32MX__)
private: volatile uint32_t *cs_pin_reg;
private: uint32_t cs_pin_mask;
private: inline void initCS () {
cs_pin_reg = portModeRegister(digitalPinToPort(mCS));
cs_pin_mask = digitalPinToBitMask(mCS);
pinMode(mCS, OUTPUT);
}
private: inline void assertCS() {
*(cs_pin_reg+8+1) = cs_pin_mask;
}
private: inline void deassertCS() {
*(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);
}
private: inline void assertCS() {
GPOC = cs_pin_mask;
}
private: inline void deassertCS() {
GPOS = cs_pin_mask;
}
#elif defined(__SAMD21G18A__)
private: volatile uint32_t *cs_pin_reg;
private: uint32_t cs_pin_mask;
private: inline void initCS () {
cs_pin_reg = portModeRegister(digitalPinToPort(mCS));
cs_pin_mask = digitalPinToBitMask(mCS);
pinMode(mCS, OUTPUT);
}
private: inline void assertCS() {
*(cs_pin_reg+5) = cs_pin_mask;
}
private: inline void deassertCS() {
*(cs_pin_reg+6) = cs_pin_mask;
}
#else
private: inline void initCS () {
pinMode (mCS, OUTPUT);
}
private: inline void assertCS() {
digitalWrite(mCS, LOW);
}
private: inline void deassertCS() {
digitalWrite(mCS, HIGH);
}
#endif
//······················································································································
// GPIO
//······················································································································
public: void gpioSetMode (const uint8_t inPin, const uint8_t inMode) ;
public: void gpioWrite (const uint8_t inPin, const uint8_t inLevel) ;
public: bool gpioRead (const uint8_t inPin) ;
public: void configureGPIO0AsXSTBY (void) ;
//······················································································································
// No copy
//······················································································································
private: ACAN2517FD (const ACAN2517FD &) = delete ;
private: ACAN2517FD & operator = (const ACAN2517FD &) = delete ;
//······················································································································
} ;
//----------------------------------------------------------------------------------------------------------------------

View file

@ -0,0 +1,238 @@
//----------------------------------------------------------------------------------------------------------------------
// 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"
//----------------------------------------------------------------------------------------------------------------------
// ACAN2517FDFilters class
//----------------------------------------------------------------------------------------------------------------------
class ACAN2517FDFilters {
//······················································································································
// EMBEDDED CLASS
//······················································································································
private: class Filter {
public: Filter * mNextFilter ;
public: const uint32_t mFilterMask ;
public: const uint32_t mAcceptanceFilter ;
public: const ACANFDCallBackRoutine mCallBackRoutine ;
public: Filter (const uint32_t inFilterMask,
const uint32_t inAcceptanceFilter,
const ACANFDCallBackRoutine inCallBackRoutine) :
mNextFilter (NULL),
mFilterMask (inFilterMask),
mAcceptanceFilter (inAcceptanceFilter),
mCallBackRoutine (inCallBackRoutine) {
}
//--- No copy
private: Filter (const Filter &) ;
private: Filter & operator = (const Filter &) ;
} ;
//······················································································································
// ENUMERATED TYPE
//······················································································································
public: typedef enum {
kFiltersOk,
kStandardIdentifierTooLarge,
kExtendedIdentifierTooLarge,
kStandardAcceptanceTooLarge,
kExtendedAcceptanceTooLarge,
kStandardMaskTooLarge,
kExtendedMaskTooLarge,
kInconsistencyBetweenMaskAndAcceptance
} FilterStatus ;
//······················································································································
// CONSTRUCTOR
//······················································································································
public: ACAN2517FDFilters (void) {}
//······················································································································
// DESTRUCTOR
//······················································································································
public: ~ ACAN2517FDFilters (void) {
while (mFirstFilter != NULL) {
Filter * next = mFirstFilter->mNextFilter ;
delete mFirstFilter ;
mFirstFilter = next ;
}
}
//······················································································································
// RECEIVE FILTERS
//······················································································································
public: void appendPassAllFilter (const ACANFDCallBackRoutine inCallBackRoutine) { // Accept any frame
Filter * f = new Filter (0, 0, inCallBackRoutine) ;
if (mFirstFilter == NULL) {
mFirstFilter = f ;
}else{
mLastFilter->mNextFilter = f ;
}
mLastFilter = f ;
mFilterCount += 1 ;
}
//······················································································································
public: void appendFormatFilter (const tFrameFormat inFormat, // Accept any identifier
const ACANFDCallBackRoutine inCallBackRoutine) {
Filter * f = new Filter (((uint32_t) 1) << 30,
(inFormat == kExtended) ? (((uint32_t) 1) << 30) : 0,
inCallBackRoutine) ;
if (mFirstFilter == NULL) {
mFirstFilter = f ;
}else{
mLastFilter->mNextFilter = f ;
}
mLastFilter = f ;
mFilterCount += 1 ;
}
//······················································································································
public: void appendFrameFilter (const tFrameFormat inFormat,
const uint32_t inIdentifier,
const ACANFDCallBackRoutine inCallBackRoutine) {
//--- Check identifier
if (inFormat == kExtended) {
if (inIdentifier > 0x1FFFFFFF) {
mFilterStatus = kExtendedIdentifierTooLarge ;
mFilterErrorIndex = mFilterCount ;
}
}else if (inIdentifier > 0x7FF) {
mFilterStatus = kStandardIdentifierTooLarge ;
mFilterErrorIndex = mFilterCount ;
}
//--- Re order bits if extended filter
const uint32_t mask = (((uint32_t) 1) << 30) | ((inFormat == kExtended) ? 0x1FFFFFFF : 0x7FF) ;
uint32_t acceptance ;
if (inFormat == kExtended) {
acceptance = ((inIdentifier >> 18) & 0x7FF) | ((inIdentifier & 0x3FFFF) << 11) | (((uint32_t) 1) << 30) ;
}else{
acceptance = inIdentifier ;
}
//--- Enter filter
Filter * f = new Filter (mask, acceptance, inCallBackRoutine) ;
if (mFirstFilter == NULL) {
mFirstFilter = f ;
}else{
mLastFilter->mNextFilter = f ;
}
mLastFilter = f ;
mFilterCount += 1 ;
}
//······················································································································
public: void appendFilter (const tFrameFormat inFormat,
const uint32_t inMask,
const uint32_t inAcceptance,
const ACANFDCallBackRoutine inCallBackRoutine) {
//--- Check consistency between mask and acceptance
if ((inMask & inAcceptance) != inAcceptance) {
mFilterStatus = kInconsistencyBetweenMaskAndAcceptance ;
mFilterErrorIndex = mFilterCount ;
}
//--- Check identifier
if (inFormat == kExtended) {
if (inAcceptance > 0x1FFFFFFF) {
mFilterStatus = kExtendedAcceptanceTooLarge ;
mFilterErrorIndex = mFilterCount ;
}
}else if (inAcceptance > 0x7FF) {
mFilterStatus = kStandardAcceptanceTooLarge ;
mFilterErrorIndex = mFilterCount ;
}
//--- Check mask
if (inFormat == kExtended) {
if (inMask > 0x1FFFFFFF) {
mFilterStatus = kExtendedMaskTooLarge ;
mFilterErrorIndex = mFilterCount ;
}
}else if (inMask > 0x7FF) {
mFilterStatus = kStandardMaskTooLarge ;
mFilterErrorIndex = mFilterCount ;
}
//--- Re order bits if extended filter
uint32_t mask = ((uint32_t) 1) << 30 ;
if (inFormat == kExtended) {
mask |= ((inMask >> 18) & 0x7FF) | ((inMask & 0x3FFFF) << 11) ;
}else{
mask |= inMask ;
}
uint32_t acceptance ;
if (inFormat == kExtended) {
acceptance = ((inAcceptance >> 18) & 0x7FF) | ((inAcceptance & 0x3FFFF) << 11) | (((uint32_t) 1) << 30) ;
}else{
acceptance = inAcceptance ;
}
//--- Enter filter
Filter * f = new Filter (mask, acceptance, inCallBackRoutine) ;
if (mFirstFilter == NULL) {
mFirstFilter = f ;
}else{
mLastFilter->mNextFilter = f ;
}
mLastFilter = f ;
mFilterCount += 1 ;
}
//······················································································································
// ACCESSORS
//······················································································································
public: FilterStatus filterStatus (void) const { return mFilterStatus ; }
public: uint8_t filterErrorIndex (void) const { return mFilterErrorIndex ; }
public: uint8_t filterCount (void) const { return mFilterCount ; }
//······················································································································
// PRIVATE PROPERTIES
//······················································································································
private: uint8_t mFilterCount = 0 ;
private: Filter * mFirstFilter = NULL ;
private: Filter * mLastFilter = NULL ;
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

@ -0,0 +1,355 @@
//----------------------------------------------------------------------------------------------------------------------
// 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 ;
switch (inOscillator) {
case OSC_4MHz:
sysClock = 4UL * 1000 * 1000 ;
break ;
case OSC_4MHz_DIVIDED_BY_2:
sysClock = 2UL * 1000 * 1000 ;
break ;
case OSC_4MHz10xPLL_DIVIDED_BY_2 :
case OSC_40MHz_DIVIDED_BY_2:
case OSC_20MHz:
sysClock = 20UL * 1000 * 1000 ;
break ;
case OSC_20MHz_DIVIDED_BY_2:
sysClock = 10UL * 1000 * 1000 ;
break ;
case OSC_4MHz10xPLL:
case OSC_40MHz:
break ;
}
return sysClock ;
}
//----------------------------------------------------------------------------------------------------------------------
// CONSTRUCTOR
//----------------------------------------------------------------------------------------------------------------------
ACAN2517FDSettings::ACAN2517FDSettings (const Oscillator inOscillator,
const uint32_t inDesiredArbitrationBitRate,
const DataBitRateFactor inDataBitRateFactor,
const uint32_t inTolerancePPM) :
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 ;
}
}
//--- 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 ;
}
}
//--- 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) ;
}
} ;
//----------------------------------------------------------------------------------------------------------------------
// 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) {
return actualArbitrationBitRate () ;
}else{
const uint32_t dataTQCount = 1 /* Sync Seg */ + mDataPhaseSegment1 + mDataPhaseSegment2 ;
return mSysClock / mBitRatePrescaler / dataTQCount ;
}
}
//----------------------------------------------------------------------------------------------------------------------
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) {
return exactArbitrationBitRate () ;
}else{
const uint32_t TQCount = 1 /* Sync Seg */ + mDataPhaseSegment1 + mDataPhaseSegment2 ;
return mSysClock == (mBitRatePrescaler * mDesiredArbitrationBitRate * TQCount * uint8_t (mDataBitRateFactor)) ;
}
}
//----------------------------------------------------------------------------------------------------------------------
bool ACAN2517FDSettings::dataBitRateIsAMultipleOfArbitrationBitRate (void) const {
bool result = mDataBitRateFactor == DataBitRateFactor::x1 ;
if (!result) {
const uint32_t dataTQCount = 1 /* Sync Seg */ + mDataPhaseSegment1 + mDataPhaseSegment2 ;
const uint32_t arbitrationTQCount = 1 /* Sync Seg */ + mArbitrationPhaseSegment1 + mArbitrationPhaseSegment2 ;
result = arbitrationTQCount == (dataTQCount * uint8_t (mDataBitRateFactor)) ;
}
return result ;
}
//----------------------------------------------------------------------------------------------------------------------
uint32_t ACAN2517FDSettings::ppmFromDesiredArbitrationBitRate (void) const {
const uint32_t TQCount = 1 /* Sync Seg */ + mArbitrationPhaseSegment1 + mArbitrationPhaseSegment2 ;
const uint32_t W = TQCount * mDesiredArbitrationBitRate * mBitRatePrescaler ;
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
return (uint32_t) ((diff * ppm) / W) ;
}
//----------------------------------------------------------------------------------------------------------------------
uint32_t ACAN2517FDSettings::arbitrationSamplePointFromBitStart (void) const {
const uint32_t nominalTQCount = 1 /* Sync Seg */ + mArbitrationPhaseSegment1 + mArbitrationPhaseSegment2 ;
const uint32_t samplePoint = 1 /* Sync Seg */ + mArbitrationPhaseSegment1 ;
const uint32_t partPerCent = 100 ;
return (samplePoint * partPerCent) / nominalTQCount ;
}
//----------------------------------------------------------------------------------------------------------------------
uint32_t ACAN2517FDSettings::dataSamplePointFromBitStart (void) const {
const uint32_t nominalTQCount = 1 /* Sync Seg */ + mDataPhaseSegment1 + mDataPhaseSegment2 ;
const uint32_t samplePoint = 1 /* Sync Seg */ + mDataPhaseSegment1 ;
const uint32_t partPerCent = 100 ;
return (samplePoint * partPerCent) / nominalTQCount ;
}
//----------------------------------------------------------------------------------------------------------------------
uint32_t ACAN2517FDSettings::CANBitSettingConsistency (void) const {
uint32_t errorCode = 0 ; // Means no error
//--- Bit rate prescaler
if (mBitRatePrescaler == 0) {
errorCode |= kBitRatePrescalerIsZero ;
}else if (mBitRatePrescaler > MAX_BRP) {
errorCode |= kBitRatePrescalerIsGreaterThan256 ;
}
//--- Arbitration Phase Segment 1
if (mArbitrationPhaseSegment1 < 2) {
errorCode |= kArbitrationPhaseSegment1IsLowerThan2 ;
}else if (mArbitrationPhaseSegment1 > MAX_ARBITRATION_PHASE_SEGMENT_1) {
errorCode |= kArbitrationPhaseSegment1IsGreaterThan256 ;
}
//--- Arbitration Phase Segment 2
if (mArbitrationPhaseSegment2 == 0) {
errorCode |= kArbitrationPhaseSegment2IsZero ;
}else if (mArbitrationPhaseSegment2 > MAX_ARBITRATION_PHASE_SEGMENT_2) {
errorCode |= kArbitrationPhaseSegment2IsGreaterThan128 ;
}
//--- Arbitration SJW
if (mArbitrationSJW == 0) {
errorCode |= kArbitrationSJWIsZero ;
}else if (mArbitrationSJW > MAX_ARBITRATION_SJW) {
errorCode |= kArbitrationSJWIsGreaterThan128 ;
}
if (mArbitrationSJW > mArbitrationPhaseSegment1) {
errorCode |= kArbitrationSJWIsGreaterThanPhaseSegment1 ;
}
if (mArbitrationSJW > mArbitrationPhaseSegment2) {
errorCode |= kArbitrationSJWIsGreaterThanPhaseSegment2 ;
}
//--- Data bit rate ?
if (mDataBitRateFactor != DataBitRateFactor::x1) {
if (! dataBitRateIsAMultipleOfArbitrationBitRate ()) {
errorCode |= kArbitrationTQCountNotDivisibleByDataBitRateFactor ;
}
//--- Data Phase Segment 1
if (mDataPhaseSegment1 < 2) {
errorCode |= kDataPhaseSegment1IsLowerThan2 ;
}else if (mDataPhaseSegment1 > MAX_DATA_PHASE_SEGMENT_1) {
errorCode |= kDataPhaseSegment1IsGreaterThan32 ;
}
//--- Data Phase Segment 2
if (mDataPhaseSegment2 == 0) {
errorCode |= kDataPhaseSegment2IsZero ;
}else if (mDataPhaseSegment2 > MAX_DATA_PHASE_SEGMENT_2) {
errorCode |= kDataPhaseSegment2IsGreaterThan16 ;
}
//--- Data SJW
if (mDataSJW == 0) {
errorCode |= kDataSJWIsZero ;
}else if (mDataSJW > MAX_DATA_SJW) {
errorCode |= kDataSJWIsGreaterThan16 ;
}
if (mDataSJW > mDataPhaseSegment1) {
errorCode |= kDataSJWIsGreaterThanPhaseSegment1 ;
}
if (mDataSJW > mDataPhaseSegment2) {
errorCode |= kDataSJWIsGreaterThanPhaseSegment2 ;
}
}
//---
return errorCode ;
}
//----------------------------------------------------------------------------------------------------------------------
// RAM USAGE
//----------------------------------------------------------------------------------------------------------------------
uint32_t ACAN2517FDSettings::ramUsage (void) const {
uint32_t result = 0 ;
//--- TXQ
result += objectSizeForPayload (mControllerTXQBufferPayload) * mControllerTXQSize ;
//--- Receive FIFO (FIFO #1)
result += objectSizeForPayload (mControllerReceiveFIFOPayload) * mControllerReceiveFIFOSize ;
//--- Send FIFO (FIFO #2)
result += objectSizeForPayload (mControllerTransmitFIFOPayload) * mControllerTransmitFIFOSize ;
//---
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

@ -0,0 +1,296 @@
//----------------------------------------------------------------------------------------------------------------------
// A CAN driver for MCP2517FD (CANFD mode)
// by Pierre Molinaro
// https://github.com/pierremolinaro/acan2517FD
//
//----------------------------------------------------------------------------------------------------------------------
#pragma once
//----------------------------------------------------------------------------------------------------------------------
#include "ACANFD_DataBitRateFactor.h"
//----------------------------------------------------------------------------------------------------------------------
// ACAN2517FDSettings class
//----------------------------------------------------------------------------------------------------------------------
class ACAN2517FDSettings {
//······················································································································
// ENUMERATED TYPES
//······················································································································
public: typedef enum : uint8_t {
OSC_4MHz,
OSC_4MHz_DIVIDED_BY_2,
OSC_4MHz10xPLL,
OSC_4MHz10xPLL_DIVIDED_BY_2,
OSC_20MHz,
OSC_20MHz_DIVIDED_BY_2,
OSC_40MHz,
OSC_40MHz_DIVIDED_BY_2
} Oscillator ;
public: typedef enum : uint8_t {
CLKO_DIVIDED_BY_1,
CLKO_DIVIDED_BY_2,
CLKO_DIVIDED_BY_4,
CLKO_DIVIDED_BY_10,
SOF
} CLKOpin ;
public: typedef enum : uint8_t {
NormalFD = 0,
Sleep = 1,
InternalLoopBack = 2,
ListenOnly = 3,
Configuration = 4,
ExternalLoopBack = 5,
Normal20B = 6,
RestrictedOperation = 7
} OperationMode ;
public: typedef enum : uint8_t {Disabled, ThreeAttempts, UnlimitedNumber} RetransmissionAttempts ;
public: typedef enum : uint8_t {
PAYLOAD_8 = 0,
PAYLOAD_12 = 1,
PAYLOAD_16 = 2,
PAYLOAD_20 = 3,
PAYLOAD_24 = 4,
PAYLOAD_32 = 5,
PAYLOAD_48 = 6,
PAYLOAD_64 = 7
} PayloadSize ;
//······················································································································
// Deprecated enumeration (now use DataBitRateFactor declared in ACANFD_DataBitRateFactor.h)
//······················································································································
public : typedef enum : uint8_t {
DATA_BITRATE_x1 = 1,
DATA_BITRATE_x2 = 2,
DATA_BITRATE_x3 = 3,
DATA_BITRATE_x4 = 4,
DATA_BITRATE_x5 = 5,
DATA_BITRATE_x6 = 6,
DATA_BITRATE_x7 = 7,
DATA_BITRATE_x8 = 8,
DATA_BITRATE_x9 = 9,
DATA_BITRATE_x10 = 10
} DataBitRateFactor_Deprecated ;
//······················································································································
// 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)
//······················································································································
public: ACAN2517FDSettings (const Oscillator inOscillator,
const uint32_t inDesiredArbitrationBitRate,
const DataBitRateFactor_Deprecated inDataBitRateFactor,
const uint32_t inTolerancePPM = 1000) :
ACAN2517FDSettings (inOscillator, inDesiredArbitrationBitRate, DataBitRateFactor (inDataBitRateFactor), inTolerancePPM) {
}
//······················································································································
// CAN BIT TIMING
//······················································································································
private: Oscillator mOscillator ;
private: uint32_t mSysClock ; // In Hz
public: const uint32_t mDesiredArbitrationBitRate ; // In kb/s
public: const DataBitRateFactor mDataBitRateFactor ;
//--- Data bit rate; if mDataBitRateFactor==1, theses properties are not used for configuring the MCP2517FD.
public: uint8_t mDataPhaseSegment1 = 0 ; // if mDataBitRateFactor > 1: 2...32, else equal to mArbitrationPhaseSegment1
public: uint8_t mDataPhaseSegment2 = 0 ; // if mDataBitRateFactor > 1: 1...16, else equal to mArbitrationPhaseSegment2
public: uint8_t mDataSJW = 0 ; // if mDataBitRateFactor > 1: 1...16, else equal to mArbitrationSJW
//--- Bit rate prescaler is common to arbitration and data bit rates
public: uint16_t mBitRatePrescaler = 0 ; // 1...256
//--- Arbitration bit rate
public: uint16_t mArbitrationPhaseSegment1 = 0 ; // 2...256
public: uint8_t mArbitrationPhaseSegment2 = 0 ; // 1...128
public: uint8_t mArbitrationSJW = 0 ; // 1...128
public: bool mArbitrationBitRateClosedToDesiredRate = false ; // The above configuration is not correct
//--- Transmitter Delay Compensation Offset
public: int8_t mTDCO = 0 ; // -64 ... +63
//······················································································································
// MCP2517FD TXCAN pin is Open Drain ?
//······················································································································
public: bool mTXCANIsOpenDrain = false ; // false --> Push/Pull Output, true --> Open Drain Output
//······················································································································
// MCP2517FD INT pin is Open Drain ?
//······················································································································
public: bool mINTIsOpenDrain = false ; // false --> Push/Pull Output, true --> Open Drain Output
//······················································································································
// 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)
//······················································································································
public: CLKOpin mCLKOPin = CLKO_DIVIDED_BY_10 ;
//······················································································································
// Requested mode
//······················································································································
public: OperationMode mRequestedMode = NormalFD ;
//······················································································································
// TRANSMIT FIFO
//······················································································································
//--- Driver transmit buffer size
public: uint16_t mDriverTransmitFIFOSize = 16 ; // >= 0
//--- Controller transmit FIFO size
public: uint8_t mControllerTransmitFIFOSize = 1 ; // 1 ... 32
//--- Payload controller transmit FIFO size
public: PayloadSize mControllerTransmitFIFOPayload = PAYLOAD_64 ;
//--- Controller transmit FIFO priority (0 --> lowest, 31 --> highest)
public: uint8_t mControllerTransmitFIFOPriority = 0 ; // 0 ... 31
//--- Controller transmit FIFO retransmission attempts
public: RetransmissionAttempts mControllerTransmitFIFORetransmissionAttempts = UnlimitedNumber ;
//······················································································································
// TXQ BUFFER
//······················································································································
//--- TXQ buffer size (0 --> TXQ disabled)
public: uint8_t mControllerTXQSize = 0 ; // 0 ... 32
//--- Payload controller TXQ buffer size
public: PayloadSize mControllerTXQBufferPayload = PAYLOAD_64 ;
//--- TXQ buffer priority (0 --> lowest, 31 --> highest)
public: uint8_t mControllerTXQBufferPriority = 31 ; // 0 ... 31
//--- Controller TXQ buffer retransmission attempts
public: RetransmissionAttempts mControllerTXQBufferRetransmissionAttempts = UnlimitedNumber ;
//······················································································································
// RECEIVE FIFO
//······················································································································
//--- Driver receive buffer size
public: uint16_t mDriverReceiveFIFOSize = 32 ; // > 0
//--- Payload receive FIFO size
public: PayloadSize mControllerReceiveFIFOPayload = PAYLOAD_64 ;
//--- Controller receive FIFO size
public: uint8_t mControllerReceiveFIFOSize = 27 ; // 1 ... 32
//······················································································································
// SYSCLOCK frequency computation
//······················································································································
public: static uint32_t sysClock (const Oscillator inOscillator) ;
//······················································································································
// Accessors
//······················································································································
public: Oscillator oscillator (void) const { return mOscillator ; }
public: uint32_t sysClock (void) const { return mSysClock ; }
public: uint32_t actualArbitrationBitRate (void) const ;
public: uint32_t actualDataBitRate (void) const ;
public: bool exactArbitrationBitRate (void) const ;
public: bool exactDataBitRate (void) const ;
public: bool dataBitRateIsAMultipleOfArbitrationBitRate (void) const ;
//······················································································································
// 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)
//······················································································································
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 %)
//······················································································································
public: uint32_t arbitrationSamplePointFromBitStart (void) const ;
public: uint32_t dataSamplePointFromBitStart (void) const ;
//······················································································································
// Bit settings are consistent ? (returns 0 if ok)
//······················································································································
public: uint32_t CANBitSettingConsistency (void) const ;
//······················································································································
// 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 ;
//······················································································································
// Max values
//······················································································································
public: static const uint16_t MAX_BRP = 256 ;
public: static const uint16_t MAX_ARBITRATION_PHASE_SEGMENT_1 = 256 ;
public: static const uint8_t MAX_ARBITRATION_PHASE_SEGMENT_2 = 128 ;
public: static const uint8_t MAX_ARBITRATION_SJW = 128 ;
public: static const uint16_t MAX_DATA_PHASE_SEGMENT_1 = 32 ;
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,119 @@
//----------------------------------------------------------------------------------------------------------------------
// 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

View file

@ -0,0 +1,35 @@
//----------------------------------------------------------------------------------------------------------------------
// A CANFD driver
// by Pierre Molinaro
// This header is common to libraries
// 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,
x2 = 2,
x3 = 3,
x4 = 4,
x5 = 5,
x6 = 6,
x7 = 7,
x8 = 8,
x9 = 9,
x10 = 10
} ;
//----------------------------------------------------------------------------------------------------------------------
#endif

View file

@ -0,0 +1,134 @@
//-----------------------------------------------------------------------------
// Generic CANFD Message
// by Pierre Molinaro
//
// https://github.com/pierremolinaro/acan2517FD
//
//-----------------------------------------------------------------------------
#ifndef GENERIC_CANFD_MESSAGE_DEFINED
#define GENERIC_CANFD_MESSAGE_DEFINED
//-----------------------------------------------------------------------------
#include "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
// You can use the "pad" method for padding with 0 bytes to the next valid length
class CANFDMessage {
//·············································································
// Constructors
//·············································································
public : CANFDMessage (void) :
id (0), // Frame identifier
ext (false), // false -> base frame, true -> extended frame
type (CANFD_WITH_BIT_RATE_SWITCH),
idx (0), // This field is used by the driver
len (0), // Length of data (0 ... 64)
data () {
}
//·············································································
public : CANFDMessage (const CANMessage & inMessage) :
id (inMessage.id), // Frame identifier
ext (inMessage.ext), // false -> base frame, true -> extended frame
type (inMessage.rtr ? CAN_REMOTE : CAN_DATA),
idx (inMessage.idx), // This field is used by the driver
len (inMessage.len), // Length of data (0 ... 64)
data () {
data64 [0] = inMessage.data64 ;
}
//·············································································
// Enumerated Type
//·············································································
public: typedef enum : uint8_t {
CAN_REMOTE,
CAN_DATA,
CANFD_NO_BIT_RATE_SWITCH,
CANFD_WITH_BIT_RATE_SWITCH
} Type ;
//·············································································
// Properties
//·············································································
public : uint32_t id ; // Frame identifier
public : bool ext ; // false -> base frame, true -> extended frame
public : Type type ;
public : uint8_t idx ; // This field is used by the driver
public : uint8_t len ; // Length of data (0 ... 64)
public : union {
uint64_t data64 [ 8] ; // Caution: subject to endianness
int64_t data_s64 [ 8] ; // Caution: subject to endianness
uint32_t data32 [16] ; // Caution: subject to endianness
int32_t data_s32 [16] ; // Caution: subject to endianness
float dataFloat [16] ; // Caution: subject to endianness
uint16_t data16 [32] ; // Caution: subject to endianness
int16_t data_s16 [32] ; // Caution: subject to endianness
int8_t data_s8 [64] ;
uint8_t data [64] ;
} ;
//·············································································
// Methods
//·············································································
public: void pad (void) {
uint8_t paddedLength = len ;
if ((len > 8) && (len < 12)) {
paddedLength = 12 ;
}else if ((len > 12) && (len < 16)) {
paddedLength = 16 ;
}else if ((len > 16) && (len < 20)) {
paddedLength = 20 ;
}else if ((len > 20) && (len < 24)) {
paddedLength = 24 ;
}else if ((len > 24) && (len < 32)) {
paddedLength = 32 ;
}else if ((len > 32) && (len < 48)) {
paddedLength = 48 ;
}else if ((len > 48) && (len < 64)) {
paddedLength = 64 ;
}
while (len < paddedLength) {
data [len] = 0 ;
len += 1 ;
}
}
//·············································································
public: bool isValid (void) const {
if ((type == CAN_REMOTE) || (type == CAN_DATA)) { // Remote frame
return len <= 8 ;
}else{ // Data frame
return
(len <= 8) || (len == 12) || (len == 16) || (len == 20)
||
(len == 24) || (len == 32) || (len == 48) || (len == 64)
;
}
}
//·············································································
} ;
//-----------------------------------------------------------------------------
typedef void (*ACANFDCallBackRoutine) (const CANFDMessage & inMessage) ;
//-----------------------------------------------------------------------------
#endif

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,21 @@
MIT License
Copyright (c) 2018 Pierre Molinaro
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.

View file

@ -0,0 +1,118 @@
## MCP2517FD and MCP2518FD CAN Controller Library for Arduino (in CAN FD mode)
### Compatibility with the other ACAN libraries
This library is fully compatible with the Teensy 3.x ACAN library https://github.com/pierremolinaro/acan, ACAN2515 library https://github.com/pierremolinaro/acan2515, and the ACAN2517 library https://github.com/pierremolinaro/acan2517, it uses a very similar API.
### ACAN2517FD library description
ACAN2517FD is a driver for the MCP2517FD and MCP2518FD CAN Controllers, in CAN FD mode. It runs on any Arduino compatible board.
> The ACAN2517 library handles the MCP2517FD and MCP2518FD CAN Controller, in CAN 2.0B mode.
The library supports the 4MHz, 20 MHz and 40 MHz oscillator clock.
The driver supports many bit rates: the CAN bit timing calculator finds settings for standard 62.5 kbit/s, 125 kbit/s, 250 kbit/s, 500 kbit/s, 1 Mbit/s, but also for an exotic bit rate as 727 kbit/s. If the desired bit rate cannot be achieved, the `begin` method does not configure the hardware and returns an error code.
> Driver API is fully described by the PDF file in the `extras` directory.
### Demo Sketch
> The demo sketch is in the `examples/LoopBackDemo` directory.
Configuration is a four-step operation.
1. Instanciation of the `settings` object : the constructor has one parameter: the wished CAN bit rate. The `settings` is fully initialized.
2. You can override default settings. Here, we set the `mRequestedMode` property to true, enabling to run demo code without any additional hardware (no CAN transceiver needed). We can also for example change the receive buffer size by setting the `mReceiveBufferSize` property.
3. Calling the `begin` method configures the driver and starts CAN bus participation. Any message can be sent, any frame on the bus is received. No default filter to provide.
4. You check the `errorCode` value to detect configuration error(s).
```cpp
static const byte MCP2517_CS = 20 ; // CS input of MCP2517FD, adapt to your design
static const byte MCP2517_INT = 37 ; // INT output of MCP2517FD, adapt to your design
ACAN2517FD can (MCP2517_CS, SPI, MCP2517_INT) ; // You can use SPI2, SPI3, if provided by your microcontroller
void setup () {
Serial.begin (9600) ;
while (!Serial) {}
Serial.println ("Hello") ;
// Arbitration bit rate: 125 kbit/s, data bit rate: 500 kbit/s
ACAN2517FDSettings settings (ACAN2517FDSettings::OSC_4MHz10xPLL,
125 * 1000, ACAN2517FDSettings::DATA_BITRATE_x4) ;
settings.mRequestedMode = ACAN2517FDSettings::InternalLoopBack ; // Select loopback mode
const uint32_t errorCode = can.begin (settings, [] { can.isr () ; }) ;
if (0 == errorCode) {
Serial.println ("Can ok") ;
}else{
Serial.print ("Error Can: 0x") ;
Serial.println (errorCode, HEX) ;
}
}
```
Now, an example of the `loop` function. As we have selected loop back mode, every sent frame is received.
```cpp
static unsigned gSendDate = 0 ;
static unsigned gSentCount = 0 ;
static unsigned gReceivedCount = 0 ;
void loop () {
CANFDMessage message ;
if (gSendDate < millis ()) {
message.id = 0x542 ;
const bool ok = can.tryToSend (message) ;
if (ok) {
gSendDate += 2000 ;
gSentCount += 1 ;
Serial.print ("Sent: ") ;
Serial.println (gSentCount) ;
}
}
if (can.receive (message)) {
gReceivedCount += 1 ;
Serial.print ("Received: ") ;
Serial.println (gReceivedCount) ;
}
}
```
`CANFDMessage` is the class that defines a CAN FD message. The `message` object is fully initialized by the default constructor. Here, we set the `id` to `0x542` for sending a standard data frame, without data, with this identifier.
The `can.tryToSend` tries to send the message. It returns `true` if the message has been sucessfully added to the driver transmit buffer.
The `gSendDate` variable handles sending a CAN message every 2000 ms.
`can.receive` returns `true` if a message has been received, and assigned to the `message`argument.
### Use of Optional Reception Filtering
The MCP2517 CAN Controller implements 32 acceptance masks and 32 acceptance filters. The driver API enables you to fully manage these registers.
For example (`LoopBackDemoTeensy3xWithFilters` sketch):
```cpp
ACAN2517FDSettings settings (ACAN2517FDSettings::OSC_4MHz10xPLL, 125 * 1000) ;
settings.mRequestedMode = ACAN2517FDSettings::InternalLoopBack ; // Select loopback mode
ACAN2517Filters filters ;
// Filter #0: receive standard frame with identifier 0x123
filters.appendFrameFilter (kStandard, 0x123, receiveFromFilter0) ;
// Filter #1: receive extended frame with identifier 0x12345678
filters.appendFrameFilter (kExtended, 0x12345678, receiveFromFilter1) ;
// Filter #2: receive standard frame with identifier 0x3n4
filters.appendFilter (kStandard, 0x70F, 0x304, receiveFromFilter2) ;
const uint32_t errorCode = can.begin (settings, [] { can.isr () ; }, filters) ;
```
These settings enable the acceptance of standard frames whose identifier is 0x123, extended frames whose identifier is 0x12345678, and data frames whose identifier is 0x304, 0x314, ..., 0x3E4, 0x3F4.
The `receiveFromFilter0`, `receiveFromFilter1`, `receiveFromFilter2` functions are call back functions, handled by the `can.dispatchReceivedMessage` function:
```cpp
void loop () {
can.dispatchReceivedMessage () ; // Do not use can.receive any more
...
}
```