reworked CAN Stack
This commit is contained in:
240
Software/src/can_hal.cpp
Normal file
240
Software/src/can_hal.cpp
Normal file
@@ -0,0 +1,240 @@
|
||||
#include "can_hal.h"
|
||||
#include "dtc.h"
|
||||
// #include "debugger.h" // optional für Logs
|
||||
|
||||
// ==== Interner Zustand/Helper ====
|
||||
MCP_CAN CAN0(GPIO_CS_CAN);
|
||||
|
||||
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
|
||||
|
||||
// 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;
|
||||
delay(1);
|
||||
} while ((millis() - t0) < settleMs);
|
||||
return false;
|
||||
}
|
||||
|
||||
// LOOPBACK-Selftest (ohne Bus)
|
||||
static bool _selftest_loopback(uint16_t windowMs)
|
||||
{
|
||||
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) {
|
||||
(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; }
|
||||
}
|
||||
}
|
||||
delay(1);
|
||||
}
|
||||
|
||||
(void)_trySetMode(MCP_NORMAL, s_modeSettleMs);
|
||||
return got;
|
||||
}
|
||||
|
||||
// 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;
|
||||
const uint32_t t0 = millis();
|
||||
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)
|
||||
{
|
||||
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);
|
||||
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)
|
||||
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) {
|
||||
CAN0.init_Filt(i, 0, _std_to_hw(0x000));
|
||||
}
|
||||
s_nextFiltSlot = 0;
|
||||
|
||||
if (!_trySetMode(MCP_NORMAL, s_modeSettleMs)) {
|
||||
MaintainDTC(DTC_CAN_TRANSCEIVER_FAILED, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Erfolgreich
|
||||
MaintainDTC(DTC_CAN_TRANSCEIVER_FAILED, false);
|
||||
s_ready = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
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);
|
||||
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;
|
||||
|
||||
const bool ok = (CAN0.init_Mask(bank, ext ? 1 : 0, rawMask) == CAN_OK);
|
||||
|
||||
if (!CAN_HAL_SetMode(MCP_NORMAL)) {
|
||||
MaintainDTC(DTC_CAN_TRANSCEIVER_FAILED, true);
|
||||
return false;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool CAN_HAL_SetStdMask11(uint8_t bank, uint16_t mask11)
|
||||
{
|
||||
return CAN_HAL_SetMask(bank, false, _std_to_hw(mask11));
|
||||
}
|
||||
|
||||
void CAN_HAL_ClearFilters()
|
||||
{
|
||||
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) {
|
||||
CAN0.init_Filt(i, 0, _std_to_hw(0x000));
|
||||
}
|
||||
s_nextFiltSlot = 0;
|
||||
|
||||
if (!CAN_HAL_SetMode(MCP_NORMAL)) {
|
||||
MaintainDTC(DTC_CAN_TRANSCEIVER_FAILED, true);
|
||||
}
|
||||
}
|
||||
|
||||
bool CAN_HAL_AddFilter(const CanFilter& f)
|
||||
{
|
||||
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);
|
||||
|
||||
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)
|
||||
{
|
||||
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) {
|
||||
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];
|
||||
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)) {
|
||||
MaintainDTC(DTC_CAN_TRANSCEIVER_FAILED, true);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
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) {
|
||||
// Echte Lese-Fehler -> vermutlich SPI/Controller-Problem
|
||||
MaintainDTC(DTC_CAN_TRANSCEIVER_FAILED, true);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
// ==== Diagnose/Utilities ====
|
||||
|
||||
uint8_t CAN_HAL_GetErrorFlags()
|
||||
{
|
||||
// getError() liefert MCP_EFLG Snapshot (Lib-abhängig)
|
||||
return CAN0.getError();
|
||||
}
|
||||
|
||||
void CAN_HAL_GetErrorCounters(uint8_t& tec, uint8_t& rec)
|
||||
{
|
||||
tec = CAN0.errorCountTX();
|
||||
rec = CAN0.errorCountRX();
|
||||
}
|
Reference in New Issue
Block a user