Merge pull request #1 from caternuson/initial

Add initial working code
This commit is contained in:
Limor "Ladyada" Fried 2023-03-31 23:11:05 -04:00 committed by GitHub
commit c628716304
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 4177 additions and 33 deletions

View file

@ -1,4 +1,4 @@
# Adafruit CAN Library
This is an Arduino library for the Adafruit CAN.
This is an Arduino library for the supporting boards with native CAN peripherals.

View file

@ -1,9 +0,0 @@
// TODO: Add a simpletest example for CAN
void setup() {
}
void loop() {
}

View file

@ -0,0 +1,62 @@
/*
* Adafruit Feather M4 CAN Receiver Callback Example
*/
#include <CANSAME5x.h>
CANSAME5x CAN;
void setup() {
Serial.begin(9600);
while (!Serial);
Serial.println("CAN Receiver Callback");
// start the CAN bus at 250 kbps
if (!CAN.begin(250E3)) {
Serial.println("Starting CAN failed!");
while (1);
}
// register the receive callback
CAN.onReceive(onReceive);
}
void loop() {
// do nothing
}
void onReceive(int packetSize) {
// received a packet
Serial.print("Received ");
if (CAN.packetExtended()) {
Serial.print("extended ");
}
if (CAN.packetRtr()) {
// Remote transmission request, packet contains no data
Serial.print("RTR ");
}
Serial.print("packet with id 0x");
Serial.print(CAN.packetId(), HEX);
if (CAN.packetRtr()) {
Serial.print(" and requested length ");
Serial.println(CAN.packetDlc());
} else {
Serial.print(" and length ");
Serial.println(packetSize);
// only print packet data for non-RTR packets
while (CAN.available()) {
Serial.print((char)CAN.read());
}
Serial.println();
}
Serial.println();
}

View file

@ -0,0 +1,63 @@
/*
* Adafruit Feather M4 CAN Receiver Example
*/
#include <CANSAME5x.h>
CANSAME5x CAN;
void setup() {
Serial.begin(9600);
while (!Serial);
Serial.println("CAN Receiver");
pinMode(PIN_CAN_STANDBY, OUTPUT);
digitalWrite(PIN_CAN_STANDBY, false); // turn off STANDBY
pinMode(PIN_CAN_BOOSTEN, OUTPUT);
digitalWrite(PIN_CAN_BOOSTEN, true); // turn on booster
// start the CAN bus at 250 kbps
if (!CAN.begin(250000)) {
Serial.println("Starting CAN failed!");
while (1);
}
}
void loop() {
// try to parse packet
int packetSize = CAN.parsePacket();
if (packetSize) {
// received a packet
Serial.print("Received ");
if (CAN.packetExtended()) {
Serial.print("extended ");
}
if (CAN.packetRtr()) {
// Remote transmission request, packet contains no data
Serial.print("RTR ");
}
Serial.print("packet with id 0x");
Serial.print(CAN.packetId(), HEX);
if (CAN.packetRtr()) {
Serial.print(" and requested length ");
Serial.println(CAN.packetDlc());
} else {
Serial.print(" and length ");
Serial.println(packetSize);
// only print packet data for non-RTR packets
while (CAN.available()) {
Serial.print((char)CAN.read());
}
Serial.println();
}
Serial.println();
}
}

View file

@ -0,0 +1,56 @@
/*
* Adafruit Feather M4 CAN Sender Example
*/
#include <CANSAME5x.h>
CANSAME5x CAN;
void setup() {
Serial.begin(9600);
while (!Serial);
Serial.println("CAN Sender");
pinMode(PIN_CAN_STANDBY, OUTPUT);
digitalWrite(PIN_CAN_STANDBY, false); // turn off STANDBY
pinMode(PIN_CAN_BOOSTEN, OUTPUT);
digitalWrite(PIN_CAN_BOOSTEN, true); // turn on booster
// start the CAN bus at 250 kbps
if (!CAN.begin(250000)) {
Serial.println("Starting CAN failed!");
while (1);
}
}
void loop() {
// send packet: id is 11 bits, packet can contain up to 8 bytes of data
Serial.print("Sending packet ... ");
CAN.beginPacket(0x12);
CAN.write('h');
CAN.write('e');
CAN.write('l');
CAN.write('l');
CAN.write('o');
CAN.endPacket();
Serial.println("done");
delay(1000);
// send extended packet: id is 29 bits, packet can contain up to 8 bytes of data
Serial.print("Sending extended packet ... ");
CAN.beginExtendedPacket(0xabcdef);
CAN.write('w');
CAN.write('o');
CAN.write('r');
CAN.write('l');
CAN.write('d');
CAN.endPacket();
Serial.println("done");
delay(1000);
}

