intermediate: fork of esp32 nmea2000
This commit is contained in:
parent
7c321ecf83
commit
d6cf87291c
|
@ -0,0 +1,291 @@
|
|||
/**
|
||||
* @section License
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2017, Thomas Barth, barth-dev.de
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, 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:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 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 IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __DRIVERS_CAN_REGDEF_H_
|
||||
#define __DRIVERS_CAN_REGDEF_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
CAN_SPEED_100KBPS=100, /**< \brief CAN Node runs at 100kBit/s. */
|
||||
CAN_SPEED_125KBPS=125, /**< \brief CAN Node runs at 125kBit/s. */
|
||||
CAN_SPEED_250KBPS=250, /**< \brief CAN Node runs at 250kBit/s. */
|
||||
CAN_SPEED_500KBPS=500, /**< \brief CAN Node runs at 500kBit/s. */
|
||||
CAN_SPEED_800KBPS=800, /**< \brief CAN Node runs at 800kBit/s. */
|
||||
CAN_SPEED_1000KBPS=1000 /**< \brief CAN Node runs at 1000kBit/s. */
|
||||
}CAN_speed_t;
|
||||
|
||||
/**
|
||||
* \brief CAN frame type (standard/extended)
|
||||
*/
|
||||
typedef enum {
|
||||
CAN_frame_std=0, /**< Standard frame, using 11 bit identifer. */
|
||||
CAN_frame_ext=1 /**< Extended frame, using 29 bit identifer. */
|
||||
}CAN_frame_format_t;
|
||||
|
||||
/**
|
||||
* \brief CAN RTR
|
||||
*/
|
||||
typedef enum {
|
||||
CAN_no_RTR=0, /**< No RTR frame. */
|
||||
CAN_RTR=1 /**< RTR frame. */
|
||||
}CAN_RTR_t;
|
||||
|
||||
/** \brief Frame information record type */
|
||||
typedef union{uint32_t U; /**< \brief Unsigned access */
|
||||
struct {
|
||||
uint8_t DLC:4; /**< \brief [3:0] DLC, Data length container */
|
||||
unsigned int unknown_2:2; /**< \brief \internal unknown */
|
||||
CAN_RTR_t RTR:1; /**< \brief [6:6] RTR, Remote Transmission Request */
|
||||
CAN_frame_format_t FF:1; /**< \brief [7:7] Frame Format, see# CAN_frame_format_t*/
|
||||
unsigned int reserved_24:24; /**< \brief \internal Reserved */
|
||||
} B;
|
||||
} CAN_FIR_t;
|
||||
|
||||
/** \brief Start address of CAN registers */
|
||||
#define MODULE_CAN ((volatile CAN_Module_t *)0x3ff6b000)
|
||||
|
||||
/** \brief Get standard message ID */
|
||||
#define _CAN_GET_STD_ID (((uint32_t)MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.STD.ID[0] << 3) | \
|
||||
(MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.STD.ID[1] >> 5))
|
||||
|
||||
/** \brief Get extended message ID */
|
||||
#define _CAN_GET_EXT_ID (((uint32_t)MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[0] << 21) | \
|
||||
(MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[1] << 13) | \
|
||||
(MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[2] << 5) | \
|
||||
(MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[3] >> 3 ))
|
||||
|
||||
/** \brief Set standard message ID */
|
||||
#define _CAN_SET_STD_ID(x) MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.STD.ID[0] = ((x) >> 3); \
|
||||
MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.STD.ID[1] = ((x) << 5);
|
||||
|
||||
/** \brief Set extended message ID */
|
||||
#define _CAN_SET_EXT_ID(x) MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[0] = ((x) >> 21); \
|
||||
MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[1] = ((x) >> 13); \
|
||||
MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[2] = ((x) >> 5); \
|
||||
MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[3] = ((x) << 3); \
|
||||
|
||||
/** \brief Interrupt status register */
|
||||
typedef enum {
|
||||
__CAN_IRQ_RX= BIT(0), /**< \brief RX Interrupt */
|
||||
__CAN_IRQ_TX= BIT(1), /**< \brief TX Interrupt */
|
||||
__CAN_IRQ_ERR= BIT(2), /**< \brief Error Interrupt */
|
||||
__CAN_IRQ_DATA_OVERRUN= BIT(3), /**< \brief Date Overrun Interrupt */
|
||||
__CAN_IRQ_WAKEUP= BIT(4), /**< \brief Wakeup Interrupt */
|
||||
__CAN_IRQ_ERR_PASSIVE= BIT(5), /**< \brief Passive Error Interrupt */
|
||||
__CAN_IRQ_ARB_LOST= BIT(6), /**< \brief Arbitration lost interrupt */
|
||||
__CAN_IRQ_BUS_ERR= BIT(7), /**< \brief Bus error Interrupt */
|
||||
}__CAN_IRQ_t;
|
||||
|
||||
|
||||
/** \brief OCMODE options. */
|
||||
typedef enum {
|
||||
__CAN_OC_BOM= 0b00, /**< \brief bi-phase output mode */
|
||||
__CAN_OC_TOM= 0b01, /**< \brief test output mode */
|
||||
__CAN_OC_NOM= 0b10, /**< \brief normal output mode */
|
||||
__CAN_OC_COM= 0b11, /**< \brief clock output mode */
|
||||
}__CAN_OCMODE_t;
|
||||
|
||||
|
||||
/**
|
||||
* CAN controller (SJA1000).
|
||||
*/
|
||||
typedef struct {
|
||||
union{uint32_t U; /**< \brief Unsigned access */
|
||||
struct {
|
||||
unsigned int RM:1; /**< \brief MOD.0 Reset Mode */
|
||||
unsigned int LOM:1; /**< \brief MOD.1 Listen Only Mode */
|
||||
unsigned int STM:1; /**< \brief MOD.2 Self Test Mode */
|
||||
unsigned int AFM:1; /**< \brief MOD.3 Acceptance Filter Mode */
|
||||
unsigned int SM:1; /**< \brief MOD.4 Sleep Mode */
|
||||
unsigned int reserved_27:27; /**< \brief \internal Reserved */
|
||||
} B;
|
||||
} MOD;
|
||||
union{uint32_t U; /**< \brief Unsigned access */
|
||||
struct {
|
||||
unsigned int TR:1; /**< \brief CMR.0 Transmission Request */
|
||||
unsigned int AT:1; /**< \brief CMR.1 Abort Transmission */
|
||||
unsigned int RRB:1; /**< \brief CMR.2 Release Receive Buffer */
|
||||
unsigned int CDO:1; /**< \brief CMR.3 Clear Data Overrun */
|
||||
unsigned int GTS:1; /**< \brief CMR.4 Go To Sleep */
|
||||
unsigned int reserved_27:27; /**< \brief \internal Reserved */
|
||||
} B;
|
||||
} CMR;
|
||||
union{uint32_t U; /**< \brief Unsigned access */
|
||||
struct {
|
||||
unsigned int RBS:1; /**< \brief SR.0 Receive Buffer Status */
|
||||
unsigned int DOS:1; /**< \brief SR.1 Data Overrun Status */
|
||||
unsigned int TBS:1; /**< \brief SR.2 Transmit Buffer Status */
|
||||
unsigned int TCS:1; /**< \brief SR.3 Transmission Complete Status */
|
||||
unsigned int RS:1; /**< \brief SR.4 Receive Status */
|
||||
unsigned int TS:1; /**< \brief SR.5 Transmit Status */
|
||||
unsigned int ES:1; /**< \brief SR.6 Error Status */
|
||||
unsigned int BS:1; /**< \brief SR.7 Bus Status */
|
||||
unsigned int reserved_24:24; /**< \brief \internal Reserved */
|
||||
} B;
|
||||
} SR;
|
||||
union{uint32_t U; /**< \brief Unsigned access */
|
||||
struct {
|
||||
unsigned int RI:1; /**< \brief IR.0 Receive Interrupt */
|
||||
unsigned int TI:1; /**< \brief IR.1 Transmit Interrupt */
|
||||
unsigned int EI:1; /**< \brief IR.2 Error Interrupt */
|
||||
unsigned int DOI:1; /**< \brief IR.3 Data Overrun Interrupt */
|
||||
unsigned int WUI:1; /**< \brief IR.4 Wake-Up Interrupt */
|
||||
unsigned int EPI:1; /**< \brief IR.5 Error Passive Interrupt */
|
||||
unsigned int ALI:1; /**< \brief IR.6 Arbitration Lost Interrupt */
|
||||
unsigned int BEI:1; /**< \brief IR.7 Bus Error Interrupt */
|
||||
unsigned int reserved_24:24; /**< \brief \internal Reserved */
|
||||
} B;
|
||||
} IR;
|
||||
union{uint32_t U; /**< \brief Unsigned access */
|
||||
struct {
|
||||
unsigned int RIE:1; /**< \brief IER.0 Receive Interrupt Enable */
|
||||
unsigned int TIE:1; /**< \brief IER.1 Transmit Interrupt Enable */
|
||||
unsigned int EIE:1; /**< \brief IER.2 Error Interrupt Enable */
|
||||
unsigned int DOIE:1; /**< \brief IER.3 Data Overrun Interrupt Enable */
|
||||
unsigned int WUIE:1; /**< \brief IER.4 Wake-Up Interrupt Enable */
|
||||
unsigned int EPIE:1; /**< \brief IER.5 Error Passive Interrupt Enable */
|
||||
unsigned int ALIE:1; /**< \brief IER.6 Arbitration Lost Interrupt Enable */
|
||||
unsigned int BEIE:1; /**< \brief IER.7 Bus Error Interrupt Enable */
|
||||
unsigned int reserved_24:24; /**< \brief \internal Reserved */
|
||||
} B;
|
||||
} IER;
|
||||
uint32_t RESERVED0;
|
||||
union{uint32_t U; /**< \brief Unsigned access */
|
||||
struct {
|
||||
unsigned int BRP:6; /**< \brief BTR0[5:0] Baud Rate Prescaler */
|
||||
unsigned int SJW:2; /**< \brief BTR0[7:6] Synchronization Jump Width*/
|
||||
unsigned int reserved_24:24; /**< \brief \internal Reserved */
|
||||
} B;
|
||||
} BTR0;
|
||||
union{uint32_t U; /**< \brief Unsigned access */
|
||||
struct {
|
||||
unsigned int TSEG1:4; /**< \brief BTR1[3:0] Timing Segment 1 */
|
||||
unsigned int TSEG2:3; /**< \brief BTR1[6:4] Timing Segment 2*/
|
||||
unsigned int SAM:1; /**< \brief BTR1.7 Sampling*/
|
||||
unsigned int reserved_24:24; /**< \brief \internal Reserved */
|
||||
} B;
|
||||
} BTR1;
|
||||
union{uint32_t U; /**< \brief Unsigned access */
|
||||
struct {
|
||||
unsigned int OCMODE:2; /**< \brief OCR[1:0] Output Control Mode, see # */
|
||||
unsigned int OCPOL0:1; /**< \brief OCR.2 Output Control Polarity 0 */
|
||||
unsigned int OCTN0:1; /**< \brief OCR.3 Output Control Transistor N0 */
|
||||
unsigned int OCTP0:1; /**< \brief OCR.4 Output Control Transistor P0 */
|
||||
unsigned int OCPOL1:1; /**< \brief OCR.5 Output Control Polarity 1 */
|
||||
unsigned int OCTN1:1; /**< \brief OCR.6 Output Control Transistor N1 */
|
||||
unsigned int OCTP1:1; /**< \brief OCR.7 Output Control Transistor P1 */
|
||||
unsigned int reserved_24:24; /**< \brief \internal Reserved */
|
||||
} B;
|
||||
} OCR;
|
||||
uint32_t RESERVED1[2];
|
||||
union{uint32_t U; /**< \brief Unsigned access */
|
||||
struct {
|
||||
unsigned int ALC:8; /**< \brief ALC[7:0] Arbitration Lost Capture */
|
||||
unsigned int reserved_24:24; /**< \brief \internal Reserved */
|
||||
} B;
|
||||
} ALC;
|
||||
union{uint32_t U; /**< \brief Unsigned access */
|
||||
struct {
|
||||
unsigned int ECC:8; /**< \brief ECC[7:0] Error Code Capture */
|
||||
unsigned int reserved_24:24; /**< \brief \internal Reserved */
|
||||
} B;
|
||||
} ECC;
|
||||
union{uint32_t U; /**< \brief Unsigned access */
|
||||
struct {
|
||||
unsigned int EWLR:8; /**< \brief EWLR[7:0] Error Warning Limit */
|
||||
unsigned int reserved_24:24; /**< \brief \internal Reserved */
|
||||
} B;
|
||||
} EWLR;
|
||||
union{uint32_t U; /**< \brief Unsigned access */
|
||||
struct {
|
||||
unsigned int RXERR:8; /**< \brief RXERR[7:0] Receive Error Counter */
|
||||
unsigned int reserved_24:24; /**< \brief \internal Reserved */
|
||||
} B;
|
||||
} RXERR;
|
||||
union{uint32_t U; /**< \brief Unsigned access */
|
||||
struct {
|
||||
unsigned int TXERR:8; /**< \brief TXERR[7:0] Transmit Error Counter */
|
||||
unsigned int reserved_24:24; /**< \brief \internal Reserved */
|
||||
} B;
|
||||
} TXERR;
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint32_t CODE[4]; /**< \brief Acceptance Message ID */
|
||||
uint32_t MASK[4]; /**< \brief Acceptance Mask */
|
||||
uint32_t RESERVED2[5];
|
||||
} ACC; /**< \brief Acceptance filtering */
|
||||
struct {
|
||||
CAN_FIR_t FIR; /**< \brief Frame information record */
|
||||
union{
|
||||
struct {
|
||||
uint32_t ID[2]; /**< \brief Standard frame message-ID*/
|
||||
uint32_t data[8]; /**< \brief Standard frame payload */
|
||||
uint32_t reserved[2];
|
||||
} STD; /**< \brief Standard frame format */
|
||||
struct {
|
||||
uint32_t ID[4]; /**< \brief Extended frame message-ID*/
|
||||
uint32_t data[8]; /**< \brief Extended frame payload */
|
||||
} EXT; /**< \brief Extended frame format */
|
||||
}TX_RX; /**< \brief RX/TX interface */
|
||||
}FCTRL; /**< \brief Function control regs */
|
||||
} MBX_CTRL; /**< \brief Mailbox control */
|
||||
union{uint32_t U; /**< \brief Unsigned access */
|
||||
struct {
|
||||
unsigned int RMC:8; /**< \brief RMC[7:0] RX Message Counter */
|
||||
unsigned int reserved_24:24; /**< \brief \internal Reserved Enable */
|
||||
} B;
|
||||
} RMC;
|
||||
union{uint32_t U; /**< \brief Unsigned access */
|
||||
struct {
|
||||
unsigned int RBSA:8; /**< \brief RBSA[7:0] RX Buffer Start Address */
|
||||
unsigned int reserved_24:24; /**< \brief \internal Reserved Enable */
|
||||
} B;
|
||||
} RBSA;
|
||||
union{uint32_t U; /**< \brief Unsigned access */
|
||||
struct {
|
||||
unsigned int COD:3; /**< \brief CDR[2:0] CLKOUT frequency selector based of fOSC*/
|
||||
unsigned int COFF:1; /**< \brief CDR.3 CLKOUT off*/
|
||||
unsigned int reserved_1:1; /**< \brief \internal Reserved */
|
||||
unsigned int RXINTEN:1; /**< \brief CDR.5 This bit allows the TX1 output to be used as a dedicated receive interrupt output*/
|
||||
unsigned int CBP:1; /**< \brief CDR.6 allows to bypass the CAN input comparator and is only possible in reset mode.*/
|
||||
unsigned int CAN_M:1; /**< \brief CDR.7 If CDR.7 is at logic 0 the CAN controller operates in BasicCAN mode. If set to logic 1 the CAN controller operates in PeliCAN mode. Write access is only possible in reset mode*/
|
||||
unsigned int reserved_24:24; /**< \brief \internal Reserved */
|
||||
} B;
|
||||
} CDR;
|
||||
uint32_t IRAM[2];
|
||||
}CAN_Module_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __DRIVERS_CAN_REGDEF_H_ */
|
|
@ -0,0 +1,348 @@
|
|||
/*
|
||||
NMEA2000_esp32.cpp
|
||||
|
||||
Copyright (c) 2015-2020 Timo Lappalainen, Kave Oy, www.kave.fi
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, 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:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 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 IN THE SOFTWARE.
|
||||
|
||||
Inherited NMEA2000 object for ESP32 modules. See also NMEA2000 library.
|
||||
|
||||
Thanks to Thomas Barth, barth-dev.de, who has written ESP32 CAN code. To avoid extra
|
||||
libraries, I implemented his code directly to the NMEA2000_esp32 to avoid extra
|
||||
can.h library, which may cause even naming problem.
|
||||
*/
|
||||
|
||||
#include "driver/periph_ctrl.h"
|
||||
|
||||
#include "soc/dport_reg.h"
|
||||
#include "NMEA2000_esp32.h"
|
||||
|
||||
bool tNMEA2000_esp32::CanInUse=false;
|
||||
tNMEA2000_esp32 *pNMEA2000_esp32=0;
|
||||
|
||||
void ESP32Can1Interrupt(void *);
|
||||
|
||||
#define ECDEBUG(fmt,args...) if(debugStream){debugStream->printf(fmt, ## args);}
|
||||
|
||||
//*****************************************************************************
|
||||
tNMEA2000_esp32::tNMEA2000_esp32(gpio_num_t _TxPin,
|
||||
gpio_num_t _RxPin,
|
||||
Print *dbg) :
|
||||
tNMEA2000(), IsOpen(false),
|
||||
speed(CAN_SPEED_250KBPS), TxPin(_TxPin), RxPin(_RxPin),
|
||||
RxQueue(NULL), TxQueue(NULL) {
|
||||
debugStream=dbg;
|
||||
}
|
||||
|
||||
//*****************************************************************************
|
||||
bool tNMEA2000_esp32::CANSendFrame(unsigned long id, unsigned char len, const unsigned char *buf, bool /*wait_sent*/) {
|
||||
if ( uxQueueSpacesAvailable(TxQueue)==0 ) return false; // can not send to queue
|
||||
|
||||
tCANFrame frame;
|
||||
frame.id=id;
|
||||
frame.len=len>8?8:len;
|
||||
memcpy(frame.buf,buf,len);
|
||||
ECDEBUG("CanSendFrame Error TX %d\n",MODULE_CAN->TXERR.U);
|
||||
ECDEBUG("CanSendFrame Error Overrun %d\n",errOverrun);
|
||||
ECDEBUG("CanSendFrame Error Arbitration %d\n",errArb);
|
||||
ECDEBUG("CanSendFrame Error Bus %d\n",errBus);
|
||||
ECDEBUG("CanSendFrame Error Recovery %d\n",errRecovery);
|
||||
xQueueSendToBack(TxQueue,&frame,0); // Add frame to queue
|
||||
if ( MODULE_CAN->SR.B.TBS==0 ) {
|
||||
ECDEBUG("CanSendFrame: wait for ISR to send %d\n",frame.id);
|
||||
return true; // Currently sending, ISR takes care of sending
|
||||
}
|
||||
|
||||
if ( MODULE_CAN->SR.B.TBS==1 ) { // Check again and restart send, if is not going on
|
||||
xQueueReceive(TxQueue,&frame,0);
|
||||
ECDEBUG("CanSendFrame: send direct %d\n",frame.id);
|
||||
CAN_send_frame(frame);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//*****************************************************************************
|
||||
void tNMEA2000_esp32::InitCANFrameBuffers() {
|
||||
if (MaxCANReceiveFrames<10 ) MaxCANReceiveFrames=50; // ESP32 has plenty of RAM
|
||||
if (MaxCANSendFrames<10 ) MaxCANSendFrames=40;
|
||||
uint16_t CANGlobalBufSize=MaxCANSendFrames-4;
|
||||
MaxCANSendFrames=4; // we do not need much libary internal buffer since driver has them.
|
||||
RxQueue=xQueueCreate(MaxCANReceiveFrames,sizeof(tCANFrame));
|
||||
TxQueue=xQueueCreate(CANGlobalBufSize,sizeof(tCANFrame));
|
||||
|
||||
tNMEA2000::InitCANFrameBuffers(); // call main initialization
|
||||
}
|
||||
|
||||
//*****************************************************************************
|
||||
bool tNMEA2000_esp32::CANOpen() {
|
||||
if (IsOpen) return true;
|
||||
|
||||
if (CanInUse) return false; // currently prevent accidental second instance. Maybe possible in future.
|
||||
|
||||
pNMEA2000_esp32=this;
|
||||
IsOpen=true;
|
||||
CAN_init();
|
||||
|
||||
CanInUse=IsOpen;
|
||||
|
||||
return IsOpen;
|
||||
}
|
||||
|
||||
//*****************************************************************************
|
||||
bool tNMEA2000_esp32::CANGetFrame(unsigned long &id, unsigned char &len, unsigned char *buf) {
|
||||
bool HasFrame=false;
|
||||
tCANFrame frame;
|
||||
|
||||
//receive next CAN frame from queue
|
||||
if ( xQueueReceive(RxQueue,&frame, 0)==pdTRUE ) {
|
||||
HasFrame=true;
|
||||
id=frame.id;
|
||||
len=frame.len;
|
||||
memcpy(buf,frame.buf,frame.len);
|
||||
}
|
||||
|
||||
return HasFrame;
|
||||
}
|
||||
|
||||
//*****************************************************************************
|
||||
void tNMEA2000_esp32::CAN_init() {
|
||||
|
||||
//Time quantum
|
||||
double __tq;
|
||||
|
||||
|
||||
// A soft reset of the ESP32 leaves it's CAN controller in an undefined state so a reset is needed.
|
||||
// Reset CAN controller to same state as it would be in after a power down reset.
|
||||
periph_module_reset(PERIPH_CAN_MODULE);
|
||||
|
||||
|
||||
//enable module
|
||||
DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_CAN_CLK_EN);
|
||||
DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_CAN_RST);
|
||||
|
||||
//configure RX pin
|
||||
gpio_set_direction(RxPin,GPIO_MODE_INPUT);
|
||||
gpio_matrix_in(RxPin,CAN_RX_IDX,0);
|
||||
gpio_pad_select_gpio(RxPin);
|
||||
|
||||
//set to PELICAN mode
|
||||
MODULE_CAN->CDR.B.CAN_M=0x1;
|
||||
|
||||
//synchronization jump width is the same for all baud rates
|
||||
MODULE_CAN->BTR0.B.SJW =0x1;
|
||||
|
||||
//TSEG2 is the same for all baud rates
|
||||
MODULE_CAN->BTR1.B.TSEG2 =0x1;
|
||||
|
||||
//select time quantum and set TSEG1
|
||||
switch (speed) {
|
||||
case CAN_SPEED_1000KBPS:
|
||||
MODULE_CAN->BTR1.B.TSEG1 =0x4;
|
||||
__tq = 0.125;
|
||||
break;
|
||||
|
||||
case CAN_SPEED_800KBPS:
|
||||
MODULE_CAN->BTR1.B.TSEG1 =0x6;
|
||||
__tq = 0.125;
|
||||
break;
|
||||
default:
|
||||
MODULE_CAN->BTR1.B.TSEG1 =0xc;
|
||||
__tq = ((float)1000/speed) / 16;
|
||||
}
|
||||
|
||||
//set baud rate prescaler
|
||||
MODULE_CAN->BTR0.B.BRP=(uint8_t)round((((APB_CLK_FREQ * __tq) / 2) - 1)/1000000)-1;
|
||||
|
||||
/* Set sampling
|
||||
* 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
|
||||
* 0 -> single; the bus is sampled once; recommended for high speed buses (SAE class C)*/
|
||||
MODULE_CAN->BTR1.B.SAM =0x1;
|
||||
|
||||
//enable all interrupts
|
||||
MODULE_CAN->IER.U = 0xef; // bit 0x10 contains Baud Rate Prescaler Divider (BRP_DIV) bit
|
||||
|
||||
//no acceptance filtering, as we want to fetch all messages
|
||||
MODULE_CAN->MBX_CTRL.ACC.CODE[0] = 0;
|
||||
MODULE_CAN->MBX_CTRL.ACC.CODE[1] = 0;
|
||||
MODULE_CAN->MBX_CTRL.ACC.CODE[2] = 0;
|
||||
MODULE_CAN->MBX_CTRL.ACC.CODE[3] = 0;
|
||||
MODULE_CAN->MBX_CTRL.ACC.MASK[0] = 0xff;
|
||||
MODULE_CAN->MBX_CTRL.ACC.MASK[1] = 0xff;
|
||||
MODULE_CAN->MBX_CTRL.ACC.MASK[2] = 0xff;
|
||||
MODULE_CAN->MBX_CTRL.ACC.MASK[3] = 0xff;
|
||||
|
||||
//set to normal mode
|
||||
MODULE_CAN->OCR.B.OCMODE=__CAN_OC_NOM;
|
||||
|
||||
//clear error counters
|
||||
MODULE_CAN->TXERR.U = 0;
|
||||
MODULE_CAN->RXERR.U = 0;
|
||||
(void)MODULE_CAN->ECC;
|
||||
|
||||
//clear interrupt flags
|
||||
(void)MODULE_CAN->IR.U;
|
||||
|
||||
//install CAN ISR
|
||||
esp_intr_alloc(ETS_CAN_INTR_SOURCE,0,ESP32Can1Interrupt,NULL,NULL);
|
||||
|
||||
//configure TX pin
|
||||
// We do late configure, since some initialization above caused CAN Tx flash
|
||||
// shortly causing one error frame on startup. By setting CAN pin here
|
||||
// it works right.
|
||||
gpio_set_direction(TxPin,GPIO_MODE_OUTPUT);
|
||||
gpio_matrix_out(TxPin,CAN_TX_IDX,0,0);
|
||||
gpio_pad_select_gpio(TxPin);
|
||||
|
||||
//Showtime. Release Reset Mode.
|
||||
MODULE_CAN->MOD.B.RM = 0;
|
||||
}
|
||||
|
||||
//*****************************************************************************
|
||||
void tNMEA2000_esp32::CAN_read_frame() {
|
||||
tCANFrame frame;
|
||||
CAN_FIR_t FIR;
|
||||
|
||||
//get FIR
|
||||
FIR.U=MODULE_CAN->MBX_CTRL.FCTRL.FIR.U;
|
||||
frame.len=FIR.B.DLC>8?8:FIR.B.DLC;
|
||||
|
||||
// Handle only extended frames
|
||||
if (FIR.B.FF==CAN_frame_ext) { //extended frame
|
||||
//Get Message ID
|
||||
frame.id = _CAN_GET_EXT_ID;
|
||||
|
||||
//deep copy data bytes
|
||||
for( size_t i=0; i<frame.len; i++ ) {
|
||||
frame.buf[i]=MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.data[i];
|
||||
}
|
||||
|
||||
//send frame to input queue
|
||||
xQueueSendToBackFromISR(RxQueue,&frame,0);
|
||||
}
|
||||
|
||||
//Let the hardware know the frame has been read.
|
||||
MODULE_CAN->CMR.B.RRB=1;
|
||||
}
|
||||
|
||||
//*****************************************************************************
|
||||
void tNMEA2000_esp32::CAN_send_frame(tCANFrame &frame) {
|
||||
CAN_FIR_t FIR;
|
||||
|
||||
FIR.U=0;
|
||||
FIR.B.DLC=frame.len>8?8:frame.len;
|
||||
FIR.B.FF=CAN_frame_ext;
|
||||
|
||||
//copy frame information record
|
||||
MODULE_CAN->MBX_CTRL.FCTRL.FIR.U=FIR.U;
|
||||
|
||||
//Write message ID
|
||||
_CAN_SET_EXT_ID(frame.id);
|
||||
|
||||
// Copy the frame data to the hardware
|
||||
for ( size_t i=0; i<frame.len; i++) {
|
||||
MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.data[i]=frame.buf[i];
|
||||
}
|
||||
|
||||
// Transmit frame
|
||||
MODULE_CAN->CMR.B.TR=1;
|
||||
}
|
||||
static int tx_error_count=0;
|
||||
#define CAN_MAX_TX_RETRY 12
|
||||
|
||||
void tNMEA2000_esp32::CAN_bus_off_recovery(){
|
||||
MODULE_CAN->MOD.B.RM = 1;
|
||||
MODULE_CAN->TXERR.U = 127;
|
||||
MODULE_CAN->RXERR.U = 0;
|
||||
MODULE_CAN->MOD.B.RM = 0;
|
||||
}
|
||||
|
||||
//*****************************************************************************
|
||||
void tNMEA2000_esp32::InterruptHandler() {
|
||||
//Interrupt flag buffer
|
||||
uint32_t interrupt;
|
||||
|
||||
// Read interrupt status and clear flags
|
||||
interrupt = (MODULE_CAN->IR.U & 0xff);
|
||||
|
||||
// Handle TX complete interrupt
|
||||
if ((interrupt & __CAN_IRQ_TX) != 0) {
|
||||
tCANFrame frame;
|
||||
if ( (xQueueReceiveFromISR(TxQueue,&frame,NULL)==pdTRUE) ) {
|
||||
CAN_send_frame(frame);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle RX frame available interrupt
|
||||
if ((interrupt & __CAN_IRQ_RX) != 0) {
|
||||
CAN_read_frame();
|
||||
}
|
||||
|
||||
// Handle error interrupts.
|
||||
if ((interrupt & (__CAN_IRQ_ERR //0x4
|
||||
|
||||
| __CAN_IRQ_WAKEUP //0x10
|
||||
| __CAN_IRQ_ERR_PASSIVE //0x20
|
||||
|
||||
|
||||
)) != 0) {
|
||||
/*handler*/
|
||||
|
||||
}
|
||||
//https://www.esp32.com/viewtopic.php?t=5010
|
||||
// Handle error interrupts.
|
||||
if (interrupt & __CAN_IRQ_DATA_OVERRUN ) { //0x08
|
||||
errOverrun++;
|
||||
MODULE_CAN->CMR.B.CDO=1;
|
||||
(void)MODULE_CAN->SR.U; // read SR after write to CMR to settle register changes
|
||||
}
|
||||
if (interrupt & __CAN_IRQ_ARB_LOST ) { //0x40
|
||||
errArb++;
|
||||
(void)MODULE_CAN->ALC.U; // must be read to re-enable interrupt
|
||||
tx_error_count++;
|
||||
}
|
||||
if (interrupt & __CAN_IRQ_BUS_ERR ) { //0x80
|
||||
errBus++;
|
||||
(void)MODULE_CAN->ECC.U; // must be read to re-enable interrupt
|
||||
tx_error_count+=2;
|
||||
}
|
||||
if (tx_error_count>=2*CAN_MAX_TX_RETRY || MODULE_CAN->SR.B.ES) {
|
||||
MODULE_CAN->CMR.B.AT=1; // abort transmission
|
||||
(void)MODULE_CAN->SR.U; // read SR after write to CMR to settle register changes
|
||||
tx_error_count=0;
|
||||
if (MODULE_CAN->SR.B.ES) {
|
||||
errRecovery++;
|
||||
CAN_bus_off_recovery();
|
||||
}
|
||||
return;
|
||||
}
|
||||
//should we really recover here?
|
||||
if (MODULE_CAN->SR.B.BS){
|
||||
MODULE_CAN->CMR.B.AT=1; // abort transmission
|
||||
(void)MODULE_CAN->SR.U; // read SR after write to CMR to settle register changes
|
||||
tx_error_count=0;
|
||||
errRecovery++;
|
||||
CAN_bus_off_recovery();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//*****************************************************************************
|
||||
void ESP32Can1Interrupt(void *) {
|
||||
pNMEA2000_esp32->InterruptHandler();
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
NMEA2000_esp32.h
|
||||
|
||||
Copyright (c) 2015-2020 Timo Lappalainen, Kave Oy, www.kave.fi
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, 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:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 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 IN THE SOFTWARE.
|
||||
|
||||
Inherited NMEA2000 object for ESP32 modules. See also NMEA2000 library.
|
||||
|
||||
Thanks to Thomas Barth, barth-dev.de, who has written ESP32 CAN code. To avoid extra
|
||||
libraries, I implemented his code directly to the NMEA2000_esp32 to avoid extra
|
||||
can.h library, which may cause even naming problem.
|
||||
|
||||
The library sets as default CAN Tx pin to GPIO 16 and CAN Rx pint to GPIO 4. If you
|
||||
want to use other pins (I have not tested can any pins be used), add defines e.g.
|
||||
#define ESP32_CAN_TX_PIN GPIO_NUM_34
|
||||
#define ESP32_CAN_RX_PIN GPIO_NUM_35
|
||||
before including NMEA2000_esp32.h or NMEA2000_CAN.h
|
||||
*/
|
||||
|
||||
#ifndef _NMEA2000_ESP32_H_
|
||||
#define _NMEA2000_ESP32_H_
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "NMEA2000.h"
|
||||
#include "N2kMsg.h"
|
||||
#include "ESP32_CAN_def.h"
|
||||
|
||||
#ifndef ESP32_CAN_TX_PIN
|
||||
#define ESP32_CAN_TX_PIN GPIO_NUM_16
|
||||
#endif
|
||||
#ifndef ESP32_CAN_RX_PIN
|
||||
#define ESP32_CAN_RX_PIN GPIO_NUM_4
|
||||
#endif
|
||||
|
||||
class tNMEA2000_esp32 : public tNMEA2000
|
||||
{
|
||||
private:
|
||||
bool IsOpen;
|
||||
static bool CanInUse;
|
||||
|
||||
protected:
|
||||
struct tCANFrame {
|
||||
uint32_t id; // can identifier
|
||||
uint8_t len; // length of data
|
||||
uint8_t buf[8];
|
||||
};
|
||||
|
||||
protected:
|
||||
CAN_speed_t speed;
|
||||
gpio_num_t TxPin;
|
||||
gpio_num_t RxPin;
|
||||
QueueHandle_t RxQueue;
|
||||
QueueHandle_t TxQueue;
|
||||
Print *debugStream;
|
||||
int errOverrun=0;
|
||||
int errArb=0;
|
||||
int errBus=0;
|
||||
int errRecovery=0;
|
||||
|
||||
protected:
|
||||
void CAN_read_frame(); // Read frame to queue within interrupt
|
||||
void CAN_send_frame(tCANFrame &frame); // Send frame
|
||||
void CAN_init();
|
||||
void CAN_bus_off_recovery(); //recover from bus off
|
||||
|
||||
protected:
|
||||
bool CANSendFrame(unsigned long id, unsigned char len, const unsigned char *buf, bool wait_sent=true);
|
||||
bool CANOpen();
|
||||
bool CANGetFrame(unsigned long &id, unsigned char &len, unsigned char *buf);
|
||||
virtual void InitCANFrameBuffers();
|
||||
|
||||
public:
|
||||
tNMEA2000_esp32(gpio_num_t _TxPin=ESP32_CAN_TX_PIN,
|
||||
gpio_num_t _RxPin=ESP32_CAN_RX_PIN,
|
||||
Print *debugStream=NULL);
|
||||
|
||||
void InterruptHandler();
|
||||
};
|
||||
|
||||
#endif
|
|
@ -21,7 +21,7 @@ platform = espressif32
|
|||
framework = arduino
|
||||
lib_deps =
|
||||
ttlappalainen/NMEA2000-library @ ^4.17.2
|
||||
ttlappalainen/NMEA2000_esp32 @ ^1.0.3
|
||||
#ttlappalainen/NMEA2000_esp32 @ ^1.0.3
|
||||
ttlappalainen/NMEA0183 @ ^1.7.1
|
||||
bblanchon/ArduinoJson@^6.18.5
|
||||
ottowinter/ESPAsyncWebServer-esphome@^2.0.1
|
||||
|
|
24
src/main.cpp
24
src/main.cpp
|
@ -14,6 +14,7 @@
|
|||
#include "GwAppInfo.h"
|
||||
// #define GW_MESSAGE_DEBUG_ENABLED
|
||||
//#define FALLBACK_SERIAL
|
||||
//#define CAN_ESP_DEBUG
|
||||
const unsigned long HEAP_REPORT_TIME=2000; //set to 0 to disable heap reporting
|
||||
#include <Arduino.h>
|
||||
#include "GwApi.h"
|
||||
|
@ -27,7 +28,6 @@ const unsigned long HEAP_REPORT_TIME=2000; //set to 0 to disable heap reporting
|
|||
#define N2K_CERTIFICATION_LEVEL 0xff
|
||||
#endif
|
||||
|
||||
#include <NMEA2000_CAN.h> // This will automatically choose right CAN library and create suitable NMEA2000 object
|
||||
#include <ActisenseReader.h>
|
||||
#include <Seasmart.h>
|
||||
#include <N2kMessages.h>
|
||||
|
@ -66,6 +66,17 @@ const unsigned long HEAP_REPORT_TIME=2000; //set to 0 to disable heap reporting
|
|||
#include "GwChannel.h"
|
||||
#include "GwChannelList.h"
|
||||
|
||||
#include <NMEA2000_esp32.h> // forked from https://github.com/ttlappalainen/NMEA2000_esp32
|
||||
#ifdef FALLBACK_SERIAL
|
||||
#ifdef CAN_ESP_DEBUG
|
||||
#define CDBS &Serial
|
||||
#else
|
||||
#define CDBS NULL
|
||||
#endif
|
||||
tNMEA2000 &NMEA2000=*(new tNMEA2000_esp32(ESP32_CAN_TX_PIN,ESP32_CAN_RX_PIN,CDBS));
|
||||
#else
|
||||
tNMEA2000 &NMEA2000=*(new tNMEA2000_esp32());
|
||||
#endif
|
||||
|
||||
|
||||
#define MAX_NMEA2000_MESSAGE_SEASMART_SIZE 500
|
||||
|
@ -212,7 +223,12 @@ void handleN2kMessage(const tN2kMsg &n2kMsg,int sourceId, bool isConverted=false
|
|||
}
|
||||
if (sourceId != N2K_CHANNEL_ID && sendOutN2k){
|
||||
countNMEA2KOut.add(n2kMsg.PGN);
|
||||
NMEA2000.SendMsg(n2kMsg);
|
||||
if (NMEA2000.SendMsg(n2kMsg)){
|
||||
countNMEA2KOut.add(n2kMsg.PGN);
|
||||
}
|
||||
else{
|
||||
countNMEA2KOut.addFail(n2kMsg.PGN);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -622,8 +638,8 @@ void setup() {
|
|||
#ifdef FALLBACK_SERIAL
|
||||
fallbackSerial=true;
|
||||
//falling back to old style serial for logging
|
||||
Serial.begin(baud);
|
||||
Serial.printf("fallback serial enabled, error was %d\n",st);
|
||||
Serial.begin(115200);
|
||||
Serial.printf("fallback serial enabled\n");
|
||||
logger.prefix="FALLBACK:";
|
||||
#endif
|
||||
userCodeHandler.startInitTasks(MIN_USER_TASK);
|
||||
|
|
Loading…
Reference in New Issue