added Function to create CAN-Traces from WebUI
Some checks failed
CI-Build/Kettenoeler/pipeline/head There was a failure building this commit
Some checks failed
CI-Build/Kettenoeler/pipeline/head There was a failure building this commit
This commit is contained in:
@@ -1,24 +1,42 @@
|
||||
#include "can_hal.h"
|
||||
#include "dtc.h"
|
||||
// #include "debugger.h" // optional für Logs
|
||||
#include <string.h> // memcpy, memcmp
|
||||
|
||||
// ==== Interner Zustand/Helper ====
|
||||
// =====================
|
||||
// Interner Zustand/Helper
|
||||
// =====================
|
||||
MCP_CAN CAN0(GPIO_CS_CAN);
|
||||
|
||||
static bool s_ready = false;
|
||||
static bool s_ready = false;
|
||||
static uint8_t s_nextFiltSlot = 0; // 0..5 (MCP2515 hat 6 Filter-Slots)
|
||||
static uint16_t s_modeSettleMs = 10; // Default aus Config
|
||||
|
||||
// Trace-Hook
|
||||
static CanTraceSink s_traceSink = nullptr;
|
||||
|
||||
// RAW-Sniffer-Steuerung (Filter offen + Restore der vorherigen Konfig)
|
||||
static bool s_rawSnifferEnabled = false;
|
||||
|
||||
// Spiegel der "Normal"-Konfiguration (damit wir nach RAW wiederherstellen können)
|
||||
static uint16_t s_savedStdMask[2] = {0x000, 0x000};
|
||||
static struct
|
||||
{
|
||||
uint32_t id;
|
||||
bool ext;
|
||||
} s_savedFilt[6];
|
||||
static uint8_t s_savedFiltCount = 0;
|
||||
|
||||
// 11-bit: Lib erwartet (value << 16)
|
||||
static inline uint32_t _std_to_hw(uint16_t v11) { return ((uint32_t)v11) << 16; }
|
||||
|
||||
// „Bestätigter“ Mode-Wechsel mithilfe der Lib-Funktion setMode(newMode)
|
||||
// Viele Forks nutzen intern mcp2515_requestNewMode(); wir retryen kurz.
|
||||
static bool _trySetMode(uint8_t mode, uint16_t settleMs)
|
||||
{
|
||||
const uint32_t t0 = millis();
|
||||
do {
|
||||
if (CAN0.setMode(mode) == CAN_OK) return true;
|
||||
do
|
||||
{
|
||||
if (CAN0.setMode(mode) == CAN_OK)
|
||||
return true;
|
||||
delay(1);
|
||||
} while ((millis() - t0) < settleMs);
|
||||
return false;
|
||||
@@ -27,22 +45,32 @@ static bool _trySetMode(uint8_t mode, uint16_t settleMs)
|
||||
// LOOPBACK-Selftest (ohne Bus)
|
||||
static bool _selftest_loopback(uint16_t windowMs)
|
||||
{
|
||||
if (!_trySetMode(MCP_LOOPBACK, s_modeSettleMs)) return false;
|
||||
if (!_trySetMode(MCP_LOOPBACK, s_modeSettleMs))
|
||||
return false;
|
||||
|
||||
const unsigned long tid = 0x123;
|
||||
uint8_t tx[8] = {0xA5, 0x5A, 0x11, 0x22, 0x33, 0x44, 0x77, 0x88};
|
||||
if (CAN0.sendMsgBuf(tid, 0, 8, tx) != CAN_OK) {
|
||||
if (CAN0.sendMsgBuf(tid, 0, 8, tx) != CAN_OK)
|
||||
{
|
||||
(void)_trySetMode(MCP_NORMAL, s_modeSettleMs);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool got = false;
|
||||
const uint32_t t0 = millis();
|
||||
while ((millis() - t0) < windowMs) {
|
||||
if (CAN0.checkReceive() == CAN_MSGAVAIL) {
|
||||
unsigned long rid; uint8_t len, rx[8];
|
||||
if (CAN0.readMsgBuf(&rid, &len, rx) == CAN_OK) {
|
||||
if (rid == tid && len == 8 && memcmp(tx, rx, 8) == 0) { got = true; break; }
|
||||
while ((millis() - t0) < windowMs)
|
||||
{
|
||||
if (CAN0.checkReceive() == CAN_MSGAVAIL)
|
||||
{
|
||||
unsigned long rid;
|
||||
uint8_t len, rx[8];
|
||||
if (CAN0.readMsgBuf(&rid, &len, rx) == CAN_OK)
|
||||
{
|
||||
if (rid == tid && len == 8 && memcmp(tx, rx, 8) == 0)
|
||||
{
|
||||
got = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
delay(1);
|
||||
@@ -55,60 +83,153 @@ static bool _selftest_loopback(uint16_t windowMs)
|
||||
// Optional: kurzer ListenOnly-Hörtest (nur Heuristik, keine DTC-Änderung)
|
||||
static void _probe_listen_only(uint16_t ms)
|
||||
{
|
||||
if (ms == 0) return;
|
||||
if (!_trySetMode(MCP_LISTENONLY, s_modeSettleMs)) return;
|
||||
if (ms == 0)
|
||||
return;
|
||||
if (!_trySetMode(MCP_LISTENONLY, s_modeSettleMs))
|
||||
return;
|
||||
const uint32_t t0 = millis();
|
||||
while ((millis() - t0) < ms) {
|
||||
if (CAN0.checkReceive() == CAN_MSGAVAIL) break;
|
||||
while ((millis() - t0) < ms)
|
||||
{
|
||||
if (CAN0.checkReceive() == CAN_MSGAVAIL)
|
||||
break;
|
||||
delay(1);
|
||||
}
|
||||
(void)_trySetMode(MCP_NORMAL, s_modeSettleMs);
|
||||
}
|
||||
|
||||
// ==== Öffentliche API ====
|
||||
|
||||
bool CAN_HAL_Init(const CanHalConfig& cfg)
|
||||
// Offen konfigurieren (RAW-Sniffer)
|
||||
static bool _apply_open_filters()
|
||||
{
|
||||
s_ready = false;
|
||||
s_modeSettleMs = cfg.modeSettleMs ? cfg.modeSettleMs : 10;
|
||||
|
||||
// 1) SPI/MCP starten
|
||||
// HIER: MCP_STDEXT statt MCP_STD, damit die Lib nicht ins default/Failure läuft
|
||||
if (CAN0.begin(MCP_STDEXT, cfg.baud, cfg.clock) != CAN_OK) {
|
||||
MaintainDTC(DTC_CAN_TRANSCEIVER_FAILED, true);
|
||||
if (!_trySetMode(MODE_CONFIG, s_modeSettleMs))
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2) Loopback‑Selftest (ohne Bus)
|
||||
if (!_selftest_loopback(20)) {
|
||||
MaintainDTC(DTC_CAN_TRANSCEIVER_FAILED, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3) Optional Listen‑Only‑Probe (nur Info)
|
||||
_probe_listen_only(cfg.listenOnlyProbeMs);
|
||||
|
||||
// 4) Default: Filter/Masks neutral, Mode NORMAL
|
||||
// -> Für Masken/Filter müssen wir in CONFIG sein (hier: MODE_CONFIG laut deiner Lib)
|
||||
if (!_trySetMode(MODE_CONFIG, s_modeSettleMs)) {
|
||||
MaintainDTC(DTC_CAN_TRANSCEIVER_FAILED, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
// weit offen (STD)
|
||||
// Masken 0 -> alles durchlassen
|
||||
CAN0.init_Mask(0, 0, _std_to_hw(0x000));
|
||||
CAN0.init_Mask(1, 0, _std_to_hw(0x000));
|
||||
for (uint8_t i = 0; i < 6; ++i) {
|
||||
// Filter egal
|
||||
for (uint8_t i = 0; i < 6; ++i)
|
||||
{
|
||||
CAN0.init_Filt(i, 0, _std_to_hw(0x000));
|
||||
}
|
||||
s_nextFiltSlot = 0;
|
||||
|
||||
if (!_trySetMode(MCP_NORMAL, s_modeSettleMs)) {
|
||||
return _trySetMode(MCP_NORMAL, s_modeSettleMs);
|
||||
}
|
||||
|
||||
// Gespeicherte Normal-Konfiguration anwenden
|
||||
static bool _apply_saved_filters()
|
||||
{
|
||||
if (!_trySetMode(MODE_CONFIG, s_modeSettleMs))
|
||||
return false;
|
||||
|
||||
CAN0.init_Mask(0, 0, _std_to_hw(s_savedStdMask[0]));
|
||||
CAN0.init_Mask(1, 0, _std_to_hw(s_savedStdMask[1]));
|
||||
|
||||
// Erst alle Filter neutralisieren
|
||||
for (uint8_t i = 0; i < 6; ++i)
|
||||
{
|
||||
CAN0.init_Filt(i, 0, _std_to_hw(0x000));
|
||||
}
|
||||
|
||||
// Dann gespeicherte Filter wieder setzen
|
||||
s_nextFiltSlot = 0;
|
||||
for (uint8_t i = 0; i < s_savedFiltCount && s_nextFiltSlot < 6; ++i)
|
||||
{
|
||||
const auto &F = s_savedFilt[i];
|
||||
const uint32_t hwId = F.ext ? F.id : _std_to_hw((uint16_t)F.id);
|
||||
CAN0.init_Filt(s_nextFiltSlot++, F.ext ? 1 : 0, hwId);
|
||||
}
|
||||
|
||||
return _trySetMode(MCP_NORMAL, s_modeSettleMs);
|
||||
}
|
||||
|
||||
// =====================
|
||||
// Öffentliche API
|
||||
// =====================
|
||||
|
||||
void CAN_HAL_SetTraceSink(CanTraceSink sink)
|
||||
{
|
||||
s_traceSink = sink;
|
||||
}
|
||||
|
||||
void CAN_HAL_EnableRawSniffer(bool enable)
|
||||
{
|
||||
if (enable == s_rawSnifferEnabled)
|
||||
return;
|
||||
|
||||
if (enable)
|
||||
{
|
||||
// Auf RAW öffnen
|
||||
if (_apply_open_filters())
|
||||
{
|
||||
s_rawSnifferEnabled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Falls es nicht klappt, lieber Defekt melden als im Zwischending zu bleiben
|
||||
MaintainDTC(DTC_CAN_TRANSCEIVER_FAILED, true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Gespeicherte "Normal"-Konfiguration wieder aktivieren
|
||||
if (_apply_saved_filters())
|
||||
{
|
||||
s_rawSnifferEnabled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
MaintainDTC(DTC_CAN_TRANSCEIVER_FAILED, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CAN_HAL_IsRawSnifferEnabled()
|
||||
{
|
||||
return s_rawSnifferEnabled;
|
||||
}
|
||||
|
||||
bool CAN_HAL_Init(const CanHalConfig &cfg)
|
||||
{
|
||||
s_ready = false;
|
||||
s_modeSettleMs = cfg.modeSettleMs ? cfg.modeSettleMs : 10;
|
||||
s_traceSink = nullptr;
|
||||
s_rawSnifferEnabled = false;
|
||||
|
||||
// 1) SPI/MCP starten (STDEXT ist robust gegen Fehlpfade in Lib-Forks)
|
||||
if (CAN0.begin(MCP_STDEXT, cfg.baud, cfg.clock) != CAN_OK)
|
||||
{
|
||||
MaintainDTC(DTC_CAN_TRANSCEIVER_FAILED, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Erfolgreich
|
||||
// 2) Loopback-Selftest (ohne Bus)
|
||||
if (!_selftest_loopback(20))
|
||||
{
|
||||
MaintainDTC(DTC_CAN_TRANSCEIVER_FAILED, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3) Optional Listen-Only-Probe (nur Info)
|
||||
_probe_listen_only(cfg.listenOnlyProbeMs);
|
||||
|
||||
// 4) Default: Filter/Masks offen, Mode NORMAL
|
||||
if (!_apply_open_filters())
|
||||
{
|
||||
MaintainDTC(DTC_CAN_TRANSCEIVER_FAILED, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initiale "Normal"-Spiegelung: alles offen (bis die App später echte Filter setzt)
|
||||
s_savedStdMask[0] = 0x000;
|
||||
s_savedStdMask[1] = 0x000;
|
||||
s_savedFiltCount = 0;
|
||||
for (uint8_t i = 0; i < 6; ++i)
|
||||
{
|
||||
s_savedFilt[i].id = 0;
|
||||
s_savedFilt[i].ext = false;
|
||||
}
|
||||
|
||||
MaintainDTC(DTC_CAN_TRANSCEIVER_FAILED, false);
|
||||
s_ready = true;
|
||||
return true;
|
||||
@@ -119,18 +240,30 @@ bool CAN_HAL_IsReady() { return s_ready; }
|
||||
bool CAN_HAL_SetMode(uint8_t mode)
|
||||
{
|
||||
const bool ok = _trySetMode(mode, s_modeSettleMs);
|
||||
if (!ok) MaintainDTC(DTC_CAN_TRANSCEIVER_FAILED, true);
|
||||
if (!ok)
|
||||
MaintainDTC(DTC_CAN_TRANSCEIVER_FAILED, true);
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool CAN_HAL_SetMask(uint8_t bank, bool ext, uint32_t rawMask)
|
||||
{
|
||||
if (bank > 1) return false;
|
||||
if (!CAN_HAL_SetMode(MODE_CONFIG)) return false;
|
||||
if (bank > 1)
|
||||
return false;
|
||||
|
||||
if (!CAN_HAL_SetMode(MODE_CONFIG))
|
||||
return false;
|
||||
|
||||
const bool ok = (CAN0.init_Mask(bank, ext ? 1 : 0, rawMask) == CAN_OK);
|
||||
|
||||
if (!CAN_HAL_SetMode(MCP_NORMAL)) {
|
||||
// Spiegeln (nur STD-11 Spiegel führen wir – ext-Masken selten; bei ext ignorieren)
|
||||
if (!ext)
|
||||
{
|
||||
const uint16_t m11 = (uint16_t)(rawMask >> 16);
|
||||
s_savedStdMask[bank] = m11;
|
||||
}
|
||||
|
||||
if (!CAN_HAL_SetMode(MCP_NORMAL))
|
||||
{
|
||||
MaintainDTC(DTC_CAN_TRANSCEIVER_FAILED, true);
|
||||
return false;
|
||||
}
|
||||
@@ -144,85 +277,170 @@ bool CAN_HAL_SetStdMask11(uint8_t bank, uint16_t mask11)
|
||||
|
||||
void CAN_HAL_ClearFilters()
|
||||
{
|
||||
if (!CAN_HAL_SetMode(MODE_CONFIG)) {
|
||||
if (!CAN_HAL_SetMode(MODE_CONFIG))
|
||||
{
|
||||
MaintainDTC(DTC_CAN_TRANSCEIVER_FAILED, true);
|
||||
return;
|
||||
}
|
||||
|
||||
CAN0.init_Mask(0, 0, _std_to_hw(0x000));
|
||||
CAN0.init_Mask(1, 0, _std_to_hw(0x000));
|
||||
for (uint8_t i = 0; i < 6; ++i) {
|
||||
for (uint8_t i = 0; i < 6; ++i)
|
||||
{
|
||||
CAN0.init_Filt(i, 0, _std_to_hw(0x000));
|
||||
}
|
||||
s_nextFiltSlot = 0;
|
||||
|
||||
if (!CAN_HAL_SetMode(MCP_NORMAL)) {
|
||||
// Spiegel auch zurücksetzen
|
||||
s_savedStdMask[0] = 0x000;
|
||||
s_savedStdMask[1] = 0x000;
|
||||
s_savedFiltCount = 0;
|
||||
for (uint8_t i = 0; i < 6; ++i)
|
||||
{
|
||||
s_savedFilt[i].id = 0;
|
||||
s_savedFilt[i].ext = false;
|
||||
}
|
||||
|
||||
if (!CAN_HAL_SetMode(MCP_NORMAL))
|
||||
{
|
||||
MaintainDTC(DTC_CAN_TRANSCEIVER_FAILED, true);
|
||||
}
|
||||
}
|
||||
|
||||
bool CAN_HAL_AddFilter(const CanFilter& f)
|
||||
bool CAN_HAL_AddFilter(const CanFilter &f)
|
||||
{
|
||||
if (s_nextFiltSlot >= 6) return false;
|
||||
if (!CAN_HAL_SetMode(MODE_CONFIG)) {
|
||||
if (s_nextFiltSlot >= 6)
|
||||
return false;
|
||||
if (!CAN_HAL_SetMode(MODE_CONFIG))
|
||||
{
|
||||
MaintainDTC(DTC_CAN_TRANSCEIVER_FAILED, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint32_t hwId = f.ext ? f.id : _std_to_hw((uint16_t)f.id);
|
||||
const uint8_t slot = s_nextFiltSlot++;
|
||||
const bool ok = (CAN0.init_Filt(slot, f.ext ? 1 : 0, hwId) == CAN_OK);
|
||||
const uint8_t slot = s_nextFiltSlot++;
|
||||
const bool ok = (CAN0.init_Filt(slot, f.ext ? 1 : 0, hwId) == CAN_OK);
|
||||
|
||||
if (!CAN_HAL_SetMode(MCP_NORMAL)) {
|
||||
// Spiegeln
|
||||
if (ok)
|
||||
{
|
||||
if (s_savedFiltCount < 6)
|
||||
{
|
||||
s_savedFilt[s_savedFiltCount].id = f.ext ? f.id : (uint32_t)((uint16_t)f.id);
|
||||
s_savedFilt[s_savedFiltCount].ext = f.ext;
|
||||
++s_savedFiltCount;
|
||||
}
|
||||
}
|
||||
|
||||
if (!CAN_HAL_SetMode(MCP_NORMAL))
|
||||
{
|
||||
MaintainDTC(DTC_CAN_TRANSCEIVER_FAILED, true);
|
||||
return false;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool CAN_HAL_SetFilters(const CanFilter* list, size_t count)
|
||||
bool CAN_HAL_SetFilters(const CanFilter *list, size_t count)
|
||||
{
|
||||
if (!CAN_HAL_SetMode(MODE_CONFIG)) {
|
||||
if (!CAN_HAL_SetMode(MODE_CONFIG))
|
||||
{
|
||||
MaintainDTC(DTC_CAN_TRANSCEIVER_FAILED, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Slots zurücksetzen
|
||||
s_nextFiltSlot = 0;
|
||||
for (uint8_t i = 0; i < 6; ++i) {
|
||||
for (uint8_t i = 0; i < 6; ++i)
|
||||
{
|
||||
CAN0.init_Filt(i, 0, _std_to_hw(0x000));
|
||||
}
|
||||
|
||||
// Setzen
|
||||
for (size_t i = 0; i < count && s_nextFiltSlot < 6; ++i) {
|
||||
const auto& f = list[i];
|
||||
for (size_t i = 0; i < count && s_nextFiltSlot < 6; ++i)
|
||||
{
|
||||
const auto &f = list[i];
|
||||
const uint32_t hwId = f.ext ? f.id : _std_to_hw((uint16_t)f.id);
|
||||
CAN0.init_Filt(s_nextFiltSlot++, f.ext ? 1 : 0, hwId);
|
||||
}
|
||||
|
||||
if (!CAN_HAL_SetMode(MCP_NORMAL)) {
|
||||
// Spiegel aktualisieren
|
||||
s_savedFiltCount = 0;
|
||||
for (size_t i = 0; i < count && i < 6; ++i)
|
||||
{
|
||||
s_savedFilt[i].id = list[i].ext ? list[i].id : (uint32_t)((uint16_t)list[i].id);
|
||||
s_savedFilt[i].ext = list[i].ext;
|
||||
++s_savedFiltCount;
|
||||
}
|
||||
|
||||
if (!CAN_HAL_SetMode(MCP_NORMAL))
|
||||
{
|
||||
MaintainDTC(DTC_CAN_TRANSCEIVER_FAILED, true);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CAN_HAL_Read(unsigned long& id, uint8_t& len, uint8_t data[8])
|
||||
bool CAN_HAL_Read(unsigned long &id, uint8_t &len, uint8_t data[8])
|
||||
{
|
||||
if (CAN0.checkReceive() != CAN_MSGAVAIL) return false;
|
||||
if (CAN0.readMsgBuf(&id, &len, data) != CAN_OK) {
|
||||
if (CAN0.checkReceive() != CAN_MSGAVAIL)
|
||||
return false;
|
||||
|
||||
if (CAN0.readMsgBuf(&id, &len, data) != CAN_OK)
|
||||
{
|
||||
// Echte Lese-Fehler -> vermutlich SPI/Controller-Problem
|
||||
MaintainDTC(DTC_CAN_TRANSCEIVER_FAILED, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
// MCP_CAN schreibt Flags in das ID-Wort:
|
||||
// bit31 = EXT, bit30 = RTR, Rest = rohe ID (11 oder 29 Bit)
|
||||
const bool ext = (id & 0x80000000UL) != 0;
|
||||
const bool rtr = (id & 0x40000000UL) != 0; // aktuell nur informativ
|
||||
|
||||
// "Saubere" ID für Aufrufer herstellen
|
||||
const uint32_t clean_id = ext ? (id & 0x1FFFFFFFUL) : (id & 0x7FFUL);
|
||||
id = clean_id;
|
||||
|
||||
// Trace-Hook (RX)
|
||||
if (s_traceSink)
|
||||
{
|
||||
CanLogFrame f{};
|
||||
f.ts_ms = millis();
|
||||
f.id = clean_id;
|
||||
f.ext = ext;
|
||||
f.rx = true;
|
||||
f.dlc = len;
|
||||
if (len)
|
||||
memcpy(f.data, data, len);
|
||||
s_traceSink(f);
|
||||
}
|
||||
|
||||
// Optional: Wenn du RTR-Frames speziell behandeln willst, könntest du hier
|
||||
// (rtr==true) markieren/loggen oder len=0 erzwingen. Für jetzt: einfach durchreichen.
|
||||
(void)rtr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t CAN_HAL_Send(unsigned long id, bool ext, uint8_t len, const uint8_t* data)
|
||||
uint8_t CAN_HAL_Send(unsigned long id, bool ext, uint8_t len, const uint8_t *data)
|
||||
{
|
||||
// Sende-Fehler (CAN_FAILTX) müssen nicht zwingend Transceiver-Defekte sein (z. B. Bus-Off).
|
||||
// Höhere Ebene kann bei Bedarf DTCs setzen. Hier nur durchreichen.
|
||||
return CAN0.sendMsgBuf(id, ext ? 1 : 0, len, const_cast<uint8_t*>(data));
|
||||
// Senden
|
||||
uint8_t st = CAN0.sendMsgBuf(id, ext ? 1 : 0, len, const_cast<uint8_t *>(data));
|
||||
|
||||
// Trace-Hook (TX) nur bei Erfolg loggen – optional: immer loggen
|
||||
if (st == CAN_OK && s_traceSink)
|
||||
{
|
||||
CanLogFrame f{};
|
||||
f.ts_ms = millis();
|
||||
f.id = id;
|
||||
f.ext = ext;
|
||||
f.rx = false;
|
||||
f.dlc = len;
|
||||
if (len)
|
||||
memcpy(f.data, data, len);
|
||||
s_traceSink(f);
|
||||
}
|
||||
return st;
|
||||
}
|
||||
|
||||
// ==== Diagnose/Utilities ====
|
||||
@@ -233,7 +451,7 @@ uint8_t CAN_HAL_GetErrorFlags()
|
||||
return CAN0.getError();
|
||||
}
|
||||
|
||||
void CAN_HAL_GetErrorCounters(uint8_t& tec, uint8_t& rec)
|
||||
void CAN_HAL_GetErrorCounters(uint8_t &tec, uint8_t &rec)
|
||||
{
|
||||
tec = CAN0.errorCountTX();
|
||||
rec = CAN0.errorCountRX();
|
||||
|
Reference in New Issue
Block a user