View file

@ -1,9 +1,9 @@
name=Adafruit CAN Library
name=Adafruit CAN
version=0.2.0
author=Adafruit
maintainer=Adafruit <info@adafruit.com>
sentence=Arduino library for CAN.
paragrah=Arduino library for CAN.
sentence=Arduino library for native CAN.
paragraph=Arduino library for native CAN.
category=Sensors
url=https://github.com/adafruit/Adafruit_CAN
architectures=*
architectures=samd

View file

@ -1,7 +0,0 @@
/*!
* @section author Author
*/
#include "Adafruit_CAN.h"
// TODO: Add code

View file

@ -1,12 +0,0 @@
#ifndef __CAN_H__
#define __CAN_H__
#include <Arduino.h>
class Adafruit_CAN {
public:
private:
}
#endif

216
src/CANController.cpp Normal file
View file

@ -0,0 +1,216 @@
// Copyright (c) Sandeep Mistry. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include "CANController.h"
CANControllerClass::CANControllerClass() :
_onReceive(NULL),
_packetBegun(false),
_txId(-1),
_txExtended(-1),
_txRtr(false),
_txDlc(0),
_txLength(0),
_rxId(-1),
_rxExtended(false),
_rxRtr(false),
_rxDlc(0),
_rxLength(0),
_rxIndex(0)
{
// overide Stream timeout value
setTimeout(0);
}
CANControllerClass::~CANControllerClass()
{
}
int CANControllerClass::begin(long /*baudRate*/)
{
_packetBegun = false;
_txId = -1;
_txRtr =false;
_txDlc = 0;
_txLength = 0;
_rxId = -1;
_rxRtr = false;
_rxDlc = 0;
_rxLength = 0;
_rxIndex = 0;
return 1;
}
void CANControllerClass::end()
{
}
int CANControllerClass::beginPacket(int id, int dlc, bool rtr)
{
if (id < 0 || id > 0x7FF) {
return 0;
}
if (dlc > 8) {
return 0;
}
_packetBegun = true;
_txId = id;
_txExtended = false;
_txRtr = rtr;
_txDlc = dlc;
_txLength = 0;
memset(_txData, 0x00, sizeof(_txData));
return 1;
}
int CANControllerClass::beginExtendedPacket(long id, int dlc, bool rtr)
{
if (id < 0 || id > 0x1FFFFFFF) {
return 0;
}
if (dlc > 8) {
return 0;
}
_packetBegun = true;
_txId = id;
_txExtended = true;
_txRtr = rtr;
_txDlc = dlc;
_txLength = 0;
memset(_txData, 0x00, sizeof(_txData));
return 1;
}
int CANControllerClass::endPacket()
{
if (!_packetBegun) {
return 0;
}
_packetBegun = false;
if (_txDlc >= 0) {
_txLength = _txDlc;
}
return 1;
}
int CANControllerClass::parsePacket()
{
return 0;
}
long CANControllerClass::packetId()
{
return _rxId;
}
bool CANControllerClass::packetExtended()
{
return _rxExtended;
}
bool CANControllerClass::packetRtr()
{
return _rxRtr;
}
int CANControllerClass::packetDlc()
{
return _rxDlc;
}
size_t CANControllerClass::write(uint8_t byte)
{
return write(&byte, sizeof(byte));
}
size_t CANControllerClass::write(const uint8_t *buffer, size_t size)
{
if (!_packetBegun) {
return 0;
}
if (size > (sizeof(_txData) - _txLength)) {
size = sizeof(_txData) - _txLength;
}
memcpy(&_txData[_txLength], buffer, size);
_txLength += size;
return size;
}
int CANControllerClass::available()
{
return (_rxLength - _rxIndex);
}
int CANControllerClass::read()
{
if (!available()) {
return -1;
}
return _rxData[_rxIndex++];
}
int CANControllerClass::peek()
{
if (!available()) {
return -1;
}
return _rxData[_rxIndex];
}
void CANControllerClass::flush()
{
}
void CANControllerClass::onReceive(void(*callback)(int))
{
_onReceive = callback;
}
int CANControllerClass::filter(int /*id*/, int /*mask*/)
{
return 0;
}
int CANControllerClass::filterExtended(long /*id*/, long /*mask*/)
{
return 0;
}
int CANControllerClass::observe()
{
return 0;
}
int CANControllerClass::loopback()
{
return 0;
}
int CANControllerClass::sleep()
{
return 0;
}
int CANControllerClass::wakeup()
{
return 0;
}

