mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-03 17:59:27 +02:00
Feature: CAN-FD + EGMP batteries! (#244)
* ACAN2517FD lib addition, integrate support for canfd, and add initial EGMP battery capability
This commit is contained in:
parent
c5118a8a09
commit
63371c8b60
20 changed files with 3626 additions and 6 deletions
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
423
Software/src/battery/KIA-E-GMP-BATTERY.cpp
Normal file
423
Software/src/battery/KIA-E-GMP-BATTERY.cpp
Normal 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
|
41
Software/src/battery/KIA-E-GMP-BATTERY.h
Normal file
41
Software/src/battery/KIA-E-GMP-BATTERY.h
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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) \
|
||||
|
|
|
@ -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
|
||||
|
|
1347
Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD.cpp
Normal file
1347
Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD.cpp
Normal file
File diff suppressed because it is too large
Load diff
350
Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD.h
Normal file
350
Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD.h
Normal 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 ;
|
||||
|
||||
//······················································································································
|
||||
|
||||
} ;
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
|
238
Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FDFilters.h
Normal file
238
Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FDFilters.h
Normal 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
|
|
@ -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] ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
296
Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FDSettings.h
Normal file
296
Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FDSettings.h
Normal 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 ;
|
||||
|
||||
//······················································································································
|
||||
|
||||
} ;
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
|
119
Software/src/lib/pierremolinaro-ACAN2517FD/ACANFDBuffer.h
Normal file
119
Software/src/lib/pierremolinaro-ACAN2517FD/ACANFDBuffer.h
Normal 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
|
|
@ -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
|
134
Software/src/lib/pierremolinaro-ACAN2517FD/CANFDMessage.h
Normal file
134
Software/src/lib/pierremolinaro-ACAN2517FD/CANFDMessage.h
Normal 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
|
49
Software/src/lib/pierremolinaro-ACAN2517FD/CANMessage.h
Normal file
49
Software/src/lib/pierremolinaro-ACAN2517FD/CANMessage.h
Normal 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
|
21
Software/src/lib/pierremolinaro-ACAN2517FD/LICENSE
Normal file
21
Software/src/lib/pierremolinaro-ACAN2517FD/LICENSE
Normal 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.
|
118
Software/src/lib/pierremolinaro-ACAN2517FD/README.md
Normal file
118
Software/src/lib/pierremolinaro-ACAN2517FD/README.md
Normal 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
|
||||
...
|
||||
}
|
||||
```
|
Loading…
Add table
Add a link
Reference in a new issue