gh-pages set up
This commit is contained in:
parent
c628716304
commit
94376be457
12 changed files with 0 additions and 4208 deletions
0
.nojekyll
Normal file
0
.nojekyll
Normal file
|
|
@ -1,4 +0,0 @@
|
|||
# Adafruit CAN Library
|
||||
|
||||
This is an Arduino library for the supporting boards with native CAN peripherals.
|
||||
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
/*
|
||||
* 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();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
/*
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
||||
23
keywords.txt
23
keywords.txt
|
|
@ -1,23 +0,0 @@
|
|||
#######################################
|
||||
# Syntax Coloring Map for CAN library
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
|
||||
#######################################
|
||||
# Structures (KEYWORD3)
|
||||
#######################################
|
||||
|
||||
|
||||
######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
name=Adafruit CAN
|
||||
version=0.2.0
|
||||
author=Adafruit
|
||||
maintainer=Adafruit <info@adafruit.com>
|
||||
sentence=Arduino library for native CAN.
|
||||
paragraph=Arduino library for native CAN.
|
||||
category=Sensors
|
||||
url=https://github.com/adafruit/Adafruit_CAN
|
||||
architectures=samd
|
||||
|
|
@ -1,216 +0,0 @@
|
|||
// 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;
|
||||
}
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
// 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
|
||||
|
|
@ -1,567 +0,0 @@
|
|||
// 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
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
// 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
3084
src/CANSAME5x_port.h
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue