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