/ Adafruit_ZeroCAN.cpp
Adafruit_ZeroCAN.cpp
1 /*! 2 * @file Adafruit_ZeroCAN.cpp 3 * 4 * @mainpage Adafruit CAN peripheral driver for SAME51 chips 5 * 6 * @section intro_sec Introduction 7 * 8 * CAN peripheral driver for SAME51 chips 9 * 10 * 11 * Adafruit invests time and resources providing this open source code, 12 * please support Adafruit and open-source hardware by purchasing 13 * products from Adafruit! 14 * 15 * @section author Author 16 * 17 * Written by Jeff Epler for Adafruit Industries. 18 * 19 * @section license License 20 * 21 * BSD license, all text here must be included in any redistribution. 22 * 23 */ 24 25 #include <algorithm> 26 27 #include <stdint.h> 28 #include <stdlib.h> 29 30 #include "Adafruit_ZeroCAN.h" 31 #include "wiring_private.h" 32 33 #include "same51.h" 34 35 #define hw (reinterpret_cast<Can *>(this->_hw)) 36 37 #define DIV_ROUND(a, b) (((a) + (b) / 2) / (b)) 38 #define DIV_ROUND_UP(a, b) (((a) + (b)-1) / (b)) 39 40 #define GCLK_CAN1 GCLK_PCHCTRL_GEN_GCLK1_Val 41 #define ADAFRUIT_ZEROCAN_TX_BUFFER_SIZE (1) 42 #define ADAFRUIT_ZEROCAN_RX_FILTER_SIZE (14) 43 #define ADAFRUIT_ZEROCAN_RX_FIFO_SIZE (8) 44 #define ADAFRUIT_ZEROCAN_MAX_MESSAGE_LENGTH (8) 45 46 #define CAN1_FUNCTION (EPioType(7)) 47 48 // This appears to be a typo (transposition error) in the ASF4 headers 49 // It's called the "Extended ID Filter Entry" 50 typedef CanMramXifde CanMramXidfe; 51 52 typedef uint32_t can_filter_t; 53 54 struct _adafruit_ZeroCAN_tx_buf { 55 CAN_TXBE_0_Type txb0; 56 CAN_TXBE_1_Type txb1; 57 __attribute__((aligned(4))) uint8_t data[8]; 58 }; 59 60 struct _adafruit_ZeroCAN_rx_fifo { 61 CAN_RXF0E_0_Type rxf0; 62 CAN_RXF0E_1_Type rxf1; 63 __attribute((aligned(4))) uint8_t data[ADAFRUIT_ZEROCAN_MAX_MESSAGE_LENGTH]; 64 } can_rx_fifo_t; 65 66 struct _adafruit_ZeroCAN_state { 67 _adafruit_ZeroCAN_tx_buf tx_buffer[ADAFRUIT_ZEROCAN_TX_BUFFER_SIZE]; 68 _adafruit_ZeroCAN_rx_fifo rx0_fifo[ADAFRUIT_ZEROCAN_RX_FIFO_SIZE]; 69 _adafruit_ZeroCAN_rx_fifo rx1_fifo[ADAFRUIT_ZEROCAN_RX_FIFO_SIZE]; 70 CanMramSidfe standard_rx_filter[ADAFRUIT_ZEROCAN_RX_FILTER_SIZE]; 71 CanMramXifde extended_rx_filter[ADAFRUIT_ZEROCAN_RX_FILTER_SIZE]; 72 }; 73 74 namespace { 75 // This data must be in the first 64kB of RAM. The "canram" section 76 // receives special support from the linker file in the Feather M4 CAN's 77 // board support package. 78 // TODO support CAN0 and CAN1 simultaneously (state would be an array of 2) 79 __attribute__((section("canram"))) _adafruit_ZeroCAN_state can_state; 80 81 constexpr uint32_t can_frequency = VARIANT_GCLK1_FREQ; 82 bool compute_nbtp(uint32_t baudrate, CAN_NBTP_Type &result) { 83 uint32_t clocks_per_bit = DIV_ROUND(can_frequency, baudrate); 84 uint32_t clocks_to_sample = DIV_ROUND(clocks_per_bit * 7, 8); 85 uint32_t clocks_after_sample = clocks_per_bit - clocks_to_sample; 86 uint32_t divisor = std::max(DIV_ROUND_UP(clocks_to_sample, 256), 87 DIV_ROUND_UP(clocks_after_sample, 128)); 88 if (divisor > 32) { 89 return false; 90 } 91 result.bit.NTSEG1 = DIV_ROUND(clocks_to_sample, divisor) - 2; 92 result.bit.NTSEG2 = DIV_ROUND(clocks_after_sample, divisor) - 1; 93 result.bit.NBRP = divisor - 1; 94 result.bit.NSJW = DIV_ROUND(clocks_after_sample, divisor * 4); 95 return true; 96 } 97 } // namespace 98 99 Adafruit_ZeroCAN::Adafruit_ZeroCAN(uint8_t TX_PIN, uint8_t RX_PIN) 100 : _tx(TX_PIN), _rx(RX_PIN) {} 101 #ifdef PIN_CAN_TX 102 Adafruit_ZeroCAN::Adafruit_ZeroCAN() : _tx(PIN_CAN_TX), _rx(PIN_CAN_RX) {} 103 #else 104 Adafruit_ZeroCAN::Adafruit_ZeroCAN() : _tx(-1) {} 105 #endif 106 107 bool Adafruit_ZeroCAN::begin(int baudrate, bool loopback, bool silent) { 108 if (_tx == -1) { 109 return false; 110 } 111 112 CAN_NBTP_Type nbtp; 113 if (!compute_nbtp(baudrate, nbtp)) { 114 return false; 115 } 116 117 // TODO: Support the CAN0 peripheral, which uses pinmux 8 118 _hw = reinterpret_cast<void *>(CAN1); 119 state = &can_state; 120 memset(state, 0, sizeof(*state)); 121 122 pinPeripheral(_tx, CAN1_FUNCTION); 123 pinPeripheral(_rx, CAN1_FUNCTION); 124 125 GCLK->PCHCTRL[CAN1_GCLK_ID].reg = GCLK_CAN1 | (1 << GCLK_PCHCTRL_CHEN_Pos); 126 127 // reset and allow configuration change 128 hw->CCCR.bit.INIT = 1; 129 while (!hw->CCCR.bit.INIT) { 130 } 131 hw->CCCR.bit.CCE = 1; 132 133 // set loopback and silent modes 134 hw->CCCR.bit.MON = silent; 135 hw->CCCR.bit.TEST = loopback; 136 hw->TEST.bit.LBCK = loopback; 137 138 // All TX data has an 8 byte payload (max) 139 { 140 CAN_TXESC_Type esc = {}; 141 esc.bit.TBDS = CAN_TXESC_TBDS_DATA8_Val; 142 CAN1->TXESC.reg = esc.reg; 143 } 144 145 // Set up TX buffer 146 { 147 CAN_TXBC_Type bc = {}; 148 bc.bit.TBSA = (uint32_t)state->tx_buffer; 149 bc.bit.NDTB = ADAFRUIT_ZEROCAN_TX_BUFFER_SIZE; 150 bc.bit.TFQM = 0; // Messages are transmitted in the order submitted 151 CAN1->TXBC.reg = bc.reg; 152 } 153 154 // All RX data has an 8 byte payload (max) 155 { 156 CAN_RXESC_Type esc = {}; 157 esc.bit.F0DS = CAN_RXESC_F0DS_DATA8_Val; 158 esc.bit.F1DS = CAN_RXESC_F1DS_DATA8_Val; 159 esc.bit.RBDS = CAN_RXESC_RBDS_DATA8_Val; 160 hw->RXESC.reg = esc.reg; 161 } 162 163 // Set up RX fifo 0 164 { 165 CAN_RXF0C_Type rxf = {}; 166 rxf.bit.F0SA = (uint32_t)state->rx0_fifo; 167 rxf.bit.F0S = ADAFRUIT_ZEROCAN_RX_FIFO_SIZE; 168 hw->RXF0C.reg = rxf.reg; 169 } 170 171 // Set up RX fifo 1 172 { 173 CAN_RXF1C_Type rxf = {}; 174 rxf.bit.F1SA = (uint32_t)state->rx1_fifo; 175 rxf.bit.F1S = ADAFRUIT_ZEROCAN_RX_FIFO_SIZE; 176 hw->RXF1C.reg = rxf.reg; 177 } 178 179 // Reject all packets not explicitly requested 180 { 181 CAN_GFC_Type gfc = {}; 182 gfc.bit.RRFE = 0; 183 gfc.bit.ANFS = CAN_GFC_ANFS_REJECT_Val; 184 gfc.bit.ANFE = CAN_GFC_ANFE_REJECT_Val; 185 hw->GFC.reg = gfc.reg; 186 } 187 188 // Set up standard RX filters 189 { 190 CAN_SIDFC_Type dfc = {}; 191 dfc.bit.LSS = ADAFRUIT_ZEROCAN_RX_FILTER_SIZE; 192 dfc.bit.FLSSA = (uint32_t)state->standard_rx_filter; 193 hw->SIDFC.reg = dfc.reg; 194 } 195 196 // Set up extended RX filters 197 { 198 CAN_XIDFC_Type dfc = {}; 199 dfc.bit.LSE = ADAFRUIT_ZEROCAN_RX_FILTER_SIZE; 200 dfc.bit.FLESA = (uint32_t)state->extended_rx_filter; 201 hw->XIDFC.reg = dfc.reg; 202 } 203 204 // Set nominal baud rate 205 hw->NBTP.reg = nbtp.reg; 206 207 // hardware is ready for use 208 CAN1->CCCR.bit.CCE = 0; 209 CAN1->CCCR.bit.INIT = 0; 210 while (CAN1->CCCR.bit.INIT) { 211 } 212 213 return true; 214 } 215 216 int Adafruit_ZeroCAN::transmitErrorCount() const { return hw->ECR.bit.TEC; } 217 218 int Adafruit_ZeroCAN::receiveErrorCount() const { return hw->ECR.bit.REC; } 219 220 Adafruit_ZeroCAN::BusState Adafruit_ZeroCAN::busState() const { 221 CAN_PSR_Type psr; 222 psr.reg = hw->PSR.reg; 223 224 if (psr.bit.BO) { 225 return BUS_OFF; 226 } 227 if (psr.bit.EP) { 228 return ERROR_PASSIVE; 229 } 230 if (psr.bit.EW) { 231 return ERROR_WARNING; 232 } 233 return ERROR_ACTIVE; 234 } 235 236 bool Adafruit_ZeroCAN::send(const Message &m) { 237 _adafruit_ZeroCAN_tx_buf &buf = state->tx_buffer[0]; 238 buf.txb0.bit.ESI = false; 239 buf.txb0.bit.XTD = m.extended; 240 buf.txb0.bit.RTR = m.rtr; 241 if (m.extended) { 242 buf.txb0.bit.ID = m.id; 243 } else { 244 buf.txb0.bit.ID = m.id << 18; 245 } 246 buf.txb1.bit.MM = 0; 247 buf.txb1.bit.EFC = 0; 248 buf.txb1.bit.FDF = 0; 249 buf.txb1.bit.BRS = 0; 250 buf.txb1.bit.DLC = m.size; 251 252 if (!m.rtr) { 253 memcpy(buf.data, m.data, sizeof(m.data)); 254 } 255 256 // TX buffer add request 257 hw->TXBAR.reg = 1; 258 259 // wait 8ms (hard coded for now) for TX to occur 260 for (int i = 0; i < 8000; i++) { 261 if (hw->TXBTO.reg & 1) { 262 return true; 263 } 264 delayMicroseconds(1); 265 } 266 267 return false; 268 } 269 270 void Adafruit_ZeroCAN::restart() { 271 hw->CCCR.bit.INIT = 0; 272 while (hw->CCCR.bit.INIT) { 273 } 274 }