A CAN driver for the ESP32

Last year I ordered an ESP32, a chip that got quiet a lot of attention because it is small, affordable and comes with a free SDK. I worked with the predecessor, the ESP8266 and I think that the ESP32 is way easier to use and the SDK (called ESP-IDF) is much more powerful compared with the ESP8266.

Time to get hands on the ESP32 as I want a CAN/CANopen-Wifi bridge. The first few months nothing happend and I already thought that the chip will never arrive but finally I got mail from China. I started with a simple “Hello world” but soon found that there is no CAN driver included in the SDK. After some research I found that there is a CAN port and that the CAN module on the ESP32 is SJA1000 compatible.I stripped the driver to work out of the box with the latest SDK version. The code is not perfect (some magic numbers etc.) but should be a good starting point for an own driver.

As I do not want to go to jail because you build something with the code and someone dies:

This software is a PROTOTYPE version and is not designed or intended for use in production, especially not for safety-critical applications! The user represents and warrants that it will NOT use or redistribute the Software for such purposes. This prototype is for research purposes only. This software is provided “AS IS,” without a warranty of any kind.

 

First we need the API definition for CAN:

/**
 * \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 CAN Frame structure */
typedef struct {
	CAN_FIR_t	FIR;						/**< \brief Frame information record*/
    uint32_t 	MsgID;     					/**< \brief Message ID */
    union {
        uint8_t u8[8];						/**< \brief Payload byte access*/
        uint32_t u32[2];					/**< \brief Payload u32 access*/
    } data;
}CAN_frame_t;


/**
 * \brief Initialize the CAN Module
 *
 * \return 0 CAN Module had been initialized
 */
int CAN_init(void);

/**
 * \brief Send a can frame
 *
 * \param	p_frame	Pointer to the frame to be send, see #CAN_frame_t
 * \return  0 Frame has been written to the module
 */
int CAN_write_frame(const CAN_frame_t* p_frame);

/**
 * \brief Stops the CAN Module
 *
 * \return 0 CAN Module was stopped
 */
int CAN_stop(void);

 

Configuration

/** \brief CAN Node Bus speed */
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 configuration structure */
typedef struct  {
   CAN_speed_t         speed;         /**< \brief CAN speed. */
    gpio_num_t          tx_pin_id;      /**< \brief TX pin. */
    gpio_num_t          rx_pin_id;      /**< \brief RX pin. */
    QueueHandle_t       rx_queue;      /**< \brief Handler to FreeRTOS RX queue. */
}CAN_device_t;

extern CAN_device_t CAN_cfg;

CAN_device_t CAN_cfg = {
   .speed=CAN_SPEED_500KBPS,
   .tx_pin_id = GPIO_NUM_5,
   .rx_pin_id = GPIO_NUM_4,
   .rx_queue=NULL,
};

 

Register definition

/** \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;

 

Module Initialization

int CAN_init(){

	//Time quantum
	double __tq;

    //enable module
    SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_CAN_CLK_EN);
    CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_CAN_RST);

    //configure TX pin
    gpio_set_direction(CAN_cfg.tx_pin_id,GPIO_MODE_OUTPUT);
    gpio_matrix_out(CAN_cfg.tx_pin_id,CAN_TX_IDX,0,0);
    gpio_pad_select_gpio(CAN_cfg.tx_pin_id);

    //configure RX pin
	gpio_set_direction(CAN_cfg.rx_pin_id,GPIO_MODE_INPUT);
	gpio_matrix_in(CAN_cfg.rx_pin_id,CAN_RX_IDX,0);
	gpio_pad_select_gpio(CAN_cfg.rx_pin_id);

    //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(CAN_cfg.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/CAN_cfg.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 = 0xff;

    //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,CAN_isr,NULL,NULL);

    //Showtime. Release Reset Mode.
    MODULE_CAN->MOD.B.RM = 0;

    return 0;
}

 

Interrupt Service Routine (for all interrupts)

static void CAN_isr(void *arg_p){
    //Interrupt flag buffer
	__CAN_IRQ_t interrupt;

    // Read interrupt status and clears flags
    interrupt = MODULE_CAN->IR.U;

    // Handle TX complete interrupt
    if ((interrupt & __CAN_IRQ_TX) != 0) {
    	/*handler*/
    }

    // 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_DATA_OVERRUN			//0x8
                      | __CAN_IRQ_WAKEUP				//0x10
                      | __CAN_IRQ_ERR_PASSIVE			//0x20
                      | __CAN_IRQ_ARB_LOST				//0x40
                      | __CAN_IRQ_BUS_ERR				//0x80
	)) != 0) {
    	/*handler*/
    }
}

 

Read a Frame from the FIFO

static void CAN_read_frame(){

	//byte iterator
	uint8_t __byte_i;

	//frame read buffer
	CAN_frame_t __frame;

    //check if we have a queue. If not, operation is aborted.
    if (CAN_cfg.rx_queue == NULL){
        // Let the hardware know the frame has been read.
        MODULE_CAN->CMR.B.RRB=1;
        return;
    }

	//get FIR
	__frame.FIR.U=MODULE_CAN->MBX_CTRL.FCTRL.FIR.U;

    //check if this is a standard or extended CAN frame
    //standard frame
    if(__frame.FIR.B.FF==CAN_frame_std){

        //Get Message ID
        __frame.MsgID = _CAN_GET_STD_ID;

        //deep copy data bytes
        for(__byte_i=0;__byte_i<__frame.FIR.B.DLC;__byte_i++)
        	__frame.data.u8[__byte_i]=MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.STD.data[__byte_i];

    }
    //extended frame
    else{

        //Get Message ID
        __frame.MsgID = _CAN_GET_EXT_ID;

        //deep copy data bytes
        for(__byte_i=0;__byte_i<__frame.FIR.B.DLC;__byte_i++)
        	__frame.data.u8[__byte_i]=MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.data[__byte_i];

    }

    //send frame to input queue
    xQueueSendFromISR(CAN_cfg.rx_queue,&__frame,0);

    //Let the hardware know the frame has been read.
    MODULE_CAN->CMR.B.RRB=1;
}

 

Writing a CAN frame

int CAN_write_frame(const CAN_frame_t* p_frame){

	//byte iterator
	uint8_t __byte_i;

	//copy frame information record
	MODULE_CAN->MBX_CTRL.FCTRL.FIR.U=p_frame->FIR.U;

	//standard frame
	if(p_frame->FIR.B.FF==CAN_frame_std){

		//Write message ID
		_CAN_SET_STD_ID(p_frame->MsgID);

	    // Copy the frame data to the hardware
	    for(__byte_i=0;__byte_iFIR.B.DLC;__byte_i++)
	    	MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.STD.data[__byte_i]=p_frame->data.u8[__byte_i];

	}
	//extended frame
	else{

		//Write message ID
		_CAN_SET_EXT_ID(p_frame->MsgID);

	    // Copy the frame data to the hardware
	    for(__byte_i=0;__byte_iFIR.B.DLC;__byte_i++)
	    	MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.data[__byte_i]=p_frame->data.u8[__byte_i];

	}

    // Transmit frame
    MODULE_CAN->CMR.B.TR=1;

    return 0;
}

 

Stop CAN Module

int CAN_stop(){

	//enter reset mode
	MODULE_CAN->MOD.B.RM = 1;

	return 0;
}

 

An example project can be downloaded here:

GitHub

Download

20 thoughts on “A CAN driver for the ESP32”

  1. hi, you had done a very interesting work.
    im interested in developing a can driver for ESP32 and your work is very promising and well coded.
    can i use your code in my academic project and can you send me or post an example.
    thank you in advance.

  2. Hey guys,

    sorry for the late reply, I just have seen your posts. I will prepare an example project next week, including the latest version of the driver (dynamic baudrate calculation), based on the latest ESP-IDF. Of course you can use the code in your projects but plase not that there is absolutely no warrenty and it comes “AS IS”.

    Regards
    Thomas

  3. Hello again.
    Can you tell me if I need config anything to receive CAN2.0B extended frames?
    I’ve changed already CAN_read_frame function. I’am using MODULE_CAN->MBX_CTRL.FCTRL.FIR.B.FF flag to assembly message using MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.
    But I don’t get any message. I’ve been testing CAN2.0A standard frames at 500kbit and it is working correctly. But other device use 2.0B extended frames at 250kbit and I don’t get any message.
    Can you have any idea what can I check?

    • So you don’t receive any interrupts?
      I have not tried extended frames but my assumption is that the the acceptance filter is blocking them.
      I am afraid you have to check the SJA1000 manual and look where the acceptance filter for extended frames is configured. I guess its within the reserved field of ACC.

      • I receive interrupts only with bus error. But it is normal, in working 500kbit also have some errors after start. I’ll try with acceptance filter. Thank you!

        • Unfortunately, acceptance filter has correct setup (according to SJA1000 documentation).
          Have you any other idea what can be wrong? Maybe config bits for 250kbits? But I think I was reading than you have been trying this speed (esp32.com).

          • There is currently a pending pull request.
            I will try if I can test and merge it tomorrow, If not, I will have no access to a computer for the next weeks, so please test it on your own.

  4. Hi, thnx for posting this code. I am in the process of building a CAN listener. After looking at your code I’ve noticed that you are using pin GPIO5 and 6. Can it be set to any pins or are there limitations to which pins can be used? I am aware that pins 35 to 39 can only be used as inputs.

    • Hi,

      it depends on the board you are using. Depending on the board, some pins might be already in use. I am not aware of limitations in the GPIO matrix but also haven’t digged too deep into this topic. Maybe simply start with proven in use pins, check if your setup is working and then move to your desired pins.

  5. Hi ,
    I tried to build your sample project on esp-idf . but its showing the following errors.

    CC build/mbedtls/library/havege.o
    /bin/sh: //main.d: Is a directory
    make[1]: *** [/home/USER/esp-idf/make/component_wrapper.mk:275: //main.o] Error 1
    make: *** [C:/msys32/home/USER/esp-idf/make/project.mk:450: component-main-build] Error 2
    make: *** Waiting for unfinished jobs….

    please help..

  6. Hi Thomas,

    Thanks for the wonderful work.

    Quick question: Can you confirm if the SJA1000 can be connected to the car’s OBD II port or not? If not, where do we need to connect it in the car ?

    Thanks.

    • You would need an additional CAN Transceiver like the MCP2551. If you can see something depends on if the manufacturer has routed a “interesting” CAN Network to the connector and if the network only speaks classical CAN.

      Please be aware that connecting a normal CAN device to CAN-FD Networks can cause problems. For this reason and many others, be very careful when messing with a car.

Leave a Comment

This site uses cookies. By continuing to browse the site you are agreeing to our use of cookies.