diff --git a/examples/MCP2515-CAN-Sniffer/MCP2515-CAN-Sniffer.ino b/examples/MCP2515-CAN-Sniffer/MCP2515-CAN-Sniffer.ino index f735a2d..e69614f 100644 --- a/examples/MCP2515-CAN-Sniffer/MCP2515-CAN-Sniffer.ino +++ b/examples/MCP2515-CAN-Sniffer/MCP2515-CAN-Sniffer.ino @@ -38,7 +38,9 @@ ArduinoMCP2515 mcp2515([]() { digitalWrite(MKRCAN_MCP2515_CS_PIN, LOW); }, [](uint8_t const d) { return SPI.transfer(d); }, micros, onReceiveBufferFull, - nullptr); + nullptr, + [](MCP2515::EFLG const err_flag) { Serial.print("MCP2515::OnError, error code = "); Serial.println(MCP2515::toStr(err_flag)); }, + [](MCP2515::EFLG const err_flag) { Serial.print("MCP2515::OnError, error code = "); Serial.println(MCP2515::toStr(err_flag)); }); /************************************************************************************** * SETUP/LOOP diff --git a/keywords.txt b/keywords.txt index dd25dcc..a002674 100644 --- a/keywords.txt +++ b/keywords.txt @@ -9,6 +9,7 @@ ArduinoMCP2515 KEYWORD1 CanBitRate KEYWORD1 MCP2515 KEYWORD1 +EFLG KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) @@ -24,6 +25,7 @@ setConfigMode KEYWORD2 enableFilter KEYWORD2 transmit KEYWORD2 onExternalEventHandler KEYWORD2 +toStr KEYWORD2 ####################################### # Constants (LITERAL1) diff --git a/src/ArduinoMCP2515.cpp b/src/ArduinoMCP2515.cpp index cf54efb..2a058b3 100644 --- a/src/ArduinoMCP2515.cpp +++ b/src/ArduinoMCP2515.cpp @@ -87,13 +87,17 @@ ArduinoMCP2515::ArduinoMCP2515(SpiSelectFunc select, SpiTransferFunc transfer, MicroSecondFunc micros, OnReceiveBufferFullFunc on_rx_buf_full, - OnTransmitBufferEmptyFunc on_tx_buf_empty) + OnTransmitBufferEmptyFunc on_tx_buf_empty, + OnCanErrorFunc on_error, + OnCanWarningFunc on_warning) : _io{select, deselect, transfer} , _cfg{_io} , _ctrl{_io} , _micros{micros} , _on_rx_buf_full{on_rx_buf_full} , _on_tx_buf_empty{on_tx_buf_empty} +, _on_error{on_error} +, _on_warning{on_warning} { } @@ -121,6 +125,13 @@ void ArduinoMCP2515::begin() _cfg.enableIntFlag(CANINTE::RX0IE); _cfg.enableIntFlag(CANINTE::RX1IE); } + + /* Conditionally enable error interrupt + * only if we have error callbacks + * registered. + */ + if (_on_error || _on_warning) + _cfg.enableIntFlag(CANINTE::ERRIE); } void ArduinoMCP2515::setBitRate(CanBitRate const bit_rate) @@ -164,6 +175,9 @@ bool ArduinoMCP2515::transmit(uint32_t const id, uint8_t const * data, uint8_t c void ArduinoMCP2515::onExternalEventHandler() { + /* Obtain current status and call the appropriate callback + * handlers to facilitate the necessary actions. + */ uint8_t const status = _ctrl.status(); if(isBitSet(status, bp(STATUS::RX0IF))) onReceiveBuffer_0_Full(); @@ -171,6 +185,46 @@ void ArduinoMCP2515::onExternalEventHandler() if(isBitSet(status, bp(STATUS::TX0IF))) onTransmitBuffer_0_Empty(); if(isBitSet(status, bp(STATUS::TX1IF))) onTransmitBuffer_1_Empty(); if(isBitSet(status, bp(STATUS::TX2IF))) onTransmitBuffer_2_Empty(); + + + /* Only perform error checks if a callback has been + * registered. Otherwise, it's an unnecessary SPI + * transaction which consumes valuable processing + * time. + */ + if (!_on_error && !_on_warning) + return; + + /* Check if an error flag is set and - should an error flag + * be set - deal with it. + */ + uint8_t const error_flag = _ctrl.error(); + + bool const is_error = (error_flag > EFLG_ERR_MASK) > 0; + if (is_error && _on_error) + { + _on_error(static_cast(error_flag & EFLG_ERR_MASK)); + + /* RX0OVR and RX1OVR need to be cleared manually, + * otherwise the error will persist and we will + * not be able to ever again obtain received + * CAN frames. + */ + if (isBitSet(error_flag, bp(EFLG::RX0OVR))) + _ctrl.clearErrFlag(EFLG::RX0OVR); + + if (isBitSet(error_flag, bp(EFLG::RX1OVR))) + _ctrl.clearErrFlag(EFLG::RX1OVR); + } + + bool const is_warning = (error_flag > EFLG_WAR_MASK) > 0; + if (is_warning && _on_warning) + _on_warning(static_cast(error_flag & EFLG_WAR_MASK)); + + /* Finally clear the error interrupt flag so that we are not + * continuously caught in the ERROR handling loop. + */ + _ctrl.clearIntFlag(CANINTF::ERRIF); } /************************************************************************************** diff --git a/src/ArduinoMCP2515.h b/src/ArduinoMCP2515.h index f8925d3..5f7cac3 100644 --- a/src/ArduinoMCP2515.h +++ b/src/ArduinoMCP2515.h @@ -90,6 +90,8 @@ typedef std::function OnTransmitBufferEmptyFunc; +typedef std::function OnCanErrorFunc; +typedef std::function OnCanWarningFunc; /************************************************************************************** * CLASS DECLARATION @@ -105,7 +107,18 @@ class ArduinoMCP2515 MCP2515::SpiTransferFunc transfer, MicroSecondFunc micros, OnReceiveBufferFullFunc on_rx_buf_full, - OnTransmitBufferEmptyFunc on_tx_buf_empty); + OnTransmitBufferEmptyFunc on_tx_buf_empty, + OnCanErrorFunc on_error, + OnCanWarningFunc on_warning); + + ArduinoMCP2515(MCP2515::SpiSelectFunc select, + MCP2515::SpiDeselectFunc deselect, + MCP2515::SpiTransferFunc transfer, + MicroSecondFunc micros, + OnReceiveBufferFullFunc on_rx_buf_full, + OnTransmitBufferEmptyFunc on_tx_buf_empty) + : ArduinoMCP2515{select, deselect, transfer, micros, on_rx_buf_full, on_tx_buf_empty, nullptr, nullptr} + { } void begin(); @@ -137,6 +150,8 @@ class ArduinoMCP2515 MicroSecondFunc _micros; OnReceiveBufferFullFunc _on_rx_buf_full; OnTransmitBufferEmptyFunc _on_tx_buf_empty; + OnCanErrorFunc _on_error; + OnCanWarningFunc _on_warning; bool transmitCANFrame (uint32_t const id, uint8_t const * data, uint8_t const len); void onReceiveBuffer_0_Full (); diff --git a/src/MCP2515/MCP2515_Const.cpp b/src/MCP2515/MCP2515_Const.cpp new file mode 100644 index 0000000..f16c96b --- /dev/null +++ b/src/MCP2515/MCP2515_Const.cpp @@ -0,0 +1,45 @@ +/** + * This software is distributed under the terms of the MIT License. + * Copyright (c) 2020 LXRobotics. + * Author: Alexander Entinger + * Contributors: https://github.com/107-systems/107-Arduino-MCP2515/graphs/contributors. + */ + +/************************************************************************************** + * INCLUDE + **************************************************************************************/ + +#include "MCP2515_Const.h" + +/************************************************************************************** + * NAMESPACE + **************************************************************************************/ + +namespace MCP2515 +{ + +/************************************************************************************** + * FUNCTION DEFINITION + **************************************************************************************/ + +const char * toStr(EFLG const err_flag) +{ + switch(err_flag) + { + case EFLG::RX1OVR: return "RX1OVR"; break; + case EFLG::RX0OVR: return "RX0OVR"; break; + case EFLG::TXBO : return "TXBO"; break; + case EFLG::TXEP : return "TXEP"; break; + case EFLG::RXEP : return "RXEP"; break; + case EFLG::TXWAR : return "TXWAR"; break; + case EFLG::RXWAR : return "RXWAR"; break; + case EFLG::EWARN : return "EWARN"; break; + default: __builtin_unreachable(); return ""; break; + } +} + +/************************************************************************************** + * NAMESPACE + **************************************************************************************/ + +} /* MCP2515 */ diff --git a/src/MCP2515/MCP2515_Const.h b/src/MCP2515/MCP2515_Const.h index 874d174..f76020f 100644 --- a/src/MCP2515/MCP2515_Const.h +++ b/src/MCP2515/MCP2515_Const.h @@ -154,6 +154,7 @@ enum class STATUS : uint8_t enum class CANINTE : uint8_t { + ERRIE = 5, TX2IE = 4, TX1IE = 3, TX0IE = 2, @@ -222,6 +223,18 @@ enum class TXBnCTRL : uint8_t TXREQ = 3, }; +enum class EFLG : uint8_t +{ + RX1OVR = 7, + RX0OVR = 6, + TXBO = 5, + TXEP = 4, + RXEP = 3, + TXWAR = 2, + RXWAR = 1, + EWARN = 0, +}; + /************************************************************************************** * CONVERSION FUNCTIONS **************************************************************************************/ @@ -238,6 +251,12 @@ constexpr auto bm(Enumeration const value) -> typename std::underlying_type(len, 8)); } +uint8_t MCP2515_Control::error() +{ + return _io.readRegister(Register::EFLG); +} /************************************************************************************** * NAMESPACE diff --git a/src/MCP2515/MCP2515_Control.h b/src/MCP2515/MCP2515_Control.h index 852ba6b..3cadbcb 100644 --- a/src/MCP2515/MCP2515_Control.h +++ b/src/MCP2515/MCP2515_Control.h @@ -33,13 +33,15 @@ class MCP2515_Control MCP2515_Control(MCP2515_Io & io); - void transmit (TxB const txb, uint32_t const id, uint8_t const * data, uint8_t const len); - void receive (RxB const rxb, uint32_t & id, uint8_t * data, uint8_t & len); + void transmit(TxB const txb, uint32_t const id, uint8_t const * data, uint8_t const len); + void receive (RxB const rxb, uint32_t & id, uint8_t * data, uint8_t & len); + + uint8_t error(); inline void reset () { _io.reset(); } inline uint8_t status () { return _io.status(); } - inline void clearIntFlag(CANINTF const int_flag) { _io.clrBit(Register::CANINTF, bp(int_flag)); } - + inline void clearIntFlag(CANINTF const int_flag) { _io.clrBit(Register::CANINTF, bp(int_flag)); } + inline void clearErrFlag(EFLG const err_flag) { _io.clrBit(Register::EFLG, bp(err_flag)); } private: