diff --git a/Adafruit_ZeroCAN.cpp b/Adafruit_ZeroCAN.cpp index db5cd99..9fe57fd 100644 --- a/Adafruit_ZeroCAN.cpp +++ b/Adafruit_ZeroCAN.cpp @@ -29,13 +29,13 @@ #include "Adafruit_ZeroCAN.h" #include "wiring_private.h" - + #include "same51.h" -#define hw (reinterpret_cast(this->_hw)) +#define hw (reinterpret_cast(this->_hw)) -#define DIV_ROUND(a, b) (((a) + (b)/2) / (b)) -#define DIV_ROUND_UP(a, b) (((a) + (b) - 1) / (b)) +#define DIV_ROUND(a, b) (((a) + (b) / 2) / (b)) +#define DIV_ROUND_UP(a, b) (((a) + (b)-1) / (b)) #define GCLK_CAN1 GCLK_PCHCTRL_GEN_GCLK1_Val #define ADAFRUIT_ZEROCAN_TX_BUFFER_SIZE (1) @@ -52,53 +52,52 @@ typedef CanMramXifde CanMramXidfe; typedef uint32_t can_filter_t; struct _adafruit_ZeroCAN_tx_buf { - CAN_TXBE_0_Type txb0; - CAN_TXBE_1_Type txb1; - __attribute__((aligned(4))) - uint8_t data[8]; + CAN_TXBE_0_Type txb0; + CAN_TXBE_1_Type txb1; + __attribute__((aligned(4))) uint8_t data[8]; }; struct _adafruit_ZeroCAN_rx_fifo { - CAN_RXF0E_0_Type rxf0; - CAN_RXF0E_1_Type rxf1; - __attribute((aligned(4))) - uint8_t data[ADAFRUIT_ZEROCAN_MAX_MESSAGE_LENGTH]; + CAN_RXF0E_0_Type rxf0; + CAN_RXF0E_1_Type rxf1; + __attribute((aligned(4))) uint8_t data[ADAFRUIT_ZEROCAN_MAX_MESSAGE_LENGTH]; } can_rx_fifo_t; struct _adafruit_ZeroCAN_state { - _adafruit_ZeroCAN_tx_buf tx_buffer[ADAFRUIT_ZEROCAN_TX_BUFFER_SIZE]; - _adafruit_ZeroCAN_rx_fifo rx0_fifo[ADAFRUIT_ZEROCAN_RX_FIFO_SIZE]; - _adafruit_ZeroCAN_rx_fifo rx1_fifo[ADAFRUIT_ZEROCAN_RX_FIFO_SIZE]; - CanMramSidfe standard_rx_filter[ADAFRUIT_ZEROCAN_RX_FILTER_SIZE]; - CanMramXifde extended_rx_filter[ADAFRUIT_ZEROCAN_RX_FILTER_SIZE]; + _adafruit_ZeroCAN_tx_buf tx_buffer[ADAFRUIT_ZEROCAN_TX_BUFFER_SIZE]; + _adafruit_ZeroCAN_rx_fifo rx0_fifo[ADAFRUIT_ZEROCAN_RX_FIFO_SIZE]; + _adafruit_ZeroCAN_rx_fifo rx1_fifo[ADAFRUIT_ZEROCAN_RX_FIFO_SIZE]; + CanMramSidfe standard_rx_filter[ADAFRUIT_ZEROCAN_RX_FILTER_SIZE]; + CanMramXifde extended_rx_filter[ADAFRUIT_ZEROCAN_RX_FILTER_SIZE]; }; namespace { - // This data must be in the first 64kB of RAM. The "canram" section - // receives special support from the linker file in the Feather M4 CAN's - // board support package. - // TODO support CAN0 and CAN1 simultaneously (state would be an array of 2) - __attribute__((section("canram"))) - _adafruit_ZeroCAN_state can_state; +// This data must be in the first 64kB of RAM. The "canram" section +// receives special support from the linker file in the Feather M4 CAN's +// board support package. +// TODO support CAN0 and CAN1 simultaneously (state would be an array of 2) +__attribute__((section("canram"))) _adafruit_ZeroCAN_state can_state; - constexpr uint32_t can_frequency = VARIANT_GCLK1_FREQ; - bool compute_nbtp(uint32_t baudrate, CAN_NBTP_Type &result) { - uint32_t clocks_per_bit = DIV_ROUND(can_frequency, baudrate); - uint32_t clocks_to_sample = DIV_ROUND(clocks_per_bit * 7, 8); - uint32_t clocks_after_sample = clocks_per_bit - clocks_to_sample; - uint32_t divisor = std::max(DIV_ROUND_UP(clocks_to_sample, 256), DIV_ROUND_UP(clocks_after_sample, 128)); - if (divisor > 32) { - return false; - } - result.bit.NTSEG1 = DIV_ROUND(clocks_to_sample, divisor) - 2; - result.bit.NTSEG2 = DIV_ROUND(clocks_after_sample, divisor) - 2; - result.bit.NBRP = divisor - 1; - result.bit.NSJW = DIV_ROUND(clocks_after_sample, divisor * 4); - return true; - } +constexpr uint32_t can_frequency = VARIANT_GCLK1_FREQ; +bool compute_nbtp(uint32_t baudrate, CAN_NBTP_Type &result) { + uint32_t clocks_per_bit = DIV_ROUND(can_frequency, baudrate); + uint32_t clocks_to_sample = DIV_ROUND(clocks_per_bit * 7, 8); + uint32_t clocks_after_sample = clocks_per_bit - clocks_to_sample; + uint32_t divisor = std::max(DIV_ROUND_UP(clocks_to_sample, 256), + DIV_ROUND_UP(clocks_after_sample, 128)); + if (divisor > 32) { + return false; + } + result.bit.NTSEG1 = DIV_ROUND(clocks_to_sample, divisor) - 2; + result.bit.NTSEG2 = DIV_ROUND(clocks_after_sample, divisor) - 2; + result.bit.NBRP = divisor - 1; + result.bit.NSJW = DIV_ROUND(clocks_after_sample, divisor * 4); + return true; } +} // namespace -Adafruit_ZeroCAN::Adafruit_ZeroCAN(uint8_t TX_PIN, uint8_t RX_PIN) : _tx(TX_PIN), _rx(RX_PIN) {} +Adafruit_ZeroCAN::Adafruit_ZeroCAN(uint8_t TX_PIN, uint8_t RX_PIN) + : _tx(TX_PIN), _rx(RX_PIN) {} #ifdef PIN_CAN_TX Adafruit_ZeroCAN::Adafruit_ZeroCAN() : _tx(PIN_CAN_TX), _rx(PIN_CAN_RX) {} #else @@ -106,168 +105,170 @@ Adafruit_ZeroCAN::Adafruit_ZeroCAN() : _tx(-1) {} #endif bool Adafruit_ZeroCAN::begin(int baudrate, bool loopback, bool silent) { - if (_tx == -1) { - return false; - } - - CAN_NBTP_Type nbtp; - if (!compute_nbtp(baudrate, nbtp)) { - return false; - } + if (_tx == -1) { + return false; + } - // TODO: Support the CAN0 peripheral, which uses pinmux 8 - _hw = reinterpret_cast(CAN1); - state = &can_state; - memset(state, 0, sizeof(*state)); + CAN_NBTP_Type nbtp; + if (!compute_nbtp(baudrate, nbtp)) { + return false; + } - pinPeripheral(_tx, CAN1_FUNCTION); - pinPeripheral(_rx, CAN1_FUNCTION); + // TODO: Support the CAN0 peripheral, which uses pinmux 8 + _hw = reinterpret_cast(CAN1); + state = &can_state; + memset(state, 0, sizeof(*state)); - GCLK->PCHCTRL[CAN1_GCLK_ID].reg = GCLK_CAN1 | (1 << GCLK_PCHCTRL_CHEN_Pos); + pinPeripheral(_tx, CAN1_FUNCTION); + pinPeripheral(_rx, CAN1_FUNCTION); - // reset and allow configuration change - hw->CCCR.bit.INIT = 1; - while (!hw->CCCR.bit.INIT) { - } - hw->CCCR.bit.CCE = 1; - - // set loopback and silent modes - hw->CCCR.bit.MON = silent; - hw->CCCR.bit.TEST = loopback; - hw->TEST.bit.LBCK = loopback; + GCLK->PCHCTRL[CAN1_GCLK_ID].reg = GCLK_CAN1 | (1 << GCLK_PCHCTRL_CHEN_Pos); - // All TX data has an 8 byte payload (max) - { - CAN_TXESC_Type esc = {}; - esc.bit.TBDS = CAN_TXESC_TBDS_DATA8_Val; - CAN1->TXESC.reg = esc.reg; - } + // reset and allow configuration change + hw->CCCR.bit.INIT = 1; + while (!hw->CCCR.bit.INIT) { + } + hw->CCCR.bit.CCE = 1; - // Set up TX buffer - { - CAN_TXBC_Type bc = {}; - bc.bit.TBSA = (uint32_t)state->tx_buffer; - bc.bit.NDTB = ADAFRUIT_ZEROCAN_TX_BUFFER_SIZE; - bc.bit.TFQM = 0; // Messages are transmitted in the order submitted - CAN1->TXBC.reg = bc.reg; - } + // set loopback and silent modes + hw->CCCR.bit.MON = silent; + hw->CCCR.bit.TEST = loopback; + hw->TEST.bit.LBCK = loopback; - // All RX data has an 8 byte payload (max) - { - CAN_RXESC_Type esc = {}; - esc.bit.F0DS = CAN_RXESC_F0DS_DATA8_Val; - esc.bit.F1DS = CAN_RXESC_F1DS_DATA8_Val; - esc.bit.RBDS = CAN_RXESC_RBDS_DATA8_Val; - hw->RXESC.reg = esc.reg; - } + // All TX data has an 8 byte payload (max) + { + CAN_TXESC_Type esc = {}; + esc.bit.TBDS = CAN_TXESC_TBDS_DATA8_Val; + CAN1->TXESC.reg = esc.reg; + } - // Set up RX fifo 0 - { - CAN_RXF0C_Type rxf = {}; - rxf.bit.F0SA = (uint32_t)state->rx0_fifo; - rxf.bit.F0S = ADAFRUIT_ZEROCAN_RX_FIFO_SIZE; - hw->RXF0C.reg = rxf.reg; - } + // Set up TX buffer + { + CAN_TXBC_Type bc = {}; + bc.bit.TBSA = (uint32_t)state->tx_buffer; + bc.bit.NDTB = ADAFRUIT_ZEROCAN_TX_BUFFER_SIZE; + bc.bit.TFQM = 0; // Messages are transmitted in the order submitted + CAN1->TXBC.reg = bc.reg; + } - // Set up RX fifo 1 - { - CAN_RXF1C_Type rxf = {}; - rxf.bit.F1SA = (uint32_t)state->rx1_fifo; - rxf.bit.F1S = ADAFRUIT_ZEROCAN_RX_FIFO_SIZE; - hw->RXF1C.reg = rxf.reg; - } + // All RX data has an 8 byte payload (max) + { + CAN_RXESC_Type esc = {}; + esc.bit.F0DS = CAN_RXESC_F0DS_DATA8_Val; + esc.bit.F1DS = CAN_RXESC_F1DS_DATA8_Val; + esc.bit.RBDS = CAN_RXESC_RBDS_DATA8_Val; + hw->RXESC.reg = esc.reg; + } - // Reject all packets not explicitly requested - { - CAN_GFC_Type gfc = {}; - gfc.bit.RRFE = 0; - gfc.bit.ANFS = CAN_GFC_ANFS_REJECT_Val; - gfc.bit.ANFE = CAN_GFC_ANFE_REJECT_Val; - hw->GFC.reg = gfc.reg; - } + // Set up RX fifo 0 + { + CAN_RXF0C_Type rxf = {}; + rxf.bit.F0SA = (uint32_t)state->rx0_fifo; + rxf.bit.F0S = ADAFRUIT_ZEROCAN_RX_FIFO_SIZE; + hw->RXF0C.reg = rxf.reg; + } - // Set up standard RX filters - { - CAN_SIDFC_Type dfc = {}; - dfc.bit.LSS = ADAFRUIT_ZEROCAN_RX_FILTER_SIZE; - dfc.bit.FLSSA = (uint32_t)state->standard_rx_filter; - hw->SIDFC.reg = dfc.reg; - } + // Set up RX fifo 1 + { + CAN_RXF1C_Type rxf = {}; + rxf.bit.F1SA = (uint32_t)state->rx1_fifo; + rxf.bit.F1S = ADAFRUIT_ZEROCAN_RX_FIFO_SIZE; + hw->RXF1C.reg = rxf.reg; + } - // Set up extended RX filters - { - CAN_XIDFC_Type dfc = {}; - dfc.bit.LSE = ADAFRUIT_ZEROCAN_RX_FILTER_SIZE; - dfc.bit.FLESA = (uint32_t)state->extended_rx_filter; - hw->XIDFC.reg = dfc.reg; - } + // Reject all packets not explicitly requested + { + CAN_GFC_Type gfc = {}; + gfc.bit.RRFE = 0; + gfc.bit.ANFS = CAN_GFC_ANFS_REJECT_Val; + gfc.bit.ANFE = CAN_GFC_ANFE_REJECT_Val; + hw->GFC.reg = gfc.reg; + } - // Set nominal baud rate - hw->NBTP.reg = nbtp.reg; + // Set up standard RX filters + { + CAN_SIDFC_Type dfc = {}; + dfc.bit.LSS = ADAFRUIT_ZEROCAN_RX_FILTER_SIZE; + dfc.bit.FLSSA = (uint32_t)state->standard_rx_filter; + hw->SIDFC.reg = dfc.reg; + } - // hardware is ready for use - CAN1->CCCR.bit.CCE = 0; - CAN1->CCCR.bit.INIT = 0; - while(CAN1->CCCR.bit.INIT) { - } + // Set up extended RX filters + { + CAN_XIDFC_Type dfc = {}; + dfc.bit.LSE = ADAFRUIT_ZEROCAN_RX_FILTER_SIZE; + dfc.bit.FLESA = (uint32_t)state->extended_rx_filter; + hw->XIDFC.reg = dfc.reg; + } - return true; + // Set nominal baud rate + hw->NBTP.reg = nbtp.reg; + + // hardware is ready for use + CAN1->CCCR.bit.CCE = 0; + CAN1->CCCR.bit.INIT = 0; + while (CAN1->CCCR.bit.INIT) { + } + + return true; } -int Adafruit_ZeroCAN::transmitErrorCount() const { - return hw->ECR.bit.TEC; -} +int Adafruit_ZeroCAN::transmitErrorCount() const { return hw->ECR.bit.TEC; } -int Adafruit_ZeroCAN::receiveErrorCount() const { - return hw->ECR.bit.REC; -} +int Adafruit_ZeroCAN::receiveErrorCount() const { return hw->ECR.bit.REC; } Adafruit_ZeroCAN::BusState Adafruit_ZeroCAN::busState() const { - CAN_PSR_Type psr; - psr.reg = hw->PSR.reg; + CAN_PSR_Type psr; + psr.reg = hw->PSR.reg; - if (psr.bit.BO) { return BUS_OFF; } - if (psr.bit.EP) { return ERROR_PASSIVE; } - if (psr.bit.EW) { return ERROR_WARNING; } - return ERROR_ACTIVE; + if (psr.bit.BO) { + return BUS_OFF; + } + if (psr.bit.EP) { + return ERROR_PASSIVE; + } + if (psr.bit.EW) { + return ERROR_WARNING; + } + return ERROR_ACTIVE; } bool Adafruit_ZeroCAN::send(const Message &m) { - _adafruit_ZeroCAN_tx_buf &buf = state->tx_buffer[0]; - buf.txb0.bit.ESI = false; - buf.txb0.bit.XTD = m.extended; - buf.txb0.bit.RTR = m.rtr; - if(m.extended) { - buf.txb0.bit.ID = m.id; - } else { - buf.txb0.bit.ID = m.id << 18; + _adafruit_ZeroCAN_tx_buf &buf = state->tx_buffer[0]; + buf.txb0.bit.ESI = false; + buf.txb0.bit.XTD = m.extended; + buf.txb0.bit.RTR = m.rtr; + if (m.extended) { + buf.txb0.bit.ID = m.id; + } else { + buf.txb0.bit.ID = m.id << 18; + } + buf.txb1.bit.MM = 0; + buf.txb1.bit.EFC = 0; + buf.txb1.bit.FDF = 0; + buf.txb1.bit.BRS = 0; + buf.txb1.bit.DLC = m.size; + + if (!m.rtr) { + memcpy(buf.data, m.data, sizeof(m.data)); + } + + // TX buffer add request + hw->TXBAR.reg = 1; + + // wait 8ms (hard coded for now) for TX to occur + for (int i = 0; i < 8000; i++) { + if (hw->TXBTO.reg & 1) { + return true; } - buf.txb1.bit.MM = 0; - buf.txb1.bit.EFC = 0; - buf.txb1.bit.FDF = 0; - buf.txb1.bit.BRS = 0; - buf.txb1.bit.DLC = m.size; + delayMicroseconds(1); + } - if (!m.rtr) { - memcpy(buf.data, m.data, sizeof(m.data)); - } - - // TX buffer add request - hw->TXBAR.reg = 1; - - // wait 8ms (hard coded for now) for TX to occur - for (int i=0; i<8000; i++) { - if(hw->TXBTO.reg & 1) { - return true; - } - delayMicroseconds(1); - } - - return false; + return false; } void Adafruit_ZeroCAN::restart() { - hw->CCCR.bit.INIT = 0; - while(hw->CCCR.bit.INIT) { - } + hw->CCCR.bit.INIT = 0; + while (hw->CCCR.bit.INIT) { + } } diff --git a/Adafruit_ZeroCAN.h b/Adafruit_ZeroCAN.h index 3f94354..ab9a5a1 100644 --- a/Adafruit_ZeroCAN.h +++ b/Adafruit_ZeroCAN.h @@ -21,50 +21,47 @@ struct _adafruit_ZeroCAN_state; class Adafruit_ZeroCAN { public: - enum BusState { - ERROR_ACTIVE, ERROR_WARNING, ERROR_PASSIVE, BUS_OFF - }; + enum BusState { ERROR_ACTIVE, ERROR_WARNING, ERROR_PASSIVE, BUS_OFF }; - struct Message { - uint32_t id; - bool extended; - bool rtr; - uint8_t size; - uint8_t data[8]; - }; + struct Message { + uint32_t id; + bool extended; + bool rtr; + uint8_t size; + uint8_t data[8]; + }; - struct Match { - uint32_t id; - uint32_t mask; - bool extended; - }; + struct Match { + uint32_t id; + uint32_t mask; + bool extended; + }; - class Listener { - public: - Listener(); - bool begin(Adafruit_ZeroCAN &can, Match *matches, size_t nmatch); - ~Listener(); - bool in_waiting() const; - bool read(Message &); - }; + class Listener { + public: + Listener(); + bool begin(Adafruit_ZeroCAN &can, Match *matches, size_t nmatch); + ~Listener(); + bool in_waiting() const; + bool read(Message &); + }; - Adafruit_ZeroCAN(uint8_t TX_PIN, uint8_t RX_PIN); - Adafruit_ZeroCAN(); - ~Adafruit_ZeroCAN() {} - - bool begin(int baudrate, bool loopback, bool silent); + Adafruit_ZeroCAN(uint8_t TX_PIN, uint8_t RX_PIN); + Adafruit_ZeroCAN(); + ~Adafruit_ZeroCAN() {} - int transmitErrorCount() const; - int receiveErrorCount() const; - BusState busState() const; + bool begin(int baudrate, bool loopback, bool silent); - bool send(const Message &m); - void restart(); + int transmitErrorCount() const; + int receiveErrorCount() const; + BusState busState() const; + + bool send(const Message &m); + void restart(); private: - struct State; - int8_t _tx, _rx; - void *_hw; - _adafruit_ZeroCAN_state *state; + struct State; + int8_t _tx, _rx; + void *_hw; + _adafruit_ZeroCAN_state *state; }; -