/ 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  }