├── README.md ├── examples ├── CANTestESP32_Both │ └── CANTestESP32_Both.ino ├── CANTestESP32_BuiltIn │ └── CANTestESP32_BuiltIn.ino ├── CANTestESP32_BuiltIn_AltPins │ └── CANTestESP32_BuiltIn_AltPins.ino ├── CANTestESP32_BuiltIn_BitModification │ └── CANTestESP32_BuiltIn_BitModification.ino ├── CANTestESP32_CAN1 │ └── CANTestESP32_CAN1.ino └── CANTestESP32_FDMode │ └── CANTestESP32_FDMode.ino ├── keywords.txt ├── library.properties ├── license.txt └── src ├── can_config.h ├── can_regdef.h ├── esp32_can.cpp ├── esp32_can.h ├── esp32_can_builtin.cpp ├── esp32_can_builtin.h ├── esp32_can_builtin_lowlevel.cpp ├── esp32_can_builtin_lowlevel.h ├── mcp2515.cpp ├── mcp2515.h ├── mcp2515_defs.h ├── mcp2517fd.cpp ├── mcp2517fd.h ├── mcp2517fd_defines.h └── mcp2517fd_regs.h /README.md: -------------------------------------------------------------------------------- 1 | esp32_can 2 | ========== 3 | 4 | A new, unified library all inclusive of code needed to make it operate. Implements 5 | a CAN driver for the built-in CAN hardware on an ESP32. Also implements a driver 6 | for the MCP2517FD SPI connected CAN module. The builtin CAN is called CAN0, 7 | the MCP2517FD is called CAN1. This library is specifically meant to be used with 8 | the EVTV ESP32-Due board. However, with small modifications either driver found 9 | within this library could be used on other boards. 10 | 11 | This library requires the can_common library. That library is a common base that 12 | other libraries can be built off of to allow a more universal API for CAN. 13 | 14 | The needed can_common library is found here: https://github.com/collin80/can_common 15 | -------------------------------------------------------------------------------- /examples/CANTestESP32_Both/CANTestESP32_Both.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Demo of using both CAN ports on the EVTV ESP32 board. Shows an example of many techniques in the library: 3 | 1. Initializing both 4 | 2. Set Can0 to forward any traffic where the ID starts with 0x1?? where ?? could be any number 0 - FF 5 | 3. Set Can1 to forward any traffic where the ID starts with 0x23? where ? could be any number 0 - F 6 | 4. Do both of the above via a C style callback 7 | 5. Display any other frames over the serial port 8 | */ 9 | 10 | #include "esp32_can.h" 11 | 12 | void handleCAN0CB(CAN_FRAME *frame) 13 | { 14 | CAN1.sendFrame(*frame); 15 | } 16 | 17 | void handleCAN1CB(CAN_FRAME *frame) 18 | { 19 | CAN0.sendFrame(*frame); 20 | } 21 | 22 | void printFrame(CAN_FRAME &frame) 23 | { 24 | // Print message 25 | Serial.print("ID: "); 26 | Serial.println(frame.id,HEX); 27 | Serial.print("Ext: "); 28 | if(frame.extended) { 29 | Serial.println("Y"); 30 | } else { 31 | Serial.println("N"); 32 | } 33 | Serial.print("Len: "); 34 | Serial.println(frame.length,DEC); 35 | for(int i = 0;i < frame.length; i++) { 36 | Serial.print(frame.data.uint8[i],HEX); 37 | Serial.print(" "); 38 | } 39 | Serial.println(); 40 | } 41 | 42 | void setup() { 43 | Serial.begin(115200); 44 | 45 | Serial.println("Initializing ..."); 46 | 47 | // Initialize builtin CAN controller at the specified speed 48 | if(CAN0.begin(500000)) 49 | { 50 | Serial.println("Builtin CAN Init OK ..."); 51 | } else { 52 | Serial.println("BuiltIn CAN Init Failed ..."); 53 | } 54 | 55 | // Initialize MCP2517FD CAN controller at the specified speed 56 | if(CAN1.begin(500000)) 57 | { 58 | Serial.println("MCP2517FD Init OK ..."); 59 | } else { 60 | Serial.println("MCP2517FD Init Failed ..."); 61 | } 62 | 63 | CAN0.setRXFilter(0, 0x100, 0x700, false); 64 | CAN0.watchFor(); //allow everything else through 65 | CAN0.setCallback(0, handleCAN0CB); 66 | 67 | CAN1.setRXFilter(0, 0x230, 0x7F0, false); 68 | CAN1.watchFor(); //allow everything else through 69 | CAN1.setCallback(0, handleCAN1CB); 70 | 71 | Serial.println("Ready ...!"); 72 | } 73 | 74 | 75 | void loop() { 76 | CAN_FRAME message; 77 | 78 | if (CAN0.read(message)) { 79 | printFrame(message); 80 | } 81 | if (CAN1.read(message)) { 82 | printFrame(message); 83 | } 84 | } 85 | 86 | -------------------------------------------------------------------------------- /examples/CANTestESP32_BuiltIn/CANTestESP32_BuiltIn.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void printFrame(CAN_FRAME *message) 4 | { 5 | Serial.print(message->id, HEX); 6 | if (message->extended) Serial.print(" X "); 7 | else Serial.print(" S "); 8 | Serial.print(message->length, DEC); 9 | for (int i = 0; i < message->length; i++) { 10 | Serial.print(message->data.byte[i], HEX); 11 | Serial.print(" "); 12 | } 13 | Serial.println(); 14 | } 15 | 16 | void gotHundred(CAN_FRAME *frame) 17 | { 18 | Serial.print("Got special frame! "); 19 | printFrame(frame); 20 | } 21 | 22 | void setup() { 23 | Serial.begin(115200); 24 | 25 | Serial.println("Initializing ..."); 26 | 27 | CAN0.begin(500000); 28 | 29 | Serial.println("Ready ...!"); 30 | CAN_FRAME txFrame; 31 | txFrame.rtr = 0; 32 | txFrame.id = 0x123; 33 | txFrame.extended = false; 34 | txFrame.length = 4; 35 | txFrame.data.uint8[0] = 0x10; 36 | txFrame.data.uint8[1] = 0x1A; 37 | txFrame.data.uint8[2] = 0xFF; 38 | txFrame.data.uint8[3] = 0x5D; 39 | CAN0.sendFrame(txFrame); 40 | 41 | CAN0.watchFor(0x100, 0xF00); //setup a special filter 42 | CAN0.watchFor(); //then let everything else through anyway 43 | CAN0.setCallback(0, gotHundred); //callback on that first special filter 44 | } 45 | 46 | void loop() { 47 | byte i = 0; 48 | CAN_FRAME message; 49 | if (CAN0.read(message)) { 50 | 51 | printFrame(&message); 52 | 53 | // Send out a return message for each one received 54 | // Simply increment message id and data bytes to show proper transmission 55 | // Note: this will double the traffic on the network (provided it passes the filter above) 56 | message.id++; 57 | for (i = 0; i < message.length; i++) { 58 | message.data.uint8[i]++; 59 | } 60 | //CAN.sendFrame(message); 61 | } 62 | //or, just plain send traffic periodically 63 | /* 64 | delayMicroseconds(200); 65 | message.id++; 66 | message.length = 8; 67 | for(i=0;i 2 | 3 | void printFrame(CAN_FRAME *message) 4 | { 5 | Serial.print(message->id, HEX); 6 | if (message->extended) Serial.print(" X "); 7 | else Serial.print(" S "); 8 | Serial.print(message->length, DEC); 9 | Serial.print(" "); 10 | for (int i = 0; i < message->length; i++) { 11 | Serial.print(message->data.byte[i], HEX); 12 | Serial.print(" "); 13 | } 14 | Serial.println(); 15 | } 16 | 17 | void gotHundred(CAN_FRAME *frame) 18 | { 19 | Serial.print("Got special frame! "); 20 | printFrame(frame); 21 | } 22 | 23 | void setup() { 24 | Serial.begin(115200); 25 | 26 | Serial.println("Initializing ..."); 27 | 28 | pinMode(GPIO_NUM_16, OUTPUT); 29 | digitalWrite(GPIO_NUM_16, LOW); //enable CAN transceiver 30 | CAN0.setCANPins(GPIO_NUM_4, GPIO_NUM_5); 31 | CAN0.begin(500000); 32 | 33 | Serial.println("Ready ...!"); 34 | CAN_FRAME txFrame; 35 | txFrame.rtr = 0; 36 | txFrame.id = 0x123; 37 | txFrame.extended = false; 38 | txFrame.length = 4; 39 | txFrame.data.uint8[0] = 0x10; 40 | txFrame.data.uint8[1] = 0x1A; 41 | txFrame.data.uint8[2] = 0xFF; 42 | txFrame.data.uint8[3] = 0x5D; 43 | CAN0.sendFrame(txFrame); 44 | 45 | CAN0.watchFor(0x100, 0xF00); //setup a special filter 46 | CAN0.watchFor(); //then let everything else through anyway 47 | CAN0.setCallback(0, gotHundred); //callback on that first special filter 48 | } 49 | 50 | void loop() { 51 | byte i = 0; 52 | CAN_FRAME message; 53 | if (CAN0.read(message)) { 54 | 55 | printFrame(&message); 56 | 57 | // Send out a return message for each one received 58 | // Simply increment message id and data bytes to show proper transmission 59 | // Note: this will double the traffic on the network (provided it passes the filter above) 60 | message.id++; 61 | for (i = 0; i < message.length; i++) { 62 | message.data.uint8[i]++; 63 | } 64 | //CAN.sendFrame(message); 65 | } 66 | //or, just plain send traffic periodically 67 | /* 68 | delayMicroseconds(200); 69 | message.id++; 70 | message.length = 8; 71 | for(i=0;i 2 | 3 | void printFrame(CAN_FRAME *message) 4 | { 5 | Serial.print(message->id, HEX); 6 | if (message->extended) Serial.print(" X "); 7 | else Serial.print(" S "); 8 | Serial.print(message->length, DEC); 9 | for (int i = 0; i < message->length; i++) { 10 | Serial.print(message->data.byte[i], HEX); 11 | Serial.print(" "); 12 | } 13 | Serial.println(); 14 | } 15 | 16 | void gotHundred(CAN_FRAME *frame) 17 | { 18 | Serial.print("Got special frame! "); 19 | printFrame(frame); 20 | } 21 | 22 | void setup() { 23 | Serial.begin(115200); 24 | 25 | Serial.println("Initializing ..."); 26 | 27 | CAN0.begin(500000); 28 | 29 | Serial.println("Ready ...!"); 30 | CAN_FRAME txFrame; 31 | txFrame.rtr = 0; 32 | txFrame.id = 0x123; 33 | txFrame.extended = false; 34 | txFrame.length = 4; 35 | txFrame.data.int8[0] = 0x10; 36 | txFrame.data.int8[1] = 0x1A; 37 | txFrame.data.int8[2] = 0xFF; 38 | txFrame.data.int8[3] = 0x5D; 39 | txFrame.data.int8[7] = 0xFF; 40 | txFrame.data.bit[0] = 1; 41 | txFrame.data.bit[10] = 1; 42 | txFrame.data.bit[20] = 1; 43 | txFrame.data.bit[40] = 1; 44 | txFrame.data.bit[50] = 1; 45 | txFrame.data.bit[60] = 0; 46 | for (int i = 0; i < 8; i++) 47 | { 48 | for (int j = 7; j > -1; j--) { 49 | Serial.print(txFrame.data.bit[i * 8 + j]); 50 | } 51 | Serial.println(); 52 | } 53 | Serial.println(); 54 | CAN0.sendFrame(txFrame); 55 | 56 | CAN0.watchFor(0x100, 0xF00); //setup a special filter 57 | CAN0.watchFor(); //then let everything else through anyway 58 | CAN0.setCallback(0, gotHundred); //callback on that first special filter 59 | } 60 | 61 | void loop() { 62 | byte i = 0; 63 | CAN_FRAME message; 64 | if (CAN0.read(message)) { 65 | 66 | printFrame(&message); 67 | 68 | // Send out a return message for each one received 69 | // Simply increment message id and data bytes to show proper transmission 70 | // Note: this will double the traffic on the network (provided it passes the filter above) 71 | message.id++; 72 | for (i = 0; i < message.length; i++) { 73 | message.data.uint8[i]++; 74 | } 75 | //CAN.sendFrame(message); 76 | } 77 | //or, just plain send traffic periodically 78 | /* 79 | delayMicroseconds(200); 80 | message.id++; 81 | message.length = 8; 82 | for(i=0;i 5 | sentence=Library to facilitate CAN functionality with the on-chip CAN hardware as well as the MCP2517FD 6 | paragraph= 7 | category=Communication 8 | url=https://github.com/collin80/esp32_can 9 | architectures=esp32 10 | includes=esp32_can.h 11 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | Copyright 2018 Collin Kidder 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), 4 | to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 5 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 10 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 11 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 12 | IN THE SOFTWARE. 13 | -------------------------------------------------------------------------------- /src/can_config.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @section License 3 | * 4 | * The MIT License (MIT) 5 | * 6 | * Copyright (c) 2017, Thomas Barth, barth-dev.de 7 | * 8 | * Permission is hereby granted, free of charge, to any person 9 | * obtaining a copy of this software and associated documentation 10 | * files (the "Software"), to deal in the Software without 11 | * restriction, including without limitation the rights to use, copy, 12 | * modify, merge, publish, distribute, sublicense, and/or sell copies 13 | * of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be 17 | * included in all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 23 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 24 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 25 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | * SOFTWARE. 27 | */ 28 | 29 | #ifndef __DRIVERS_CAN_CFG_H__ 30 | #define __DRIVERS_CAN_CFG_H__ 31 | 32 | #include "freertos/FreeRTOS.h" 33 | #include "freertos/queue.h" 34 | #include "driver/gpio.h" 35 | 36 | 37 | /** \brief CAN Node Bus speed */ 38 | typedef enum { 39 | CAN_SPEED_33KBPS =33, 40 | CAN_SPEED_80KBPS =80, 41 | CAN_SPEED_100KBPS=100, /**< \brief CAN Node runs at 100kBit/s. */ 42 | CAN_SPEED_125KBPS=125, /**< \brief CAN Node runs at 125kBit/s. */ 43 | CAN_SPEED_250KBPS=250, /**< \brief CAN Node runs at 250kBit/s. */ 44 | CAN_SPEED_500KBPS=500, /**< \brief CAN Node runs at 500kBit/s. */ 45 | CAN_SPEED_800KBPS=800, /**< \brief CAN Node runs at 800kBit/s. */ 46 | CAN_SPEED_1000KBPS=1000 /**< \brief CAN Node runs at 1000kBit/s. */ 47 | }CAN_speed_t; 48 | 49 | /** \brief CAN configuration structure */ 50 | typedef struct { 51 | CAN_speed_t speed; /**< \brief CAN speed. */ 52 | gpio_num_t tx_pin_id; /**< \brief TX pin. */ 53 | gpio_num_t rx_pin_id; /**< \brief RX pin. */ 54 | QueueHandle_t rx_queue; /**< \brief Handler to FreeRTOS RX queue. */ 55 | QueueHandle_t tx_queue; /**< \brief Handler to FreeRTOS TX queue */ 56 | }CAN_device_t; 57 | 58 | /** \brief CAN configuration reference */ 59 | extern CAN_device_t CAN_cfg; 60 | 61 | 62 | #endif /* __DRIVERS_CAN_CFG_H__ */ 63 | -------------------------------------------------------------------------------- /src/can_regdef.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @section License 3 | * 4 | * The MIT License (MIT) 5 | * 6 | * Copyright (c) 2017, Thomas Barth, barth-dev.de 7 | * 8 | * Permission is hereby granted, free of charge, to any person 9 | * obtaining a copy of this software and associated documentation 10 | * files (the "Software"), to deal in the Software without 11 | * restriction, including without limitation the rights to use, copy, 12 | * modify, merge, publish, distribute, sublicense, and/or sell copies 13 | * of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be 17 | * included in all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 23 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 24 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 25 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | * SOFTWARE. 27 | */ 28 | 29 | #ifndef __DRIVERS_CAN_REGDEF_H_ 30 | #define __DRIVERS_CAN_REGDEF_H_ 31 | 32 | // #include "esp32_can_builtin.h" //CAN_FIR_t 33 | #include "soc/twai_struct.h" 34 | 35 | /** \brief Interrupt status register */ 36 | typedef enum { 37 | __CAN_IRQ_RX= BIT(0), /**< \brief RX Interrupt */ 38 | __CAN_IRQ_TX= BIT(1), /**< \brief TX Interrupt */ 39 | __CAN_IRQ_ERR= BIT(2), /**< \brief Error Interrupt */ 40 | __CAN_IRQ_DATA_OVERRUN= BIT(3), /**< \brief Date Overrun Interrupt */ 41 | __CAN_IRQ_WAKEUP= BIT(4), /**< \brief Wakeup Interrupt */ 42 | __CAN_IRQ_ERR_PASSIVE= BIT(5), /**< \brief Passive Error Interrupt */ 43 | __CAN_IRQ_ARB_LOST= BIT(6), /**< \brief Arbitration lost interrupt */ 44 | __CAN_IRQ_BUS_ERR= BIT(7), /**< \brief Bus error Interrupt */ 45 | }__CAN_IRQ_t; 46 | 47 | #endif /* __DRIVERS_CAN_REGDEF_H_ */ 48 | -------------------------------------------------------------------------------- /src/esp32_can.cpp: -------------------------------------------------------------------------------- 1 | #include "esp32_can.h" 2 | 3 | //The objects in here are defined weak now. If you're using an EVTV board you need to do nothing. 4 | //If you're using something else that uses different pins then just go ahead and redefine 5 | //the objects with whichever pins you need. 6 | 7 | //Set these to the proper pin numbers for you board. Set by default to correct for EVTV ESP32-Due 8 | //rxpin txpin 9 | ESP32CAN __attribute__((weak)) CAN0(GPIO_NUM_13, GPIO_NUM_14) ; 10 | 11 | //Select and uncomment the proper module you've got connected via SPI 12 | //CS, INT 13 | MCP2517FD __attribute__((weak)) CAN1(5, 27) ; 14 | //MCP2515 __attribute__((weak)) CAN1(5, 27) ; -------------------------------------------------------------------------------- /src/esp32_can.h: -------------------------------------------------------------------------------- 1 | #include "esp32_can_builtin.h" 2 | #include "mcp2517fd.h" //uncomment if you've got a mcp2517fd attached to spi 3 | //#include "mcp2515.h" //uncomment if you've got a MCP2515 attached to SPI 4 | 5 | extern ESP32CAN CAN0; 6 | //Select which external chip you've got connected 7 | extern MCP2517FD CAN1; 8 | //extern MCP2515 CAN1; 9 | 10 | extern volatile uint32_t biIntsCounter; 11 | extern volatile uint32_t biReadFrames; 12 | 13 | #define Can0 CAN0 14 | #define Can1 CAN1 -------------------------------------------------------------------------------- /src/esp32_can_builtin.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ESP32_CAN.cpp - Library for ESP32 built-in CAN module 3 | 4 | Author: Collin Kidder 5 | 6 | Created: 31/1/18 7 | */ 8 | 9 | #include "Arduino.h" 10 | #include "esp32_can_builtin.h" 11 | 12 | CAN_device_t CAN_cfg = { 13 | CAN_SPEED_500KBPS, 14 | GPIO_NUM_17, 15 | GPIO_NUM_16, 16 | NULL, 17 | NULL 18 | }; 19 | 20 | QueueHandle_t callbackQueue; 21 | extern QueueHandle_t lowLevelRXQueue; 22 | 23 | extern volatile uint32_t needReset; 24 | 25 | ESP32CAN::ESP32CAN(gpio_num_t rxPin, gpio_num_t txPin) : CAN_COMMON(32) 26 | { 27 | CAN_cfg.rx_pin_id = rxPin; 28 | CAN_cfg.tx_pin_id = txPin; 29 | cyclesSinceTraffic = 0; 30 | rxBufferSize = BI_RX_BUFFER_SIZE; //set defaults 31 | txBufferSize = BI_TX_BUFFER_SIZE; 32 | } 33 | 34 | void ESP32CAN::setCANPins(gpio_num_t rxPin, gpio_num_t txPin) 35 | { 36 | CAN_cfg.rx_pin_id = rxPin; 37 | CAN_cfg.tx_pin_id = txPin; 38 | } 39 | 40 | void CAN_WatchDog_Builtin( void *pvParameters ) 41 | { 42 | ESP32CAN* espCan = (ESP32CAN*)pvParameters; 43 | const TickType_t xDelay = 200 / portTICK_PERIOD_MS; 44 | 45 | for(;;) 46 | { 47 | vTaskDelay( xDelay ); 48 | espCan->cyclesSinceTraffic++; 49 | if (needReset) 50 | { 51 | espCan->cyclesSinceTraffic = 0; 52 | needReset = 0; 53 | if (CAN_cfg.speed > 0 && CAN_cfg.speed <= 1000000ul && espCan->initializedResources == true) 54 | { 55 | if (espCan->debuggingMode) Serial.println("Builtin CAN Forced Reset!"); 56 | CAN_stop(); 57 | CAN_init(); 58 | } 59 | } 60 | } 61 | } 62 | 63 | void task_LowLevelRX(void *pvParameters) 64 | { 65 | ESP32CAN* espCan = (ESP32CAN*)pvParameters; 66 | CAN_frame_t rxFrame; 67 | 68 | while (1) 69 | { 70 | //receive next CAN frame from queue and fire off the callback 71 | if(xQueueReceive(lowLevelRXQueue, &rxFrame, portMAX_DELAY)==pdTRUE) 72 | { 73 | espCan->processFrame(rxFrame); 74 | } 75 | } 76 | } 77 | 78 | /* 79 | Issue callbacks to registered functions and objects 80 | Used to keep this kind of thing out of the interrupt handler 81 | The callback type and mailbox are passed in the fid member of the 82 | CAN_FRAME struct. It isn't really used by anything. 83 | Layout of the storage: 84 | bit 31 - If set indicates an object callback 85 | bits 24-30 - Idx into listener table 86 | bits 0-7 - Mailbox number that triggered callback 87 | */ 88 | void task_CAN( void *pvParameters ) 89 | { 90 | ESP32CAN* espCan = (ESP32CAN*)pvParameters; 91 | CAN_FRAME rxFrame; 92 | 93 | while (1) 94 | { 95 | //receive next CAN frame from queue and fire off the callback 96 | if(xQueueReceive(callbackQueue, &rxFrame, portMAX_DELAY)==pdTRUE) 97 | { 98 | espCan->sendCallback(&rxFrame); 99 | } 100 | } 101 | } 102 | 103 | void ESP32CAN::sendCallback(CAN_FRAME *frame) 104 | { 105 | //frame buffer 106 | CANListener *thisListener; 107 | int mb; 108 | int idx; 109 | 110 | mb = (frame->fid & 0xFF); 111 | if (mb == 0xFF) mb = -1; 112 | 113 | if (frame->fid & 0x80000000ul) //object callback 114 | { 115 | idx = (frame->fid >> 24) & 0x7F; 116 | thisListener = listener[idx]; 117 | thisListener->gotFrame(frame, mb); 118 | } 119 | else //C function callback 120 | { 121 | if (mb > -1) (*cbCANFrame[mb])(frame); 122 | else (*cbGeneral)(frame); 123 | } 124 | } 125 | 126 | ESP32CAN::ESP32CAN() : CAN_COMMON(BI_NUM_FILTERS) 127 | { 128 | for (int i = 0; i < BI_NUM_FILTERS; i++) 129 | { 130 | filters[i].id = 0; 131 | filters[i].mask = 0; 132 | filters[i].extended = false; 133 | filters[i].configured = false; 134 | } 135 | initializedResources = false; 136 | cyclesSinceTraffic = 0; 137 | } 138 | 139 | void ESP32CAN::setRXBufferSize(int newSize) 140 | { 141 | rxBufferSize = newSize; 142 | } 143 | 144 | void ESP32CAN::setTXBufferSize(int newSize) 145 | { 146 | txBufferSize = newSize; 147 | } 148 | 149 | int ESP32CAN::_setFilterSpecific(uint8_t mailbox, uint32_t id, uint32_t mask, bool extended) 150 | { 151 | if (mailbox < BI_NUM_FILTERS) 152 | { 153 | filters[mailbox].id = id & mask; 154 | filters[mailbox].mask = mask; 155 | filters[mailbox].extended = extended; 156 | filters[mailbox].configured = true; 157 | return mailbox; 158 | } 159 | return -1; 160 | } 161 | 162 | int ESP32CAN::_setFilter(uint32_t id, uint32_t mask, bool extended) 163 | { 164 | for (int i = 0; i < BI_NUM_FILTERS; i++) 165 | { 166 | if (!filters[i].configured) 167 | { 168 | _setFilterSpecific(i, id, mask, extended); 169 | return i; 170 | } 171 | } 172 | if (debuggingMode) Serial.println("Could not set filter!"); 173 | return -1; 174 | } 175 | 176 | void ESP32CAN::_init() 177 | { 178 | if (debuggingMode) Serial.println("Built in CAN Init"); 179 | for (int i = 0; i < BI_NUM_FILTERS; i++) 180 | { 181 | filters[i].id = 0; 182 | filters[i].mask = 0; 183 | filters[i].extended = false; 184 | filters[i].configured = false; 185 | } 186 | 187 | if (!initializedResources) { 188 | //Queue size, item size 189 | CAN_cfg.rx_queue = xQueueCreate(rxBufferSize,sizeof(CAN_frame_t)); 190 | CAN_cfg.tx_queue = xQueueCreate(txBufferSize,sizeof(CAN_frame_t)); 191 | callbackQueue = xQueueCreate(16, sizeof(CAN_FRAME)); 192 | CAN_initRXQueue(); 193 | //func desc stack, params, priority, handle to task 194 | xTaskCreate(&task_CAN, "CAN_RX", 8192, this, 15, NULL); 195 | xTaskCreatePinnedToCore(&task_LowLevelRX, "CAN_LORX", 4096, this, 19, NULL, 1); 196 | xTaskCreatePinnedToCore(&CAN_WatchDog_Builtin, "CAN_WD_BI", 2048, this, 10, NULL, 1); 197 | initializedResources = true; 198 | } 199 | } 200 | 201 | uint32_t ESP32CAN::init(uint32_t ul_baudrate) 202 | { 203 | _init(); 204 | CAN_cfg.speed = (CAN_speed_t)(ul_baudrate / 1000); 205 | needReset = 0; 206 | return CAN_init(); 207 | } 208 | 209 | uint32_t ESP32CAN::beginAutoSpeed() 210 | { 211 | const uint32_t bauds[7] = {1000,500,250,125,800,80,33}; //list of speeds to try, scaled down by 1000x 212 | bool oldLOM = CAN_GetListenOnlyMode(); 213 | 214 | _init(); 215 | 216 | CAN_stop(); 217 | CAN_SetListenOnly(true); 218 | for (int i = 0; i < 7; i++) 219 | { 220 | CAN_cfg.speed = (CAN_speed_t)bauds[i]; 221 | CAN_stop(); //stop hardware so we can reconfigure it 222 | needReset = 0; 223 | Serial.print("Trying Speed "); 224 | Serial.print(bauds[i] * 1000); 225 | CAN_init(); //set it up 226 | delay(600); //wait a while 227 | if (cyclesSinceTraffic < 2) //only would happen if there had been traffic 228 | { 229 | CAN_stop(); 230 | CAN_SetListenOnly(oldLOM); 231 | CAN_init(); 232 | Serial.println(" SUCCESS!"); 233 | return bauds[i] * 1000; 234 | } 235 | else 236 | { 237 | Serial.println(" FAILED."); 238 | } 239 | 240 | } 241 | Serial.println("None of the tested CAN speeds worked!"); 242 | CAN_stop(); 243 | return 0; 244 | } 245 | 246 | uint32_t ESP32CAN::set_baudrate(uint32_t ul_baudrate) 247 | { 248 | CAN_stop(); 249 | CAN_cfg.speed = (CAN_speed_t)(ul_baudrate / 1000); 250 | needReset = 0; 251 | return CAN_init(); 252 | } 253 | 254 | void ESP32CAN::setListenOnlyMode(bool state) 255 | { 256 | CAN_SetListenOnly(state); 257 | } 258 | 259 | void ESP32CAN::enable() 260 | { 261 | CAN_stop(); 262 | CAN_init(); 263 | needReset = 0; 264 | } 265 | 266 | void ESP32CAN::disable() 267 | { 268 | CAN_stop(); 269 | needReset = 0; 270 | } 271 | 272 | //This function is too big to be running in interrupt context. Refactored so it doesn't. 273 | bool ESP32CAN::processFrame(CAN_frame_t &frame) 274 | { 275 | CANListener *thisListener; 276 | CAN_FRAME msg; 277 | 278 | cyclesSinceTraffic = 0; //reset counter to show that we are receiving traffic 279 | 280 | msg.id = frame.MsgID; 281 | msg.length = frame.FIR.B.DLC; 282 | msg.rtr = frame.FIR.B.RTR; 283 | msg.extended = frame.FIR.B.FF; 284 | for (int i = 0; i < 8; i++) msg.data.byte[i] = frame.data.u8[i]; 285 | 286 | for (int i = 0; i < BI_NUM_FILTERS; i++) 287 | { 288 | if (!filters[i].configured) continue; 289 | if ((msg.id & filters[i].mask) == filters[i].id && (filters[i].extended == msg.extended)) 290 | { 291 | //frame is accepted, lets see if it matches a mailbox callback 292 | if (cbCANFrame[i]) 293 | { 294 | msg.fid = i; 295 | xQueueSend(callbackQueue, &msg, 0); 296 | return true; 297 | } 298 | else if (cbGeneral) 299 | { 300 | msg.fid = 0xFF; 301 | xQueueSend(callbackQueue, &msg, 0); 302 | return true; 303 | } 304 | else 305 | { 306 | for (int listenerPos = 0; listenerPos < SIZE_LISTENERS; listenerPos++) 307 | { 308 | thisListener = listener[listenerPos]; 309 | if (thisListener != NULL) 310 | { 311 | if (thisListener->isCallbackActive(i)) 312 | { 313 | msg.fid = 0x80000000ul + (listenerPos << 24ul) + i; 314 | xQueueSend(callbackQueue, &msg, 0); 315 | return true; 316 | } 317 | else if (thisListener->isCallbackActive(numFilters)) //global catch-all 318 | { 319 | msg.fid = 0x80000000ul + (listenerPos << 24ul) + 0xFF; 320 | xQueueSend(callbackQueue, &msg, 0); 321 | return true; 322 | } 323 | } 324 | } 325 | } 326 | 327 | //otherwise, send frame to input queue 328 | xQueueSend(CAN_cfg.rx_queue, &frame, 0); 329 | if (debuggingMode) Serial.write('_'); 330 | return true; 331 | } 332 | } 333 | return false; 334 | } 335 | 336 | bool ESP32CAN::sendFrame(CAN_FRAME& txFrame) 337 | { 338 | CAN_frame_t __TX_frame; 339 | 340 | __TX_frame.MsgID = txFrame.id; 341 | __TX_frame.FIR.B.DLC = txFrame.length; 342 | __TX_frame.FIR.B.RTR = (CAN_RTR_t)txFrame.rtr; 343 | __TX_frame.FIR.B.FF = (CAN_frame_format_t)txFrame.extended; 344 | for (int i = 0; i < 8; i++) __TX_frame.data.u8[i] = txFrame.data.byte[i]; 345 | 346 | if (CAN_TX_IsBusy()) //hardware already sending, queue for sending when possible 347 | { 348 | xQueueSend(CAN_cfg.tx_queue,&__TX_frame,0); 349 | if (debuggingMode) Serial.write('<'); 350 | } 351 | else //hardware is free, send immediately 352 | { 353 | CAN_write_frame(&__TX_frame); 354 | if (debuggingMode) Serial.write('>'); 355 | } 356 | return true; 357 | } 358 | 359 | bool ESP32CAN::rx_avail() 360 | { 361 | if (!CAN_cfg.rx_queue) return false; 362 | return uxQueueMessagesWaiting(CAN_cfg.rx_queue) > 0?true:false; 363 | } 364 | 365 | uint16_t ESP32CAN::available() 366 | { 367 | if (!CAN_cfg.rx_queue) return 0; 368 | return uxQueueMessagesWaiting(CAN_cfg.rx_queue); 369 | } 370 | 371 | uint32_t ESP32CAN::get_rx_buff(CAN_FRAME &msg) 372 | { 373 | CAN_frame_t __RX_frame; 374 | //receive next CAN frame from queue 375 | if(xQueueReceive(CAN_cfg.rx_queue,&__RX_frame, 0)==pdTRUE) 376 | { 377 | msg.id = __RX_frame.MsgID; 378 | msg.length = __RX_frame.FIR.B.DLC; 379 | msg.rtr = __RX_frame.FIR.B.RTR; 380 | msg.extended = __RX_frame.FIR.B.FF; 381 | for (int i = 0; i < 8; i++) msg.data.byte[i] = __RX_frame.data.u8[i]; 382 | return true; 383 | } 384 | return false; 385 | } 386 | 387 | -------------------------------------------------------------------------------- /src/esp32_can_builtin.h: -------------------------------------------------------------------------------- 1 | /* 2 | MCP2515.h - Library for Microchip MCP2515 CAN Controller 3 | 4 | Author: David Harding 5 | Maintainer: RechargeCar Inc (http://rechargecar.com) 6 | Further Modification: Collin Kidder 7 | 8 | Created: 11/08/2010 9 | 10 | For further information see: 11 | 12 | http://ww1.microchip.com/downloads/en/DeviceDoc/21801e.pdf 13 | http://en.wikipedia.org/wiki/CAN_bus 14 | 15 | 16 | This library is free software; you can redistribute it and/or 17 | modify it under the terms of the GNU Lesser General Public 18 | License as published by the Free Software Foundation; either 19 | version 2.1 of the License, or (at your option) any later version. 20 | 21 | This library is distributed in the hope that it will be useful, 22 | but WITHOUT ANY WARRANTY; without even the implied warranty of 23 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 24 | Lesser General Public License for more details. 25 | 26 | You should have received a copy of the GNU Lesser General Public 27 | License along with this library; if not, write to the Free Software 28 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 29 | */ 30 | 31 | #ifndef __ESP32_CAN__ 32 | #define __ESP32_CAN__ 33 | 34 | #include "Arduino.h" 35 | #include 36 | #include "freertos/FreeRTOS.h" 37 | #include "freertos/task.h" 38 | #include "freertos/queue.h" 39 | #include "driver/gpio.h" 40 | #include "driver/adc.h" 41 | #include "esp_system.h" 42 | #include "esp_adc_cal.h" 43 | #include "esp32_can_builtin_lowlevel.h" 44 | 45 | //#define DEBUG_SETUP 46 | #define BI_NUM_FILTERS 32 47 | 48 | #define BI_RX_BUFFER_SIZE 64 49 | #define BI_TX_BUFFER_SIZE 16 50 | 51 | typedef struct 52 | { 53 | uint32_t mask; 54 | uint32_t id; 55 | bool extended; 56 | bool configured; 57 | } ESP32_FILTER; 58 | 59 | class ESP32CAN : public CAN_COMMON 60 | { 61 | public: 62 | ESP32CAN(gpio_num_t rxPin, gpio_num_t txPin); 63 | ESP32CAN(); 64 | 65 | //block of functions which must be overriden from CAN_COMMON to implement functionality for this hardware 66 | int _setFilterSpecific(uint8_t mailbox, uint32_t id, uint32_t mask, bool extended); 67 | int _setFilter(uint32_t id, uint32_t mask, bool extended); 68 | void _init(); 69 | uint32_t init(uint32_t ul_baudrate); 70 | uint32_t beginAutoSpeed(); 71 | uint32_t set_baudrate(uint32_t ul_baudrate); 72 | void setListenOnlyMode(bool state); 73 | void enable(); 74 | void disable(); 75 | bool sendFrame(CAN_FRAME& txFrame); 76 | bool rx_avail(); 77 | void setTXBufferSize(int newSize); 78 | void setRXBufferSize(int newSize); 79 | uint16_t available(); //like rx_avail but returns the number of waiting frames 80 | uint32_t get_rx_buff(CAN_FRAME &msg); 81 | bool processFrame(CAN_frame_t &frame); 82 | void sendCallback(CAN_FRAME *frame); 83 | 84 | void setCANPins(gpio_num_t rxPin, gpio_num_t txPin); 85 | 86 | friend void CAN_WatchDog_Builtin( void *pvParameters ); 87 | 88 | protected: 89 | bool initializedResources; 90 | int cyclesSinceTraffic; 91 | 92 | private: 93 | // Pin variables 94 | ESP32_FILTER filters[BI_NUM_FILTERS]; 95 | int txBufferSize; 96 | int rxBufferSize; 97 | }; 98 | 99 | extern QueueHandle_t callbackQueue; 100 | 101 | #endif 102 | -------------------------------------------------------------------------------- /src/esp32_can_builtin_lowlevel.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @section License 3 | * 4 | * The MIT License (MIT) 5 | * 6 | * Copyright (c) 2017, Thomas Barth, barth-dev.de 7 | * 8 | * Permission is hereby granted, free of charge, to any person 9 | * obtaining a copy of this software and associated documentation 10 | * files (the "Software"), to deal in the Software without 11 | * restriction, including without limitation the rights to use, copy, 12 | * modify, merge, publish, distribute, sublicense, and/or sell copies 13 | * of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be 17 | * included in all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 23 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 24 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 25 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | * SOFTWARE. 27 | * 28 | */ 29 | #include "freertos/FreeRTOS.h" 30 | #include "freertos/queue.h" 31 | 32 | #include "esp_intr_alloc.h" 33 | #include "soc/dport_reg.h" 34 | #include "soc/periph_defs.h" 35 | #include "soc/twai_struct.h" 36 | #include "hal/twai_ll.h" 37 | #include 38 | 39 | #include "driver/gpio.h" 40 | 41 | #include "can_regdef.h" 42 | #include "can_config.h" 43 | #include "esp32_can_builtin_lowlevel.h" 44 | #include "esp32_can.h" 45 | 46 | volatile uint32_t biIntsCounter = 0; 47 | volatile uint32_t biReadFrames = 0; 48 | volatile uint32_t needReset = 0; 49 | QueueHandle_t lowLevelRXQueue; 50 | 51 | static portMUX_TYPE builtincan_spinlock = portMUX_INITIALIZER_UNLOCKED; 52 | #define CANBI_ENTER_CRITICAL() portENTER_CRITICAL(&builtincan_spinlock) 53 | #define CANBI_EXIT_CRITICAL() portEXIT_CRITICAL(&builtincan_spinlock) 54 | 55 | extern "C" void IRAM_ATTR CAN_isr(void *arg_p) 56 | { 57 | //Interrupt flag buffer 58 | __CAN_IRQ_t interrupt; 59 | CAN_frame_t __frame; 60 | BaseType_t xHigherPriorityTaskWoken = false; 61 | 62 | CANBI_ENTER_CRITICAL(); 63 | 64 | biIntsCounter++; 65 | 66 | // Read interrupt status and clear flags 67 | interrupt = (__CAN_IRQ_t)TWAI.interrupt_reg.val; 68 | 69 | // Handle TX complete interrupt 70 | if ((interrupt & __CAN_IRQ_TX) != 0) 71 | { 72 | if (uxQueueMessagesWaitingFromISR(CAN_cfg.tx_queue) > 0) 73 | { 74 | xQueueReceiveFromISR(CAN_cfg.tx_queue, &__frame, NULL); 75 | CAN_write_frame(&__frame); 76 | } 77 | } 78 | 79 | // Handle RX frame available interrupt 80 | if ((interrupt & __CAN_IRQ_RX) != 0) 81 | { 82 | for (int rxFrames = 0; rxFrames < TWAI.rx_message_counter_reg.rmc; rxFrames++) //read all frames from the hardware RX queue 83 | if (CAN_read_frame()) xHigherPriorityTaskWoken = true; 84 | } 85 | 86 | // Handle error interrupts. 87 | if ((interrupt & (__CAN_IRQ_ERR //0x4 88 | | __CAN_IRQ_DATA_OVERRUN //0x8 89 | | __CAN_IRQ_WAKEUP //0x10 90 | | __CAN_IRQ_ERR_PASSIVE //0x20 91 | | __CAN_IRQ_BUS_ERR //0x80 92 | )) != 0) 93 | { 94 | needReset = 1; 95 | } 96 | CANBI_EXIT_CRITICAL(); 97 | 98 | if (xHigherPriorityTaskWoken) { 99 | portYIELD_FROM_ISR(); 100 | } 101 | } 102 | 103 | BaseType_t IRAM_ATTR CAN_read_frame() 104 | { 105 | //byte iterator 106 | uint8_t __byte_i; 107 | 108 | //frame read buffer 109 | CAN_frame_t __frame; 110 | BaseType_t xHigherPriorityTaskWoken; 111 | 112 | biReadFrames++; 113 | 114 | //check if we have a queue. If not, operation is aborted. 115 | if (CAN_cfg.rx_queue == NULL) 116 | { 117 | // Let the hardware know the frame has been read. 118 | TWAI.command_reg.rrb = 1; 119 | return false; 120 | } 121 | 122 | twai_ll_frame_buffer_t twaiFrame; 123 | twai_ll_get_rx_buffer(&TWAI, &twaiFrame); 124 | 125 | //get FIR 126 | __frame.FIR.U = twaiFrame.bytes[0]; 127 | 128 | //check if this is a standard or extended CAN frame 129 | //standard frame 130 | if(__frame.FIR.B.FF == CAN_frame_std) 131 | { 132 | //Get Message ID 133 | __frame.MsgID = (((uint32_t)twaiFrame.standard.id[0] << 3) | \ 134 | twaiFrame.standard.id[1] >> 5); 135 | 136 | //deep copy data bytes 137 | for(__byte_i = 0;__byte_i < __frame.FIR.B.DLC; __byte_i++) 138 | __frame.data.u8[__byte_i] = twaiFrame.standard.data[__byte_i]; 139 | } 140 | //extended frame 141 | else 142 | { 143 | //Get Message ID 144 | __frame.MsgID = (((uint32_t)twaiFrame.standard.id[0] << 21) | \ 145 | (twaiFrame.standard.id[1] << 13) | \ 146 | (twaiFrame.standard.id[2] << 5) | \ 147 | (twaiFrame.standard.id[3] >> 3 )); 148 | 149 | //deep copy data bytes 150 | for(__byte_i=0;__byte_i < __frame.FIR.B.DLC; __byte_i++) 151 | __frame.data.u8[__byte_i] = twaiFrame.extended.data[__byte_i]; 152 | } 153 | xQueueSendFromISR(lowLevelRXQueue, &__frame, &xHigherPriorityTaskWoken); 154 | 155 | //Let the hardware know the frame has been read. 156 | TWAI.command_reg.rrb = 1; 157 | 158 | return xHigherPriorityTaskWoken; 159 | } 160 | 161 | bool CAN_TX_IsBusy() 162 | { 163 | return !(TWAI.status_reg.tbs); 164 | } 165 | 166 | void CAN_SetListenOnly(bool mode) 167 | { 168 | CAN_stop(); 169 | TWAI.mode_reg.lom = mode?1:0; 170 | CAN_init(); 171 | } 172 | 173 | bool CAN_GetListenOnlyMode() 174 | { 175 | return TWAI.mode_reg.lom; 176 | } 177 | 178 | int IRAM_ATTR CAN_write_frame(const CAN_frame_t* p_frame) 179 | { 180 | //byte iterator 181 | uint8_t __byte_i; 182 | 183 | twai_ll_frame_buffer_t twaiFrame; 184 | 185 | //copy frame information record 186 | twaiFrame.bytes[0]=p_frame->FIR.U; 187 | 188 | //standard frame 189 | if(p_frame->FIR.B.FF==CAN_frame_std) 190 | { 191 | 192 | //Write message ID 193 | twaiFrame.standard.id[0] = ((p_frame->MsgID) >> 3); 194 | twaiFrame.standard.id[1] = ((p_frame->MsgID) << 5); 195 | 196 | // Copy the frame data to the hardware 197 | for(__byte_i=0;__byte_iFIR.B.DLC;__byte_i++) 198 | TWAI.tx_rx_buffer[__byte_i].byte=twaiFrame.bytes[__byte_i]; 199 | 200 | } 201 | //extended frame 202 | else 203 | { 204 | 205 | //Write message ID 206 | twaiFrame.extended.id[0] = ((p_frame->MsgID) >> 21); 207 | twaiFrame.extended.id[1] = ((p_frame->MsgID) >> 13); 208 | twaiFrame.extended.id[2] = ((p_frame->MsgID) >> 5); 209 | twaiFrame.extended.id[3] = ((p_frame->MsgID) << 3); 210 | 211 | // Copy the frame data to the hardware 212 | for(__byte_i=0;__byte_iFIR.B.DLC;__byte_i++) 213 | TWAI.tx_rx_buffer[__byte_i].byte=twaiFrame.bytes[__byte_i]; 214 | 215 | } 216 | 217 | // Transmit frame 218 | TWAI.command_reg.tr=1; 219 | 220 | return 0; 221 | } 222 | 223 | int CAN_init() 224 | { 225 | //Time quantum 226 | double __tq; 227 | 228 | CANBI_ENTER_CRITICAL(); 229 | 230 | //enable module 231 | DPORT_SET_PERI_REG_MASK(SYSTEM_PERIP_CLK_EN0_REG, SYSTEM_TWAI_CLK_EN); 232 | DPORT_CLEAR_PERI_REG_MASK(SYSTEM_PERIP_RST_EN0_REG, SYSTEM_TWAI_RST); 233 | 234 | TWAI.mode_reg.rm = 1; //first thing once module is enabled at hardware level is to make sure it is in reset 235 | 236 | //configure TX pin 237 | gpio_set_level(CAN_cfg.tx_pin_id, 1); 238 | gpio_set_direction(CAN_cfg.tx_pin_id,GPIO_MODE_OUTPUT); 239 | gpio_matrix_out(CAN_cfg.tx_pin_id,TWAI_TX_IDX,0,0); 240 | gpio_pad_select_gpio(CAN_cfg.tx_pin_id); 241 | 242 | //configure RX pin 243 | gpio_set_direction(CAN_cfg.rx_pin_id,GPIO_MODE_INPUT); 244 | gpio_matrix_in(CAN_cfg.rx_pin_id,TWAI_RX_IDX,0); 245 | gpio_pad_select_gpio(CAN_cfg.rx_pin_id); 246 | 247 | //set to PELICAN mode 248 | // MODULE_CAN->CDR.B.CAN_M = 0x1; 249 | 250 | TWAI.interrupt_enable_reg.val = 0; //disable all interrupt sources until we're ready 251 | //clear interrupt flags 252 | (void)TWAI.interrupt_reg.val; 253 | 254 | //synchronization jump width is the same for all baud rates 255 | TWAI.bus_timing_0_reg.sjw = 0x1; 256 | 257 | //TSEG2 is the same for all baud rates 258 | TWAI.bus_timing_1_reg.tseg2 = 0x1; 259 | 260 | //select time quantum and set TSEG1 261 | switch(CAN_cfg.speed) 262 | { 263 | case CAN_SPEED_1000KBPS: 264 | TWAI.bus_timing_1_reg.tseg1 = 0x4; 265 | __tq = 0.125; 266 | break; 267 | 268 | case CAN_SPEED_800KBPS: 269 | TWAI.bus_timing_1_reg.tseg1 = 0x6; 270 | __tq = 0.125; 271 | break; 272 | case CAN_SPEED_33KBPS: 273 | //changes everything... 274 | TWAI.bus_timing_1_reg.tseg2 = 0x6; 275 | TWAI.bus_timing_1_reg.tseg1 = 0xf; //16 + 1 + 7 = 24 276 | __tq = ((float)1000.0f / 33.3f) / 24.0f; 277 | break; 278 | default: 279 | TWAI.bus_timing_1_reg.tseg1 = 0xc; 280 | __tq = ((float)1000.0f / (float)CAN_cfg.speed) / 16.0f; 281 | } 282 | 283 | //set baud rate prescaler 284 | //APB_CLK_FREQ should be 80M 285 | TWAI.bus_timing_0_reg.brp = (uint8_t)round((((APB_CLK_FREQ * __tq) / 2) - 1)/1000000)-1; 286 | 287 | /* Set sampling 288 | * 1 -> triple; the bus is sampled three times; recommended for low/medium speed buses (class A and B) where filtering spikes on the bus line is beneficial 289 | * 0 -> single; the bus is sampled once; recommended for high speed buses (SAE class C)*/ 290 | TWAI.bus_timing_1_reg.sam = 0x1; 291 | 292 | //enable all interrupts (BUT NOT BIT 4 which has turned into a baud rate scalar!) 293 | TWAI.interrupt_enable_reg.val = 0xEF; //1110 1111 294 | 295 | //no acceptance filtering, as we want to fetch all messages 296 | TWAI.acceptance_filter.acr[0].val = 0xfff; 297 | TWAI.acceptance_filter.acr[1].val = 0xfff; 298 | TWAI.acceptance_filter.acr[2].val = 0xfff; 299 | TWAI.acceptance_filter.acr[3].val = 0xfff; 300 | TWAI.acceptance_filter.amr[0].val = 0xfff; 301 | TWAI.acceptance_filter.amr[1].val = 0xfff; 302 | TWAI.acceptance_filter.amr[2].val = 0xfff; 303 | TWAI.acceptance_filter.amr[3].val = 0xfff; 304 | 305 | //set to normal mode 306 | TWAI.mode_reg.val = 0; //reset flags 307 | 308 | //clear error counters 309 | TWAI.tx_error_counter_reg.txerr = 0; 310 | TWAI.rx_error_counter_reg.rxerr = 0; 311 | (void)TWAI.error_code_capture_reg.val; 312 | 313 | //clear interrupt flags 314 | (void)TWAI.interrupt_reg.val; 315 | 316 | //install CAN ISR 317 | esp_intr_alloc(ETS_TWAI_INTR_SOURCE, ESP_INTR_FLAG_IRAM, CAN_isr, NULL, NULL); 318 | 319 | //Showtime. Release Reset Mode. 320 | TWAI.mode_reg.rm = 0; 321 | 322 | CANBI_EXIT_CRITICAL(); 323 | 324 | return true; 325 | } 326 | 327 | //allocates a small queue used to buffer frames that were received in the 328 | //interrupt handler but not yet processed by the rest of the code 329 | void CAN_initRXQueue() 330 | { 331 | lowLevelRXQueue = xQueueCreate(12, sizeof(CAN_frame_t)); 332 | } 333 | 334 | int CAN_stop() 335 | { 336 | //enter reset mode 337 | TWAI.mode_reg.rm = 1; 338 | 339 | DPORT_CLEAR_PERI_REG_MASK(SYSTEM_PERIP_CLK_EN0_REG, SYSTEM_TWAI_CLK_EN); 340 | DPORT_SET_PERI_REG_MASK(SYSTEM_PERIP_RST_EN0_REG, SYSTEM_TWAI_RST); 341 | 342 | TWAI.interrupt_enable_reg.val = 0; //enable no interrupts 343 | 344 | return 0; 345 | } 346 | -------------------------------------------------------------------------------- /src/esp32_can_builtin_lowlevel.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @section License 3 | * 4 | * The MIT License (MIT) 5 | * 6 | * Copyright (c) 2017, Thomas Barth, barth-dev.de 7 | * 8 | * Permission is hereby granted, free of charge, to any person 9 | * obtaining a copy of this software and associated documentation 10 | * files (the "Software"), to deal in the Software without 11 | * restriction, including without limitation the rights to use, copy, 12 | * modify, merge, publish, distribute, sublicense, and/or sell copies 13 | * of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be 17 | * included in all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 23 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 24 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 25 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | * SOFTWARE. 27 | */ 28 | 29 | #ifndef __DRIVERS_CAN_H__ 30 | #define __DRIVERS_CAN_H__ 31 | 32 | #include 33 | #include "can_config.h" 34 | 35 | /** 36 | * \brief CAN frame type (standard/extended) 37 | */ 38 | typedef enum { 39 | CAN_frame_std=0, /**< Standard frame, using 11 bit identifer. */ 40 | CAN_frame_ext=1 /**< Extended frame, using 29 bit identifer. */ 41 | }CAN_frame_format_t; 42 | 43 | /** 44 | * \brief CAN RTR 45 | */ 46 | typedef enum { 47 | CAN_no_RTR=0, /**< No RTR frame. */ 48 | CAN_RTR=1 /**< RTR frame. */ 49 | }CAN_RTR_t; 50 | 51 | /** \brief Frame information record type */ 52 | typedef union{uint32_t U; /**< \brief Unsigned access */ 53 | struct { 54 | uint8_t DLC:4; /**< \brief [3:0] DLC, Data length container */ 55 | unsigned int unknown_2:2; /**< \brief \internal unknown */ 56 | CAN_RTR_t RTR:1; /**< \brief [6:6] RTR, Remote Transmission Request */ 57 | CAN_frame_format_t FF:1; /**< \brief [7:7] Frame Format, see# CAN_frame_format_t*/ 58 | unsigned int reserved_24:24; /**< \brief \internal Reserved */ 59 | } B; 60 | } CAN_FIR_t; 61 | 62 | 63 | /** \brief CAN Frame structure */ 64 | typedef struct { 65 | CAN_FIR_t FIR; /**< \brief Frame information record*/ 66 | uint32_t MsgID; /**< \brief Message ID */ 67 | union { 68 | uint8_t u8[8]; /**< \brief Payload byte access*/ 69 | uint32_t u32[2]; /**< \brief Payload u32 access*/ 70 | } data; 71 | }CAN_frame_t; 72 | 73 | 74 | BaseType_t CAN_read_frame(); 75 | 76 | /** 77 | * \brief Initialize the CAN Module 78 | * 79 | * \return 0 CAN Module had been initialized 80 | */ 81 | int CAN_init(void); 82 | 83 | void CAN_initRXQueue(); 84 | 85 | /** 86 | * \brief Send a can frame 87 | * 88 | * \param p_frame Pointer to the frame to be send, see #CAN_frame_t 89 | * \return 0 Frame has been written to the module or queued 90 | */ 91 | int CAN_write_frame(const CAN_frame_t* p_frame); 92 | 93 | /** 94 | * \brief Determine if hardware has a message to send over CAN 95 | * 96 | * \return True if TX hardware is busy, false otherwise 97 | */ 98 | bool CAN_TX_IsBusy(); 99 | 100 | void CAN_SetListenOnly(bool mode); 101 | bool CAN_GetListenOnlyMode(); 102 | 103 | /** 104 | * \brief Stops the CAN Module 105 | * 106 | * \return 0 CAN Module was stopped 107 | */ 108 | int CAN_stop(void); 109 | 110 | #endif 111 | -------------------------------------------------------------------------------- /src/mcp2515.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | MCP2515.cpp - Library for Microchip MCP2515 CAN Controller 3 | 4 | Author: David Harding 5 | Maintainer: RechargeCar Inc (http://rechargecar.com) 6 | Further Modification: Collin Kidder 7 | 8 | Created: 11/08/2010 9 | 10 | For further information see: 11 | 12 | http://ww1.microchip.com/downloads/en/DeviceDoc/21801e.pdf 13 | http://en.wikipedia.org/wiki/CAN_bus 14 | 15 | 16 | This library is free software; you can redistribute it and/or 17 | modify it under the terms of the GNU Lesser General Public 18 | License as published by the Free Software Foundation; either 19 | version 2.1 of the License, or (at your option) any later version. 20 | 21 | This library is distributed in the hope that it will be useful, 22 | but WITHOUT ANY WARRANTY; without even the implied warranty of 23 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 24 | Lesser General Public License for more details. 25 | 26 | You should have received a copy of the GNU Lesser General Public 27 | License along with this library; if not, write to the Free Software 28 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 29 | */ 30 | 31 | #include "Arduino.h" 32 | #include "SPI.h" 33 | #include "mcp2515.h" 34 | #include "mcp2515_defs.h" 35 | #include "esp32_can.h" 36 | 37 | SPISettings mcpSPISettings(8000000, MSBFIRST, SPI_MODE0); 38 | 39 | static TaskHandle_t intDelegateTask = NULL; 40 | 41 | QueueHandle_t callbackQueueM15; 42 | 43 | void MCP_INTHandler() { 44 | BaseType_t xHigherPriorityTaskWoken = pdFALSE; 45 | vTaskNotifyGiveFromISR(intDelegateTask, &xHigherPriorityTaskWoken); //send notice to the handler task that it can do the SPI transaction now 46 | if (xHigherPriorityTaskWoken == pdTRUE) portYIELD_FROM_ISR(); //if vTaskNotify will wake the task (and it should) then yield directly to that task now 47 | } 48 | 49 | /* 50 | Issue callbacks to registered functions and objects 51 | Used to keep this kind of thing out of the interrupt handler 52 | The callback type and mailbox are passed in the fid member of the 53 | CAN_FRAME struct. It isn't really used by anything. 54 | Layout of the storage: 55 | bit 31 - If set indicates an object callback 56 | bits 24-30 - Idx into listener table 57 | bits 0-7 - Mailbox number that triggered callback 58 | */ 59 | void task_MCP15( void *pvParameters ) 60 | { 61 | MCP2515* mcpCan = (MCP2515*)pvParameters; 62 | CAN_FRAME rxFrame; 63 | 64 | while (1) 65 | { 66 | //receive next CAN frame from queue and fire off the callback 67 | if(xQueueReceive(callbackQueueM15, &rxFrame, portMAX_DELAY)==pdTRUE) 68 | { 69 | mcpCan->sendCallback(&rxFrame); 70 | } 71 | } 72 | } 73 | 74 | void task_MCPInt15( void *pvParameters ) 75 | { 76 | MCP2515* mcpCan = (MCP2515*)pvParameters; 77 | while (1) 78 | { 79 | ulTaskNotifyTake(pdTRUE, portMAX_DELAY); //wait infinitely for this task to be notified 80 | mcpCan->intHandler(); //not truly an interrupt handler anymore but still kind of 81 | } 82 | } 83 | 84 | void MCP2515::sendCallback(CAN_FRAME *frame) 85 | { 86 | //frame buffer 87 | CANListener *thisListener; 88 | int mb; 89 | int idx; 90 | 91 | mb = (frame->fid & 0xFF); 92 | if (mb == 0xFF) mb = -1; 93 | 94 | if (frame->fid & 0x80000000ul) //object callback 95 | { 96 | idx = (frame->fid >> 24) & 0x7F; 97 | thisListener = listener[idx]; 98 | thisListener->gotFrame(frame, mb); 99 | } 100 | else //C function callback 101 | { 102 | if (mb > -1) (*cbCANFrame[mb])(frame); 103 | else (*cbGeneral)(frame); 104 | } 105 | } 106 | 107 | MCP2515::MCP2515(uint8_t CS_Pin, uint8_t INT_Pin) : CAN_COMMON(6) { 108 | pinMode(CS_Pin, OUTPUT); 109 | digitalWrite(CS_Pin,HIGH); 110 | pinMode(INT_Pin,INPUT); 111 | digitalWrite(INT_Pin,HIGH); 112 | 113 | attachInterrupt(INT_Pin, MCP_INTHandler, FALLING); 114 | 115 | _CS = CS_Pin; 116 | _INT = INT_Pin; 117 | 118 | savedBaud = 0; 119 | savedFreq = 16; 120 | running = 0; 121 | inhibitTransactions = false; 122 | initializedResources = false; 123 | } 124 | 125 | void MCP2515::initializeResources() 126 | { 127 | if (initializedResources) return; 128 | 129 | rxQueue = xQueueCreate(MCP_RX_BUFFER_SIZE, sizeof(CAN_FRAME)); 130 | txQueue = xQueueCreate(MCP_TX_BUFFER_SIZE, sizeof(CAN_FRAME)); 131 | callbackQueueM15 = xQueueCreate(16, sizeof(CAN_FRAME)); 132 | 133 | //func desc stack, params, priority, handle to task, core to pin to 134 | xTaskCreatePinnedToCore(&task_MCP15, "CAN_RX_M15", 4096, this, 3, NULL, 0); 135 | xTaskCreatePinnedToCore(&task_MCPInt15, "CAN_INT_M15", 4096, this, 10, &intDelegateTask, 0); 136 | 137 | initializedResources = true; 138 | } 139 | 140 | void MCP2515::setINTPin(uint8_t pin) 141 | { 142 | detachInterrupt(_INT); 143 | _INT = pin; 144 | pinMode(_INT,INPUT); 145 | digitalWrite(_INT,HIGH); 146 | attachInterrupt(_INT, MCP_INTHandler, FALLING); 147 | } 148 | 149 | void MCP2515::setCSPin(uint8_t pin) 150 | { 151 | _CS = pin; 152 | pinMode(_CS, OUTPUT); 153 | digitalWrite(_CS,HIGH); 154 | } 155 | 156 | /* 157 | Initialize MCP2515 158 | 159 | int CAN_Bus_Speed = transfer speed in kbps (or raw CAN speed in bits per second) 160 | int Freq = MCP2515 oscillator frequency in MHz 161 | int SJW = Synchronization Jump Width Length bits - 1 to 4 (see data sheet) 162 | 163 | returns baud rate set 164 | 165 | Sending a bus speed of 0 kbps initiates AutoBaud and returns zero if no 166 | baud rate could be determined. There must be two other active nodes on the bus! 167 | */ 168 | int MCP2515::Init(uint32_t CAN_Bus_Speed, uint8_t Freq) { 169 | if(CAN_Bus_Speed>0) { 170 | if(_init(CAN_Bus_Speed, Freq, 1, false)) { 171 | savedBaud = CAN_Bus_Speed; 172 | savedFreq = Freq; 173 | running = 1; 174 | return CAN_Bus_Speed; 175 | } 176 | } else { 177 | int i=0; 178 | uint8_t interruptFlags = 0; 179 | for(i=5; i<1000; i=i+5) { 180 | if(_init(i, Freq, 1, true)) { 181 | // check for bus activity 182 | Write(CANINTF,0); 183 | delay(500); // need the bus to be communicating within this time frame 184 | if(Interrupt()) { 185 | // determine which interrupt flags have been set 186 | interruptFlags = Read(CANINTF); 187 | if(!(interruptFlags & MERRF)) { 188 | // to get here we must have received something without errors 189 | Mode(MODE_NORMAL); 190 | savedBaud = i; 191 | savedFreq = Freq; 192 | running = 1; 193 | return i; 194 | } 195 | } 196 | } 197 | } 198 | } 199 | return 0; 200 | } 201 | 202 | int MCP2515::Init(uint32_t CAN_Bus_Speed, uint8_t Freq, uint8_t SJW) { 203 | if(SJW < 1) SJW = 1; 204 | if(SJW > 4) SJW = 4; 205 | if(CAN_Bus_Speed>0) { 206 | if(_init(CAN_Bus_Speed, Freq, SJW, false)) { 207 | savedBaud = CAN_Bus_Speed; 208 | savedFreq = Freq; 209 | running = 1; 210 | return CAN_Bus_Speed; 211 | } 212 | } else { 213 | int i=0; 214 | uint8_t interruptFlags = 0; 215 | for(i=5; i<1000; i=i+5) { 216 | if(_init(i, Freq, SJW, true)) { 217 | // check for bus activity 218 | Write(CANINTF,0); 219 | delay(500); // need the bus to be communicating within this time frame 220 | if(Interrupt()) { 221 | // determine which interrupt flags have been set 222 | interruptFlags = Read(CANINTF); 223 | if(!(interruptFlags & MERRF)) { 224 | // to get here we must have received something without errors 225 | Mode(MODE_NORMAL); 226 | savedBaud = i; 227 | savedFreq = Freq; 228 | running = 1; 229 | return i; 230 | } 231 | } 232 | } 233 | } 234 | } 235 | return 0; 236 | } 237 | 238 | bool MCP2515::_init(uint32_t CAN_Bus_Speed, uint8_t Freq, uint8_t SJW, bool autoBaud) { 239 | 240 | SPI.begin(SCK, MISO, MOSI, SS); //Set up Serial Peripheral Interface Port for CAN2 241 | SPI.setClockDivider(SPI_CLOCK_DIV32); 242 | SPI.setDataMode(SPI_MODE0); 243 | SPI.setBitOrder(MSBFIRST); 244 | 245 | if (!initializedResources) initializeResources(); 246 | 247 | // Reset MCP2515 which puts it in configuration mode 248 | Reset(); 249 | //delay(2); 250 | 251 | Write(CANINTE,0); //disable all interrupts during init 252 | Write(TXB0CTRL, 0); //reset transmit control 253 | Write(TXB1CTRL, 0); //reset transmit control 254 | Write(TXB2CTRL, 0); //reset transmit control 255 | 256 | // Calculate bit timing registers 257 | uint8_t BRP; 258 | float TQ; 259 | uint8_t BT; 260 | float tempBT; 261 | float freqMhz = Freq * 1000000.0; 262 | float bestMatchf = 10.0; 263 | int bestMatchIdx = 10; 264 | float savedBT; 265 | 266 | float speed = CAN_Bus_Speed; 267 | if (speed > 5000.0) speed *= 0.001; 268 | 269 | float NBT = 1.0 / (speed * 1000.0); // Nominal Bit Time - How long a single CAN bit should take 270 | 271 | //Now try each divisor to see which can most closely match the target. 272 | for(BRP=0; BRP < 63; BRP++) { 273 | TQ = 2.0 * (float)(BRP + 1) / (float)freqMhz; 274 | tempBT = NBT / TQ; 275 | #ifdef DEBUG_SETUP 276 | SerialUSB.print("BRP: "); 277 | SerialUSB.print(BRP); 278 | SerialUSB.print(" tempBT: "); 279 | SerialUSB.println(tempBT); 280 | #endif 281 | BT = (int)tempBT; 282 | if ( (tempBT - BT) < bestMatchf) 283 | { 284 | if (BT > 7 && BT < 25) 285 | { 286 | bestMatchf = (tempBT - BT); 287 | bestMatchIdx = BRP; 288 | savedBT = BT; 289 | } 290 | } 291 | } 292 | 293 | BT = savedBT; 294 | BRP = bestMatchIdx; 295 | #ifdef DEBUG_SETUP 296 | SerialUSB.print("BRP: "); 297 | SerialUSB.print(BRP); 298 | SerialUSB.print(" BT: "); 299 | SerialUSB.println(BT); 300 | #endif 301 | 302 | byte SPT = (0.7 * BT); // Sample point 303 | byte PRSEG = (SPT - 1) / 2; 304 | byte PHSEG1 = SPT - PRSEG - 1; 305 | byte PHSEG2 = BT - PHSEG1 - PRSEG - 1; 306 | #ifdef DEBUG_SETUP 307 | SerialUSB.print("PROP: "); 308 | SerialUSB.print(PRSEG); 309 | SerialUSB.print(" SEG1: "); 310 | SerialUSB.print(PHSEG1); 311 | SerialUSB.print(" SEG2: "); 312 | SerialUSB.println(PHSEG2); 313 | #endif 314 | // Programming requirements 315 | if(PRSEG + PHSEG1 < PHSEG2) 316 | { 317 | //SerialUSB.println("PRSEG + PHSEG1 less than PHSEG2!"); 318 | return false; 319 | } 320 | if(PHSEG2 <= SJW) 321 | { 322 | //SerialUSB.println("PHSEG2 less than SJW"); 323 | return false; 324 | } 325 | 326 | uint8_t BTLMODE = 1; 327 | uint8_t SAMPLE = 0; 328 | 329 | // Set registers 330 | byte data = (((SJW-1) << 6) | BRP); 331 | Write(CNF1, data); 332 | Write(CNF2, ((BTLMODE << 7) | (SAMPLE << 6) | ((PHSEG1-1) << 3) | (PRSEG-1))); 333 | Write(CNF3, (B10000000 | (PHSEG2-1))); 334 | Write(TXRTSCTRL,0); 335 | 336 | Write(CNF1, data); 337 | delay(1); 338 | 339 | InitFilters(false); 340 | 341 | if(!autoBaud) { 342 | // Return to Normal mode 343 | if(!Mode(MODE_NORMAL)) 344 | { 345 | //SerialUSB.println("Could not enter normal mode"); 346 | return false; 347 | } 348 | } else { 349 | // Set to Listen Only mode 350 | if(!Mode(MODE_LISTEN)) 351 | { 352 | //SerialUSB.println("Could not enter listen only mode"); 353 | return false; 354 | } 355 | } 356 | // Enable all interupts 357 | Write(CANINTE,255); 358 | 359 | // Test that we can read back from the MCP2515 what we wrote to it 360 | byte rtn = Read(CNF1); 361 | if (rtn == data) return true; 362 | else 363 | { 364 | //SerialUSB.println(data, HEX); 365 | //SerialUSB.println(rtn, HEX); 366 | return false; 367 | } 368 | 369 | return false; 370 | } 371 | 372 | uint16_t MCP2515::available() 373 | { 374 | return uxQueueMessagesWaiting(rxQueue); 375 | } 376 | 377 | int MCP2515::_setFilter(uint32_t id, uint32_t mask, bool extended) 378 | { 379 | uint32_t filterVal; 380 | boolean isExtended; 381 | 382 | for (int i = 0; i < 6; i++) 383 | { 384 | if (i < 3) 385 | GetRXFilter(FILTER0 + (i * 4), filterVal, isExtended); 386 | else 387 | GetRXFilter(FILTER3 + ((i-3) * 4), filterVal, isExtended); 388 | 389 | if (filterVal == 0 && isExtended == extended) //empty filter. Let's fill it and leave 390 | { 391 | if (i < 2) 392 | { 393 | GetRXMask(MASK0, filterVal); 394 | if (filterVal == 0) filterVal = mask; 395 | filterVal &= mask; 396 | SetRXMask(MASK0, filterVal); 397 | } 398 | else 399 | { 400 | GetRXMask(MASK1, filterVal); 401 | if (filterVal == 0) filterVal = mask; 402 | filterVal &= mask; 403 | SetRXMask(MASK1, filterVal); 404 | } 405 | if (i < 3) 406 | SetRXFilter(FILTER0 + (i * 4), id, extended); 407 | else 408 | SetRXFilter(FILTER3 + ((i-3) * 4), id, extended); 409 | return i; 410 | } 411 | } 412 | 413 | //if we got here then there were no free filters. Return value of deaaaaath! 414 | return -1; 415 | } 416 | 417 | //we don't exactly have mailboxes, we have filters (6 of them) but it's the same basic idea 418 | int MCP2515::_setFilterSpecific(uint8_t mailbox, uint32_t id, uint32_t mask, bool extended) 419 | { 420 | uint32_t oldMask; 421 | if (mailbox < 2) //MASK0 422 | { 423 | GetRXMask(MASK0, oldMask); 424 | oldMask &= mask; 425 | SetRXMask(MASK0, oldMask); 426 | 427 | } 428 | else //MASK1 429 | { 430 | GetRXMask(MASK1, oldMask); 431 | oldMask &= mask; 432 | SetRXMask(MASK0, oldMask); 433 | 434 | } 435 | if (mailbox < 3) 436 | SetRXFilter(FILTER0 + (mailbox * 4), id, extended); 437 | else 438 | SetRXFilter(FILTER3 + ((mailbox-3) * 4), id, extended); 439 | 440 | return mailbox; 441 | } 442 | 443 | uint32_t MCP2515::init(uint32_t ul_baudrate) 444 | { 445 | return Init(ul_baudrate, savedFreq); 446 | } 447 | 448 | uint32_t MCP2515::beginAutoSpeed() 449 | { 450 | return Init(0, savedFreq); 451 | } 452 | 453 | uint32_t MCP2515::set_baudrate(uint32_t ul_baudrate) 454 | { 455 | return Init(ul_baudrate, savedFreq); 456 | } 457 | 458 | void MCP2515::setListenOnlyMode(bool state) 459 | { 460 | if (state) 461 | Mode(MODE_LISTEN); 462 | else Mode(MODE_NORMAL); 463 | } 464 | 465 | void MCP2515::setBuffer0RolloverBUKT(bool enable) { 466 | const byte oldMode = Read(CANSTAT); 467 | 468 | if (oldMode != MODE_CONFIG) { 469 | if (!Mode(MODE_CONFIG)) { 470 | Serial.println("Unable to change to config mode to set BUKT"); 471 | return; 472 | } 473 | } 474 | 475 | byte oldValue = Read(RXB0CTRL); 476 | 477 | if (enable) { 478 | Write(RXB0CTRL, oldValue | RXB0BUKT); 479 | } else { 480 | Write(RXB0CTRL, oldValue & ~RXB0BUKT); 481 | } 482 | 483 | if (oldMode != MODE_CONFIG) { 484 | Mode(oldMode); 485 | } 486 | } 487 | 488 | void MCP2515::enable() 489 | { 490 | Mode(MODE_NORMAL); 491 | } 492 | 493 | void MCP2515::disable() 494 | { 495 | Mode(MODE_CONFIG); //should knock it off the CAN bus and keep it from doing anything 496 | } 497 | 498 | bool MCP2515::sendFrame(CAN_FRAME& txFrame) 499 | { 500 | EnqueueTX(txFrame); 501 | return true; 502 | } 503 | 504 | bool MCP2515::rx_avail() 505 | { 506 | return available()>0?true:false; 507 | } 508 | 509 | uint32_t MCP2515::get_rx_buff(CAN_FRAME &msg) 510 | { 511 | return GetRXFrame(msg); 512 | } 513 | 514 | void MCP2515::Reset() { 515 | if (!inhibitTransactions) SPI.beginTransaction(mcpSPISettings); 516 | digitalWrite(_CS,LOW); 517 | SPI.transfer(CAN_RESET); 518 | digitalWrite(_CS,HIGH); 519 | if (!inhibitTransactions) SPI.endTransaction(); 520 | } 521 | 522 | uint8_t MCP2515::Read(uint8_t address) { 523 | if (!inhibitTransactions) SPI.beginTransaction(mcpSPISettings); 524 | digitalWrite(_CS,LOW); 525 | SPI.transfer(CAN_READ); 526 | SPI.transfer(address); 527 | uint8_t data = SPI.transfer(0x00); 528 | digitalWrite(_CS,HIGH); 529 | if (!inhibitTransactions) SPI.endTransaction(); 530 | return data; 531 | } 532 | 533 | void MCP2515::Read(uint8_t address, uint8_t data[], uint8_t bytes) { 534 | // allows for sequential reading of registers starting at address - see data sheet 535 | uint8_t i; 536 | if (!inhibitTransactions) SPI.beginTransaction(mcpSPISettings); 537 | digitalWrite(_CS,LOW); 538 | SPI.transfer(CAN_READ); 539 | SPI.transfer(address); 540 | for(i=0;i>3); 567 | message.id = (message.id<<8) | ((byte1<<5) | ((byte2>>5)<<2) | (byte2 & B00000011)); 568 | message.id = (message.id<<8) | byte3; 569 | message.id = (message.id<<8) | byte4; 570 | } else { 571 | message.id = ((byte1>>5)<<8) | ((byte1<<3) | (byte2>>5)); 572 | } 573 | 574 | message.rtr=(byte5 & B01000000); 575 | message.length = (byte5 & B00001111); // Number of data bytes 576 | for(int i=0; iextended) { 630 | byte1 = byte((message->id<<3)>>24); // 8 MSBits of SID 631 | byte2 = byte((message->id<<11)>>24) & B11100000; // 3 LSBits of SID 632 | byte2 = byte2 | byte((message->id<<14)>>30); // 2 MSBits of EID 633 | byte2 = byte2 | B00001000; // EXIDE 634 | byte3 = byte((message->id<<16)>>24); // EID Bits 15-8 635 | byte4 = byte((message->id<<24)>>24); // EID Bits 7-0 636 | } else { 637 | byte1 = byte((message->id<<21)>>24); // 8 MSBits of SID 638 | byte2 = byte((message->id<<29)>>24) & B11100000; // 3 LSBits of SID 639 | byte3 = 0; // TXBnEID8 640 | byte4 = 0; // TXBnEID0 641 | } 642 | byte5 = message->length; 643 | if(message->rtr) { 644 | byte5 = byte5 | B01000000; 645 | } 646 | 647 | if (!inhibitTransactions) SPI.beginTransaction(mcpSPISettings); 648 | digitalWrite(_CS,LOW); 649 | SPI.transfer(CAN_LOAD_BUFFER | buffer); 650 | SPI.transfer(byte1); 651 | SPI.transfer(byte2); 652 | SPI.transfer(byte3); 653 | SPI.transfer(byte4); 654 | SPI.transfer(byte5); 655 | 656 | for(int i=0;ilength;i++) { 657 | SPI.transfer(message->data.byte[i]); 658 | } 659 | digitalWrite(_CS,HIGH); 660 | if (!inhibitTransactions) SPI.endTransaction(); 661 | } 662 | 663 | uint8_t MCP2515::Status() { 664 | if (!inhibitTransactions) SPI.beginTransaction(mcpSPISettings); 665 | digitalWrite(_CS,LOW); 666 | SPI.transfer(CAN_STATUS); 667 | uint8_t data = SPI.transfer(0x00); 668 | digitalWrite(_CS,HIGH); 669 | if (!inhibitTransactions) SPI.endTransaction(); 670 | return data; 671 | /* 672 | bit 7 - CANINTF.TX2IF 673 | bit 6 - TXB2CNTRL.TXREQ 674 | bit 5 - CANINTF.TX1IF 675 | bit 4 - TXB1CNTRL.TXREQ 676 | bit 3 - CANINTF.TX0IF 677 | bit 2 - TXB0CNTRL.TXREQ 678 | bit 1 - CANINTFL.RX1IF 679 | bit 0 - CANINTF.RX0IF 680 | */ 681 | } 682 | 683 | uint8_t MCP2515::RXStatus() { 684 | if (!inhibitTransactions) SPI.beginTransaction(mcpSPISettings); 685 | digitalWrite(_CS,LOW); 686 | SPI.transfer(CAN_RX_STATUS); 687 | uint8_t data = SPI.transfer(0x00); 688 | digitalWrite(_CS,HIGH); 689 | if (!inhibitTransactions) SPI.endTransaction(); 690 | return data; 691 | /* 692 | bit 7 - CANINTF.RX1IF 693 | bit 6 - CANINTF.RX0IF 694 | bit 5 - 695 | bit 4 - RXBnSIDL.EIDE 696 | bit 3 - RXBnDLC.RTR 697 | bit 2 | 1 | 0 | Filter Match 698 | ------|---|---|------------- 699 | 0 | 0 | 0 | RXF0 700 | 0 | 0 | 1 | RXF1 701 | 0 | 1 | 0 | RXF2 702 | 0 | 1 | 1 | RXF3 703 | 1 | 0 | 0 | RXF4 704 | 1 | 0 | 1 | RXF5 705 | 1 | 1 | 0 | RXF0 (rollover to RXB1) 706 | 1 | 1 | 1 | RXF1 (rollover to RXB1) 707 | */ 708 | } 709 | 710 | void MCP2515::BitModify(uint8_t address, uint8_t mask, uint8_t data) { 711 | // see data sheet for explanation 712 | if (!inhibitTransactions) SPI.beginTransaction(mcpSPISettings); 713 | digitalWrite(_CS,LOW); 714 | SPI.transfer(CAN_BIT_MODIFY); 715 | SPI.transfer(address); 716 | SPI.transfer(mask); 717 | SPI.transfer(data); 718 | digitalWrite(_CS,HIGH); 719 | if (!inhibitTransactions) SPI.endTransaction(); 720 | } 721 | 722 | bool MCP2515::Interrupt() { 723 | return (digitalRead(_INT)==LOW); 724 | } 725 | 726 | bool MCP2515::Mode(byte mode) { 727 | /* 728 | mode can be one of the following: 729 | MODE_CONFIG 730 | MODE_LISTEN 731 | MODE_LOOPBACK 732 | MODE_SLEEP 733 | MODE_NORMAL 734 | */ 735 | BitModify(CANCTRL, B11100000, mode); 736 | delay(10); // allow for any transmissions to complete 737 | uint8_t data = Read(CANSTAT); // check mode has been set 738 | return ((data & mode)==mode); 739 | } 740 | 741 | /*Initializes all filters to either accept all frames or accept none 742 | //This doesn't need to be called if you want to accept everything 743 | //because that's the default. So, call this with permissive = false 744 | //to state with an accept nothing state and then add acceptance masks/filters 745 | //thereafter 746 | */ 747 | void MCP2515::InitFilters(bool permissive) { 748 | uint32_t value; 749 | uint32_t value32; 750 | if (permissive) { 751 | value = 0; 752 | value32 = 0; 753 | } 754 | else 755 | { 756 | value = 0x7FF; //all 11 bits set 757 | value32 = 0x1FFFFFFF; //all 29 bits set 758 | } 759 | SetRXMask(MASK0, value32); 760 | SetRXMask(MASK1, value); 761 | SetRXFilter(FILTER0, 0, 1); 762 | SetRXFilter(FILTER1, 0, 1); 763 | SetRXFilter(FILTER2, 0, 0); 764 | SetRXFilter(FILTER3, 0, 0); 765 | SetRXFilter(FILTER4, 0, 0); 766 | SetRXFilter(FILTER5, 0, 0); 767 | } 768 | 769 | /* 770 | mask = either MASK0 or MASK1 771 | MaskValue is either an 11 or 29 bit mask value to set 772 | Note that maskes do not store whether they'd be standard or extended. Filters do that. It's a little confusing 773 | */ 774 | void MCP2515::SetRXMask(uint8_t mask, uint32_t MaskValue) { 775 | uint8_t temp_buff[4]; 776 | uint8_t oldMode; 777 | 778 | oldMode = Read(CANSTAT); 779 | Mode(MODE_CONFIG); //have to be in config mode to change mask 780 | 781 | temp_buff[0] = byte(MaskValue >> 3); 782 | temp_buff[1] = byte((MaskValue & 7) << 5); 783 | temp_buff[1] |= byte(MaskValue >> 27); 784 | temp_buff[2] = byte(MaskValue >> 19); 785 | temp_buff[3] = byte(MaskValue >> 11); 786 | 787 | Write(mask, temp_buff, 4); //send the four byte mask out to the proper address 788 | 789 | Mode(oldMode); 790 | } 791 | 792 | /* 793 | filter = FILTER0, FILTER1, FILTER2, FILTER3, FILTER4, FILTER5 (pick one) 794 | FilterValue = 11 or 29 bit filter to use 795 | ext is true if this filter should apply to extended frames or false if it should apply to standard frames. 796 | Do note that, while this function looks a lot like the mask setting function is is NOT identical 797 | It might be able to be though... The setting of EXIDE would probably just be ignored by the mask 798 | */ 799 | void MCP2515::SetRXFilter(uint8_t filter, uint32_t FilterValue, bool ext) { 800 | uint8_t temp_buff[4]; 801 | uint8_t oldMode; 802 | 803 | oldMode = Read(CANSTAT); 804 | 805 | Mode(MODE_CONFIG); //have to be in config mode to change mask 806 | temp_buff[0] = byte(FilterValue >> 3); 807 | temp_buff[1] = byte((FilterValue & 7) << 5); 808 | temp_buff[2] = 0; 809 | temp_buff[3] = 0; 810 | 811 | if (ext) { //fill out all 29 bits 812 | temp_buff[1] |= 1 << 3; //set EXIDE 813 | temp_buff[1] |= byte(FilterValue >> 27); 814 | temp_buff[2] = byte(FilterValue >> 19); 815 | temp_buff[3] = byte(FilterValue >> 11); 816 | } 817 | 818 | Write(filter, temp_buff, 4); //send the four byte mask out to the proper address 819 | 820 | Mode(oldMode); 821 | } 822 | 823 | void MCP2515::GetRXFilter(uint8_t filter, uint32_t &filterVal, boolean &isExtended) 824 | { 825 | uint8_t temp_buff[4]; 826 | uint8_t oldMode; 827 | 828 | oldMode = Read(CANSTAT); 829 | 830 | Mode(MODE_CONFIG); //have to be in config mode to change mask 831 | 832 | Read(filter, temp_buff, 4); 833 | 834 | //these 11 bits are used either way and stored the same way either way 835 | filterVal = temp_buff[0] << 3; 836 | filterVal |= temp_buff[1] >> 5; 837 | isExtended = false; 838 | 839 | if (temp_buff[1] & B00001000) //extended / 29 bit filter - get the remaining 18 bits we need 840 | { 841 | isExtended = true; 842 | filterVal |= (temp_buff[1] & 3) << 27; 843 | filterVal |= temp_buff[2] << 19; 844 | filterVal |= temp_buff[3] << 11; 845 | } 846 | 847 | Mode(oldMode); 848 | } 849 | 850 | void MCP2515::GetRXMask(uint8_t mask, uint32_t &filterVal) 851 | { 852 | uint8_t temp_buff[4]; 853 | uint8_t oldMode; 854 | 855 | oldMode = Read(CANSTAT); 856 | 857 | Mode(MODE_CONFIG); //have to be in config mode to change mask 858 | 859 | Read(mask, temp_buff, 4); 860 | 861 | filterVal = temp_buff[0] << 3; 862 | filterVal |= temp_buff[1] >> 5; 863 | filterVal |= (temp_buff[1] & 3) << 27; 864 | filterVal |= temp_buff[2] << 19; 865 | filterVal |= temp_buff[3] << 11; 866 | 867 | Mode(oldMode); 868 | } 869 | 870 | 871 | //Places the given frame into the receive queue 872 | void MCP2515::EnqueueRX(CAN_FRAME& newFrame) { 873 | xQueueSendFromISR(rxQueue, &newFrame, NULL); 874 | } 875 | 876 | //Places the given frame into the transmit queue 877 | //Well, maybe. If there is currently an open hardware buffer 878 | //it will place it into hardware immediately instead of using 879 | //the software queue 880 | void MCP2515::EnqueueTX(CAN_FRAME& newFrame) { 881 | BaseType_t xHigherPriorityTaskWoken = pdFALSE; 882 | xQueueSend(txQueue, &newFrame, 0); 883 | xHigherPriorityTaskWoken = xTaskNotifyGive(intDelegateTask); //send notice to the handler task that it can do the SPI transaction now 884 | //if (xHigherPriorityTaskWoken == pdTRUE) 885 | } 886 | 887 | bool MCP2515::GetRXFrame(CAN_FRAME &frame) { 888 | if (xQueueReceive(rxQueue, &frame, 0) == pdTRUE) return true; 889 | return false; 890 | } 891 | 892 | void MCP2515::intHandler(void) { 893 | CAN_FRAME message; 894 | uint32_t ctrlVal; 895 | inhibitTransactions = true; 896 | SPI.beginTransaction(mcpSPISettings); 897 | // determine which interrupt flags have been set 898 | uint8_t interruptFlags = Read(CANINTF); 899 | uint8_t status = Status(); 900 | //Serial.println(status); 901 | /* 902 | bit 7 - CANINTF.TX2IF 128 903 | bit 6 - TXB2CNTRL.TXREQ 64 904 | bit 5 - CANINTF.TX1IF 32 905 | bit 4 - TXB1CNTRL.TXREQ 16 906 | bit 3 - CANINTF.TX0IF 8 907 | bit 2 - TXB0CNTRL.TXREQ 4 908 | bit 1 - CANINTF.RX1IF 2 909 | bit 0 - CANINTF.RX0IF 1 910 | */ 911 | 912 | if((status & 1)) //RX buff 0 full 913 | { 914 | // read from RX buffer 0 915 | message = ReadBuffer(RXB0); 916 | ctrlVal = Read(RXB0CTRL); 917 | handleFrameDispatch(&message, ctrlVal & 1); 918 | } 919 | if((status & 2)) //RX buff 1 full 920 | { 921 | // read from RX buffer 1 922 | message = ReadBuffer(RXB1); 923 | ctrlVal = Read(RXB1CTRL); 924 | handleFrameDispatch(&message, ctrlVal & 7); 925 | } 926 | if((status & 4) == 0) //TX buffer 0 is not pending a transaction 927 | { 928 | if (uxQueueMessagesWaitingFromISR(txQueue)) { 929 | xQueueReceiveFromISR(txQueue, &message, 0); 930 | LoadBuffer(TXB0, &message); 931 | SendBuffer(TXB0); 932 | } 933 | } 934 | if((status & 16) == 0) //TX buffer 1 not pending any message to send 935 | { 936 | if (uxQueueMessagesWaitingFromISR(txQueue)) { 937 | xQueueReceiveFromISR(txQueue, &message, 0); 938 | LoadBuffer(TXB1, &message); 939 | SendBuffer(TXB1); 940 | } 941 | } 942 | if((status & 64) == 0) //TX buffer 2 not pending any message to send 943 | { 944 | if (uxQueueMessagesWaitingFromISR(txQueue)) { 945 | xQueueReceiveFromISR(txQueue, &message, 0); 946 | LoadBuffer(TXB2, &message); 947 | SendBuffer(TXB2); 948 | } 949 | } 950 | if(interruptFlags & ERRIF) { 951 | //Serial.println("E"); 952 | } 953 | if(interruptFlags & MERRF) { 954 | //Serial.println("M"); 955 | } 956 | 957 | Write(CANINTF, 0); //Now, acknowledge the interrupts by clearing the intf bits 958 | Write(EFLG, 0); //clear RX overflow flags 959 | 960 | inhibitTransactions = false; 961 | SPI.endTransaction(); 962 | } 963 | 964 | void MCP2515::handleFrameDispatch(CAN_FRAME *frame, int filterHit) 965 | { 966 | CANListener *thisListener; 967 | 968 | //First, try to send a callback. If no callback registered then buffer the frame. 969 | if (cbCANFrame[filterHit]) 970 | { 971 | frame->fid = filterHit; 972 | xQueueSendFromISR(callbackQueueM15, frame, 0); 973 | return; 974 | } 975 | else if (cbGeneral) 976 | { 977 | frame->fid = 0xFF; 978 | xQueueSendFromISR(callbackQueueM15, frame, 0); 979 | return; 980 | } 981 | else 982 | { 983 | for (int listenerPos = 0; listenerPos < SIZE_LISTENERS; listenerPos++) 984 | { 985 | thisListener = listener[listenerPos]; 986 | if (thisListener != NULL) 987 | { 988 | if (thisListener->isCallbackActive(filterHit)) 989 | { 990 | frame->fid = 0x80000000ul + (listenerPos << 24ul) + filterHit; 991 | xQueueSendFromISR(callbackQueueM15, frame, 0); 992 | return; 993 | } 994 | else if (thisListener->isCallbackActive(numFilters)) //global catch-all 995 | { 996 | frame->fid = 0x80000000ul + (listenerPos << 24ul) + 0xFF; 997 | xQueueSendFromISR(callbackQueueM15, frame, 0); 998 | return; 999 | } 1000 | } 1001 | } 1002 | } 1003 | //if none of the callback types caught this frame then queue it in the buffer 1004 | xQueueSendFromISR(rxQueue, frame, NULL); 1005 | } 1006 | -------------------------------------------------------------------------------- /src/mcp2515.h: -------------------------------------------------------------------------------- 1 | /* 2 | MCP2515.h - Library for Microchip MCP2515 CAN Controller 3 | 4 | Author: David Harding 5 | Maintainer: RechargeCar Inc (http://rechargecar.com) 6 | Further Modification: Collin Kidder 7 | 8 | Created: 11/08/2010 9 | 10 | For further information see: 11 | 12 | http://ww1.microchip.com/downloads/en/DeviceDoc/21801e.pdf 13 | http://en.wikipedia.org/wiki/CAN_bus 14 | 15 | 16 | This library is free software; you can redistribute it and/or 17 | modify it under the terms of the GNU Lesser General Public 18 | License as published by the Free Software Foundation; either 19 | version 2.1 of the License, or (at your option) any later version. 20 | 21 | This library is distributed in the hope that it will be useful, 22 | but WITHOUT ANY WARRANTY; without even the implied warranty of 23 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 24 | Lesser General Public License for more details. 25 | 26 | You should have received a copy of the GNU Lesser General Public 27 | License along with this library; if not, write to the Free Software 28 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 29 | */ 30 | 31 | #ifndef MCP2515_h 32 | #define MCP2515_h 33 | 34 | #include "Arduino.h" 35 | #include "mcp2515_defs.h" 36 | #include 37 | 38 | //#define DEBUG_SETUP 39 | 40 | #define MCP_RX_BUFFER_SIZE 32 41 | #define MCP_TX_BUFFER_SIZE 16 42 | 43 | class MCP2515 : public CAN_COMMON 44 | { 45 | public: 46 | // Constructor defining which pins to use for CS and INT 47 | MCP2515(uint8_t CS_Pin, uint8_t INT_Pin); 48 | 49 | // Overloaded initialization function 50 | int Init(uint32_t baud, uint8_t freq); 51 | int Init(uint32_t baud, uint8_t freq, uint8_t sjw); 52 | 53 | //block of functions which must be overriden from CAN_COMMON to implement functionality for this hardware 54 | int _setFilterSpecific(uint8_t mailbox, uint32_t id, uint32_t mask, bool extended); 55 | int _setFilter(uint32_t id, uint32_t mask, bool extended); 56 | uint32_t init(uint32_t ul_baudrate); 57 | uint32_t beginAutoSpeed(); 58 | uint32_t set_baudrate(uint32_t ul_baudrate); 59 | void setListenOnlyMode(bool state); 60 | void enable(); 61 | void disable(); 62 | bool sendFrame(CAN_FRAME& txFrame); 63 | bool rx_avail(); 64 | uint16_t available(); //like rx_avail but returns the number of waiting frames 65 | uint32_t get_rx_buff(CAN_FRAME &msg); 66 | 67 | // Basic MCP2515 SPI Command Set 68 | void Reset(); 69 | byte Read(uint8_t address); 70 | void Read(uint8_t address, uint8_t data[], uint8_t bytes); 71 | CAN_FRAME ReadBuffer(uint8_t buffer); 72 | void Write(uint8_t address, uint8_t data); 73 | void Write(uint8_t address, uint8_t data[], uint8_t bytes); 74 | void LoadBuffer(uint8_t buffer, CAN_FRAME *message); 75 | void SendBuffer(uint8_t buffers); 76 | uint8_t Status(); 77 | uint8_t RXStatus(); 78 | void BitModify(uint8_t address, uint8_t mask, uint8_t data); 79 | 80 | // Extra functions 81 | bool Interrupt(); // Expose state of INT pin 82 | bool Mode(uint8_t mode); // Returns TRUE if mode change successful 83 | void setINTPin(uint8_t pin); 84 | void setCSPin(uint8_t pin); 85 | void EnqueueRX(CAN_FRAME& newFrame); 86 | void EnqueueTX(CAN_FRAME& newFrame); 87 | bool GetRXFrame(CAN_FRAME &frame); 88 | void SetRXFilter(uint8_t filter, uint32_t FilterValue, bool ext); 89 | void SetRXMask(uint8_t mask, uint32_t MaskValue); 90 | void GetRXFilter(uint8_t filter, uint32_t &filterVal, boolean &isExtended); 91 | void GetRXMask(uint8_t mask, uint32_t &filterVal); 92 | void sendCallback(CAN_FRAME *frame); 93 | void setBuffer0RolloverBUKT(bool enable); 94 | 95 | void InitFilters(bool permissive); 96 | void intHandler(); 97 | private: 98 | bool _init(uint32_t baud, uint8_t freq, uint8_t sjw, bool autoBaud); 99 | void handleFrameDispatch(CAN_FRAME *frame, int filterHit); 100 | void initializeResources(); 101 | // Pin variables 102 | uint8_t _CS; 103 | uint8_t _INT; 104 | QueueHandle_t rxQueue; 105 | QueueHandle_t txQueue; 106 | volatile uint16_t savedBaud; 107 | volatile uint8_t savedFreq; 108 | volatile uint8_t running; //1 if out of init code, 0 if still trying to initialize (auto baud detecting) 109 | volatile bool inhibitTransactions; 110 | bool initializedResources; 111 | // Definitions for software buffers 112 | }; 113 | 114 | #endif 115 | -------------------------------------------------------------------------------- /src/mcp2515_defs.h: -------------------------------------------------------------------------------- 1 | /* 2 | MCP2515_defs.h - Library for Microchip MCP2515 CAN Controller 3 | 4 | Author: David Harding 5 | Maintainer: RechargeCar Inc (http://rechargecar.com) 6 | Further Modification: Collin Kidder 7 | 8 | Created: 11/08/2010 9 | 10 | For further information see: 11 | 12 | http://ww1.microchip.com/downloads/en/DeviceDoc/21801e.pdf 13 | http://en.wikipedia.org/wiki/CAN_bus 14 | 15 | 16 | This library is free software; you can redistribute it and/or 17 | modify it under the terms of the GNU Lesser General Public 18 | License as published by the Free Software Foundation; either 19 | version 2.1 of the License, or (at your option) any later version. 20 | 21 | This library is distributed in the hope that it will be useful, 22 | but WITHOUT ANY WARRANTY; without even the implied warranty of 23 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 24 | Lesser General Public License for more details. 25 | 26 | You should have received a copy of the GNU Lesser General Public 27 | License along with this library; if not, write to the Free Software 28 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 29 | */ 30 | 31 | #ifndef MCP2515_defs_h 32 | #define MCP2515_defs_h 33 | 34 | #include "Arduino.h" 35 | 36 | // MCP2515 SPI Commands 37 | #define CAN_RESET 0xC0 38 | #define CAN_READ 0x03 39 | #define CAN_WRITE 0x02 40 | #define CAN_RTS 0x80 41 | #define CAN_STATUS 0xA0 42 | #define CAN_BIT_MODIFY 0x05 43 | #define CAN_RX_STATUS 0xB0 44 | #define CAN_READ_BUFFER 0x90 45 | #define CAN_LOAD_BUFFER 0X40 46 | 47 | // Register Bit Masks 48 | // CANSTAT 49 | #define MODE_CONFIG 0x80 50 | #define MODE_LISTEN 0x60 51 | #define MODE_LOOPBACK 0x40 52 | #define MODE_SLEEP 0x20 53 | #define MODE_NORMAL 0x00 54 | // CANINTF 55 | #define RX0IF 0x01 56 | #define RX1IF 0x02 57 | #define TX0IF 0x04 58 | #define TX1IF 0x08 59 | #define TX2IF 0x10 60 | #define ERRIF 0x20 61 | #define WAKIF 0x40 62 | #define MERRF 0x80 63 | 64 | // Configuration Registers 65 | #define CANSTAT 0x0E 66 | #define CANCTRL 0x0F 67 | #define BFPCTRL 0x0C 68 | #define TEC 0x1C 69 | #define REC 0x1D 70 | #define CNF3 0x28 71 | #define CNF2 0x29 72 | #define CNF1 0x2A 73 | #define CANINTE 0x2B 74 | #define CANINTF 0x2C 75 | #define EFLG 0x2D 76 | #define TXRTSCTRL 0x0D 77 | 78 | // TX Buffer 0 79 | #define TXB0CTRL 0x30 80 | #define TXB0SIDH 0x31 81 | #define TXB0SIDL 0x32 82 | #define TXB0EID8 0x33 83 | #define TXB0EID0 0x34 84 | #define TXB0DLC 0x35 85 | #define TXB0D0 0x36 86 | #define TXB0D1 0x37 87 | #define TXB0D2 0x38 88 | #define TXB0D3 0x39 89 | #define TXB0D4 0x3A 90 | #define TXB0D5 0x3B 91 | #define TXB0D6 0x3C 92 | #define TXB0D7 0x3D 93 | 94 | // TX Buffer 1 95 | #define TXB1CTRL 0x40 96 | #define TXB1SIDH 0x41 97 | #define TXB1SIDL 0x42 98 | #define TXB1EID8 0x43 99 | #define TXB1EID0 0x44 100 | #define TXB1DLC 0x45 101 | #define TXB1D0 0x46 102 | #define TXB1D1 0x47 103 | #define TXB1D2 0x48 104 | #define TXB1D3 0x49 105 | #define TXB1D4 0x4A 106 | #define TXB1D5 0x4B 107 | #define TXB1D6 0x4C 108 | #define TXB1D7 0x4D 109 | 110 | // TX Buffer 2 111 | #define TXB2CTRL 0x50 112 | #define TXB2SIDH 0x51 113 | #define TXB2SIDL 0x52 114 | #define TXB2EID8 0x53 115 | #define TXB2EID0 0x54 116 | #define TXB2DLC 0x55 117 | #define TXB2D0 0x56 118 | #define TXB2D1 0x57 119 | #define TXB2D2 0x58 120 | #define TXB2D3 0x59 121 | #define TXB2D4 0x5A 122 | #define TXB2D5 0x5B 123 | #define TXB2D6 0x5C 124 | #define TXB2D7 0x5D 125 | 126 | // RX Buffer 0 127 | #define RXB0CTRL 0x60 128 | #define RXB0SIDH 0x61 129 | #define RXB0SIDL 0x62 130 | #define RXB0EID8 0x63 131 | #define RXB0EID0 0x64 132 | #define RXB0DLC 0x65 133 | #define RXB0BUKT 0x04 134 | #define RXB0D0 0x66 135 | #define RXB0D1 0x67 136 | #define RXB0D2 0x68 137 | #define RXB0D3 0x69 138 | #define RXB0D4 0x6A 139 | #define RXB0D5 0x6B 140 | #define RXB0D6 0x6C 141 | #define RXB0D7 0x6D 142 | 143 | // RX Buffer 1 144 | #define RXB1CTRL 0x70 145 | #define RXB1SIDH 0x71 146 | #define RXB1SIDL 0x72 147 | #define RXB1EID8 0x73 148 | #define RXB1EID0 0x74 149 | #define RXB1DLC 0x75 150 | #define RXB1D0 0x76 151 | #define RXB1D1 0x77 152 | #define RXB1D2 0x78 153 | #define RXB1D3 0x79 154 | #define RXB1D4 0x7A 155 | #define RXB1D5 0x7B 156 | #define RXB1D6 0x7C 157 | #define RXB1D7 0x7D 158 | 159 | //RX Filter 0 160 | #define FILTER0 0x00 161 | #define RXF0SIDH 0x00 162 | #define RXF0SIDL 0x01 163 | #define RXF0EID8 0x02 164 | #define RXF0EID0 0x03 165 | 166 | //RX Filter 1 167 | #define FILTER1 0x04 168 | #define RXF1SIDH 0x04 169 | #define RXF1SIDL 0x05 170 | #define RXF1EID8 0x06 171 | #define RXF1EID0 0x07 172 | 173 | //RX Filter 2 174 | #define FILTER2 0x08 175 | #define RXF2SIDH 0x08 176 | #define RXF2SIDL 0x09 177 | #define RXF2EID8 0x0A 178 | #define RXF2EID0 0x0B 179 | 180 | //RX Filter 3 181 | #define FILTER3 0x10 182 | #define RXF3SIDH 0x10 183 | #define RXF3SIDL 0x11 184 | #define RXF3EID8 0x12 185 | #define RXF3EID0 0x13 186 | 187 | //RX Filter 4 188 | #define FILTER4 0x14 189 | #define RXF4SIDH 0x14 190 | #define RXF4SIDL 0x15 191 | #define RXF4EID8 0x16 192 | #define RXF4EID0 0x17 193 | 194 | //RX Filter 5 195 | #define FILTER5 0x18 196 | #define RXF5SIDH 0x18 197 | #define RXF5SIDL 0x19 198 | #define RXF5EID8 0x1A 199 | #define RXF5EID0 0x1B 200 | 201 | //RX Mask 0 202 | #define MASK0 0x20 203 | #define RXM0SIDH 0x20 204 | #define RXM0SIDL 0x21 205 | #define RXM0EID8 0x22 206 | #define RXM0EID0 0x23 207 | 208 | //RX Mask 1 209 | #define MASK1 0x24 210 | #define RXM1SIDH 0x24 211 | #define RXM1SIDL 0x25 212 | #define RXM1EID8 0x26 213 | #define RXM1EID0 0x27 214 | 215 | // Buffer Bit Masks 216 | #define RXB0 0x00 217 | #define RXB1 0x02 218 | #define TXB0 0x01 219 | #define TXB1 0x02 220 | #define TXB2 0x04 221 | #define TXB_ALL TXB0 | TXB1 | TXB2 222 | 223 | #endif 224 | -------------------------------------------------------------------------------- /src/mcp2517fd.cpp: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include 3 | #include "mcp2517fd.h" 4 | #include "mcp2517fd_defines.h" 5 | #include "mcp2517fd_regs.h" 6 | 7 | /* 8 | No amount of reception by itself seems to cause any mess ups in this library. 9 | But, transmitting rapidly causes it to break badly. At that point even in debugging mode 10 | there is no output of any kind. No debugging characters at all come through. It just dies. 11 | It seems savvycan says that the connection itself dies but the serial console is still going 12 | and the ESP32 itself and ESP32RET do not seem locked up. 13 | */ 14 | 15 | 16 | //20Mhz is the fastest we can go 17 | #define FD_SPI_SPEED 10000000 18 | 19 | SPISettings fdSPISettings(FD_SPI_SPEED, MSBFIRST, SPI_MODE0); 20 | 21 | //Modified to loop, waiting for 1ms then pretending an interrupt came in 22 | //basically switches to a polled system where we do not pay attention to actual interrupts 23 | void task_MCPIntFD( void *pvParameters ) 24 | { 25 | const TickType_t iDelay = portTICK_PERIOD_MS; 26 | MCP2517FD* mcpCan = (MCP2517FD*)pvParameters; 27 | while (1) 28 | { 29 | vTaskDelay(iDelay); 30 | mcpCan->intHandler(); //not truly an interrupt handler anymore 31 | } 32 | } 33 | 34 | //checks ever 2 seconds to see if a CAN error happened. Will reset the CAN hardware if so. 35 | void task_ResetWatcher(void *pvParameters) 36 | { 37 | MCP2517FD* mcpCan = (MCP2517FD*)pvParameters; 38 | const TickType_t xDelay = 2000 / portTICK_PERIOD_MS; 39 | uint32_t ctrlVal; 40 | 41 | for(;;) 42 | { 43 | vTaskDelay( xDelay ); 44 | 45 | //read CiCon then see if operation mode is b111/7 which is restricted mode. If so we reset 46 | ctrlVal = mcpCan->Read(ADDR_CiCON); 47 | ctrlVal = (ctrlVal >> 21) & 7; 48 | if (ctrlVal == 7) 49 | { 50 | mcpCan->needMCPReset = true; 51 | if (mcpCan->debuggingMode) Serial.println("!!!RESTRICTED MODE!!!"); 52 | } 53 | 54 | if (mcpCan->needMCPReset) 55 | { 56 | mcpCan->needMCPReset = false; 57 | if (mcpCan->debuggingMode) Serial.println("Reset 2517FD hardware"); 58 | mcpCan->resetHardware(); 59 | } 60 | if (mcpCan->needTXFIFOReset) 61 | { 62 | mcpCan->needTXFIFOReset = false; 63 | if (mcpCan->debuggingMode) Serial.println("Reset TX FIFO"); 64 | mcpCan->txQueueSetup(); 65 | } 66 | } 67 | } 68 | 69 | //Reset the hardware to its last configured state 70 | //Also prints out the diag registers if you have debugging text enabled. 71 | //Saves then restores all filtering as well. 72 | void MCP2517FD::resetHardware() 73 | { 74 | uint32_t filters[32]; 75 | uint32_t masks[32]; 76 | uint32_t ctrl[8]; //each ctrl reg has four filters so only 8 regs not 32 77 | int idx; 78 | if (debuggingMode) 79 | { 80 | printf("Diag0: %x\n Diag1: %x ErrFlgs: %x", getCIBDIAG0(), cachedDiag1, getErrorFlags()); 81 | } 82 | cachedDiag1 = 0; 83 | 84 | for (idx = 0; idx < 32; idx++) 85 | { 86 | filters[idx] = Read(ADDR_CiFLTOBJ + (CiFILTER_OFFSET * idx)); 87 | masks[idx] = Read(ADDR_CiMASK + (CiFILTER_OFFSET * idx)); 88 | } 89 | for (idx = 0; idx < 8; idx++) ctrl[idx] = Read(ADDR_CiFLTCON + 4 * idx); 90 | 91 | if (inFDMode) initFD(savedNominalBaud, savedDataBaud); 92 | else init(savedNominalBaud); 93 | 94 | for (idx = 0; idx < 32; idx++) 95 | { 96 | Write(ADDR_CiFLTOBJ + (CiFILTER_OFFSET * idx), filters[idx]); 97 | Write(ADDR_CiMASK + (CiFILTER_OFFSET * idx), masks[idx]); 98 | } 99 | for (idx = 0; idx < 8; idx++) Write(ADDR_CiFLTCON + 4 * idx, ctrl[idx]); 100 | } 101 | 102 | /* 103 | Issue callbacks to registered functions and objects 104 | Used to keep this kind of thing out of the interrupt handler 105 | The callback type and mailbox are passed in the fid member of the 106 | CAN_FRAME struct. It isn't really used by anything. 107 | Layout of the storage: 108 | bit 31 - If set indicates an object callback 109 | bits 24-30 - Idx into listener table 110 | bits 0-7 - Mailbox number that triggered callback 111 | */ 112 | void task_MCPCAN( void *pvParameters ) 113 | { 114 | MCP2517FD* mcpCan = (MCP2517FD*)pvParameters; 115 | CAN_FRAME_FD rxFrameFD; 116 | CAN_FRAME rxFrame; 117 | 118 | while (1) 119 | { 120 | //receive next CAN frame from queue and fire off the callback 121 | if (mcpCan->callbackQueueMCP) 122 | { 123 | if (!mcpCan->inFDMode) 124 | { 125 | if(xQueueReceive(mcpCan->callbackQueueMCP, &rxFrame, portMAX_DELAY)==pdTRUE) 126 | { 127 | mcpCan->canToFD(rxFrame, rxFrameFD); 128 | mcpCan->sendCallback(&rxFrameFD); 129 | } 130 | } 131 | else 132 | { 133 | if(xQueueReceive(mcpCan->callbackQueueMCP, &rxFrameFD, portMAX_DELAY)==pdTRUE) 134 | { 135 | mcpCan->sendCallback(&rxFrameFD); 136 | } 137 | } 138 | } 139 | } 140 | } 141 | 142 | void MCP2517FD::sendCallback(CAN_FRAME_FD *frame) 143 | { 144 | //frame buffer 145 | CANListener *thisListener; 146 | int mb; 147 | int idx; 148 | CAN_FRAME stdFrame; 149 | bool isFD = false; 150 | 151 | if (!fdToCan(*frame, stdFrame)) isFD = true; 152 | 153 | mb = (frame->fid & 0xFF); 154 | if (mb == 0xFF) mb = -1; 155 | 156 | if (frame->fid & 0x80000000ul) //object callback 157 | { 158 | idx = (frame->fid >> 24) & 0x7F; 159 | thisListener = listener[idx]; 160 | if (isFD) thisListener->gotFrameFD(frame, mb); 161 | else thisListener->gotFrame(&stdFrame, mb); 162 | } 163 | else //C function callback 164 | { 165 | if (isFD) 166 | { 167 | if (mb > -1) (*cbCANFrameFD[mb])(frame); 168 | else (*cbGeneralFD)(frame); 169 | } 170 | else 171 | { 172 | if (mb > -1) (*cbCANFrame[mb])(&stdFrame); 173 | else (*cbGeneral)(&stdFrame); 174 | } 175 | } 176 | } 177 | 178 | MCP2517FD::MCP2517FD(uint8_t CS_Pin, uint8_t INT_Pin) : CAN_COMMON(32) { 179 | pinMode(CS_Pin, OUTPUT); 180 | digitalWrite(CS_Pin,HIGH); 181 | pinMode(INT_Pin,INPUT); 182 | //digitalWrite(INT_Pin,HIGH); 183 | 184 | //attachInterrupt(INT_Pin, MCPFD_INTHandler, FALLING); 185 | 186 | _CS = CS_Pin; 187 | _INT = INT_Pin; 188 | 189 | savedNominalBaud = 0; 190 | savedDataBaud = 0; 191 | savedFreq = 0; 192 | running = 0; 193 | inFDMode = false; 194 | fdSupported = true; 195 | txBufferSize = FD_TX_BUFFER_SIZE; 196 | rxBufferSize = FD_RX_BUFFER_SIZE; 197 | initializedResources = false; 198 | } 199 | 200 | void MCP2517FD::setRXBufferSize(int newSize) 201 | { 202 | rxBufferSize = newSize; 203 | } 204 | 205 | void MCP2517FD::setTXBufferSize(int newSize) 206 | { 207 | txBufferSize = newSize; 208 | } 209 | 210 | 211 | void MCP2517FD::initializeResources() 212 | { 213 | if (initializedResources) return; 214 | 215 | if (debuggingMode) Serial.println("initializeResources()"); 216 | 217 | //queues allocate the requested space plus 96 bytes overhead 218 | if (inFDMode) 219 | { 220 | rxQueue = xQueueCreate(rxBufferSize, sizeof(CAN_FRAME_FD)); 221 | txQueue = xQueueCreate(txBufferSize, sizeof(CAN_FRAME_FD)); 222 | //as in the ESP32-Builtin CAN we create a queue and task to do callbacks outside the interrupt handler 223 | callbackQueueMCP = xQueueCreate(16, sizeof(CAN_FRAME_FD)); 224 | } 225 | else 226 | { 227 | rxQueue = xQueueCreate(rxBufferSize, sizeof(CAN_FRAME)); 228 | txQueue = xQueueCreate(txBufferSize, sizeof(CAN_FRAME)); 229 | //as in the ESP32-Builtin CAN we create a queue and task to do callbacks outside the interrupt handler 230 | callbackQueueMCP = xQueueCreate(16, sizeof(CAN_FRAME)); 231 | } 232 | 233 | //func desc stack, params, priority, handle to task, which core to pin to 234 | //Tasks take up the stack you allocate here in bytes plus 388 bytes overhead 235 | xTaskCreatePinnedToCore(&task_MCPCAN, "CAN_FD_CALLBACK", 6144, this, 8, &taskHandleMCPCAN, 0); 236 | xTaskCreatePinnedToCore(&task_MCPIntFD, "CAN_FD_INT", 4096, this, 19, &intTaskFD, 0); 237 | xTaskCreatePinnedToCore(&task_ResetWatcher, "CAN_RSTWATCH", 4096, this, 7, &taskHandleReset, 0); 238 | 239 | initializedResources = true; 240 | } 241 | 242 | void MCP2517FD::setINTPin(uint8_t pin) 243 | { 244 | //detachInterrupt(_INT); 245 | _INT = pin; 246 | //pinMode(_INT,INPUT); 247 | //digitalWrite(_INT,HIGH); 248 | //attachInterrupt(_INT, MCPFD_INTHandler, FALLING); 249 | } 250 | 251 | void MCP2517FD::setCSPin(uint8_t pin) 252 | { 253 | _CS = pin; 254 | pinMode(_CS, OUTPUT); 255 | digitalWrite(_CS,HIGH); 256 | } 257 | 258 | //Some sort of horrific closed head injury caused the designers of the MCP2517FD to store 259 | //29 bit extended frames with the 18 extended bits first then the 11 standard bits added higher up 260 | //instead of just plain making the 29 bit ID or mask contiguous in the 32 bit register. So, two routines 261 | //here to massage the data bits around to match 262 | 263 | //Take a 29 bit ID or MASK and turn it into a packed format suitable for the hardware register 264 | uint32_t MCP2517FD::packExtValue(uint32_t input) 265 | { 266 | return ( ((input >> 18) & 0x7FF) + ((input & 0x3FFFF) << 11) ); 267 | } 268 | 269 | //Reverse that process for when we receive IDs in the stupid format and want the real ID 270 | uint32_t MCP2517FD::unpackExtValue(uint32_t input) 271 | { 272 | return ( ((input >> 11) & 0x3FFFF) + ((input & 0x7FF) << 18)); 273 | } 274 | 275 | void MCP2517FD::initSPI() 276 | { 277 | // Set up SPI Communication 278 | // dataMode can be SPI_MODE0 or SPI_MODE3 only for MCP2517FD 279 | SPI.begin(SCK, MISO, MOSI, SS); 280 | SPI.setClockDivider(spiFrequencyToClockDiv(FD_SPI_SPEED)); 281 | SPI.setDataMode(SPI_MODE0); 282 | SPI.setBitOrder(MSBFIRST); 283 | SPI.setHwCs(false); //allow manual control of CS line 284 | if (debuggingMode) Serial.println("MCP2517FD SPI Inited"); 285 | } 286 | 287 | /* 288 | Initialize MCP2517FD 289 | 290 | int CAN_Bus_Speed = transfer speed in kbps (or raw CAN speed in bits per second) 291 | int Freq = MCP2517FD oscillator frequency in MHz 292 | int SJW = Synchronization Jump Width Length bits - 1 to 4 (see data sheet) 293 | 294 | returns baud rate set 295 | 296 | Sending a bus speed of 0 kbps initiates AutoBaud and returns zero if no 297 | baud rate could be determined. There must be two other active nodes on the bus! 298 | */ 299 | int MCP2517FD::Init(uint32_t CAN_Bus_Speed, uint8_t Freq) { 300 | return Init(CAN_Bus_Speed, Freq, 3); 301 | } 302 | 303 | int MCP2517FD::Init(uint32_t CAN_Bus_Speed, uint8_t Freq, uint8_t SJW) { 304 | if(SJW < 1) SJW = 1; 305 | if(SJW > 4) SJW = 4; 306 | REG_CiINT interruptFlags; 307 | 308 | initSPI(); 309 | 310 | int i = 0; 311 | if(CAN_Bus_Speed > 0) 312 | { 313 | if(_init(CAN_Bus_Speed, Freq, SJW, false)) 314 | { 315 | savedNominalBaud = CAN_Bus_Speed; 316 | savedFreq = Freq; 317 | running = 1; 318 | errorFlags = 0; 319 | rxFault = false; 320 | txFault = false; 321 | faulted = false; 322 | return CAN_Bus_Speed; 323 | } 324 | } 325 | else 326 | { 327 | for(i = 20; i < 1000; i = i + 5) 328 | { 329 | if(_init(i, Freq, 1, true)) 330 | { 331 | // check for bus activity 332 | Write16(ADDR_CiINT,0); //write to INT flags to unset flags we can clear 333 | delay(500); // need the bus to be communicating within this time frame 334 | if(Interrupt()) 335 | { 336 | // determine which interrupt flags have been set 337 | interruptFlags.word = Read(ADDR_CiINT); 338 | if(!interruptFlags.bF.IF.CERRIF) 339 | { 340 | // to get here we must have received something without errors 341 | Mode(CAN_NORMAL_MODE); 342 | savedNominalBaud = i; 343 | savedFreq = Freq; 344 | running = 1; 345 | errorFlags = 0; 346 | rxFault = false; 347 | txFault = false; 348 | faulted = false; 349 | return i; 350 | } 351 | } 352 | } 353 | } 354 | } 355 | return 0; 356 | } 357 | 358 | uint32_t MCP2517FD::initFD(uint32_t nominalRate, uint32_t dataRate) 359 | { 360 | REG_CiINT interruptFlags; 361 | int i = 0; 362 | 363 | initSPI(); 364 | 365 | if(nominalRate > 0) 366 | { 367 | if(_initFD(nominalRate, dataRate, 40, 4, false)) 368 | { 369 | savedNominalBaud = nominalRate; 370 | savedDataBaud = dataRate; 371 | savedFreq = 40; 372 | running = 1; 373 | errorFlags = 0; 374 | rxFault = false; 375 | txFault = false; 376 | faulted = false; 377 | return nominalRate; 378 | } 379 | } 380 | else 381 | { 382 | for(i = 20; i < 1000; i = i + 5) 383 | { 384 | if(_initFD(i, dataRate, 40, 4, true)) 385 | { 386 | // check for bus activity 387 | Write16(ADDR_CiINTENABLE,0); //write to INT flags to unset flags we can clear 388 | delay(500); // need the bus to be communicating within this time frame 389 | if(Interrupt()) 390 | { 391 | // determine which interrupt flags have been set 392 | interruptFlags.word = Read(ADDR_CiINT); 393 | if(!interruptFlags.bF.IF.CERRIF) 394 | { 395 | // to get here we must have received something without errors 396 | Mode(CAN_NORMAL_MODE); 397 | savedNominalBaud = i; 398 | savedDataBaud = dataRate; 399 | savedFreq = 40; 400 | running = 1; 401 | errorFlags = 0; 402 | rxFault = false; 403 | txFault = false; 404 | faulted = false; 405 | return i; 406 | } 407 | } 408 | } 409 | } 410 | } 411 | return 0; 412 | } 413 | 414 | 415 | void MCP2517FD::txQueueSetup() 416 | { 417 | uint32_t debugVal; 418 | REG_CiTXQCON txQCon; 419 | const TickType_t xDelay = 1; 420 | 421 | Write8(ADDR_CiFIFOCON + 1, 0); //Unset TXREQ which should abort all pending TX frames in queue 422 | vTaskDelay(xDelay); //wait a bit for that to work 423 | 424 | txQCon.word = 0x400; //set FRESET to reset this FIFO 425 | Write(ADDR_CiTXQCON, txQCon.word); 426 | vTaskDelay(xDelay); 427 | 428 | //transmit queue set up 429 | txQCon.word = 0; 430 | txQCon.txBF.PayLoadSize = 7; //64 bytes 431 | txQCon.txBF.FifoSize = 8; //9 frame long FIFO 432 | txQCon.txBF.TxAttempts = 0; //No retries. One and done. It works or we throw it away 433 | txQCon.txBF.TxPriority = 15; //middle priority 434 | txQCon.txBF.TxEmptyIE = 0; 435 | Write(ADDR_CiTXQCON, txQCon.word); 436 | if (debuggingMode) 437 | { 438 | debugVal = Read(ADDR_CiTXQCON); 439 | Serial.println(debugVal, BIN); 440 | } 441 | } 442 | 443 | //chunks of the hardware init that are in common between standard and FD mode 444 | //Setup timer hardware, FIFOs, and Transmit Queue 445 | void MCP2517FD::commonInit() 446 | { 447 | uint32_t debugVal; 448 | if (debuggingMode) Serial.println("commonInit()"); 449 | 450 | REG_CiFIFOCON fifoCon; 451 | REG_CiTSCON tsCon; 452 | 453 | txQueueSetup(); 454 | 455 | //builtin timerstamping setup 456 | tsCon.bF.TBCPrescaler = 39; //40x slow down means 1us resolution 457 | tsCon.bF.TBCEnable = 1; 458 | Write(ADDR_CiTSCON ,tsCon.word); 459 | if (debuggingMode) 460 | { 461 | debugVal = Read(ADDR_CiTSCON); 462 | Serial.println(debugVal, BIN); 463 | } 464 | 465 | //Last FIFO we'll set up is receive fifo 466 | fifoCon.word = 0; //clear it all out to start fresh 467 | fifoCon.rxBF.TxEnable = 0; //Make FIFO1 a RX FIFO 468 | fifoCon.rxBF.FifoSize = 17; //18 frames long 469 | fifoCon.rxBF.PayLoadSize = 7; //64 byte payload possible 470 | fifoCon.rxBF.RxFullIE = 1; //if the FIFO fills up let the code know (hopefully never happens!) 471 | fifoCon.rxBF.RxNotEmptyIE = 1; //if the FIFO isn't empty let the code know 472 | fifoCon.rxBF.RxTimeStampEnable = 1; //time stamp each frame as it comes in 473 | Write(ADDR_CiFIFOCON + (CiFIFO_OFFSET * 1), fifoCon.word); //Write to FIFO1 474 | } 475 | 476 | bool MCP2517FD::_init(uint32_t CAN_Bus_Speed, uint8_t Freq, uint8_t SJW, bool autoBaud) { 477 | REG_CiNBTCFG nominalCfg; 478 | REG_CiCON canConfig; 479 | int tseg1, tseg2; 480 | uint32_t debugVal; 481 | 482 | inFDMode = false; 483 | 484 | if (debuggingMode) Serial.println("_init()"); 485 | 486 | Write(ADDR_CiINT,0); //disable all interrupts during init 487 | 488 | if (!initializedResources) initializeResources(); 489 | 490 | // Reset MCP2517FD which puts us in config mode automatically 491 | Reset(); 492 | delay(1); 493 | 494 | //but, just to be sure, explicitly ask for config mode 495 | if(!Mode(CAN_CONFIGURATION_MODE)) 496 | { 497 | if (debuggingMode) Serial.println("Could not enter configuration mode"); 498 | return false; 499 | } 500 | 501 | Write(ADDR_CiINT,0); //disable all interrupts during init 502 | Write(ADDR_CiBDIAG0, 0); //clear debug flags too. 503 | Write(ADDR_CiBDIAG1, 0); 504 | errorFlags = 0; 505 | cachedDiag1 = 0; 506 | 507 | /*the MCP2517 has a very wide range for setting the baud rate. The sys clock is 40Mhz by default. 508 | But, someone can pass a different value if using a different crystal. 509 | We'll target around "Freq" TQ (with a target 75% sample point) for each bit on the CAN 510 | so we want a prescaler that takes 1 million / target baud to get a prescaler. 511 | Obviously this makes the upper limit 1M CAN rate be a prescaler of 1. 512 | 250K is a prescaler of 4, 33.333k is a prescale of 30 513 | We need TSEG1 + TSEG2 + 1 to equal Freq 514 | */ 515 | // Set registers 516 | nominalCfg.bF.SJW = SJW; 517 | tseg1 = (unsigned int)(((float)Freq / 4.0) * 3.0) - 1; 518 | tseg2 = Freq - tseg1 - 1; 519 | nominalCfg.bF.TSEG1 = tseg1 - 1; //registers store value one less than actual 520 | nominalCfg.bF.TSEG2 = tseg2 - 1; 521 | if (tseg1 > 256 || tseg1 < 1) 522 | { 523 | if (debuggingMode) Serial.println("Nominal TSeg1 outside of limits. Invalid baud rate!"); 524 | return false; 525 | } 526 | if (tseg2 > 128 || tseg2 < 1) 527 | { 528 | if (debuggingMode) Serial.println("Nominal TSeg2 outside of limits. Invalid baud rate!"); 529 | return false; 530 | } 531 | nominalCfg.bF.BRP = (1000000ul / CAN_Bus_Speed) - 1; 532 | uint32_t calcRate = (Freq * 1000000ul) / (nominalCfg.bF.BRP + 1) / (tseg1 + tseg2 + 1); 533 | int errRate = abs(int(CAN_Bus_Speed - calcRate)); 534 | if (errRate >= (CAN_Bus_Speed / 50)) //if more than 2% error in speed setting 535 | { 536 | if (debuggingMode) 537 | { 538 | Serial.println("WARNING - Set baud does not match requested baud."); 539 | Serial.println(calcRate); 540 | } 541 | } 542 | 543 | canConfig.bF.IsoCrcEnable = 0; //Don't use newer ISO format. I think it might screw up normal can? Maybe this doesn't matter at all for std can 544 | canConfig.bF.DNetFilterCount = 0; //Don't use device net filtering 545 | canConfig.bF.ProtocolExceptionEventDisable = 0; //Allow softer handling of protocol exception 546 | canConfig.bF.WakeUpFilterEnable = 0; //Not using wakeup filter 547 | canConfig.bF.BitRateSwitchDisable = 1; //We won't allow FD rate switching in this standard init function 548 | canConfig.bF.RestrictReTxAttempts = 1; //Don't try sending frames forever if no one acks them 549 | canConfig.bF.EsiInGatewayMode = 0; //ESI reflects error status 550 | canConfig.bF.SystemErrorToListenOnly = 0; //Don't automatically switch to listen only on system error 551 | canConfig.bF.StoreInTEF = 0; // Don't store transmitted messages back into RAM 552 | canConfig.bF.TXQEnable = 1; //But, do enable the transmission queue and save space for it in RAM 553 | canConfig.bF.TxBandWidthSharing = 4; //wait 16 bit times before trying to send another frame. Allows other nodes a bit of room to butt in 554 | canConfig.bF.RequestOpMode = CAN_CONFIGURATION_MODE; 555 | Write(ADDR_CiCON, canConfig.word); 556 | Write(ADDR_CiNBTCFG, nominalCfg.word); //write the nominal bit time register 557 | if (debuggingMode) 558 | { 559 | debugVal = Read(ADDR_CiCON); 560 | printf("CiCON: %x\n", debugVal); 561 | debugVal = Read(ADDR_CiNBTCFG); 562 | printf("CiNBTCFG: %x\n", debugVal); 563 | } 564 | 565 | commonInit(); 566 | 567 | Write(ADDR_CiINT,0); //disable all interrupts during init 568 | Write(ADDR_CiBDIAG0, 0); //clear debug flags too. 569 | Write(ADDR_CiBDIAG1, 0); 570 | errorFlags = 0; 571 | cachedDiag1 = 0; 572 | 573 | if(!autoBaud) 574 | { 575 | // Return to Normal mode 576 | if(!Mode(CAN_CLASSIC_MODE)) 577 | { 578 | if (debuggingMode) Serial.println("Could not enter normal mode"); 579 | return false; 580 | } 581 | } 582 | else 583 | { 584 | // Set to Listen Only mode 585 | if(!Mode(CAN_LISTEN_ONLY_MODE)) 586 | { 587 | if (debuggingMode) Serial.println("Could not enter listen only mode"); 588 | return false; 589 | } 590 | } 591 | 592 | // Enable interrupts 593 | Write(ADDR_CiINT, 0xB8030000); //Enable Invalid Msg, Bus err, sys err, rx overflow, rx fifo, tx fifo interrupts 594 | // Test that we can read back from the MCP2515 what we wrote to it 595 | uint32_t rtn = Read(ADDR_CiINT); 596 | if ((rtn & 0xFFFF0000) == 0xB8030000) 597 | { 598 | if (debuggingMode) Serial.println("MCP2517 Init Success"); 599 | errorFlags = 0; 600 | cachedDiag1 = 0; 601 | Write(ADDR_CiBDIAG0, 0); //clear debug flags too. 602 | Write(ADDR_CiBDIAG1, 0); 603 | rxFault = false; 604 | txFault = false; 605 | faulted = false; 606 | 607 | return true; 608 | } 609 | else 610 | { 611 | if (debuggingMode) 612 | { 613 | printf("MCP2517 Init Failed. CiINT = %x", rtn); 614 | } 615 | return false; 616 | } 617 | return false; 618 | } 619 | 620 | bool MCP2517FD::_initFD(uint32_t nominalSpeed, uint32_t dataSpeed, uint8_t freq, uint8_t sjw, bool autoBaud) 621 | { 622 | REG_CiNBTCFG nominalCfg; 623 | REG_CiDBTCFG dataCfg; 624 | REG_CiCON canConfig; 625 | REG_CiTDC txDelayConfig; 626 | int tseg1, tseg2; 627 | 628 | uint32_t neededTQ; 629 | 630 | inFDMode = true; 631 | 632 | if (debuggingMode) Serial.println("_initFD()"); 633 | 634 | if (!initializedResources) initializeResources(); 635 | 636 | if (nominalSpeed < 125000) return 0; //won't work, die - Keep in mind that the FD spec doesn't want less than 500k here though 637 | if (dataSpeed < 1000000ul) return 0; //also won't work. 638 | 639 | // Reset MCP2517FD which puts us in config mode automatically 640 | Reset(); 641 | delay(1); 642 | 643 | Write(ADDR_CiINT,0); //disable all interrupts during init 644 | 645 | /*Forget everything said about the baud rate generator in the _init function above. When we 646 | plan to be in FD mode it is best if the baud rate generator uses the same prescaler for both 647 | the nominal and data rates. 648 | */ 649 | // Set registers 650 | nominalCfg.bF.SJW = sjw; 651 | neededTQ = (freq * 1000000ul) / nominalSpeed; 652 | tseg1 = ((neededTQ * 8) / 10) - 1; //set sample point at 80% 653 | tseg2 = neededTQ - tseg1 - 1; 654 | if (tseg1 > 256 || tseg1 < 1) 655 | { 656 | if (debuggingMode) Serial.println("Nominal TSeg1 outside of limits. Invalid baud rates!"); 657 | return false; 658 | } 659 | if (tseg2 > 128 || tseg2 < 1) 660 | { 661 | if (debuggingMode) Serial.println("Nominal TSeg2 outside of limits. Invalid baud rates!"); 662 | return false; 663 | } 664 | 665 | nominalCfg.bF.TSEG1 = tseg1 - 1; 666 | nominalCfg.bF.TSEG2 = tseg2 - 1; 667 | nominalCfg.bF.BRP = 0; //baud rate prescaler. 0 = 1x prescale 668 | 669 | if (debuggingMode) Serial.printf("Nomimal Settings: TSEG1: %u TSEG2: %u\n", tseg1, tseg2); 670 | 671 | /*As above, we lock the prescaler at 1x and figure out the Seg1/Seg2 based on that. 672 | */ 673 | dataCfg.bF.SJW = sjw; 674 | neededTQ = (freq * 1000000ul) / dataSpeed; 675 | tseg1 = ((neededTQ * 8) / 10) - 1; //set sample point at 80% 676 | tseg2 = neededTQ - tseg1 - 1; 677 | if (tseg1 > 32 || tseg1 < 1) 678 | { 679 | if (debuggingMode) Serial.println("Data TSeg1 outside of limits. Invalid baud rates!"); 680 | return false; 681 | } 682 | if (tseg2 > 16 || tseg2 < 1) 683 | { 684 | if (debuggingMode) Serial.println("Data TSeg2 outside of limits. Invalid baud rates!"); 685 | return false; 686 | } 687 | 688 | dataCfg.bF.TSEG1 = tseg1 - 1; 689 | dataCfg.bF.TSEG2 = tseg2 - 1; 690 | dataCfg.bF.BRP = 0; 691 | 692 | txDelayConfig.bF.TDCMode = 2; //automatic mode. The module will figure out the proper delay itself 693 | txDelayConfig.bF.TDCValue = 0; //this value set by hardware in auto mode 694 | txDelayConfig.bF.TDCOffset = (dataCfg.bF.BRP + 1) * (tseg1); //set value (dbrp * dtseg1) as a basepoint 695 | 696 | if (debuggingMode) Serial.printf("Data Settings: TSEG1: %u TSEG2: %u\n", tseg1, tseg2); 697 | 698 | canConfig.bF.IsoCrcEnable = 1; //It's likely we need ISO CRC mode active to get FD to work properly 699 | canConfig.bF.DNetFilterCount = 0; //Don't use device net filtering 700 | canConfig.bF.ProtocolExceptionEventDisable = 0; //Allow softer handling of protocol exception 701 | canConfig.bF.WakeUpFilterEnable = 0; //Not using wakeup filter 702 | canConfig.bF.BitRateSwitchDisable = 0; //Let each TX frame tell us whether to use FD rate switching or not 703 | canConfig.bF.RestrictReTxAttempts = 1; //Don't try sending frames forever if no one acks them 704 | canConfig.bF.EsiInGatewayMode = 0; //ESI reflects error status 705 | canConfig.bF.SystemErrorToListenOnly = 1; //Auto switch to listen only on system err bit 706 | canConfig.bF.StoreInTEF = 0; // Don't store transmitted messages back into RAM 707 | canConfig.bF.TXQEnable = 1; //But, do enable the transmission queue and save space for it in RAM 708 | canConfig.bF.TxBandWidthSharing = 4; //wait 16 bit times before trying to send another frame. Allows other nodes a bit of room to butt in 709 | canConfig.bF.RequestOpMode = CAN_CONFIGURATION_MODE; 710 | Write(ADDR_CiCON, canConfig.word); 711 | Write(ADDR_CiNBTCFG, nominalCfg.word); //write the nominal bit time register 712 | Write(ADDR_CiDBTCFG, dataCfg.word); //and write out the data rate register too 713 | Write(ADDR_CiTDC, txDelayConfig.word); //write out transmission delay compensation register. 714 | 715 | commonInit(); 716 | 717 | if(!autoBaud) 718 | { 719 | // Return to Normal mode 720 | if(!Mode(CAN_NORMAL_MODE)) 721 | { 722 | if (debuggingMode) Serial.println("Could not enter normal mode"); 723 | return false; 724 | } 725 | } 726 | else 727 | { 728 | // Set to Listen Only mode 729 | if(!Mode(CAN_LISTEN_ONLY_MODE)) 730 | { 731 | if (debuggingMode) Serial.println("Could not enter listen only mode"); 732 | return false; 733 | } 734 | } 735 | // Enable interrupts 736 | Write(ADDR_CiINT, 0xB8030000); //Enable Invalid Msg, Bus err, sys err, rx overflow, rx fifo, tx fifo interrupts 737 | // Test that we can read back from the MCP2517FD what we wrote to it 738 | uint32_t rtn = Read(ADDR_CiINT); 739 | if ((rtn & 0xFFFF0000) == 0xB8030000) 740 | { 741 | if (debuggingMode) Serial.println("MCP2517 InitFD Success"); 742 | errorFlags = 0; 743 | rxFault = false; 744 | txFault = false; 745 | faulted = false; 746 | return true; 747 | } 748 | else 749 | { 750 | if (debuggingMode) 751 | { 752 | Serial.println(rtn, HEX); 753 | } 754 | return false; 755 | } 756 | return false; 757 | } 758 | 759 | uint16_t MCP2517FD::available() 760 | { 761 | if (!rxQueue) return 0; //why would this happen though?! 762 | return uxQueueMessagesWaiting(rxQueue); 763 | } 764 | 765 | int MCP2517FD::_setFilter(uint32_t id, uint32_t mask, bool extended) 766 | { 767 | uint8_t filterCtrl; 768 | 769 | for (int i = 0; i < 32; i++) 770 | { 771 | filterCtrl = Read(ADDR_CiFLTCON + i); 772 | if ( !(filterCtrl & 0x80) ) 773 | { 774 | _setFilterSpecific(i, id, mask, extended); 775 | return i; 776 | } 777 | } 778 | //if we got here then there were no free filters. Return value of deaaaaath! 779 | if (debuggingMode) Serial.println("Err: No filter set!"); 780 | return -1; 781 | } 782 | 783 | //we've got 32 filters each with their own mask. 784 | int MCP2517FD::_setFilterSpecific(uint8_t mailbox, uint32_t id, uint32_t mask, bool extended) 785 | { 786 | uint32_t packedID, packedMask; 787 | if (mailbox > 31) return 0; //past end of valid mailbox #said 788 | 789 | //First up, make sure it's disabled as you can't set an enabled filter/mask combination 790 | Write8(ADDR_CiFLTCON + mailbox, 0); 791 | //Then we can directly write the filter and mask out - Using extended to augment bit 30 appropriately 792 | packedID = id; 793 | packedMask = mask; 794 | if (extended) 795 | { 796 | packedID = packExtValue(packedID); 797 | packedMask = packExtValue(packedMask); 798 | } 799 | else 800 | { 801 | packedID = packedID & 0x7FF; 802 | packedMask = packedMask & 0x7FF; 803 | } 804 | packedMask |= 1 << 30; //filter/mask combo must match extended/std exactly. Won't allow both 805 | if (extended) packedID |= 1 << 30; //only allow extended frames to match 806 | Write(ADDR_CiFLTOBJ + (CiFILTER_OFFSET * mailbox), packedID); 807 | Write(ADDR_CiMASK + (CiFILTER_OFFSET * mailbox), packedMask); 808 | Write8(ADDR_CiFLTCON + mailbox, 0x80 + 1); //Enable the filter and send it to FIFO1 which is the RX FIFO 809 | return mailbox; 810 | } 811 | 812 | uint32_t MCP2517FD::init(uint32_t ul_baudrate) 813 | { 814 | return Init(ul_baudrate, 40); 815 | } 816 | 817 | uint32_t MCP2517FD::beginAutoSpeed() 818 | { 819 | return Init(0, 40); 820 | } 821 | 822 | uint32_t MCP2517FD::set_baudrate(uint32_t ul_baudrate) 823 | { 824 | return Init(ul_baudrate, 40); 825 | } 826 | 827 | uint32_t MCP2517FD::set_baudrateFD(uint32_t nominalSpeed, uint32_t dataSpeed) 828 | { 829 | return _initFD(nominalSpeed, dataSpeed, 40, 4, false); 830 | } 831 | 832 | void MCP2517FD::setListenOnlyMode(bool state) 833 | { 834 | if (state) 835 | { 836 | Mode(CAN_LISTEN_ONLY_MODE); //listen only seems to always accept FD frames 837 | } 838 | else 839 | { 840 | if (!inFDMode) Mode(CAN_CLASSIC_MODE); 841 | else Mode(CAN_NORMAL_MODE); 842 | } 843 | } 844 | 845 | void MCP2517FD::enable() 846 | { 847 | if (!inFDMode) Mode(CAN_CLASSIC_MODE); 848 | else Mode(CAN_NORMAL_MODE); 849 | } 850 | 851 | void MCP2517FD::disable() 852 | { 853 | Mode(CAN_CONFIGURATION_MODE); 854 | } 855 | 856 | bool MCP2517FD::sendFrame(CAN_FRAME& txFrame) 857 | { 858 | EnqueueTX(txFrame); 859 | return true; 860 | } 861 | 862 | bool MCP2517FD::sendFrameFD(CAN_FRAME_FD& txFrame) 863 | { 864 | EnqueueTX(txFrame); 865 | return true; 866 | } 867 | 868 | bool MCP2517FD::rx_avail() 869 | { 870 | return available() > 0 ? true : false; 871 | } 872 | 873 | uint32_t MCP2517FD::get_rx_buff(CAN_FRAME &msg) 874 | { 875 | CAN_FRAME_FD temp; 876 | bool ret = GetRXFrame(msg); 877 | //f (ret) ret = fdToCan(temp, msg); 878 | return ret; 879 | } 880 | 881 | uint32_t MCP2517FD::get_rx_buffFD(CAN_FRAME_FD &msg) 882 | { 883 | return GetRXFrame(msg); 884 | } 885 | 886 | uint32_t MCP2517FD::getErrorFlags() 887 | { 888 | return errorFlags; 889 | } 890 | 891 | uint32_t MCP2517FD::getCIBDIAG0() 892 | { 893 | return Read(ADDR_CiBDIAG0); 894 | } 895 | 896 | uint32_t MCP2517FD::getCIBDIAG1() 897 | { 898 | return Read(ADDR_CiBDIAG1); 899 | } 900 | 901 | uint32_t MCP2517FD::getBitConfig() 902 | { 903 | return Read(ADDR_CiNBTCFG); 904 | } 905 | 906 | //Prints to screen basically all of the important registers including debugging registers. 907 | void MCP2517FD::printDebug() 908 | { 909 | Serial.println(); 910 | Serial.print("Diag0: "); 911 | Serial.print(getCIBDIAG0(), HEX); 912 | Serial.print(" Diag1: "); 913 | Serial.print(getCIBDIAG1(), HEX); 914 | Serial.print(" ErrFlags: "); 915 | Serial.println(errorFlags, HEX); 916 | 917 | Serial.print("Ctrl: "); 918 | Serial.print(Read(ADDR_CiCON), HEX); 919 | Serial.print(" NomBits: "); 920 | Serial.print(Read(ADDR_CiNBTCFG), HEX); 921 | Serial.print(" DataBits: "); 922 | Serial.print(Read(ADDR_CiDBTCFG), HEX); 923 | Serial.print(" TDC: "); 924 | Serial.println(Read(ADDR_CiTDC), HEX); 925 | 926 | Serial.print("TxqCon: "); 927 | Serial.print(Read(ADDR_CiTXQCON), HEX); 928 | Serial.print(" TsCon: "); 929 | Serial.print(Read(ADDR_CiTSCON), HEX); 930 | Serial.print(" Int: "); 931 | Serial.println(Read(ADDR_CiINT), HEX); 932 | 933 | Serial.print("F0Ctrl: "); 934 | Serial.print(Read(ADDR_CiFIFOCON), HEX); 935 | Serial.print(" F0 Status: "); 936 | Serial.print(Read(ADDR_CiFIFOSTA), HEX); 937 | Serial.print(" F1Ctrl: "); 938 | Serial.print(Read(ADDR_CiFIFOCON + CiFIFO_OFFSET), HEX); 939 | Serial.print(" F1 Status: "); 940 | Serial.println(Read(ADDR_CiFIFOSTA + CiFIFO_OFFSET), HEX); 941 | } 942 | 943 | void MCP2517FD::Reset() { 944 | SPI.beginTransaction(fdSPISettings); 945 | digitalWrite(_CS,LOW); 946 | SPI.transfer(CMD_RESET); //always need to send 12 bit address too 947 | SPI.transfer(CMD_RESET); //so even reset is two bytes 948 | digitalWrite(_CS,HIGH); 949 | SPI.endTransaction(); 950 | } 951 | 952 | uint32_t MCP2517FD::Read(uint16_t address) { 953 | SPI.beginTransaction(fdSPISettings); 954 | digitalWrite(_CS,LOW); 955 | SPI.transfer((CMD_READ << 4)| ( (address >> 8) & 0xF) ); 956 | SPI.transfer(address & 0xFF); 957 | uint32_t data = SPI.transfer(0); 958 | data += (SPI.transfer(0) << 8); 959 | data += (SPI.transfer(0) << 16); 960 | data += (SPI.transfer(0) << 24); 961 | digitalWrite(_CS,HIGH); 962 | SPI.endTransaction(); 963 | return data; 964 | } 965 | 966 | uint8_t MCP2517FD::Read8(uint16_t address) 967 | { 968 | SPI.beginTransaction(fdSPISettings); 969 | digitalWrite(_CS,LOW); 970 | SPI.transfer((CMD_READ << 4) | ((address >> 8) & 0xF)); 971 | SPI.transfer(address & 0xFF); 972 | uint8_t data = SPI.transfer(0x00); 973 | digitalWrite(_CS,HIGH); 974 | SPI.endTransaction(); 975 | return data; 976 | } 977 | 978 | uint16_t MCP2517FD::Read16(uint16_t address) 979 | { 980 | SPI.beginTransaction(fdSPISettings); 981 | digitalWrite(_CS,LOW); 982 | SPI.transfer((CMD_READ << 4) | ((address >> 8) & 0xF)); 983 | SPI.transfer(address & 0xFF); 984 | uint16_t data = SPI.transfer(0); 985 | data += (SPI.transfer(0) << 8); 986 | digitalWrite(_CS,HIGH); 987 | SPI.endTransaction(); 988 | return data; 989 | } 990 | 991 | void MCP2517FD::Read(uint16_t address, uint8_t data[], uint16_t bytes) { 992 | // allows for sequential reading of registers starting at address - see data sheet 993 | SPI.beginTransaction(fdSPISettings); 994 | digitalWrite(_CS,LOW); 995 | SPI.transfer((CMD_READ << 4) | ((address >> 8) & 0xF)); 996 | SPI.transfer(address & 0xFF); 997 | SPI.transferBytes(NULL, data, bytes); //read only operation 998 | digitalWrite(_CS,HIGH); 999 | SPI.endTransaction(); 1000 | } 1001 | 1002 | uint32_t MCP2517FD::ReadFrameBuffer(uint16_t address, CAN_FRAME_FD &message) { 1003 | uint32_t buffer[19]; //76 bytes 1004 | 1005 | //there is no read buffer command anymore. Need to read from RAM on the chip 1006 | //quickly read the whole thing then process it afterward 1007 | SPI.beginTransaction(fdSPISettings); 1008 | digitalWrite(_CS,LOW); 1009 | SPI.transfer((CMD_READ << 4) | ((address >> 8) & 0xF)); 1010 | SPI.transfer(address & 0xFF); 1011 | //read enough of the RAM to get an 8 byte message. 1012 | //Then we check to see if we really need to read more. 1013 | //This prevents having to read 64 data bytes if we were just receiving normal frames. 1014 | SPI.transferBytes(NULL, (uint8_t *)&buffer[0], 20); 1015 | message.length = buffer[1] & 0xF; 1016 | switch (message.length) 1017 | { 1018 | case 9: 1019 | message.length = 12; 1020 | break; 1021 | case 10: 1022 | message.length = 16; 1023 | break; 1024 | case 11: 1025 | message.length = 20; 1026 | break; 1027 | case 12: 1028 | message.length = 24; 1029 | break; 1030 | case 13: 1031 | message.length = 32; 1032 | break; 1033 | case 14: 1034 | message.length = 48; 1035 | break; 1036 | case 15: 1037 | message.length = 64; 1038 | break; 1039 | } 1040 | int neededBytes = message.length - 8; 1041 | if (neededBytes > 0) SPI.transferBytes(NULL, (uint8_t *)&buffer[5], neededBytes); 1042 | digitalWrite(_CS,HIGH); 1043 | SPI.endTransaction(); 1044 | /*message in RAM is as follows: 1045 | The first 32 bits are the message ID, either 11 or 29 bit (12 bit not handled yet), then bit 29 is RRS 1046 | The next 32 bits have: 1047 | first 4 bits are DLC 1048 | Then 1049 | IDE (extended address) (bit 4) 1050 | RTR (Remote request) (bit 5) 1051 | BRS (Baud rate switch for data) (bit 6) 1052 | FDF (FD mode) (Bit 7) 1053 | ESI (1 = tx node error passive, 0 = tx node error active) (bit 8) 1054 | Bits 11 - 15 = Filter hit (0-31) 1055 | The next 32 bits are all the timestamp (in microseconds for us) 1056 | Then each additional byte is a data byte (up to 64 bytes) 1057 | */ 1058 | message.extended = (buffer[1] >> 4) & 1; 1059 | if (message.extended) message.id = unpackExtValue(buffer[0] & 0x1FFFFFFFull); 1060 | else message.id = buffer[0] & 0x7FF; 1061 | message.fid = 0; 1062 | message.priority = 0; 1063 | message.fdMode = (buffer[1] >> 7) & 1; 1064 | if (message.fdMode) 1065 | message.rrs = buffer[0] >> 29 & 1; 1066 | else 1067 | message.rrs = buffer[1] >> 5 & 1; 1068 | message.timestamp = buffer[2]; 1069 | if (!message.fdMode && message.length > 8) message.length = 8; 1070 | //only copy the number of words we really have to. 1071 | int copyWords = (message.length + 3) / 4; 1072 | for (int j = 0; j < copyWords; j++) message.data.uint32[j] = buffer[3 + j]; 1073 | return (buffer[1] >> 11) & 31; //return which filter produced this message 1074 | } 1075 | 1076 | void MCP2517FD::Write8(uint16_t address, uint8_t data) { 1077 | //taskDISABLE_INTERRUPTS(); 1078 | SPI.beginTransaction(fdSPISettings); 1079 | digitalWrite(_CS,LOW); 1080 | SPI.transfer((CMD_WRITE << 4) | ((address >> 8) & 0xF) ); 1081 | SPI.transfer(address & 0xFF); 1082 | SPI.transfer(data); 1083 | digitalWrite(_CS,HIGH); 1084 | SPI.endTransaction(); 1085 | //taskENABLE_INTERRUPTS(); 1086 | } 1087 | 1088 | void MCP2517FD::Write16(uint16_t address, uint16_t data) { 1089 | //taskDISABLE_INTERRUPTS(); 1090 | SPI.beginTransaction(fdSPISettings); 1091 | digitalWrite(_CS,LOW); 1092 | SPI.transfer((CMD_WRITE << 4) | ((address >> 8) & 0xF) ); 1093 | SPI.transfer(address & 0xFF); 1094 | SPI.transfer(data & 0xFF); 1095 | SPI.transfer((data >> 8) & 0xFF); 1096 | digitalWrite(_CS,HIGH); 1097 | SPI.endTransaction(); 1098 | //taskENABLE_INTERRUPTS(); 1099 | } 1100 | 1101 | void MCP2517FD::Write(uint16_t address, uint32_t data) { 1102 | //taskDISABLE_INTERRUPTS(); 1103 | SPI.beginTransaction(fdSPISettings); 1104 | digitalWrite(_CS,LOW); 1105 | SPI.transfer((CMD_WRITE << 4) | ((address >> 8) & 0xF) ); 1106 | SPI.transfer(address & 0xFF); 1107 | SPI.transfer(data & 0xFF); 1108 | SPI.transfer((data >> 8) & 0xFF); 1109 | SPI.transfer((data >> 16) & 0xFF); 1110 | SPI.transfer((data >> 24) & 0xFF); 1111 | digitalWrite(_CS,HIGH); 1112 | SPI.endTransaction(); 1113 | //taskENABLE_INTERRUPTS(); 1114 | } 1115 | 1116 | void MCP2517FD::Write(uint16_t address, uint8_t data[], uint16_t bytes) { 1117 | // allows for sequential writing of registers starting at address - see data sheet 1118 | uint8_t i; 1119 | //taskDISABLE_INTERRUPTS(); 1120 | SPI.beginTransaction(fdSPISettings); 1121 | digitalWrite(_CS,LOW); 1122 | SPI.transfer((CMD_WRITE << 4) | ((address >> 8) & 0xF) ); 1123 | SPI.transfer(address & 0xFF); 1124 | SPI.writeBytes(data, bytes); 1125 | digitalWrite(_CS,HIGH); 1126 | SPI.endTransaction(); 1127 | //taskENABLE_INTERRUPTS(); 1128 | } 1129 | 1130 | void MCP2517FD::LoadFrameBuffer(uint16_t address, CAN_FRAME_FD &message) 1131 | { 1132 | uint8_t buffer[76]; 1133 | uint32_t *buffPtr; 1134 | int dataBytes; 1135 | /*message in RAM is as follows (same as RX but without the timestamp): 1136 | The first 32 bits are the message ID, either 11 or 29 bit (12 bit not handled yet), then bit 29 is RRS 1137 | The next 32 bits have: 1138 | first 4 bits are DLC 1139 | Then 1140 | IDE (extended address) (bit 4) 1141 | RTR (Remote request) (bit 5) 1142 | BRS (Baud rate switch for data) (bit 6) 1143 | FDF (FD mode) (Bit 7) 1144 | ESI (1 = tx node error passive, 0 = tx node error active) (bit 8) 1145 | SEQ (sequence number) (Bits 9-15) - Optional sequence number for your reference 1146 | Then each additional byte is a data byte (up to 64 bytes) 1147 | */ 1148 | buffer[0] = (CMD_WRITE << 4) | ((address >> 8) & 0xF); 1149 | buffer[1] = address & 0xFF; 1150 | buffPtr = (uint32_t *)&buffer[2]; 1151 | 1152 | if (message.extended) buffPtr[0] = packExtValue(message.id); 1153 | else buffPtr[0] = message.id & 0x7FF; 1154 | 1155 | buffPtr[1] = (message.extended) ? (1 << 4) : 0; 1156 | buffPtr[1] |= (message.fdMode) ? (3 << 6) : 0; //set both the BRS and FDF bits at once 1157 | if (message.fdMode) 1158 | buffPtr[0] |= (message.rrs) ? (1 << 29) : 0; 1159 | else 1160 | buffPtr[1] |= (message.rrs) ? (1 << 5) : 0; 1161 | if (!message.fdMode && message.length > 8) message.length = 8; 1162 | dataBytes = message.length; 1163 | 1164 | //promote requested frame length to the next allowable size 1165 | if (message.length > 8 && message.length < 13) buffPtr[1] |= 9; 1166 | else if (message.length > 12 && message.length < 17) buffPtr[1] |= 10; 1167 | else if (message.length > 16 && message.length < 21) buffPtr[1] |= 11; 1168 | else if (message.length > 20 && message.length < 25) buffPtr[1] |= 12; 1169 | else if (message.length > 24 && message.length < 33) buffPtr[1] |= 13; 1170 | else if (message.length > 32 && message.length < 49) buffPtr[1] |= 14; 1171 | else if (message.length > 48 && message.length < 65) buffPtr[1] |= 15; 1172 | else 1173 | { 1174 | if (message.length < 9) buffPtr[1] |= message.length; 1175 | else buffPtr[1] |= 8; 1176 | } 1177 | 1178 | //only copy the number of data words we really have to. 1179 | int copyWords = (dataBytes + 3) / 4; 1180 | for (int j = 0; j < copyWords; j++) 1181 | { 1182 | buffPtr[2 + j] = message.data.uint32[j]; 1183 | } 1184 | 1185 | if (debuggingMode) 1186 | { 1187 | Serial.print("Load TXBufr Addr "); 1188 | Serial.print(address, HEX); 1189 | Serial.print(" Buff0 "); 1190 | Serial.print(buffPtr[0], HEX); 1191 | Serial.print(" Buff1 "); 1192 | Serial.println(buffPtr[1], HEX); 1193 | } 1194 | 1195 | //taskDISABLE_INTERRUPTS(); 1196 | SPI.beginTransaction(fdSPISettings); 1197 | digitalWrite(_CS,LOW); 1198 | SPI.writeBytes((uint8_t *) &buffer[0], 10 + (copyWords * 4)); //and only write just as many bytes as we need to for this frame 1199 | digitalWrite(_CS,HIGH); 1200 | SPI.endTransaction(); 1201 | //taskENABLE_INTERRUPTS(); 1202 | Write8(ADDR_CiFIFOCON + 1, 3); //Set UINC and TX_Request 1203 | } 1204 | 1205 | bool MCP2517FD::Interrupt() { 1206 | return (digitalRead(_INT)==LOW); 1207 | } 1208 | 1209 | bool MCP2517FD::Mode(byte mode) { 1210 | uint32_t tempMode; 1211 | if (debuggingMode) Serial.println("Mode"); 1212 | tempMode = Read(ADDR_CiCON); 1213 | tempMode &= 0xF8FFFFFF; 1214 | tempMode |= (mode << 24ul); 1215 | Write(ADDR_CiCON, tempMode); 1216 | delay(6); // allow for any transmissions to complete 1217 | uint8_t data = Read8(ADDR_CiCON + 2); 1218 | if (debuggingMode) 1219 | { 1220 | Serial.println(data, BIN); 1221 | Serial.println(mode, BIN); 1222 | } 1223 | return ((data >> 5) == mode); 1224 | } 1225 | 1226 | /*Initializes filters to either accept all frames or accept none 1227 | By default on the MCP2517FD all filters are not enabled and probably 1228 | that means you won't get anything unless you pass that you want 1229 | permissive mode. 1230 | */ 1231 | void MCP2517FD::InitFilters(bool permissive) { 1232 | uint32_t value; 1233 | uint32_t value32; 1234 | if (permissive) 1235 | { 1236 | value = 0; 1237 | value32 = 0; 1238 | } 1239 | else 1240 | { 1241 | value = 0x7FF; //all 11 bits set 1242 | value32 = 0x1FFFFFFF; //all 29 bits set 1243 | } 1244 | 1245 | _setFilterSpecific(0, value, value, false); 1246 | _setFilterSpecific(1, value32, value32, true); 1247 | } 1248 | 1249 | //Places the given frame into the receive queue 1250 | void IRAM_ATTR MCP2517FD::EnqueueRX(CAN_FRAME_FD& newFrame) { 1251 | xQueueSend(rxQueue, &newFrame, 0); 1252 | } 1253 | 1254 | void IRAM_ATTR MCP2517FD::EnqueueRX(CAN_FRAME& newFrame) { 1255 | xQueueSend(rxQueue, &newFrame, 0); 1256 | } 1257 | 1258 | 1259 | //Places the given frame either into a hardware FIFO (if there is space) 1260 | //or into the software side queue if there was no room 1261 | void MCP2517FD::EnqueueTX(CAN_FRAME_FD& newFrame) { 1262 | handleTXFifo(0, newFrame); 1263 | } 1264 | 1265 | void MCP2517FD::EnqueueTX(CAN_FRAME& newFrame) { 1266 | handleTXFifo(0, newFrame); 1267 | } 1268 | 1269 | bool MCP2517FD::GetRXFrame(CAN_FRAME_FD &frame) { 1270 | if (!rxQueue) return false; 1271 | if (xQueueReceive(rxQueue, &frame, 0) == pdTRUE) return true; 1272 | return false; 1273 | } 1274 | 1275 | bool MCP2517FD::GetRXFrame(CAN_FRAME &frame) { 1276 | if (!rxQueue) return false; 1277 | if (xQueueReceive(rxQueue, &frame, 0) == pdTRUE) return true; 1278 | return false; 1279 | } 1280 | 1281 | //Not truly an interrupt handler in the sense that it does NOT run in interrupt context 1282 | //but it does handle the MCP2517FD interrupt still. 1283 | void MCP2517FD::intHandler(void) { 1284 | CAN_FRAME_FD messageFD; 1285 | CAN_FRAME message; 1286 | uint32_t ctrlVal; 1287 | uint32_t status; 1288 | uint16_t addr; 1289 | uint32_t filtHit; 1290 | 1291 | if (!running) return; 1292 | 1293 | // determine which interrupt flags have been set 1294 | uint32_t interruptFlags = Read(ADDR_CiINT); 1295 | 1296 | //if(interruptFlags & 1) //Transmit FIFO interrupt 1297 | //{ 1298 | //Only FIFO0 is TX so no need to ask for which FIFO. 1299 | //Write8(ADDR_CiFIFOCON, 0x80); //Keep FIFO as TX but disable Queue Empty Interrupt 1300 | //handleTXFifoISR(0); 1301 | //} 1302 | //else //didn't get TX interrupt but check if we've got msgs in FIFO and see if we can queue them into hardware 1303 | { 1304 | if (uxQueueMessagesWaiting(txQueue) > 0) handleTXFifoISR(0); //if we have messages to send then try to queue them in the TX fifo 1305 | } 1306 | 1307 | if(interruptFlags & 2) //Receive FIFO interrupt 1308 | { 1309 | //no need to ask which FIFO matched, there is only one RX FIFO configured in this library 1310 | //So, ask for frames out of the FIFO until it no longer has any 1311 | status = Read( ADDR_CiFIFOSTA + (CiFIFO_OFFSET * 1) ); 1312 | while (status & 1) // there is at least one message waiting to be received 1313 | { 1314 | //Get address to read from 1315 | addr = Read(ADDR_CiFIFOUA + (CiFIFO_OFFSET * 1)); 1316 | //Then use that address to read the frame 1317 | filtHit = ReadFrameBuffer(addr + 0x400, messageFD); //stupidly the returned address from FIFOUA needs an offset 1318 | Write8(ADDR_CiFIFOCON + (CiFIFO_OFFSET * 1) + 1, 1); //set UINC (it's at bit 8 in the register so we move one byte into register and write 8 bits) 1319 | status = Read( ADDR_CiFIFOSTA + (CiFIFO_OFFSET * 1) ); //read the register again to see if there are more frames waiting 1320 | if (inFDMode) 1321 | handleFrameDispatch(messageFD, filtHit); 1322 | else 1323 | { 1324 | fdToCan(messageFD, message); 1325 | handleFrameDispatch(message, filtHit); 1326 | } 1327 | } 1328 | } 1329 | if (interruptFlags & (1 << 11)) //Receive Object Overflow 1330 | { 1331 | //once again, we know which FIFO must have overflowed - what to do about it though? 1332 | errorFlags |= 1; 1333 | } 1334 | if (interruptFlags & (1 << 12)) //System error 1335 | { 1336 | errorFlags |= 2; 1337 | } 1338 | if (interruptFlags & (1 << 13)) //CANBus error 1339 | { 1340 | errorFlags |= 4; 1341 | } 1342 | if (interruptFlags & (1 << 15)) //Invalid msg interrupt 1343 | { 1344 | errorFlags |= 8; 1345 | } 1346 | if (errorFlags > 0) 1347 | { 1348 | //if (debuggingMode) Serial.write('?'); 1349 | uint32_t diagBits = getCIBDIAG1(); //get a detailed fault status 1350 | 1351 | if (diagBits & 0x3030000) //either NBIT0 or NBIT1 error (or DBIT0, DBIT1) 1352 | { 1353 | txFault = true; 1354 | faulted = true; 1355 | Serial.print("**"); 1356 | cachedDiag1 |= diagBits; 1357 | needMCPReset = true; //if set true we'd auto reset the hardware when bit errors happen. 1358 | Write(ADDR_CiBDIAG1, 0); 1359 | } 1360 | if (diagBits & 0x40000) //18 - NACK fault 1361 | { 1362 | //Just set the TXfault flag not the genera faulted flag. This might be just a lack of CAN bus 1363 | txFault = true; 1364 | } 1365 | if (diagBits & 0x38380000) //19 - RX Fixed form, Bit stuff, or CRC error, either FD or not 1366 | { 1367 | rxFault = true; 1368 | faulted = true; 1369 | Serial.print("!!"); 1370 | cachedDiag1 |= diagBits; 1371 | needMCPReset = true; //could reset when these errors happen. Maybe count errors to decide? 1372 | Write(ADDR_CiBDIAG1, 0); 1373 | } 1374 | if (diagBits & 0x800000) //23 - TXBOERR - device went bus-off (and auto recovered) 1375 | { 1376 | //it's OK if it goes bus off and tries to recover. Don't reset as we might mess up the bus if we're insane 1377 | } 1378 | if (diagBits & 0x40000000) //30 - ESI of RX FD message was set 1379 | { 1380 | rxFault = true; 1381 | faulted = true; 1382 | } 1383 | if (diagBits & 0x80000000) //31 - DLC mismatch during RX or TX 1384 | { 1385 | //allow a dlc mismatch and don't do anything 1386 | //rxFault = true; 1387 | } 1388 | } 1389 | 1390 | //Now, acknowledge the interrupts by clearing the intf bits 1391 | Write16(ADDR_CiINT, 0); 1392 | } 1393 | 1394 | //TX fifo is 0 1395 | void MCP2517FD::handleTXFifoISR(int fifo) 1396 | { 1397 | uint32_t status; 1398 | uint16_t addr; 1399 | CAN_FRAME_FD frameFD; 1400 | CAN_FRAME frame; 1401 | uint8_t wroteFrames = 0; 1402 | 1403 | //if (fifo < 0) return; 1404 | //if (fifo > 2) return; 1405 | 1406 | //taskDISABLE_INTERRUPTS(); 1407 | 1408 | status = Read( ADDR_CiFIFOSTA + (CiFIFO_OFFSET * fifo) ); 1409 | //Write8(ADDR_CiFIFOSTA + (CiFIFO_OFFSET * fifo), 0); //reset all the fault bits in the FIFO (TXABT, TXLARB, TXERR) 1410 | //Write8(ADDR_CiBDIAG0 + (CiFIFO_OFFSET * fifo) + 1, 0); //Clear TX error counter 1411 | //Write8(ADDR_CiBDIAG1 + (CiFIFO_OFFSET * fifo) + 2, 0); //clear bits 16-23 in diag1 register 1412 | if ((status & 0x20) == 0x20) needTXFIFOReset = true; //if the queue registered a fault then set flag to have it reset 1413 | //While the FIFO has room and we still have frames to send 1414 | while ( (status & 1) && (uxQueueMessagesWaiting(txQueue) > 0) ) //while fifo is not full and we have messages waiting 1415 | { 1416 | if (debuggingMode) 1417 | { 1418 | Serial.write('~'); 1419 | } 1420 | //get address to write to 1421 | addr = Read( ADDR_CiFIFOUA + (CiFIFO_OFFSET * fifo) ); 1422 | if (inFDMode) 1423 | { 1424 | //if (debuggingMode) Serial.write('F'); 1425 | if (xQueueReceive(txQueue, &frameFD, 0) != pdTRUE) return; //abort if we can't load a frame from the queue! 1426 | } 1427 | else 1428 | { 1429 | //if (debuggingMode) Serial.write('S'); 1430 | if (xQueueReceive(txQueue, &frame, 0) != pdTRUE) return; 1431 | //hardware loading below always uses the same buffer save whether CAN or CANFD 1432 | if (!canToFD(frame, frameFD)) return; 1433 | } 1434 | LoadFrameBuffer( addr + 0x400, frameFD ); 1435 | wroteFrames = 1; 1436 | status = Read(ADDR_CiFIFOSTA + (CiFIFO_OFFSET * fifo)); 1437 | } 1438 | if (wroteFrames != 0) 1439 | { 1440 | //Write8(ADDR_CiFIFOCON + (CiFIFO_OFFSET * fifo), 0x84); //Keep as TX queue and enable interrupt for queue empty 1441 | if (debuggingMode) Serial.write('\"'); 1442 | } 1443 | //taskENABLE_INTERRUPTS(); 1444 | } 1445 | 1446 | /* 1447 | This routine used to try to load a frame into hardware if possible. But, testing has shown that 1448 | the ESP32 doesn't really take kindly to multiple pathways trying to use SPI at once. The SPI 1449 | library just plain dies if you do that. So, all SPI access is through the interrupt handler now 1450 | To make this work this routine just pretends an interrupt came in and so the interrupt handler 1451 | sees our frame and tries to send it. 1452 | */ 1453 | void MCP2517FD::handleTXFifo(int fifo, CAN_FRAME_FD &newFrame) 1454 | { 1455 | uint32_t status; 1456 | uint16_t addr; 1457 | //BaseType_t xHigherPriorityTaskWoken = pdFALSE; 1458 | 1459 | if (fifo < 0) return; 1460 | if (fifo > 2) return; 1461 | 1462 | //status = Read( ADDR_CiFIFOSTA + (CiFIFO_OFFSET * fifo) ); 1463 | //if (status & 1) //FIFO has room for this message - immediately send it to hardware 1464 | //{ 1465 | // addr = Read( ADDR_CiFIFOUA + (CiFIFO_OFFSET * fifo) ); 1466 | // LoadFrameBuffer( addr + 0x400, newFrame ); 1467 | // Write8(ADDR_CiFIFOCON + (CiFIFO_OFFSET * fifo) + 1, 3); //Set UINC and TX_Request 1468 | //} 1469 | //else //no room on hardware. Locally buffer in software 1470 | //{ 1471 | //try to queue, do not wait if we can't. If we can then pretend an interrupt happened. 1472 | if (!txQueue) return; 1473 | if (xQueueSend(txQueue, &newFrame, 0) == pdPASS) 1474 | { 1475 | if (debuggingMode) Serial.write('+'); 1476 | //xHigherPriorityTaskWoken = xTaskNotifyGive(intTaskFD); //send notice to the handler task that it can do the SPI transaction now 1477 | } 1478 | else //full queue, if debugging we'll dump the hardware state. 1479 | { 1480 | //Write8(ADDR_CiFIFOCON + (CiFIFO_OFFSET * fifo) + 1, 2); //Set TX_Request to make sure the FIFO is really trying to send 1481 | if (debuggingMode) 1482 | { 1483 | Serial.write('<'); 1484 | Serial.println(); 1485 | printDebug(); 1486 | } 1487 | } 1488 | //} 1489 | } 1490 | 1491 | void MCP2517FD::handleTXFifo(int fifo, CAN_FRAME &newFrame) 1492 | { 1493 | uint32_t status; 1494 | uint16_t addr; 1495 | CAN_FRAME_FD fd; 1496 | BaseType_t ret; 1497 | 1498 | if (fifo < 0) return; 1499 | if (fifo > 2) return; 1500 | 1501 | if (!txQueue) return; 1502 | 1503 | if (inFDMode) //if we're in FD mode the queue takes CAN-FD frames 1504 | { 1505 | canToFD(newFrame, fd); 1506 | ret = xQueueSend(txQueue, &fd, 0); 1507 | } 1508 | else 1509 | { 1510 | ret = xQueueSend(txQueue, &newFrame, 0); 1511 | } 1512 | if (ret == pdPASS) 1513 | { 1514 | if (debuggingMode) Serial.write('+'); 1515 | } 1516 | else //full queue, if debugging we'll dump the hardware state. 1517 | { 1518 | if (debuggingMode) 1519 | { 1520 | Serial.write('<'); 1521 | Serial.println(); 1522 | printDebug(); 1523 | } 1524 | } 1525 | } 1526 | 1527 | 1528 | /*The idea here is to use the fid member (which is not normally used) as a signal to 1529 | downstream of the type of callback that needs to be done. 1530 | The lowest 8 bits are used to pass the filter that matched (or FF if general callback) 1531 | If bit 31 is set then it's an object callback and the upper byte (minus that top one) are the listener position 1532 | */ 1533 | void MCP2517FD::handleFrameDispatch(CAN_FRAME_FD &frame, int filterHit) 1534 | { 1535 | CANListener *thisListener; 1536 | 1537 | //First, try to send a callback. If no callback registered then buffer the frame. 1538 | if (cbCANFrame[filterHit]) 1539 | { 1540 | frame.fid = filterHit; 1541 | xQueueSend(callbackQueueMCP, &frame, 0); 1542 | return; 1543 | } 1544 | else if (cbGeneral) 1545 | { 1546 | frame.fid = 0xFF; 1547 | xQueueSend(callbackQueueMCP, &frame, 0); 1548 | return; 1549 | } 1550 | else 1551 | { 1552 | for (int listenerPos = 0; listenerPos < SIZE_LISTENERS; listenerPos++) 1553 | { 1554 | thisListener = listener[listenerPos]; 1555 | if (thisListener != NULL) 1556 | { 1557 | if (thisListener->isCallbackActive(filterHit)) 1558 | { 1559 | frame.fid = 0x80000000ul + (listenerPos << 24ul) + filterHit; 1560 | xQueueSend(callbackQueueMCP, &frame, 0); 1561 | return; 1562 | } 1563 | else if (thisListener->isCallbackActive(numFilters)) //global catch-all 1564 | { 1565 | frame.fid = 0x80000000ul + (listenerPos << 24ul) + 0xFF; 1566 | xQueueSend(callbackQueueMCP, &frame, 0); 1567 | return; 1568 | } 1569 | } 1570 | } 1571 | } 1572 | //if none of the callback types caught this frame then queue it in the buffer 1573 | xQueueSend(rxQueue, &frame, 0); 1574 | } 1575 | 1576 | void MCP2517FD::handleFrameDispatch(CAN_FRAME &frame, int filterHit) 1577 | { 1578 | CANListener *thisListener; 1579 | 1580 | //First, try to send a callback. If no callback registered then buffer the frame. 1581 | if (cbCANFrame[filterHit]) 1582 | { 1583 | frame.fid = filterHit; 1584 | xQueueSend(callbackQueueMCP, &frame, 0); 1585 | return; 1586 | } 1587 | else if (cbGeneral) 1588 | { 1589 | frame.fid = 0xFF; 1590 | xQueueSend(callbackQueueMCP, &frame, 0); 1591 | return; 1592 | } 1593 | else 1594 | { 1595 | for (int listenerPos = 0; listenerPos < SIZE_LISTENERS; listenerPos++) 1596 | { 1597 | thisListener = listener[listenerPos]; 1598 | if (thisListener != NULL) 1599 | { 1600 | if (thisListener->isCallbackActive(filterHit)) 1601 | { 1602 | frame.fid = 0x80000000ul + (listenerPos << 24ul) + filterHit; 1603 | xQueueSend(callbackQueueMCP, &frame, 0); 1604 | return; 1605 | } 1606 | else if (thisListener->isCallbackActive(numFilters)) //global catch-all 1607 | { 1608 | frame.fid = 0x80000000ul + (listenerPos << 24ul) + 0xFF; 1609 | xQueueSend(callbackQueueMCP, &frame, 0); 1610 | return; 1611 | } 1612 | } 1613 | } 1614 | } 1615 | //if none of the callback types caught this frame then queue it in the buffer 1616 | xQueueSend(rxQueue, &frame, 0); 1617 | } 1618 | -------------------------------------------------------------------------------- /src/mcp2517fd.h: -------------------------------------------------------------------------------- 1 | #ifndef MCP2517_h 2 | #define MCP2517_h 3 | 4 | #include "Arduino.h" 5 | #include "mcp2517fd_defines.h" 6 | #include 7 | 8 | //#define DEBUG_SETUP 9 | #define FD_RX_BUFFER_SIZE 64 10 | #define FD_TX_BUFFER_SIZE 32 11 | #define FD_NUM_FILTERS 32 12 | 13 | class MCP2517FD : public CAN_COMMON 14 | { 15 | public: 16 | // Constructor defining which pins to use for CS and INT 17 | MCP2517FD(uint8_t CS_Pin, uint8_t INT_Pin); 18 | 19 | // Overloaded initialization function 20 | int Init(uint32_t nominalBaud, uint8_t freq); 21 | int Init(uint32_t CAN_Bus_Speed, uint8_t Freq, uint8_t SJW); 22 | int InitFD(uint32_t nominalBaud, uint32_t dataBaud, uint8_t freq); 23 | 24 | //block of functions which must be overriden from CAN_COMMON to implement functionality for this hardware 25 | int _setFilterSpecific(uint8_t mailbox, uint32_t id, uint32_t mask, bool extended); 26 | int _setFilter(uint32_t id, uint32_t mask, bool extended); 27 | void resetHardware(); 28 | uint32_t init(uint32_t ul_baudrate); 29 | uint32_t beginAutoSpeed(); 30 | uint32_t set_baudrate(uint32_t ul_baudrate); 31 | void setListenOnlyMode(bool state); 32 | void enable(); 33 | void disable(); 34 | bool sendFrame(CAN_FRAME& txFrame); 35 | bool rx_avail(); 36 | uint16_t available(); //like rx_avail but returns the number of waiting frames 37 | uint32_t get_rx_buff(CAN_FRAME &msg); 38 | //special FD functions required to reimplement to support FD mode 39 | uint32_t get_rx_buffFD(CAN_FRAME_FD &msg); 40 | uint32_t set_baudrateFD(uint32_t nominalSpeed, uint32_t dataSpeed); 41 | bool sendFrameFD(CAN_FRAME_FD& txFrame); 42 | uint32_t initFD(uint32_t nominalRate, uint32_t dataRate); 43 | 44 | // Basic MCP2517FD SPI Command Set 45 | void Reset(); 46 | uint32_t Read(uint16_t address); 47 | uint8_t Read8(uint16_t address); 48 | uint16_t Read16(uint16_t address); 49 | void Read(uint16_t address, uint8_t data[], uint16_t bytes); 50 | void Write8(uint16_t address, uint8_t data); 51 | void Write16(uint16_t address, uint16_t data); 52 | void Write(uint16_t address, uint32_t data); 53 | void Write(uint16_t address, uint8_t data[], uint16_t bytes); 54 | void LoadFrameBuffer(uint16_t address, CAN_FRAME_FD &message); 55 | uint32_t ReadFrameBuffer(uint16_t address, CAN_FRAME_FD &message); 56 | 57 | uint8_t Status(); 58 | uint8_t RXStatus(); 59 | 60 | // Extra functions 61 | bool Interrupt(); 62 | bool Mode(uint8_t mode); // Returns TRUE if mode change successful 63 | void setINTPin(uint8_t pin); 64 | void setCSPin(uint8_t pin); 65 | void EnqueueRX(CAN_FRAME_FD& newFrame); 66 | void EnqueueTX(CAN_FRAME_FD& newFrame); 67 | bool GetRXFrame(CAN_FRAME_FD& frame); 68 | void EnqueueRX(CAN_FRAME& newFrame); 69 | void EnqueueTX(CAN_FRAME& newFrame); 70 | bool GetRXFrame(CAN_FRAME& frame); 71 | void SetRXFilter(uint8_t filter, uint32_t FilterValue, bool ext); 72 | void SetRXMask(uint8_t mask, uint32_t MaskValue); 73 | void GetRXFilter(uint8_t filter, uint32_t &filterVal, boolean &isExtended); 74 | void GetRXMask(uint8_t mask, uint32_t &filterVal); 75 | void sendCallback(CAN_FRAME_FD *frame); 76 | void sendCallback(CAN_FRAME *frame); 77 | 78 | void InitFilters(bool permissive); 79 | void intHandler(); 80 | void printDebug(); 81 | void txQueueSetup(); 82 | void setRXBufferSize(int newSize); 83 | void setTXBufferSize(int newSize); 84 | 85 | QueueHandle_t callbackQueueMCP; 86 | TaskHandle_t intTaskFD = NULL; 87 | TaskHandle_t taskHandleMCPCAN = NULL; 88 | TaskHandle_t taskHandleReset = NULL; 89 | bool needMCPReset = false; 90 | bool needTXFIFOReset = false; 91 | bool inFDMode; 92 | 93 | private: 94 | bool _init(uint32_t baud, uint8_t freq, uint8_t sjw, bool autoBaud); 95 | bool _initFD(uint32_t nominalSpeed, uint32_t dataSpeed, uint8_t freq, uint8_t sjw, bool autoBaud); 96 | void initSPI(); 97 | void commonInit(); 98 | void handleFrameDispatch(CAN_FRAME_FD &frame, int filterHit); 99 | void handleFrameDispatch(CAN_FRAME &frame, int filterHit); 100 | void handleTXFifoISR(int fifo); 101 | void handleTXFifo(int fifo, CAN_FRAME_FD &newFrame); 102 | void handleTXFifo(int fifo, CAN_FRAME &newFrame); 103 | void initializeResources(); 104 | uint32_t packExtValue(uint32_t input); 105 | uint32_t unpackExtValue(uint32_t input); 106 | uint32_t getErrorFlags(); 107 | uint32_t getCIBDIAG0(); 108 | uint32_t getCIBDIAG1(); 109 | uint32_t getBitConfig(); 110 | 111 | // Pin variables 112 | uint8_t _CS; 113 | uint8_t _INT; 114 | volatile uint32_t savedNominalBaud; 115 | volatile uint32_t savedDataBaud; 116 | volatile uint8_t savedFreq; 117 | volatile uint8_t running; //1 if out of init code, 0 if still trying to initialize (auto baud detecting) 118 | bool initializedResources; //have we set up queues and interrupts? 119 | int rxBufferSize; 120 | int txBufferSize; 121 | QueueHandle_t rxQueue; 122 | QueueHandle_t txQueue; 123 | uint32_t errorFlags; 124 | uint32_t cachedDiag1; 125 | }; 126 | 127 | extern MCP2517FD CAN1; 128 | 129 | #endif 130 | -------------------------------------------------------------------------------- /src/mcp2517fd_defines.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | CAN FD SPI Driver: API Defines Header File 3 | 4 | Company: 5 | Microchip Technology Inc. 6 | 7 | File Name: 8 | drv_canfdspi_defines.h 9 | 10 | Summary: 11 | This header file contains object declarations used in the API. 12 | This also contains device specific defines. 13 | 14 | Description: 15 | None. 16 | *******************************************************************************/ 17 | 18 | //DOM-IGNORE-BEGIN 19 | /******************************************************************************* 20 | Copyright (c) 2016 Microchip Technology Inc. and its subsidiaries. 21 | You may use this software and any derivatives exclusively with Microchip products. 22 | 23 | THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". 24 | NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, 25 | INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, 26 | AND FITNESS FOR A PARTICULAR PURPOSE, OR ITS INTERACTION WITH MICROCHIP PRODUCTS, 27 | COMBINATION WITH ANY OTHER PRODUCTS, OR USE IN ANY APPLICATION. 28 | 29 | IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, 30 | INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER 31 | RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED 32 | OF THE POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, 33 | MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE 34 | WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE. 35 | 36 | MICROCHIP PROVIDES THIS SOFTWARE CONDITIONALLY UPON YOUR ACCEPTANCE OF THESE TERMS. 37 | *******************************************************************************/ 38 | //DOM-IGNORE-END 39 | 40 | #ifndef _DRV_CANFDSPI_DEFINES_H 41 | #define _DRV_CANFDSPI_DEFINES_H 42 | 43 | // ***************************************************************************** 44 | // ***************************************************************************** 45 | // Section: Included Files 46 | 47 | #include 48 | #include 49 | #include 50 | #include 51 | 52 | 53 | // DOM-IGNORE-BEGIN 54 | #ifdef __cplusplus // Provide C++ Compatibility 55 | extern "C" { 56 | #endif 57 | // DOM-IGNORE-END 58 | 59 | 60 | // ***************************************************************************** 61 | // ***************************************************************************** 62 | // Section: Implementation 63 | 64 | // Revision 65 | //#define REV_A 66 | #define REV_B 67 | 68 | // Select ISO/non-ISO CRC 69 | #define ISO_CRC 1 70 | 71 | // Before B0 address of filter registers was fixed 72 | #ifdef REV_A 73 | #define FIXED_FILTER_ADDRESS 74 | #endif 75 | 76 | // Number of implemented FIFOs 77 | #ifndef FPGA 78 | #define CAN_FIFO_08TO15_IMPLEMENTED 79 | #define CAN_FIFO_16TO31_IMPLEMENTED 80 | #endif 81 | 82 | // Number of implemented Filters 83 | #ifndef FPGA 84 | #define CAN_FILTER_08TO15_IMPLEMENTED 85 | #define CAN_FILTER_16TO31_IMPLEMENTED 86 | #endif 87 | 88 | // Internal oscillator implemented 89 | #ifdef MCP2520FD 90 | #define CAN_INTERNAL_OSC_PRESENT 91 | #endif 92 | 93 | // Restricted Operation Mode implemented 94 | #ifdef REV_B 95 | #define CAN_RESTRICTED_MODE_PRESENT 96 | #endif 97 | 98 | // Transmit Queue 99 | #ifdef REV_B 100 | #define CAN_TXQUEUE_IMPLEMENTED 101 | #endif 102 | 103 | // Up to A1 silicon we had to multiply user address by 4. 104 | #ifdef REV_A 105 | #define USERADDRESS_TIMES_FOUR 106 | #endif 107 | 108 | // Maximum Size of TX/RX Object 109 | #define MAX_MSG_SIZE 76 110 | 111 | // Maximum number of data bytes in message 112 | #define MAX_DATA_BYTES 64 113 | 114 | // ***************************************************************************** 115 | // ***************************************************************************** 116 | // Section: Object definitions 117 | 118 | //! CAN FIFO Channels 119 | 120 | typedef enum { 121 | CAN_FIFO_CH0, 122 | CAN_FIFO_CH1, 123 | CAN_FIFO_CH2, 124 | CAN_FIFO_CH3, 125 | CAN_FIFO_CH4, 126 | CAN_FIFO_CH5, 127 | CAN_FIFO_CH6, 128 | CAN_FIFO_CH7, 129 | #ifdef CAN_FIFO_08TO15_IMPLEMENTED 130 | CAN_FIFO_CH8, 131 | CAN_FIFO_CH9, 132 | CAN_FIFO_CH10, 133 | CAN_FIFO_CH11, 134 | CAN_FIFO_CH12, 135 | CAN_FIFO_CH13, 136 | CAN_FIFO_CH14, 137 | CAN_FIFO_CH15, 138 | #endif 139 | #ifdef CAN_FIFO_16TO31_IMPLEMENTED 140 | CAN_FIFO_CH16, 141 | CAN_FIFO_CH17, 142 | CAN_FIFO_CH18, 143 | CAN_FIFO_CH19, 144 | CAN_FIFO_CH20, 145 | CAN_FIFO_CH21, 146 | CAN_FIFO_CH22, 147 | CAN_FIFO_CH23, 148 | CAN_FIFO_CH24, 149 | CAN_FIFO_CH25, 150 | CAN_FIFO_CH26, 151 | CAN_FIFO_CH27, 152 | CAN_FIFO_CH28, 153 | CAN_FIFO_CH29, 154 | CAN_FIFO_CH30, 155 | CAN_FIFO_CH31, 156 | #endif 157 | CAN_FIFO_TOTAL_CHANNELS 158 | } CAN_FIFO_CHANNEL; 159 | 160 | #ifdef CAN_TXQUEUE_IMPLEMENTED 161 | #define CAN_FIFO_FIRST_CHANNEL CAN_FIFO_CH1 162 | #define CAN_TXQUEUE_CH0 CAN_FIFO_CH0 163 | #else 164 | #define CAN_FIFO_FIRST_CHANNEL CAN_FIFO_CH0 165 | #endif 166 | 167 | //! CAN Filter Channels 168 | 169 | typedef enum { 170 | CAN_FILTER0, 171 | CAN_FILTER1, 172 | CAN_FILTER2, 173 | CAN_FILTER3, 174 | CAN_FILTER4, 175 | CAN_FILTER5, 176 | CAN_FILTER6, 177 | CAN_FILTER7, 178 | #ifdef CAN_FILTER_08TO15_IMPLEMENTED 179 | CAN_FILTER8, 180 | CAN_FILTER9, 181 | CAN_FILTER10, 182 | CAN_FILTER11, 183 | CAN_FILTER12, 184 | CAN_FILTER13, 185 | CAN_FILTER14, 186 | CAN_FILTER15, 187 | #endif 188 | #ifdef CAN_FILTER_16TO31_IMPLEMENTED 189 | CAN_FILTER16, 190 | CAN_FILTER17, 191 | CAN_FILTER18, 192 | CAN_FILTER19, 193 | CAN_FILTER20, 194 | CAN_FILTER21, 195 | CAN_FILTER22, 196 | CAN_FILTER23, 197 | CAN_FILTER24, 198 | CAN_FILTER25, 199 | CAN_FILTER26, 200 | CAN_FILTER27, 201 | CAN_FILTER28, 202 | CAN_FILTER29, 203 | CAN_FILTER30, 204 | CAN_FILTER31, 205 | #endif 206 | CAN_FILTER_TOTAL, 207 | } CAN_FILTER; 208 | 209 | 210 | //! CAN Operation Modes 211 | 212 | typedef enum { 213 | CAN_NORMAL_MODE = 0x00, 214 | CAN_SLEEP_MODE = 0x01, 215 | CAN_INTERNAL_LOOPBACK_MODE = 0x02, 216 | CAN_LISTEN_ONLY_MODE = 0x03, 217 | CAN_CONFIGURATION_MODE = 0x04, 218 | CAN_EXTERNAL_LOOPBACK_MODE = 0x05, 219 | CAN_CLASSIC_MODE = 0x06, 220 | CAN_RESTRICTED_MODE = 0x07, 221 | CAN_INVALID_MODE = 0xFF 222 | } CAN_OPERATION_MODE; 223 | 224 | //! Transmit Bandwidth Sharing 225 | 226 | typedef enum { 227 | CAN_TXBWS_NO_DELAY, 228 | CAN_TXBWS_2, 229 | CAN_TXBWS_4, 230 | CAN_TXBWS_8, 231 | CAN_TXBWS_16, 232 | CAN_TXBWS_32, 233 | CAN_TXBWS_64, 234 | CAN_TXBWS_128, 235 | CAN_TXBWS_256, 236 | CAN_TXBWS_512, 237 | CAN_TXBWS_1024, 238 | CAN_TXBWS_2048, 239 | CAN_TXBWS_4096 240 | } CAN_TX_BANDWITH_SHARING; 241 | 242 | //! Wake-up Filter Time 243 | 244 | typedef enum { 245 | CAN_WFT00, 246 | CAN_WFT01, 247 | CAN_WFT10, 248 | CAN_WFT11 249 | } CAN_WAKEUP_FILTER_TIME; 250 | 251 | //! Data Byte Filter Number 252 | 253 | typedef enum { 254 | CAN_DNET_FILTER_DISABLE = 0, 255 | CAN_DNET_FILTER_SIZE_1_BIT, 256 | CAN_DNET_FILTER_SIZE_2_BIT, 257 | CAN_DNET_FILTER_SIZE_3_BIT, 258 | CAN_DNET_FILTER_SIZE_4_BIT, 259 | CAN_DNET_FILTER_SIZE_5_BIT, 260 | CAN_DNET_FILTER_SIZE_6_BIT, 261 | CAN_DNET_FILTER_SIZE_7_BIT, 262 | CAN_DNET_FILTER_SIZE_8_BIT, 263 | CAN_DNET_FILTER_SIZE_9_BIT, 264 | CAN_DNET_FILTER_SIZE_10_BIT, 265 | CAN_DNET_FILTER_SIZE_11_BIT, 266 | CAN_DNET_FILTER_SIZE_12_BIT, 267 | CAN_DNET_FILTER_SIZE_13_BIT, 268 | CAN_DNET_FILTER_SIZE_14_BIT, 269 | CAN_DNET_FILTER_SIZE_15_BIT, 270 | CAN_DNET_FILTER_SIZE_16_BIT, 271 | CAN_DNET_FILTER_SIZE_17_BIT, 272 | CAN_DNET_FILTER_SIZE_18_BIT 273 | } CAN_DNET_FILTER_SIZE; 274 | 275 | //! FIFO Payload Size 276 | 277 | typedef enum { 278 | CAN_PLSIZE_8, 279 | CAN_PLSIZE_12, 280 | CAN_PLSIZE_16, 281 | CAN_PLSIZE_20, 282 | CAN_PLSIZE_24, 283 | CAN_PLSIZE_32, 284 | CAN_PLSIZE_48, 285 | CAN_PLSIZE_64 286 | } CAN_FIFO_PLSIZE; 287 | 288 | //! CAN Configure 289 | 290 | typedef struct _CAN_CONFIG { 291 | uint32_t DNetFilterCount : 5; 292 | uint32_t IsoCrcEnable : 1; 293 | uint32_t ProtocolExpectionEventDisable : 1; 294 | uint32_t WakeUpFilterEnable : 1; 295 | uint32_t WakeUpFilterTime : 2; 296 | uint32_t BitRateSwitchDisable : 1; 297 | uint32_t RestrictReTxAttempts : 1; 298 | uint32_t EsiInGatewayMode : 1; 299 | uint32_t SystemErrorToListenOnly : 1; 300 | uint32_t StoreInTEF : 1; 301 | uint32_t TXQEnable : 1; 302 | uint32_t TxBandWidthSharing : 4; 303 | } CAN_CONFIG; 304 | 305 | //! CAN Transmit Channel Configure 306 | 307 | typedef struct _CAN_TX_FIFO_CONFIG { 308 | uint32_t RTREnable : 1; 309 | uint32_t TxPriority : 5; 310 | uint32_t TxAttempts : 2; 311 | uint32_t FifoSize : 5; 312 | uint32_t PayLoadSize : 3; 313 | } CAN_TX_FIFO_CONFIG; 314 | 315 | //! CAN Transmit Queue Configure 316 | 317 | typedef struct _CAN_TX_QUEUE_CONFIG { 318 | uint32_t TxPriority : 5; 319 | uint32_t TxAttempts : 2; 320 | uint32_t FifoSize : 5; 321 | uint32_t PayLoadSize : 3; 322 | } CAN_TX_QUEUE_CONFIG; 323 | 324 | //! CAN Receive Channel Configure 325 | 326 | typedef struct _CAN_RX_FIFO_CONFIG { 327 | uint32_t RxTimeStampEnable : 1; 328 | uint32_t FifoSize : 5; 329 | uint32_t PayLoadSize : 3; 330 | } CAN_RX_FIFO_CONFIG; 331 | 332 | //! CAN Transmit Event FIFO Configure 333 | 334 | typedef struct _CAN_TEF_CONFIG { 335 | uint32_t TimeStampEnable : 1; 336 | uint32_t FifoSize : 5; 337 | } CAN_TEF_CONFIG; 338 | 339 | /* CAN Message Objects */ 340 | 341 | //! CAN Message Object ID 342 | 343 | typedef struct _CAN_MSGOBJ_ID { 344 | uint32_t SID : 11; 345 | uint32_t EID : 18; 346 | uint32_t SID11 : 1; 347 | uint32_t unimplemented1 : 2; 348 | } CAN_MSGOBJ_ID; 349 | 350 | //! CAN Data Length Code 351 | 352 | typedef enum { 353 | CAN_DLC_0, 354 | CAN_DLC_1, 355 | CAN_DLC_2, 356 | CAN_DLC_3, 357 | CAN_DLC_4, 358 | CAN_DLC_5, 359 | CAN_DLC_6, 360 | CAN_DLC_7, 361 | CAN_DLC_8, 362 | CAN_DLC_12, 363 | CAN_DLC_16, 364 | CAN_DLC_20, 365 | CAN_DLC_24, 366 | CAN_DLC_32, 367 | CAN_DLC_48, 368 | CAN_DLC_64 369 | } CAN_DLC; 370 | 371 | //! CAN TX Message Object Control 372 | 373 | typedef struct _CAN_TX_MSGOBJ_CTRL { 374 | uint32_t DLC : 4; 375 | uint32_t IDE : 1; 376 | uint32_t RTR : 1; 377 | uint32_t BRS : 1; 378 | uint32_t FDF : 1; 379 | uint32_t ESI : 1; 380 | uint32_t SEQ : 7; 381 | uint32_t unimplemented1 : 16; 382 | } CAN_TX_MSGOBJ_CTRL; 383 | 384 | //! CAN RX Message Object Control 385 | 386 | typedef struct _CAN_RX_MSGOBJ_CTRL { 387 | uint32_t DLC : 4; 388 | uint32_t IDE : 1; 389 | uint32_t RTR : 1; 390 | uint32_t BRS : 1; 391 | uint32_t FDF : 1; 392 | uint32_t ESI : 1; 393 | uint32_t unimplemented1 : 2; 394 | uint32_t FilterHit : 5; 395 | uint32_t unimplemented2 : 16; 396 | } CAN_RX_MSGOBJ_CTRL; 397 | 398 | //! CAN Message Time Stamp 399 | typedef uint32_t CAN_MSG_TIMESTAMP; 400 | 401 | //! CAN TX Message Object 402 | 403 | typedef union _CAN_TX_MSGOBJ { 404 | 405 | struct { 406 | CAN_MSGOBJ_ID id; 407 | CAN_TX_MSGOBJ_CTRL ctrl; 408 | CAN_MSG_TIMESTAMP timeStamp; 409 | } bF; 410 | uint32_t word[3]; 411 | uint8_t byte[12]; 412 | } CAN_TX_MSGOBJ; 413 | 414 | //! CAN RX Message Object 415 | 416 | typedef union _CAN_RX_MSGOBJ { 417 | 418 | struct { 419 | CAN_MSGOBJ_ID id; 420 | CAN_RX_MSGOBJ_CTRL ctrl; 421 | CAN_MSG_TIMESTAMP timeStamp; 422 | } bF; 423 | uint32_t word[3]; 424 | uint8_t byte[12]; 425 | } CAN_RX_MSGOBJ; 426 | 427 | //! CAN TEF Message Object 428 | 429 | typedef union _CAN_TEF_MSGOBJ { 430 | 431 | struct { 432 | CAN_MSGOBJ_ID id; 433 | CAN_TX_MSGOBJ_CTRL ctrl; 434 | CAN_MSG_TIMESTAMP timeStamp; 435 | } bF; 436 | uint32_t word[3]; 437 | uint8_t byte[12]; 438 | } CAN_TEF_MSGOBJ; 439 | 440 | //! CAN Filter Object ID 441 | 442 | typedef struct _CAN_FILTEROBJ_ID { 443 | uint32_t SID : 11; 444 | uint32_t EID : 18; 445 | uint32_t SID11 : 1; 446 | uint32_t EXIDE : 1; 447 | uint32_t unimplemented1 : 1; 448 | } CAN_FILTEROBJ_ID; 449 | 450 | //! CAN Mask Object ID 451 | 452 | typedef struct _CAN_MASKOBJ_ID { 453 | uint32_t MSID : 11; 454 | uint32_t MEID : 18; 455 | uint32_t MSID11 : 1; 456 | uint32_t MIDE : 1; 457 | uint32_t unimplemented1 : 1; 458 | } CAN_MASKOBJ_ID; 459 | 460 | //! CAN RX FIFO Status 461 | 462 | typedef enum { 463 | CAN_RX_FIFO_EMPTY = 0, 464 | CAN_RX_FIFO_STATUS_MASK = 0x0F, 465 | CAN_RX_FIFO_NOT_EMPTY = 0x01, 466 | CAN_RX_FIFO_HALF_FULL = 0x02, 467 | CAN_RX_FIFO_FULL = 0x04, 468 | CAN_RX_FIFO_OVERFLOW = 0x08 469 | } CAN_RX_FIFO_STATUS; 470 | 471 | //! CAN TX FIFO Status 472 | 473 | typedef enum { 474 | CAN_TX_FIFO_FULL = 0, 475 | CAN_TX_FIFO_STATUS_MASK = 0x1F7, 476 | CAN_TX_FIFO_NOT_FULL = 0x01, 477 | CAN_TX_FIFO_HALF_FULL = 0x02, 478 | CAN_TX_FIFO_EMPTY = 0x04, 479 | CAN_TX_FIFO_ATTEMPTS_EXHAUSTED = 0x10, 480 | CAN_TX_FIFO_ERROR = 0x20, 481 | CAN_TX_FIFO_ARBITRATION_LOST = 0x40, 482 | CAN_TX_FIFO_ABORTED = 0x80, 483 | CAN_TX_FIFO_TRANSMITTING = 0x100 484 | } CAN_TX_FIFO_STATUS; 485 | 486 | //! CAN TEF FIFO Status 487 | 488 | typedef enum { 489 | CAN_TEF_FIFO_EMPTY = 0, 490 | CAN_TEF_FIFO_STATUS_MASK = 0x0F, 491 | CAN_TEF_FIFO_NOT_EMPTY = 0x01, 492 | CAN_TEF_FIFO_HALF_FULL = 0x02, 493 | CAN_TEF_FIFO_FULL = 0x04, 494 | CAN_TEF_FIFO_OVERFLOW = 0x08 495 | } CAN_TEF_FIFO_STATUS; 496 | 497 | //! CAN Module Event (Interrupts) 498 | 499 | typedef enum { 500 | CAN_NO_EVENT = 0, 501 | CAN_ALL_EVENTS = 0xFF1F, 502 | CAN_TX_EVENT = 0x0001, 503 | CAN_RX_EVENT = 0x0002, 504 | CAN_TIME_BASE_COUNTER_EVENT = 0x0004, 505 | CAN_OPERATION_MODE_CHANGE_EVENT = 0x0008, 506 | CAN_TEF_EVENT = 0x0010, 507 | 508 | CAN_RAM_ECC_EVENT = 0x0100, 509 | CAN_SPI_CRC_EVENT = 0x0200, 510 | CAN_TX_ATTEMPTS_EVENT = 0x0400, 511 | CAN_RX_OVERFLOW_EVENT = 0x0800, 512 | CAN_SYSTEM_ERROR_EVENT = 0x1000, 513 | CAN_BUS_ERROR_EVENT = 0x2000, 514 | CAN_BUS_WAKEUP_EVENT = 0x4000, 515 | CAN_RX_INVALID_MESSAGE_EVENT = 0x8000 516 | } CAN_MODULE_EVENT; 517 | 518 | //! CAN TX FIFO Event (Interrupts) 519 | 520 | typedef enum { 521 | CAN_TX_FIFO_NO_EVENT = 0, 522 | CAN_TX_FIFO_ALL_EVENTS = 0x17, 523 | CAN_TX_FIFO_NOT_FULL_EVENT = 0x01, 524 | CAN_TX_FIFO_HALF_FULL_EVENT = 0x02, 525 | CAN_TX_FIFO_EMPTY_EVENT = 0x04, 526 | CAN_TX_FIFO_ATTEMPTS_EXHAUSTED_EVENT = 0x10 527 | } CAN_TX_FIFO_EVENT; 528 | 529 | //! CAN RX FIFO Event (Interrupts) 530 | 531 | typedef enum { 532 | CAN_RX_FIFO_NO_EVENT = 0, 533 | CAN_RX_FIFO_ALL_EVENTS = 0x0F, 534 | CAN_RX_FIFO_NOT_EMPTY_EVENT = 0x01, 535 | CAN_RX_FIFO_HALF_FULL_EVENT = 0x02, 536 | CAN_RX_FIFO_FULL_EVENT = 0x04, 537 | CAN_RX_FIFO_OVERFLOW_EVENT = 0x08 538 | } CAN_RX_FIFO_EVENT; 539 | 540 | //! CAN TEF FIFO Event (Interrupts) 541 | 542 | typedef enum { 543 | CAN_TEF_FIFO_NO_EVENT = 0, 544 | CAN_TEF_FIFO_ALL_EVENTS = 0x0F, 545 | CAN_TEF_FIFO_NOT_EMPTY_EVENT = 0x01, 546 | CAN_TEF_FIFO_HALF_FULL_EVENT = 0x02, 547 | CAN_TEF_FIFO_FULL_EVENT = 0x04, 548 | CAN_TEF_FIFO_OVERFLOW_EVENT = 0x08 549 | } CAN_TEF_FIFO_EVENT; 550 | 551 | 552 | //! Secondary Sample Point Mode 553 | typedef enum { 554 | CAN_SSP_MODE_OFF, 555 | CAN_SSP_MODE_MANUAL, 556 | CAN_SSP_MODE_AUTO 557 | } CAN_SSP_MODE; 558 | 559 | //! CAN Error State 560 | 561 | typedef enum { 562 | CAN_ERROR_FREE_STATE = 0, 563 | CAN_ERROR_ALL = 0x3F, 564 | CAN_TX_RX_WARNING_STATE = 0x01, 565 | CAN_RX_WARNING_STATE = 0x02, 566 | CAN_TX_WARNING_STATE = 0x04, 567 | CAN_RX_BUS_PASSIVE_STATE = 0x08, 568 | CAN_TX_BUS_PASSIVE_STATE = 0x10, 569 | CAN_TX_BUS_OFF_STATE = 0x20 570 | } CAN_ERROR_STATE; 571 | 572 | //! CAN Time Stamp Mode Select 573 | 574 | typedef enum { 575 | CAN_TS_SOF = 0x00, 576 | CAN_TS_EOF = 0x01, 577 | CAN_TS_RES = 0x02 578 | } CAN_TS_MODE; 579 | 580 | //! CAN ECC EVENT 581 | 582 | typedef enum { 583 | CAN_ECC_NO_EVENT = 0x00, 584 | CAN_ECC_ALL_EVENTS = 0x06, 585 | CAN_ECC_SEC_EVENT = 0x02, 586 | CAN_ECC_DED_EVENT = 0x04 587 | } CAN_ECC_EVENT; 588 | 589 | //! CAN CRC EVENT 590 | 591 | typedef enum { 592 | CAN_CRC_NO_EVENT = 0x00, 593 | CAN_CRC_ALL_EVENTS = 0x03, 594 | CAN_CRC_CRCERR_EVENT = 0x01, 595 | CAN_CRC_FORMERR_EVENT = 0x02 596 | } CAN_CRC_EVENT; 597 | 598 | //! GPIO Pin Position 599 | 600 | typedef enum { 601 | GPIO_PIN_0, 602 | GPIO_PIN_1 603 | } GPIO_PIN_POS; 604 | 605 | //! GPIO Pin Modes 606 | 607 | typedef enum { 608 | GPIO_MODE_INT, 609 | GPIO_MODE_GPIO 610 | } GPIO_PIN_MODE; 611 | 612 | //! GPIO Pin Directions 613 | 614 | typedef enum { 615 | GPIO_OUTPUT, 616 | GPIO_INPUT 617 | } GPIO_PIN_DIRECTION; 618 | 619 | //! GPIO Open Drain Mode 620 | 621 | typedef enum { 622 | GPIO_PUSH_PULL, 623 | GPIO_OPEN_DRAIN 624 | } GPIO_OPEN_DRAIN_MODE; 625 | 626 | //! GPIO Pin State 627 | 628 | typedef enum { 629 | GPIO_LOW, 630 | GPIO_HIGH 631 | } GPIO_PIN_STATE; 632 | 633 | //! Clock Output Mode 634 | 635 | typedef enum { 636 | GPIO_CLKO_CLOCK, 637 | GPIO_CLKO_SOF 638 | } GPIO_CLKO_MODE; 639 | 640 | //! CAN Bus Diagnostic flags 641 | 642 | typedef struct _CAN_BUS_DIAG_FLAGS { 643 | uint32_t NBIT0_ERR : 1; 644 | uint32_t NBIT1_ERR : 1; 645 | uint32_t NACK_ERR : 1; 646 | uint32_t NFORM_ERR : 1; 647 | uint32_t NSTUFF_ERR : 1; 648 | uint32_t NCRC_ERR : 1; 649 | uint32_t unimplemented1 : 1; 650 | uint32_t TXBO_ERR : 1; 651 | uint32_t DBIT0_ERR : 1; 652 | uint32_t DBIT1_ERR : 1; 653 | uint32_t unimplemented2 : 1; 654 | uint32_t DFORM_ERR : 1; 655 | uint32_t DSTUFF_ERR : 1; 656 | uint32_t DCRC_ERR : 1; 657 | uint32_t ESI : 1; 658 | uint32_t DLC_MISMATCH : 1; 659 | } CAN_BUS_DIAG_FLAGS; 660 | 661 | //! CAN Bus Diagnostic Error Counts 662 | 663 | typedef struct _CAN_BUS_ERROR_COUNT { 664 | uint8_t NREC; 665 | uint8_t NTEC; 666 | uint8_t DREC; 667 | uint8_t DTEC; 668 | } CAN_BUS_ERROR_COUNT; 669 | 670 | //! CAN BUS DIAGNOSTICS 671 | 672 | typedef union _CAN_BUS_DIAGNOSTIC { 673 | 674 | struct { 675 | CAN_BUS_ERROR_COUNT errorCount; 676 | uint16_t errorFreeMsgCount; 677 | CAN_BUS_DIAG_FLAGS flag; 678 | } bF; 679 | uint32_t word[2]; 680 | uint8_t byte[8]; 681 | } CAN_BUS_DIAGNOSTIC; 682 | 683 | //! TXREQ Channel Bits 684 | // Multiple channels can be or'ed together 685 | 686 | typedef enum { 687 | CAN_TXREQ_CH0 = 0x00000001, 688 | CAN_TXREQ_CH1 = 0x00000002, 689 | CAN_TXREQ_CH2 = 0x00000004, 690 | CAN_TXREQ_CH3 = 0x00000008, 691 | CAN_TXREQ_CH4 = 0x00000010, 692 | CAN_TXREQ_CH5 = 0x00000020, 693 | CAN_TXREQ_CH6 = 0x00000040, 694 | CAN_TXREQ_CH7 = 0x00000080, 695 | 696 | CAN_TXREQ_CH8 = 0x00000100, 697 | CAN_TXREQ_CH9 = 0x00000200, 698 | CAN_TXREQ_CH10 = 0x00000400, 699 | CAN_TXREQ_CH11 = 0x00000800, 700 | CAN_TXREQ_CH12 = 0x00001000, 701 | CAN_TXREQ_CH13 = 0x00002000, 702 | CAN_TXREQ_CH14 = 0x00004000, 703 | CAN_TXREQ_CH15 = 0x00008000, 704 | 705 | CAN_TXREQ_CH16 = 0x00010000, 706 | CAN_TXREQ_CH17 = 0x00020000, 707 | CAN_TXREQ_CH18 = 0x00040000, 708 | CAN_TXREQ_CH19 = 0x00080000, 709 | CAN_TXREQ_CH20 = 0x00100000, 710 | CAN_TXREQ_CH21 = 0x00200000, 711 | CAN_TXREQ_CH22 = 0x00400000, 712 | CAN_TXREQ_CH23 = 0x00800000, 713 | 714 | CAN_TXREQ_CH24 = 0x01000000, 715 | CAN_TXREQ_CH25 = 0x02000000, 716 | CAN_TXREQ_CH26 = 0x04000000, 717 | CAN_TXREQ_CH27 = 0x08000000, 718 | CAN_TXREQ_CH28 = 0x10000000, 719 | CAN_TXREQ_CH29 = 0x20000000, 720 | CAN_TXREQ_CH30 = 0x40000000, 721 | CAN_TXREQ_CH31 = 0x80000000 722 | } CAN_TXREQ_CHANNEL; 723 | 724 | //! Oscillator Control 725 | 726 | typedef struct _CAN_OSC_CTRL { 727 | uint32_t PllEnable : 1; 728 | uint32_t OscDisable : 1; 729 | uint32_t SclkDivide : 1; 730 | uint32_t ClkOutDivide : 2; 731 | } CAN_OSC_CTRL; 732 | 733 | //! Oscillator Status 734 | 735 | typedef struct _CAN_OSC_STATUS { 736 | uint32_t PllReady : 1; 737 | uint32_t OscReady : 1; 738 | uint32_t SclkReady : 1; 739 | } CAN_OSC_STATUS; 740 | 741 | //! ICODE 742 | 743 | typedef enum { 744 | CAN_ICODE_FIFO_CH0, 745 | CAN_ICODE_FIFO_CH1, 746 | CAN_ICODE_FIFO_CH2, 747 | CAN_ICODE_FIFO_CH3, 748 | CAN_ICODE_FIFO_CH4, 749 | CAN_ICODE_FIFO_CH5, 750 | CAN_ICODE_FIFO_CH6, 751 | CAN_ICODE_FIFO_CH7, 752 | #ifdef CAN_FIFO_08TO15_IMPLEMENTED 753 | CAN_ICODE_FIFO_CH8, 754 | CAN_ICODE_FIFO_CH9, 755 | CAN_ICODE_FIFO_CH10, 756 | CAN_ICODE_FIFO_CH11, 757 | CAN_ICODE_FIFO_CH12, 758 | CAN_ICODE_FIFO_CH13, 759 | CAN_ICODE_FIFO_CH14, 760 | CAN_ICODE_FIFO_CH15, 761 | #endif 762 | #ifdef CAN_FIFO_16TO31_IMPLEMENTED 763 | CAN_ICODE_FIFO_CH16, 764 | CAN_ICODE_FIFO_CH17, 765 | CAN_ICODE_FIFO_CH18, 766 | CAN_ICODE_FIFO_CH19, 767 | CAN_ICODE_FIFO_CH20, 768 | CAN_ICODE_FIFO_CH21, 769 | CAN_ICODE_FIFO_CH22, 770 | CAN_ICODE_FIFO_CH23, 771 | CAN_ICODE_FIFO_CH24, 772 | CAN_ICODE_FIFO_CH25, 773 | CAN_ICODE_FIFO_CH26, 774 | CAN_ICODE_FIFO_CH27, 775 | CAN_ICODE_FIFO_CH28, 776 | CAN_ICODE_FIFO_CH29, 777 | CAN_ICODE_FIFO_CH30, 778 | CAN_ICODE_FIFO_CH31, 779 | #endif 780 | CAN_ICODE_TOTAL_CHANNELS, 781 | CAN_ICODE_NO_INT = 0x40, 782 | CAN_ICODE_CERRIF, 783 | CAN_ICODE_WAKIF, 784 | CAN_ICODE_RXOVIF, 785 | CAN_ICODE_ADDRERR_SERRIF, 786 | CAN_ICODE_MABOV_SERRIF, 787 | CAN_ICODE_TBCIF, 788 | CAN_ICODE_MODIF, 789 | CAN_ICODE_IVMIF, 790 | CAN_ICODE_TEFIF, 791 | CAN_ICODE_TXATIF, 792 | CAN_ICODE_RESERVED 793 | } CAN_ICODE; 794 | 795 | //! RXCODE 796 | 797 | typedef enum { 798 | CAN_RXCODE_FIFO_CH0, 799 | CAN_RXCODE_FIFO_CH1, 800 | CAN_RXCODE_FIFO_CH2, 801 | CAN_RXCODE_FIFO_CH3, 802 | CAN_RXCODE_FIFO_CH4, 803 | CAN_RXCODE_FIFO_CH5, 804 | CAN_RXCODE_FIFO_CH6, 805 | CAN_RXCODE_FIFO_CH7, 806 | #ifdef CAN_FIFO_08TO15_IMPLEMENTED 807 | CAN_RXCODE_FIFO_CH8, 808 | CAN_RXCODE_FIFO_CH9, 809 | CAN_RXCODE_FIFO_CH10, 810 | CAN_RXCODE_FIFO_CH11, 811 | CAN_RXCODE_FIFO_CH12, 812 | CAN_RXCODE_FIFO_CH13, 813 | CAN_RXCODE_FIFO_CH14, 814 | CAN_RXCODE_FIFO_CH15, 815 | #endif 816 | #ifdef CAN_FIFO_16TO31_IMPLEMENTED 817 | CAN_RXCODE_FIFO_CH16, 818 | CAN_RXCODE_FIFO_CH17, 819 | CAN_RXCODE_FIFO_CH18, 820 | CAN_RXCODE_FIFO_CH19, 821 | CAN_RXCODE_FIFO_CH20, 822 | CAN_RXCODE_FIFO_CH21, 823 | CAN_RXCODE_FIFO_CH22, 824 | CAN_RXCODE_FIFO_CH23, 825 | CAN_RXCODE_FIFO_CH24, 826 | CAN_RXCODE_FIFO_CH25, 827 | CAN_RXCODE_FIFO_CH26, 828 | CAN_RXCODE_FIFO_CH27, 829 | CAN_RXCODE_FIFO_CH28, 830 | CAN_RXCODE_FIFO_CH29, 831 | CAN_RXCODE_FIFO_CH30, 832 | CAN_RXCODE_FIFO_CH31, 833 | #endif 834 | CAN_RXCODE_TOTAL_CHANNELS, 835 | CAN_RXCODE_NO_INT = 0x40, 836 | CAN_RXCODE_RESERVED 837 | } CAN_RXCODE; 838 | 839 | //! TXCODE 840 | 841 | typedef enum { 842 | CAN_TXCODE_FIFO_CH0, 843 | CAN_TXCODE_FIFO_CH1, 844 | CAN_TXCODE_FIFO_CH2, 845 | CAN_TXCODE_FIFO_CH3, 846 | CAN_TXCODE_FIFO_CH4, 847 | CAN_TXCODE_FIFO_CH5, 848 | CAN_TXCODE_FIFO_CH6, 849 | CAN_TXCODE_FIFO_CH7, 850 | #ifdef CAN_FIFO_08TO15_IMPLEMENTED 851 | CAN_TXCODE_FIFO_CH8, 852 | CAN_TXCODE_FIFO_CH9, 853 | CAN_TXCODE_FIFO_CH10, 854 | CAN_TXCODE_FIFO_CH11, 855 | CAN_TXCODE_FIFO_CH12, 856 | CAN_TXCODE_FIFO_CH13, 857 | CAN_TXCODE_FIFO_CH14, 858 | CAN_TXCODE_FIFO_CH15, 859 | #endif 860 | #ifdef CAN_FIFO_16TO31_IMPLEMENTED 861 | CAN_TXCODE_FIFO_CH16, 862 | CAN_TXCODE_FIFO_CH17, 863 | CAN_TXCODE_FIFO_CH18, 864 | CAN_TXCODE_FIFO_CH19, 865 | CAN_TXCODE_FIFO_CH20, 866 | CAN_TXCODE_FIFO_CH21, 867 | CAN_TXCODE_FIFO_CH22, 868 | CAN_TXCODE_FIFO_CH23, 869 | CAN_TXCODE_FIFO_CH24, 870 | CAN_TXCODE_FIFO_CH25, 871 | CAN_TXCODE_FIFO_CH26, 872 | CAN_TXCODE_FIFO_CH27, 873 | CAN_TXCODE_FIFO_CH28, 874 | CAN_TXCODE_FIFO_CH29, 875 | CAN_TXCODE_FIFO_CH30, 876 | CAN_TXCODE_FIFO_CH31, 877 | #endif 878 | CAN_TXCODE_TOTAL_CHANNELS, 879 | CAN_TXCODE_NO_INT = 0x40, 880 | CAN_TXCODE_RESERVED 881 | } CAN_TXCODE; 882 | 883 | #ifdef __cplusplus 884 | } 885 | #endif 886 | #endif // _DRV_CANFDSPI_DEFINES_H 887 | -------------------------------------------------------------------------------- /src/mcp2517fd_regs.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | CAN FD SPI Driver: Register Header File 3 | 4 | Company: 5 | Microchip Technology Inc. 6 | 7 | File Name: 8 | drv_canfdspi_register.h 9 | 10 | Summary: 11 | This header file contains SPI instruction defines, register address defines, 12 | register structures, and reset values of registers. 13 | 14 | Description: 15 | This file is used by the API. 16 | *******************************************************************************/ 17 | 18 | //DOM-IGNORE-BEGIN 19 | /******************************************************************************* 20 | Copyright (c) 2016 Microchip Technology Inc. and its subsidiaries. 21 | You may use this software and any derivatives exclusively with Microchip products. 22 | 23 | THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". 24 | NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, 25 | INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, 26 | AND FITNESS FOR A PARTICULAR PURPOSE, OR ITS INTERACTION WITH MICROCHIP PRODUCTS, 27 | COMBINATION WITH ANY OTHER PRODUCTS, OR USE IN ANY APPLICATION. 28 | 29 | IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, 30 | INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER 31 | RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED 32 | OF THE POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, 33 | MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE 34 | WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE. 35 | 36 | MICROCHIP PROVIDES THIS SOFTWARE CONDITIONALLY UPON YOUR ACCEPTANCE OF THESE TERMS. 37 | *******************************************************************************/ 38 | //DOM-IGNORE-END 39 | 40 | #ifndef _DRV_CANFDSPI_REGISTER_H 41 | #define _DRV_CANFDSPI_REGISTER_H 42 | 43 | // ***************************************************************************** 44 | // ***************************************************************************** 45 | // Section: Included Files 46 | 47 | // DOM-IGNORE-BEGIN 48 | #ifdef __cplusplus // Provide C++ Compatibility 49 | extern "C" { 50 | #endif 51 | // DOM-IGNORE-END 52 | 53 | 54 | // ***************************************************************************** 55 | // ***************************************************************************** 56 | /* SPI Instruction Set */ 57 | #define CMD_RESET 0x00 58 | #define CMD_READ 0x03 59 | #define CMD_READ_CRC 0x0B 60 | #define CMD_WRITE 0x02 61 | #define CMD_WRITE_CRC 0x0A 62 | #define CMD_WRITE_SAFE 0x0C 63 | 64 | // ***************************************************************************** 65 | // ***************************************************************************** 66 | /* Register Addresses */ 67 | 68 | /* can_fd_ubp */ 69 | #define ADDR_CiCON 0x000 70 | #define ADDR_CiNBTCFG 0x004 71 | #define ADDR_CiDBTCFG 0x008 72 | #define ADDR_CiTDC 0x00C 73 | 74 | #define ADDR_CiTBC 0x010 75 | #define ADDR_CiTSCON 0x014 76 | #define ADDR_CiVEC 0x018 77 | #define ADDR_CiINT 0x01C 78 | #define ADDR_CiINTFLAG ADDR_CiINT 79 | #define ADDR_CiINTENABLE (ADDR_CiINT+2) 80 | 81 | #define ADDR_CiRXIF 0x020 82 | #define ADDR_CiTXIF 0x024 83 | #define ADDR_CiRXOVIF 0x028 84 | #define ADDR_CiTXATIF 0x02C 85 | 86 | #define ADDR_CiTXREQ 0x030 87 | #define ADDR_CiTREC 0x034 88 | #define ADDR_CiBDIAG0 0x038 89 | #define ADDR_CiBDIAG1 0x03C 90 | 91 | #define ADDR_CiTEFCON 0x040 92 | #define ADDR_CiTEFSTA 0x044 93 | #define ADDR_CiTEFUA 0x048 94 | #define ADDR_CiFIFOBA 0x04C 95 | 96 | #define ADDR_CiFIFOCON 0x050 97 | #define ADDR_CiFIFOSTA 0x054 98 | #define ADDR_CiFIFOUA 0x058 99 | #define CiFIFO_OFFSET (3*4) 100 | 101 | #define ADDR_CiTXQCON 0x050 102 | #define ADDR_CiTXQSTA 0x054 103 | #define ADDR_CiTXQUA 0x058 104 | 105 | #ifdef FIXED_FILTER_ADDRESS 106 | // Up to A1, the filter start address was fixed 107 | #define ADDR_CiFLTCON 0x1D0 108 | #define ADDR_CiFLTOBJ 0x1F0 109 | #define ADDR_CiMASK 0x1F4 110 | #else 111 | // Starting with B0, the filters start right after the FIFO control/status registers 112 | #define ADDR_CiFLTCON (ADDR_CiFIFOCON+(CiFIFO_OFFSET*CAN_FIFO_TOTAL_CHANNELS)) 113 | #define ADDR_CiFLTOBJ (ADDR_CiFLTCON + CAN_FIFO_TOTAL_CHANNELS) 114 | #define ADDR_CiMASK (ADDR_CiFLTOBJ+4) 115 | #endif 116 | 117 | #define CiFILTER_OFFSET (2*4) 118 | 119 | /* MCP2517 Specific */ 120 | #define ADDR_OSC 0xE00 121 | #define ADDR_IOCON 0xE04 122 | #define ADDR_CRC 0xE08 123 | #define ADDR_ECCCON 0xE0C 124 | #define ADDR_ECCSTA 0xE10 125 | 126 | /* RAM addresses */ 127 | #define cRAM_SIZE 2048 128 | #define cRAMADDR_START 0x400 129 | #define cRAMADDR_END (cRAMADDR_START+cRAM_SIZE) 130 | 131 | // ***************************************************************************** 132 | // ***************************************************************************** 133 | /* Register Structures */ 134 | 135 | // ***************************************************************************** 136 | //! General 32-bit Register 137 | 138 | typedef union _REG_t { 139 | uint8_t byte[4]; 140 | uint32_t word; 141 | } REG_t; 142 | 143 | 144 | // ***************************************************************************** 145 | // ***************************************************************************** 146 | /* can_fd_ubp */ 147 | 148 | // ***************************************************************************** 149 | //! CAN Control Register 150 | 151 | typedef union _REG_CiCON { 152 | 153 | struct { 154 | uint32_t DNetFilterCount : 5; 155 | uint32_t IsoCrcEnable : 1; 156 | uint32_t ProtocolExceptionEventDisable : 1; 157 | uint32_t unimplemented1 : 1; 158 | uint32_t WakeUpFilterEnable : 1; 159 | uint32_t WakeUpFilterTime : 2; 160 | uint32_t unimplemented2 : 1; 161 | uint32_t BitRateSwitchDisable : 1; 162 | uint32_t unimplemented3 : 3; 163 | uint32_t RestrictReTxAttempts : 1; 164 | uint32_t EsiInGatewayMode : 1; 165 | uint32_t SystemErrorToListenOnly : 1; 166 | uint32_t StoreInTEF : 1; 167 | uint32_t TXQEnable : 1; 168 | uint32_t OpMode : 3; 169 | uint32_t RequestOpMode : 3; 170 | uint32_t AbortAllTx : 1; 171 | uint32_t TxBandWidthSharing : 4; 172 | } bF; 173 | uint32_t word; 174 | uint8_t byte[4]; 175 | } REG_CiCON; 176 | 177 | // ***************************************************************************** 178 | //! Nominal Bit Time Configuration Register 179 | 180 | typedef union _REG_CiNBTCFG { 181 | 182 | struct { 183 | uint32_t SJW : 7; 184 | uint32_t unimplemented1 : 1; 185 | uint32_t TSEG2 : 7; 186 | uint32_t unimplemented2 : 1; 187 | uint32_t TSEG1 : 8; 188 | uint32_t BRP : 8; 189 | } bF; 190 | uint32_t word; 191 | uint8_t byte[4]; 192 | } REG_CiNBTCFG; 193 | 194 | // ***************************************************************************** 195 | //! Data Bit Time Configuration Register 196 | 197 | typedef union _REG_CiDBTCFG { 198 | 199 | struct { 200 | uint32_t SJW : 4; 201 | uint32_t unimplemented1 : 4; 202 | uint32_t TSEG2 : 4; 203 | uint32_t unimplemented2 : 4; 204 | uint32_t TSEG1 : 5; 205 | uint32_t unimplemented3 : 3; 206 | uint32_t BRP : 8; 207 | } bF; 208 | uint32_t word; 209 | uint8_t byte[4]; 210 | } REG_CiDBTCFG; 211 | 212 | // ***************************************************************************** 213 | //! Transmitter Delay Compensation Register 214 | 215 | typedef union _REG_CiTDC { 216 | 217 | struct { 218 | uint32_t TDCValue : 6; 219 | uint32_t unimplemented1 : 2; 220 | uint32_t TDCOffset : 7; 221 | uint32_t unimplemented2 : 1; 222 | uint32_t TDCMode : 2; 223 | uint32_t unimplemented3 : 6; 224 | uint32_t SID11Enable : 1; 225 | uint32_t EdgeFilterEnable : 1; 226 | uint32_t unimplemented4 : 6; 227 | } bF; 228 | uint32_t word; 229 | uint8_t byte[4]; 230 | } REG_CiTDC; 231 | 232 | // ***************************************************************************** 233 | //! Time Stamp Configuration Register 234 | 235 | typedef union _REG_CiTSCON { 236 | 237 | struct { 238 | uint32_t TBCPrescaler : 10; 239 | uint32_t unimplemented1 : 6; 240 | uint32_t TBCEnable : 1; 241 | uint32_t TimeStampEOF : 1; 242 | uint32_t unimplemented2 : 14; 243 | } bF; 244 | uint32_t word; 245 | uint8_t byte[4]; 246 | } REG_CiTSCON; 247 | 248 | // ***************************************************************************** 249 | //! Interrupt Vector Register 250 | 251 | typedef union _REG_CiVEC { 252 | 253 | struct { 254 | uint32_t ICODE : 7; 255 | uint32_t unimplemented1 : 1; 256 | uint32_t FilterHit : 5; 257 | uint32_t unimplemented2 : 3; 258 | uint32_t TXCODE : 7; 259 | uint32_t unimplemented3 : 1; 260 | uint32_t RXCODE : 7; 261 | uint32_t unimplemented4 : 1; 262 | } bF; 263 | uint32_t word; 264 | uint8_t byte[4]; 265 | } REG_CiVEC; 266 | 267 | // ***************************************************************************** 268 | //! Interrupt Flags 269 | 270 | typedef struct _CAN_INT_FLAGS { 271 | uint32_t TXIF : 1; 272 | uint32_t RXIF : 1; 273 | uint32_t TBCIF : 1; 274 | uint32_t MODIF : 1; 275 | uint32_t TEFIF : 1; 276 | uint32_t unimplemented1 : 3; 277 | 278 | uint32_t ECCIF : 1; 279 | uint32_t SPICRCIF : 1; 280 | uint32_t TXATIF : 1; 281 | uint32_t RXOVIF : 1; 282 | uint32_t SERRIF : 1; 283 | uint32_t CERRIF : 1; 284 | uint32_t WAKIF : 1; 285 | uint32_t IVMIF : 1; 286 | } CAN_INT_FLAGS; 287 | 288 | // ***************************************************************************** 289 | //! Interrupt Enables 290 | 291 | typedef struct _CAN_INT_ENABLES { 292 | uint32_t TXIE : 1; 293 | uint32_t RXIE : 1; 294 | uint32_t TBCIE : 1; 295 | uint32_t MODIE : 1; 296 | uint32_t TEFIE : 1; 297 | uint32_t unimplemented2 : 3; 298 | 299 | uint32_t ECCIE : 1; 300 | uint32_t SPICRCIE : 1; 301 | uint32_t TXATIE : 1; 302 | uint32_t RXOVIE : 1; 303 | uint32_t SERRIE : 1; 304 | uint32_t CERRIE : 1; 305 | uint32_t WAKIE : 1; 306 | uint32_t IVMIE : 1; 307 | } CAN_INT_ENABLES; 308 | 309 | // ***************************************************************************** 310 | //! Interrupt Register 311 | 312 | typedef union _REG_CiINT { 313 | 314 | struct { 315 | CAN_INT_FLAGS IF; 316 | CAN_INT_ENABLES IE; 317 | } bF; 318 | uint32_t word; 319 | uint8_t byte[4]; 320 | } REG_CiINT; 321 | 322 | // ***************************************************************************** 323 | //! Interrupt Flag Register 324 | 325 | typedef union _REG_CiINTFLAG { 326 | CAN_INT_FLAGS IF; 327 | uint16_t word; 328 | uint8_t byte[2]; 329 | } REG_CiINTFLAG; 330 | 331 | // ***************************************************************************** 332 | //! Interrupt Enable Register 333 | 334 | typedef union _REG_CiINTENABLE { 335 | CAN_INT_ENABLES IE; 336 | uint16_t word; 337 | uint8_t byte[2]; 338 | } REG_CiINTENABLE; 339 | 340 | // ***************************************************************************** 341 | //! Transmit/Receive Error Count Register 342 | 343 | typedef union _REG_CiTREC { 344 | 345 | struct { 346 | uint32_t RxErrorCount : 8; 347 | uint32_t TxErrorCount : 8; 348 | uint32_t ErrorStateWarning : 1; 349 | uint32_t RxErrorStateWarning : 1; 350 | uint32_t TxErrorStateWarning : 1; 351 | uint32_t RxErrorStatePassive : 1; 352 | uint32_t TxErrorStatePassive : 1; 353 | uint32_t TxErrorStateBusOff : 1; 354 | uint32_t unimplemented1 : 10; 355 | } bF; 356 | uint32_t word; 357 | uint8_t byte[4]; 358 | } REG_CiTREC; 359 | 360 | // ***************************************************************************** 361 | //! Diagnostic Register 0 362 | 363 | typedef union _REG_CiBDIAG0 { 364 | 365 | struct { 366 | uint32_t NRxErrorCount : 8; 367 | uint32_t NTxErrorCount : 8; 368 | uint32_t DRxErrorCount : 8; 369 | uint32_t DTxErrorCount : 8; 370 | } bF; 371 | uint32_t word; 372 | uint8_t byte[4]; 373 | } REG_CiBDIAG0; 374 | 375 | // ***************************************************************************** 376 | //! Diagnostic Register 1 377 | 378 | typedef union _REG_CiBDIAG1 { 379 | 380 | struct { 381 | uint32_t ErrorFreeMsgCount : 16; 382 | 383 | uint32_t NBit0Error : 1; 384 | uint32_t NBit1Error : 1; 385 | uint32_t NAckError : 1; 386 | uint32_t NFormError : 1; 387 | uint32_t NStuffError : 1; 388 | uint32_t NCRCError : 1; 389 | uint32_t unimplemented1 : 1; 390 | uint32_t TXBOError : 1; 391 | uint32_t DBit0Error : 1; 392 | uint32_t DBit1Error : 1; 393 | uint32_t DAckError : 1; 394 | uint32_t DFormError : 1; 395 | uint32_t DStuffError : 1; 396 | uint32_t DCRCError : 1; 397 | uint32_t ESI : 1; 398 | uint32_t unimplemented2 : 1; 399 | } bF; 400 | uint32_t word; 401 | uint8_t byte[4]; 402 | } REG_CiBDIAG1; 403 | 404 | // ***************************************************************************** 405 | //! Transmit Event FIFO Control Register 406 | 407 | typedef union _REG_CiTEFCON { 408 | 409 | struct { 410 | uint32_t TEFNEIE : 1; 411 | uint32_t TEFHFIE : 1; 412 | uint32_t TEFFULIE : 1; 413 | uint32_t TEFOVIE : 1; 414 | uint32_t unimplemented1 : 1; 415 | uint32_t TimeStampEnable : 1; 416 | uint32_t unimplemented2 : 2; 417 | uint32_t UINC : 1; 418 | uint32_t unimplemented3 : 1; 419 | uint32_t FRESET : 1; 420 | uint32_t unimplemented4 : 13; 421 | uint32_t FifoSize : 5; 422 | uint32_t unimplemented5 : 3; 423 | } bF; 424 | uint32_t word; 425 | uint8_t byte[4]; 426 | } REG_CiTEFCON; 427 | 428 | // ***************************************************************************** 429 | //! Transmit Event FIFO Status Register 430 | 431 | typedef union _REG_CiTEFSTA { 432 | 433 | struct { 434 | uint32_t TEFNotEmptyIF : 1; 435 | uint32_t TEFHalfFullIF : 1; 436 | uint32_t TEFFullIF : 1; 437 | uint32_t TEFOVIF : 1; 438 | uint32_t unimplemented1 : 28; 439 | } bF; 440 | uint32_t word; 441 | uint8_t byte[4]; 442 | } REG_CiTEFSTA; 443 | 444 | // ***************************************************************************** 445 | //! Transmit Queue Control Register 446 | 447 | typedef union _REG_CiTXQCON { 448 | 449 | struct { 450 | uint32_t TxNotFullIE : 1; 451 | uint32_t unimplemented1 : 1; 452 | uint32_t TxEmptyIE : 1; 453 | uint32_t unimplemented2 : 1; 454 | uint32_t TxAttemptIE : 1; 455 | uint32_t unimplemented3 : 2; 456 | uint32_t TxEnable : 1; 457 | uint32_t UINC : 1; 458 | uint32_t TxRequest : 1; 459 | uint32_t FRESET : 1; 460 | uint32_t unimplemented4 : 5; 461 | uint32_t TxPriority : 5; 462 | uint32_t TxAttempts : 2; 463 | uint32_t unimplemented5 : 1; 464 | uint32_t FifoSize : 5; 465 | uint32_t PayLoadSize : 3; 466 | } txBF; 467 | uint32_t word; 468 | uint8_t byte[4]; 469 | } REG_CiTXQCON; 470 | 471 | // ***************************************************************************** 472 | //! Transmit Queue Status Register 473 | 474 | typedef union _REG_CiTXQSTA { 475 | 476 | struct { 477 | uint32_t TxNotFullIF : 1; 478 | uint32_t unimplemented1 : 1; 479 | uint32_t TxEmptyIF : 1; 480 | uint32_t unimplemented2 : 1; 481 | uint32_t TxAttemptIF : 1; 482 | uint32_t TxError : 1; 483 | uint32_t TxLostArbitration : 1; 484 | uint32_t TxAborted : 1; 485 | uint32_t FifoIndex : 5; 486 | uint32_t unimplemented3 : 19; 487 | } txBF; 488 | uint32_t word; 489 | uint8_t byte[4]; 490 | } REG_CiTXQSTA; 491 | 492 | // ***************************************************************************** 493 | //! FIFO Control Register 494 | 495 | typedef union _REG_CiFIFOCON { 496 | // Receive FIFO 497 | 498 | struct { 499 | uint32_t RxNotEmptyIE : 1; 500 | uint32_t RxHalfFullIE : 1; 501 | uint32_t RxFullIE : 1; 502 | uint32_t RxOverFlowIE : 1; 503 | uint32_t unimplemented1 : 1; 504 | uint32_t RxTimeStampEnable : 1; 505 | uint32_t unimplemented2 : 1; 506 | uint32_t TxEnable : 1; 507 | uint32_t UINC : 1; 508 | uint32_t unimplemented3 : 1; 509 | uint32_t FRESET : 1; 510 | uint32_t unimplemented4 : 13; 511 | uint32_t FifoSize : 5; 512 | uint32_t PayLoadSize : 3; 513 | } rxBF; 514 | 515 | // Transmit FIFO 516 | 517 | struct { 518 | uint32_t TxNotFullIE : 1; 519 | uint32_t TxHalfFullIE : 1; 520 | uint32_t TxEmptyIE : 1; 521 | uint32_t unimplemented1 : 1; 522 | uint32_t TxAttemptIE : 1; 523 | uint32_t unimplemented2 : 1; 524 | uint32_t RTREnable : 1; 525 | uint32_t TxEnable : 1; 526 | uint32_t UINC : 1; 527 | uint32_t TxRequest : 1; 528 | uint32_t FRESET : 1; 529 | uint32_t unimplemented3 : 5; 530 | uint32_t TxPriority : 5; 531 | uint32_t TxAttempts : 2; 532 | uint32_t unimplemented4 : 1; 533 | uint32_t FifoSize : 5; 534 | uint32_t PayLoadSize : 3; 535 | } txBF; 536 | uint32_t word; 537 | uint8_t byte[4]; 538 | } REG_CiFIFOCON; 539 | 540 | // ***************************************************************************** 541 | //! FIFO Status Register 542 | 543 | typedef union _REG_CiFIFOSTA { 544 | // Receive FIFO 545 | 546 | struct { 547 | uint32_t RxNotEmptyIF : 1; 548 | uint32_t RxHalfFullIF : 1; 549 | uint32_t RxFullIF : 1; 550 | uint32_t RxOverFlowIF : 1; 551 | uint32_t unimplemented1 : 4; 552 | uint32_t FifoIndex : 5; 553 | uint32_t unimplemented2 : 19; 554 | } rxBF; 555 | 556 | // Transmit FIFO 557 | 558 | struct { 559 | uint32_t TxNotFullIF : 1; 560 | uint32_t TxHalfFullIF : 1; 561 | uint32_t TxEmptyIF : 1; 562 | uint32_t unimplemented1 : 1; 563 | uint32_t TxAttemptIF : 1; 564 | uint32_t TxError : 1; 565 | uint32_t TxLostArbitration : 1; 566 | uint32_t TxAborted : 1; 567 | uint32_t FifoIndex : 5; 568 | uint32_t unimplemented2 : 19; 569 | } txBF; 570 | uint32_t word; 571 | uint8_t byte[4]; 572 | } REG_CiFIFOSTA; 573 | 574 | // ***************************************************************************** 575 | //! FIFO User Address Register 576 | 577 | typedef union _REG_CiFIFOUA { 578 | 579 | struct { 580 | uint32_t UserAddress : 12; 581 | uint32_t unimplemented1 : 20; 582 | } bF; 583 | uint32_t word; 584 | uint8_t byte[4]; 585 | } REG_CiFIFOUA; 586 | 587 | // ***************************************************************************** 588 | //! Filter Control Register 589 | 590 | typedef union _REG_CiFLTCON_BYTE { 591 | 592 | struct { 593 | uint32_t BufferPointer : 5; 594 | uint32_t unimplemented1 : 2; 595 | uint32_t Enable : 1; 596 | } bF; 597 | uint8_t byte; 598 | } REG_CiFLTCON_BYTE; 599 | 600 | // ***************************************************************************** 601 | //! Filter Object Register 602 | 603 | typedef union _REG_CiFLTOBJ { 604 | CAN_FILTEROBJ_ID bF; 605 | uint32_t word; 606 | uint8_t byte[4]; 607 | } REG_CiFLTOBJ; 608 | 609 | // ***************************************************************************** 610 | //! Mask Object Register 611 | 612 | typedef union _REG_CiMASK { 613 | CAN_MASKOBJ_ID bF; 614 | uint32_t word; 615 | uint8_t byte[4]; 616 | } REG_CiMASK; 617 | 618 | 619 | // ***************************************************************************** 620 | // ***************************************************************************** 621 | /* MCP2517 Specific */ 622 | 623 | // ***************************************************************************** 624 | //! Oscillator Control Register 625 | 626 | typedef union _REG_OSC { 627 | 628 | struct { 629 | uint32_t PllEnable : 1; 630 | uint32_t unimplemented1 : 1; 631 | uint32_t OscDisable : 1; 632 | uint32_t unimplemented2 : 1; 633 | uint32_t SCLKDIV : 1; 634 | uint32_t CLKODIV : 2; 635 | uint32_t unimplemented3 : 1; 636 | uint32_t PllReady : 1; 637 | uint32_t unimplemented4 : 1; 638 | uint32_t OscReady : 1; 639 | uint32_t unimplemented5 : 1; 640 | uint32_t SclkReady : 1; 641 | uint32_t unimplemented6 : 19; 642 | } bF; 643 | uint32_t word; 644 | uint8_t byte[4]; 645 | } REG_OSC; 646 | 647 | // ***************************************************************************** 648 | //! I/O Control Register 649 | 650 | typedef union _REG_IOCON { 651 | 652 | struct { 653 | uint32_t TRIS0 : 1; 654 | uint32_t TRIS1 : 1; 655 | uint32_t unimplemented1 : 2; 656 | uint32_t ClearAutoSleepOnMatch : 1; 657 | uint32_t AutoSleepEnable : 1; 658 | uint32_t XcrSTBYEnable : 1; 659 | uint32_t unimplemented2 : 1; 660 | uint32_t LAT0 : 1; 661 | uint32_t LAT1 : 1; 662 | uint32_t unimplemented3 : 5; 663 | uint32_t HVDETSEL : 1; 664 | uint32_t GPIO0 : 1; 665 | uint32_t GPIO1 : 1; 666 | uint32_t unimplemented4 : 6; 667 | uint32_t PinMode0 : 1; 668 | uint32_t PinMode1 : 1; 669 | uint32_t unimplemented5 : 2; 670 | uint32_t TXCANOpenDrain : 1; 671 | uint32_t SOFOutputEnable : 1; 672 | uint32_t INTPinOpenDrain : 1; 673 | uint32_t unimplemented6 : 1; 674 | } bF; 675 | uint32_t word; 676 | uint8_t byte[4]; 677 | } REG_IOCON; 678 | 679 | // ***************************************************************************** 680 | //! CRC Regsiter 681 | 682 | typedef union _REG_CRC { 683 | 684 | struct { 685 | uint32_t CRC : 16; 686 | uint32_t CRCERRIF : 1; 687 | uint32_t FERRIF : 1; 688 | uint32_t unimplemented1 : 6; 689 | uint32_t CRCERRIE : 1; 690 | uint32_t FERRIE : 1; 691 | uint32_t unimplemented2 : 6; 692 | } bF; 693 | uint32_t word; 694 | uint8_t byte[4]; 695 | } REG_CRC; 696 | 697 | // ***************************************************************************** 698 | //! ECC Control Register 699 | 700 | typedef union _REG_ECCCON { 701 | 702 | struct { 703 | uint32_t EccEn : 1; 704 | uint32_t SECIE : 1; 705 | uint32_t DEDIE : 1; 706 | uint32_t unimplemented1 : 5; 707 | uint32_t Parity : 7; 708 | uint32_t unimplemented2 : 17; 709 | } bF; 710 | uint32_t word; 711 | uint8_t byte[4]; 712 | } REG_ECCCON; 713 | 714 | // ***************************************************************************** 715 | //! ECC Status Register 716 | 717 | typedef union _REG_ECCSTA { 718 | 719 | struct { 720 | uint32_t unimplemented1 : 1; 721 | uint32_t SECIF : 1; 722 | uint32_t DEDIF : 1; 723 | uint32_t unimplemented2 : 13; 724 | uint32_t ErrorAddress : 12; 725 | uint32_t unimplemented3 : 4; 726 | } bF; 727 | uint32_t word; 728 | uint8_t byte[4]; 729 | } REG_ECCSTA; 730 | 731 | 732 | // ***************************************************************************** 733 | // ***************************************************************************** 734 | /* Register Reset Values */ 735 | 736 | // ***************************************************************************** 737 | /* can_fd_ubp */ 738 | 739 | // Control Register Reset Values up to FIFOs 740 | #define N_CAN_CTRL_REGS 20 741 | static uint32_t canControlResetValues[] = { 742 | /* Address 0x000 to 0x00C */ 743 | #ifdef CAN_TXQUEUE_IMPLEMENTED 744 | 0x04980760, 0x003E0F0F, 0x000E0303, 0x00021000, 745 | #else 746 | 0x04880760, 0x003E0F0F, 0x000E0303, 0x00021000, 747 | #endif 748 | /* Address 0x010 to 0x01C */ 749 | 0x00000000, 0x00000000, 0x40400040, 0x00000000, 750 | /* Address 0x020 to 0x02C */ 751 | 0x00000000, 0x00000000, 0x00000000, 0x00000000, 752 | /* Address 0x030 to 0x03C */ 753 | 0x00000000, 0x00200000, 0x00000000, 0x00000000, 754 | /* Address 0x040 to 0x04C */ 755 | 0x00000400, 0x00000000, 0x00000000, 0x00000000 756 | }; 757 | 758 | // FIFO Register Reset Values 759 | #define N_CAN_FIFO_REGS (CAN_FIFO_TOTAL_CHANNELS*CiFIFO_OFFSET) 760 | static uint32_t canFifoResetValues[] = { 761 | 0x00600400, 0x00000000, 0x00000000 762 | }; 763 | 764 | // Filter Control Register Reset Values 765 | #define N_CAN_FILTER_CTRL_REGS (CAN_FILTER_TOTAL/4) 766 | static uint32_t canFilterControlResetValue = 0x00000000; 767 | 768 | // Filter and Mask Object Reset Values 769 | #define N_CAN_FILTER_OBJ_REGS (CAN_FILTER_TOTAL*CiFILTER_OFFSET) 770 | static uint32_t canFilterObjectResetValues[] = { 771 | 0x00000000, 0x00000000 772 | }; 773 | 774 | // ***************************************************************************** 775 | /* MCP2517 */ 776 | 777 | #ifdef MCP2517FD 778 | #define N_MCP2517_CTRL_REGS 5 779 | static uint32_t mcp2517ControlResetValues[] = { 780 | 0x00000460, 0x00000003, 0x00000000, 0x00000000, 0x00000000 781 | }; 782 | #endif 783 | 784 | #ifdef __cplusplus 785 | } 786 | #endif 787 | 788 | #endif // _DRV_CANFDSPI_REGISTER_H 789 | --------------------------------------------------------------------------------