99
1010#include  " same51.h" 
1111
12+ namespace 
13+ {
14+ #include  " CANSAME5x_port.h" 
15+ }
16+ 
1217#define  hw  (reinterpret_cast <Can *>(this ->_hw))
1318#define  state  (reinterpret_cast <_canSAME5x_state *>(this ->_state))
1419
1520#define  DIV_ROUND (a, b ) (((a) + (b) / 2 ) / (b))
1621#define  DIV_ROUND_UP (a, b ) (((a) + (b)-1 ) / (b))
1722
1823#define  GCLK_CAN1  GCLK_PCHCTRL_GEN_GCLK1_Val
24+ #define  GCLK_CAN0  GCLK_PCHCTRL_GEN_GCLK1_Val
1925#define  ADAFRUIT_ZEROCAN_TX_BUFFER_SIZE  (1 )
2026#define  ADAFRUIT_ZEROCAN_RX_FILTER_SIZE  (1 )
2127#define  ADAFRUIT_ZEROCAN_RX_FIFO_SIZE  (8 )
2228#define  ADAFRUIT_ZEROCAN_MAX_MESSAGE_LENGTH  (8 )
2329
24- #define  CAN0_FUNCTION  (EPioType(8 ))
25- #define  CAN1_FUNCTION  (EPioType(7 ))
26- 
2730namespace 
2831{
32+ 
33+ template  <class  T , std::size_t  N>
34+ constexpr  size_t  size (const  T  (&array)[N]) noexcept 
35+ {
36+     return  N;
37+ }
38+ 
39+ 
2940//  Adapted from ASF3 interrupt_sam_nvic.c:
3041
3142volatile  unsigned  long  cpu_irq_critical_section_counter = 0 ;
@@ -93,8 +104,7 @@ struct _canSAME5x_state
93104//  This data must be in the first 64kB of RAM.  The "canram" section
94105//  receives special support from the linker file in the Feather M4 CAN's
95106//  board support package.
96- //  TODO support CAN0 and CAN1 simultaneously (state would be an array of 2)
97- __attribute__ ((section(" canram"  ))) _canSAME5x_state can_state;
107+ __attribute__ ((section(" .canram"  ))) _canSAME5x_state can_state[2];
98108
99109constexpr  uint32_t  can_frequency = VARIANT_GCLK1_FREQ;
100110bool  compute_nbtp (uint32_t  baudrate, CAN_NBTP_Type &result)
@@ -113,6 +123,24 @@ bool compute_nbtp(uint32_t baudrate, CAN_NBTP_Type &result)
113123  result.bit .NSJW  = DIV_ROUND (clocks_after_sample, divisor * 4 );
114124  return  true ;
115125}
126+ 
127+ EPioType find_pin (const  can_function *table, size_t  n, int  arduino_pin, int  &instance) {
128+   if (arduino_pin < 0  || arduino_pin > PINS_COUNT) {
129+     return  (EPioType)-1 ;
130+   }
131+ 
132+   unsigned  port = g_APinDescription[arduino_pin].ulPort ;
133+   unsigned  pin = g_APinDescription[arduino_pin].ulPin ;
134+   for (size_t  i = 0 ; i<n; i++) {
135+     if (table[i].port  == port && table[i].pin  == pin) {
136+       if (instance == -1  || table[i].instance  == instance) {
137+         instance = table[i].instance ;
138+         return  EPioType (table[i].mux );
139+       }
140+     }
141+   }
142+ }
143+ 
116144} //  namespace
117145
118146CANSAME5x::CANSAME5x (uint8_t  TX_PIN, uint8_t  RX_PIN)
@@ -130,22 +158,33 @@ int CANSAME5x::begin(long baudrate) {
130158    return  0 ;
131159  }
132160
161+   int  instance = -1 ;
162+   EPioType tx_function = find_pin (can_tx, size (can_tx), _tx, instance);
163+   EPioType rx_function = find_pin (can_rx, size (can_rx), _rx, instance);
164+ 
165+   if (tx_function == EPioType (-1 ) || rx_function == EPioType (-1 ) || instance == -1 ) {
166+     return  0 ;
167+   }
168+ 
133169  CAN_NBTP_Type nbtp;
134170  if  (!compute_nbtp (baudrate, nbtp)) {
135171    return  0 ;
136172  }
137173
138-   //  TODO: Support the CAN0 peripheral, which uses pinmux 8
139-   _hw = reinterpret_cast <void  *>(CAN1);
140-   _state = reinterpret_cast <void  *>(&can_state);
141-   _idx = 1 ;
142-   memset (state, 0 , sizeof (*state));
174+   _idx = instance;
175+   _hw = reinterpret_cast <void  *>(_idx == 0  ? CAN0 : CAN1);
176+   _state = reinterpret_cast <void  *>(&can_state[_idx]);
143177
144-   pinPeripheral (_tx, CAN1_FUNCTION);
145-   pinPeripheral (_rx, CAN1_FUNCTION);
178+   memset (state, 0 , sizeof (*state));
146179
147-   GCLK->PCHCTRL [CAN1_GCLK_ID].reg  = GCLK_CAN1 | (1  << GCLK_PCHCTRL_CHEN_Pos);
180+   pinPeripheral (_tx, tx_function);
181+   pinPeripheral (_rx, rx_function);
148182
183+   if (_idx == 0 ) {
184+     GCLK->PCHCTRL [CAN0_GCLK_ID].reg  = GCLK_CAN0 | (1  << GCLK_PCHCTRL_CHEN_Pos);
185+   } else  {
186+     GCLK->PCHCTRL [CAN1_GCLK_ID].reg  = GCLK_CAN1 | (1  << GCLK_PCHCTRL_CHEN_Pos);
187+   }
149188  //  reset and allow configuration change
150189  hw->CCCR .bit .INIT  = 1 ;
151190  while  (!hw->CCCR .bit .INIT ) {
@@ -223,7 +262,12 @@ int CANSAME5x::begin(long baudrate) {
223262
224263  //  Enable receive IRQ (masked until enabled in NVIC)
225264  hw->IE .bit .RF0NE  = true ;
226-   hw->ILE .bit .EINT0  = true ;
265+   if  (_idx == 0 ) {
266+     hw->ILE .bit .EINT0  = true ;
267+   } else  {
268+     hw->ILE .bit .EINT1  = true ;
269+   }
270+   hw->ILS .bit .RF0NL  = _idx;
227271
228272  //  Set nominal baud rate
229273  hw->NBTP .reg  = nbtp.reg ;
@@ -336,23 +380,17 @@ void CANSAME5x::onReceive(void(*callback)(int))
336380{
337381  CANControllerClass::onReceive (callback);
338382
339-   static_assert (CAN0_IRQn + 1  == CAN1_IRQn);
340- 
341-   auto  irq = IRQn_Type (CAN0_IRQn + _idx);
383+   auto  irq = _idx == 0  ? CAN0_IRQn : CAN1_IRQn;
342384  if (callback) {
343- Serial.println (" enabling irq"  );
344385    NVIC_EnableIRQ (irq);
345386  } else  {
346- Serial.println (" disabling irq"  );
347387    NVIC_DisableIRQ (irq);
348388  }
349389}
350390
351391void  CANSAME5x::handleInterrupt () {
352392  uint32_t  ir = hw->IR .reg ;
353393
354- //  Serial.print("in irq");
355- //  Serial.println(ir, HEX);
356394  if  (ir & CAN_IR_RF0N) {
357395    while (int  i = parsePacket ()) _onReceive (i);
358396  }
@@ -373,7 +411,6 @@ int CANSAME5x::filter(int id, int mask)
373411  state->extended_rx_filter [0 ].XIDFE_0 .bit .EFEC  = CAN_XIDFE_0_EFEC_REJECT_Val;
374412  state->extended_rx_filter [0 ].XIDFE_1 .bit .EFID2  = 0 ; //  mask
375413  state->extended_rx_filter [0 ].XIDFE_1 .bit .EFT  = CAN_XIDFE_1_EFT_CLASSIC_Val;
376- // TODO
377414}
378415
379416int  CANSAME5x::filterExtended (long  id, long  mask)
@@ -439,7 +476,7 @@ int CANSAME5x::wakeup() {
439476  return  1 ;
440477}
441478void  CANSAME5x::onInterrupt () {
442-   for (int  i=0 ; i<2 ; i++) {
479+   for (int  i=0 ; i<size (instances) ; i++) {
443480    CANSAME5x *instance = instances[i];
444481    if (instance) {
445482      instance->handleInterrupt ();
0 commit comments