71
src/CANController.h Normal file
View file

@ -0,0 +1,71 @@
// Copyright (c) Sandeep Mistry. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#ifndef CAN_CONTROLLER_H
#define CAN_CONTROLLER_H
#include <Arduino.h>
class CANControllerClass : public Stream {
public:
virtual int begin(long baudRate);
virtual void end();
int beginPacket(int id, int dlc = -1, bool rtr = false);
int beginExtendedPacket(long id, int dlc = -1, bool rtr = false);
virtual int endPacket();
virtual int parsePacket();
long packetId();
bool packetExtended();
bool packetRtr();
int packetDlc();
// from Print
virtual size_t write(uint8_t byte);
virtual size_t write(const uint8_t *buffer, size_t size);
// from Stream
virtual int available();
virtual int read();
virtual int peek();
virtual void flush();
virtual void onReceive(void(*callback)(int));
virtual int filter(int id) { return filter(id, 0x7ff); }
virtual int filter(int id, int mask);
virtual int filterExtended(long id) { return filterExtended(id, 0x1fffffff); }
virtual int filterExtended(long id, long mask);
virtual int observe();
virtual int loopback();
virtual int sleep();
virtual int wakeup();
protected:
CANControllerClass();
virtual ~CANControllerClass();
protected:
void (*_onReceive)(int);
bool _packetBegun;
long _txId;
bool _txExtended;
bool _txRtr;
int _txDlc;
int _txLength;
uint8_t _txData[8];
long _rxId;
bool _rxExtended;
bool _rxRtr;
int _rxDlc;
int _rxLength;
int _rxIndex;
uint8_t _rxData[8];
};
#endif

567
src/CANSAME5x.cpp Normal file
View file

@ -0,0 +1,567 @@
// Copyright 2020 © Jeff Epler for Adafruit Industries. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full
// license information.
#if defined(ADAFRUIT_FEATHER_M4_CAN)
#include <stdint.h>
#include <stdlib.h>
#include "CANSAME5x.h"
#include "wiring_private.h"
#define DEBUG_CAN (0)
#if DEBUG_CAN
#define DEBUG_PRINT(...) (Serial.print(__VA_ARGS__), ((void)0))
#define DEBUG_PRINTLN(...) (Serial.println(__VA_ARGS__), ((void)0))
#else
#define DEBUG_PRINT(...) ((void)0)
#define DEBUG_PRINTLN(...) ((void)0)
#endif
namespace {
#include "CANSAME5x_port.h"
}
#define hw (reinterpret_cast<Can *>(this->_hw))
#define state (reinterpret_cast<_canSAME5x_state *>(this->_state))
#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 GCLK_CAN0 GCLK_PCHCTRL_GEN_GCLK1_Val
#define ADAFRUIT_ZEROCAN_TX_BUFFER_SIZE (1)
#define ADAFRUIT_ZEROCAN_RX_FILTER_SIZE (1)
#define ADAFRUIT_ZEROCAN_RX_FIFO_SIZE (8)
#define ADAFRUIT_ZEROCAN_MAX_MESSAGE_LENGTH (8)
namespace {
template <class T, std::size_t N>
constexpr size_t size(const T (&array)[N]) noexcept {
return N;
}
// Adapted from ASF3 interrupt_sam_nvic.c:
volatile unsigned long cpu_irq_critical_section_counter = 0;
volatile unsigned char cpu_irq_prev_interrupt_state = 0;
void cpu_irq_enter_critical(void) {
if (!cpu_irq_critical_section_counter) {
if (__get_PRIMASK() == 0) { // IRQ enabled?
__disable_irq(); // Disable it
__DMB();
cpu_irq_prev_interrupt_state = 1;
} else {
// Make sure the to save the prev state as false
cpu_irq_prev_interrupt_state = 0;
}
}
cpu_irq_critical_section_counter++;
}
void cpu_irq_leave_critical(void) {
// Check if the user is trying to leave a critical section
// when not in a critical section
if (cpu_irq_critical_section_counter > 0) {
cpu_irq_critical_section_counter--;
// Only enable global interrupts when the counter
// reaches 0 and the state of the global interrupt flag
// was enabled when entering critical state */
if ((!cpu_irq_critical_section_counter) && cpu_irq_prev_interrupt_state) {
__DMB();
__enable_irq();
}
}
}
// This appears to be a typo (transposition error) in the ASF4 headers
// It's called the "Extended ID Filter Entry"
typedef CanMramXifde CanMramXidfe;
typedef uint32_t can_filter_t;
struct _canSAME5x_tx_buf {
CAN_TXBE_0_Type txb0;
CAN_TXBE_1_Type txb1;
__attribute__((aligned(4))) uint8_t data[8];
};
struct _canSAME5x_rx_fifo {
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 _canSAME5x_state {
_canSAME5x_tx_buf tx_buffer[ADAFRUIT_ZEROCAN_TX_BUFFER_SIZE];
_canSAME5x_rx_fifo rx_fifo[ADAFRUIT_ZEROCAN_RX_FIFO_SIZE];
CanMramSidfe standard_rx_filter[ADAFRUIT_ZEROCAN_RX_FILTER_SIZE];
CanMramXifde extended_rx_filter[ADAFRUIT_ZEROCAN_RX_FILTER_SIZE];
};
// 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.
__attribute__((section(".canram"))) _canSAME5x_state can_state[2];
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 = 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) - 1;
result.bit.NBRP = divisor - 1;
result.bit.NSJW = DIV_ROUND(clocks_after_sample, divisor * 4);
return true;
}
EPioType find_pin(const can_function *table, size_t n, int arduino_pin,
int &instance) {
if (arduino_pin < 0 || arduino_pin > PINS_COUNT) {
return (EPioType)-1;
}
unsigned port = g_APinDescription[arduino_pin].ulPort;
unsigned pin = g_APinDescription[arduino_pin].ulPin;
for (size_t i = 0; i < n; i++) {
if (table[i].port == port && table[i].pin == pin) {
if (instance == -1 || table[i].instance == instance) {
DEBUG_PRINT("found #");
DEBUG_PRINTLN(i);
instance = table[i].instance;
return EPioType(table[i].mux);
}
}
}
return (EPioType)-1;
}
} // namespace
CANSAME5x::CANSAME5x(uint8_t TX_PIN, uint8_t RX_PIN)
: _tx(TX_PIN), _rx(RX_PIN) {}
#ifdef PIN_CAN_TX
CANSAME5x::CANSAME5x() : _tx(PIN_CAN_TX), _rx(PIN_CAN_RX) {}
#else
CANSAME5x::CANSAME5x() : _tx(-1) {}
#endif
CANSAME5x::~CANSAME5x() {}
int CANSAME5x::begin(long baudrate) {
if (_tx == -1) {
return 0;
}
DEBUG_PRINT("_rx ");
DEBUG_PRINT(_rx);
DEBUG_PRINT(" ulPort=");
DEBUG_PRINT(g_APinDescription[_rx].ulPort);
DEBUG_PRINT(" ulPin=");
DEBUG_PRINTLN(g_APinDescription[_rx].ulPin);
DEBUG_PRINTLN("rx pin table");
for (size_t i = 0; i < size(can_rx); i++) {
DEBUG_PRINT(i);
DEBUG_PRINT(" port=");
DEBUG_PRINT(can_rx[i].port);
DEBUG_PRINT(" pin=");
DEBUG_PRINT(can_rx[i].pin);
DEBUG_PRINT(" instance=");
DEBUG_PRINTLN(can_rx[i].instance);
}
DEBUG_PRINT("_tx ");
DEBUG_PRINT(_tx);
DEBUG_PRINT(" ulPort=");
DEBUG_PRINT(g_APinDescription[_tx].ulPort);
DEBUG_PRINT(" ulPin=");
DEBUG_PRINTLN(g_APinDescription[_tx].ulPin);
DEBUG_PRINTLN("tx pin table");
for (size_t i = 0; i < size(can_tx); i++) {
DEBUG_PRINT(i);
DEBUG_PRINT(" port=");
DEBUG_PRINT(can_tx[i].port);
DEBUG_PRINT(" pin=");
DEBUG_PRINT(can_tx[i].pin);
DEBUG_PRINT(" instance=");
DEBUG_PRINTLN(can_tx[i].instance);
}
int instance = -1;
EPioType tx_function = find_pin(can_tx, size(can_tx), _tx, instance);
EPioType rx_function = find_pin(can_rx, size(can_rx), _rx, instance);
if (tx_function == EPioType(-1) || rx_function == EPioType(-1) ||
instance == -1) {
return 0;
}
CAN_NBTP_Type nbtp;
if (!compute_nbtp(baudrate, nbtp)) {
return 0;
}
_idx = instance;
_hw = reinterpret_cast<void *>(_idx == 0 ? CAN0 : CAN1);
_state = reinterpret_cast<void *>(&can_state[_idx]);
memset(state, 0, sizeof(*state));
pinPeripheral(_tx, tx_function);
pinPeripheral(_rx, rx_function);
if (_idx == 0) {
GCLK->PCHCTRL[CAN0_GCLK_ID].reg = GCLK_CAN0 | (1 << GCLK_PCHCTRL_CHEN_Pos);
} else {
GCLK->PCHCTRL[CAN1_GCLK_ID].reg = GCLK_CAN1 | (1 << GCLK_PCHCTRL_CHEN_Pos);
}
// reset and allow configuration change
hw->CCCR.bit.INIT = 1;
while (!hw->CCCR.bit.INIT) {
}
hw->CCCR.bit.CCE = 1;
// All TX data has an 8 byte payload (max)
{
CAN_TXESC_Type esc = {};
esc.bit.TBDS = CAN_TXESC_TBDS_DATA8_Val;
hw->TXESC.reg = esc.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
hw->TXBC.reg = bc.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;
}
// Set up RX fifo 0
{
CAN_RXF0C_Type rxf = {};
rxf.bit.F0SA = (uint32_t)state->rx_fifo;
rxf.bit.F0S = ADAFRUIT_ZEROCAN_RX_FIFO_SIZE;
hw->RXF0C.reg = rxf.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;
}
// Initially, receive all standard and extended packets to FIFO 0
state->standard_rx_filter[0].SIDFE_0.bit.SFID1 = 0; // ID
state->standard_rx_filter[0].SIDFE_0.bit.SFID2 = 0; // mask
state->standard_rx_filter[0].SIDFE_0.bit.SFEC = CAN_SIDFE_0_SFEC_STF0M_Val;
state->standard_rx_filter[0].SIDFE_0.bit.SFT = CAN_SIDFE_0_SFT_CLASSIC_Val;
state->extended_rx_filter[0].XIDFE_0.bit.EFID1 = 0; // ID
state->extended_rx_filter[0].XIDFE_0.bit.EFEC = CAN_XIDFE_0_EFEC_STF0M_Val;
state->extended_rx_filter[0].XIDFE_1.bit.EFID2 = 0; // mask
state->extended_rx_filter[0].XIDFE_1.bit.EFT = CAN_XIDFE_1_EFT_CLASSIC_Val;
// 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 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;
}
// Enable receive IRQ (masked until enabled in NVIC)
hw->IE.bit.RF0NE = true;
if (_idx == 0) {
hw->ILE.bit.EINT0 = true;
} else {
hw->ILE.bit.EINT1 = true;
}
hw->ILS.bit.RF0NL = _idx;
// Set nominal baud rate
hw->NBTP.reg = nbtp.reg;
// hardware is ready for use
hw->CCCR.bit.CCE = 0;
hw->CCCR.bit.INIT = 0;
while (hw->CCCR.bit.INIT) {
}
instances[_idx] = this;
return 1;
}
void CANSAME5x::end() {
instances[_idx] = 0;
pinMode(_tx, INPUT);
pinMode(_rx, INPUT);
// reset and disable clock
hw->CCCR.bit.INIT = 1;
while (!hw->CCCR.bit.INIT) {
}
if (_idx == 0) {
GCLK->PCHCTRL[CAN0_GCLK_ID].reg = 0;
} else {
GCLK->PCHCTRL[CAN1_GCLK_ID].reg = 0;
}
}
int CANSAME5x::endPacket() {
if (!CANControllerClass::endPacket()) {
return 0;
}
bus_autorecover();
// TODO wait for TX buffer to free
_canSAME5x_tx_buf &buf = state->tx_buffer[0];
buf.txb0.bit.ESI = false;
buf.txb0.bit.XTD = _txExtended;
buf.txb0.bit.RTR = _txRtr;
if (_txExtended) {
buf.txb0.bit.ID = _txId;
} else {
buf.txb0.bit.ID = _txId << 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 = _txLength;
if (!_txRtr) {
memcpy(buf.data, _txData, _txLength);
}
// 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;
}
yield();
}
return 1;
}
int CANSAME5x::_parsePacket() {
if (!hw->RXF0S.bit.F0FL) {
return 0;
}
int index = hw->RXF0S.bit.F0GI;
auto &hw_message = state->rx_fifo[index];
_rxExtended = hw_message.rxf0.bit.XTD;
_rxRtr = hw_message.rxf0.bit.RTR;
_rxDlc = hw_message.rxf1.bit.DLC;
if (_rxExtended) {
_rxId = hw_message.rxf0.bit.ID;
} else {
_rxId = hw_message.rxf0.bit.ID >> 18;
}
if (_rxRtr) {
_rxLength = 0;
} else {
_rxLength = _rxDlc;
memcpy(_rxData, hw_message.data, _rxLength);
}
_rxIndex = 0;
hw->RXF0A.bit.F0AI = index;
return _rxDlc;
}
int CANSAME5x::parsePacket() {
cpu_irq_enter_critical();
bus_autorecover();
int result = _parsePacket();
cpu_irq_leave_critical();
return result;
}
void CANSAME5x::onReceive(void (*callback)(int)) {
CANControllerClass::onReceive(callback);
auto irq = _idx == 0 ? CAN0_IRQn : CAN1_IRQn;
if (callback) {
NVIC_EnableIRQ(irq);
} else {
NVIC_DisableIRQ(irq);
}
}
void CANSAME5x::handleInterrupt() {
uint32_t ir = hw->IR.reg;
if (ir & CAN_IR_RF0N) {
while (int i = parsePacket())
_onReceive(i);
}
hw->IR.reg = ir;
}
int CANSAME5x::filter(int id, int mask) {
// accept matching standard messages
state->standard_rx_filter[0].SIDFE_0.bit.SFID1 = id;
state->standard_rx_filter[0].SIDFE_0.bit.SFID2 = mask;
state->standard_rx_filter[0].SIDFE_0.bit.SFEC = CAN_SIDFE_0_SFEC_STF0M_Val;
state->standard_rx_filter[0].SIDFE_0.bit.SFT = CAN_SIDFE_0_SFT_CLASSIC_Val;
// reject all extended messages
state->extended_rx_filter[0].XIDFE_0.bit.EFID1 = 0; // ID
state->extended_rx_filter[0].XIDFE_0.bit.EFEC = CAN_XIDFE_0_EFEC_REJECT_Val;
state->extended_rx_filter[0].XIDFE_1.bit.EFID2 = 0; // mask
state->extended_rx_filter[0].XIDFE_1.bit.EFT = CAN_XIDFE_1_EFT_CLASSIC_Val;
return 1;
}
int CANSAME5x::filterExtended(long id, long mask) {
// reject all standard messages
state->standard_rx_filter[0].SIDFE_0.bit.SFID1 = 0;
state->standard_rx_filter[0].SIDFE_0.bit.SFID2 = 0;
state->standard_rx_filter[0].SIDFE_0.bit.SFEC = CAN_SIDFE_0_SFEC_REJECT_Val;
state->standard_rx_filter[0].SIDFE_0.bit.SFT = CAN_SIDFE_0_SFT_CLASSIC_Val;
// accept matching extended messages
state->extended_rx_filter[0].XIDFE_0.bit.EFID1 = id;
state->extended_rx_filter[0].XIDFE_0.bit.EFEC = CAN_XIDFE_0_EFEC_STF0M_Val;
state->extended_rx_filter[0].XIDFE_1.bit.EFID2 = mask;
state->extended_rx_filter[0].XIDFE_1.bit.EFT = CAN_XIDFE_1_EFT_CLASSIC_Val;
return 1;
}
int CANSAME5x::observe() {
hw->CCCR.bit.INIT = 1;
while (!hw->CCCR.bit.INIT) {
}
hw->CCCR.bit.CCE = 1;
hw->CCCR.bit.MON = 1;
hw->CCCR.bit.CCE = 0;
hw->CCCR.bit.INIT = 0;
while (hw->CCCR.bit.INIT) {
}
return 1;
}
int CANSAME5x::loopback() {
hw->CCCR.bit.INIT = 1;
while (!hw->CCCR.bit.INIT) {
}
hw->CCCR.bit.CCE = 1;
hw->CCCR.bit.TEST = 1;
hw->TEST.bit.LBCK = 1;
hw->CCCR.bit.CCE = 0;
hw->CCCR.bit.INIT = 0;
while (hw->CCCR.bit.INIT) {
}
return 1;
}
int CANSAME5x::sleep() {
hw->CCCR.bit.CSR = 1;
while (!hw->CCCR.bit.CSA) {
}
if (_idx == 0) {
GCLK->PCHCTRL[CAN0_GCLK_ID].reg = 0;
} else {
GCLK->PCHCTRL[CAN1_GCLK_ID].reg = 0;
}
return 1;
}
int CANSAME5x::wakeup() {
if (_idx == 0) {
GCLK->PCHCTRL[CAN0_GCLK_ID].reg = GCLK_CAN0 | (1 << GCLK_PCHCTRL_CHEN_Pos);
} else {
GCLK->PCHCTRL[CAN1_GCLK_ID].reg = GCLK_CAN1 | (1 << GCLK_PCHCTRL_CHEN_Pos);
}
hw->CCCR.bit.INIT = 0;
while (hw->CCCR.bit.INIT) {
}
return 1;
}
void CANSAME5x::bus_autorecover() {
if (hw->PSR.bit.BO) {
DEBUG_PRINTLN("bus autorecovery activated");
hw->CCCR.bit.INIT = 0;
while (hw->CCCR.bit.INIT) {
}
}
}
void CANSAME5x::onInterrupt() {
for (int i = 0; i < size(instances); i++) {
CANSAME5x *instance = instances[i];
if (instance) {
instance->handleInterrupt();
}
}
}
extern "C" __attribute__((externally_visible)) void CAN0_Handler() {
cpu_irq_enter_critical();
CANSAME5x::onInterrupt();
cpu_irq_leave_critical();
}
extern "C" __attribute__((externally_visible)) void CAN1_Handler() {
cpu_irq_enter_critical();
CANSAME5x::onInterrupt();
cpu_irq_leave_critical();
}
CANSAME5x *CANSAME5x::instances[2];
#endif

53
src/CANSAME5x.h Normal file
View file

@ -0,0 +1,53 @@
// Copyright 2020 © Jeff Epler for Adafruit Industries. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full
// license information.
#include "CANController.h"
class CANSAME5x : public CANControllerClass {
public:
CANSAME5x();
CANSAME5x(uint8_t tx_pin, uint8_t rx_pin);
~CANSAME5x() final;
int begin(long baudRate) final;
void end() final;
int endPacket() final;
int parsePacket() final;
void onReceive(void (*callback)(int)) final;
using CANControllerClass::filter;
int filter(int id, int mask) final;
using CANControllerClass::filterExtended;
int filterExtended(long id, long mask) final;
int observe() final;
int loopback() final;
int sleep() final;
int wakeup() final;
// void dumpRegisters(Stream &out);
private:
void bus_autorecover();
void handleInterrupt();
int _parsePacket();
private:
int8_t _tx, _rx;
int8_t _idx;
// intr_handle_t _intrHandle;
void *_state;
void *_hw;
static CANSAME5x *instances[2];
static void onInterrupt();
friend void CAN0_Handler(void);
friend void CAN1_Handler(void);
};

3084
src/CANSAME5x_port.h Normal file

File diff suppressed because it is too large Load diff