From ff30f479e1f9e8f5f528e0b28a5dc12f53088b72 Mon Sep 17 00:00:00 2001 From: zelll Date: Sun, 7 May 2017 15:35:17 +0800 Subject: [PATCH] ESP32: CAN module (#1958) * can extension * can extension: bit timing and filter * can -> CAN * post CAN data callback * CAN docs * CAN: fixed receive, filter, extended frame * reorder fn in can.md, remove driver_can/Kconfig * fixed a leak when can.stop() --- components/driver_can/CAN.c | 241 +++++++++++++++++++++ components/driver_can/component.mk | 9 + components/driver_can/include/CAN.h | 69 ++++++ components/driver_can/include/CAN_config.h | 62 ++++++ components/driver_can/include/can_regdef.h | 237 ++++++++++++++++++++ components/modules/Kconfig | 6 + components/modules/can.c | 168 ++++++++++++++ docs/en/modules/can.md | 77 +++++++ mkdocs.yml | 1 + 9 files changed, 870 insertions(+) create mode 100644 components/driver_can/CAN.c create mode 100644 components/driver_can/component.mk create mode 100644 components/driver_can/include/CAN.h create mode 100644 components/driver_can/include/CAN_config.h create mode 100644 components/driver_can/include/can_regdef.h create mode 100644 components/modules/can.c create mode 100644 docs/en/modules/can.md diff --git a/components/driver_can/CAN.c b/components/driver_can/CAN.c new file mode 100644 index 00000000..674f8b17 --- /dev/null +++ b/components/driver_can/CAN.c @@ -0,0 +1,241 @@ +/** + * @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. + * + */ + +#include "CAN.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" + +#include "esp_intr.h" +#include "soc/dport_reg.h" +#include + +#include "driver/gpio.h" + +#include "can_regdef.h" +#include "CAN_config.h" + + +static void CAN_read_frame(); +static void CAN_isr(void *arg_p); + + +static void CAN_isr(void *arg_p){ + + uint8_t interrupt; + + // Read interrupt status and clears flags + interrupt = MODULE_CAN->IR.U; + + // Handle TX complete interrupt + if ((interrupt & __CAN_IRQ_TX) != 0) { + + } + + // Handle RX frame available interrupt + if ((interrupt & __CAN_IRQ_RX) != 0) { + if (CAN_cfg.rx_queue == NULL) + return; + + 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) { + + } +} + + +static void CAN_read_frame(){ + + //byte iterator + uint8_t __byte_i; + + //frame read buffer + CAN_frame_t __frame; + + // for extended format frames, FF is 1. + if(MODULE_CAN->MBX_CTRL.FCTRL.FIR.B.FF==1){ + //Get Message ID + __frame.MsgID = (((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)); + + //get DLC + __frame.DLC = MODULE_CAN->MBX_CTRL.FCTRL.FIR.B.DLC; + __frame.Extended = 1; + //deep copy data bytes + for(__byte_i=0;__byte_i<__frame.DLC;__byte_i++) + __frame.data.u8[__byte_i]=MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.data[__byte_i]; + } else { + //Get Message ID + __frame.MsgID = (((uint32_t)MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.STD.ID[0] << 3) | (MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.STD.ID[1]>>5)); + + //get DLC + __frame.DLC = MODULE_CAN->MBX_CTRL.FCTRL.FIR.B.DLC; + __frame.Extended = 0; + //deep copy data bytes + for(__byte_i=0;__byte_i<__frame.DLC;__byte_i++) + __frame.data.u8[__byte_i]=MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.STD.data[__byte_i]; + } + // Let the hardware know the frame has been read. + MODULE_CAN->CMR.B.RRB=1; + + //send frame to input queue + xQueueSendFromISR(CAN_cfg.rx_queue,&__frame,0); +} + + +int CAN_write_frame(const CAN_frame_t* p_frame){ + + //byte iterator + uint8_t __byte_i; + + if(p_frame->Extended) { + MODULE_CAN->MBX_CTRL.FCTRL.FIR.U=p_frame->DLC | 0x80; + + //Write message ID + MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[0] = ((p_frame->MsgID & 0x1fe00000) >> 21); + MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[1] = ((p_frame->MsgID & 0x001fe000) >> 13); + MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[2] = ((p_frame->MsgID & 0x00001fe0) >> 5); + MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[3] = ((p_frame->MsgID & 0x0000001f) << 3); + + // Copy the frame data to the hardware + for(__byte_i=0;__byte_iDLC;__byte_i++) + MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.data[__byte_i]=p_frame->data.u8[__byte_i]; + } else { + //set frame format to standard and no RTR (needs to be done in a single write) + MODULE_CAN->MBX_CTRL.FCTRL.FIR.U=p_frame->DLC; + + //Write message ID + MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.STD.ID[0] = ((p_frame->MsgID) >> 3); + MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.STD.ID[1] = ((p_frame->MsgID) << 5); + + // Copy the frame data to the hardware + for(__byte_i=0;__byte_iDLC;__byte_i++) + MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.STD.data[__byte_i]=p_frame->data.u8[__byte_i]; + } + // Transmit frame + MODULE_CAN->CMR.B.TR=1; + + return 0; +} + + + +int CAN_init(){ + + //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; + + //select time quantum and set TSEG1 + switch(CAN_cfg.speed){ + case CAN_SPEED_1000KBPS: + case CAN_SPEED_800KBPS: + MODULE_CAN->BTR1.B.TSEG1 = 8 - 1; + MODULE_CAN->BTR1.B.TSEG2 = 1 - 1; + MODULE_CAN->BTR0.B.BRP = APB_CLK_FREQ / CAN_cfg.speed / 2000 / (1 + 8 + 1) - 1; + break; + default: + MODULE_CAN->BTR1.B.TSEG1 = 13 - 1; + MODULE_CAN->BTR1.B.TSEG2 = 2 - 1; + MODULE_CAN->BTR0.B.BRP = APB_CLK_FREQ / CAN_cfg.speed/ 2000 / (1 + 13 + 2) - 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; + + MODULE_CAN->MOD.B.AFM = CAN_cfg.dual_filter? 0 : 1; + + //no acceptance filtering, as we want to fetch all messages + MODULE_CAN->MBX_CTRL.ACC.CODE[0] = CAN_cfg.code >> 24; + MODULE_CAN->MBX_CTRL.ACC.CODE[1] = (CAN_cfg.code >> 16) & 0x00ff; + MODULE_CAN->MBX_CTRL.ACC.CODE[2] = (CAN_cfg.code >> 8) & 0x00ff; + MODULE_CAN->MBX_CTRL.ACC.CODE[3] = CAN_cfg.code & 0x00ff; + MODULE_CAN->MBX_CTRL.ACC.MASK[0] = CAN_cfg.mask >> 24; + MODULE_CAN->MBX_CTRL.ACC.MASK[1] = (CAN_cfg.mask >> 16) & 0x00ff; + MODULE_CAN->MBX_CTRL.ACC.MASK[2] = (CAN_cfg.mask >> 8) & 0x00ff; + MODULE_CAN->MBX_CTRL.ACC.MASK[3] = CAN_cfg.mask & 0x00ff; + + //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; +} + +int CAN_stop(){ + + MODULE_CAN->MOD.B.RM = 1; + + return 0; +} diff --git a/components/driver_can/component.mk b/components/driver_can/component.mk new file mode 100644 index 00000000..8c8d52ab --- /dev/null +++ b/components/driver_can/component.mk @@ -0,0 +1,9 @@ +# +# Main component makefile. +# +# This Makefile can be left empty. By default, it will take the sources in the +# src/ directory, compile them and link them into lib(subdirectory_name).a +# in the build directory. This behaviour is entirely configurable, +# please read the ESP-IDF documents if you need to do this. +# + diff --git a/components/driver_can/include/CAN.h b/components/driver_can/include/CAN.h new file mode 100644 index 00000000..feb6d4fd --- /dev/null +++ b/components/driver_can/include/CAN.h @@ -0,0 +1,69 @@ +/** + * @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_H__ +#define __DRIVERS_CAN_H__ + +#include +#include "CAN_config.h" + +/** \brief CAN Frame structure */ +typedef struct { + uint8_t Extended; + uint32_t MsgID; /**< \brief Message ID */ + uint32_t DLC; /**< \brief Length */ + 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); + +#endif diff --git a/components/driver_can/include/CAN_config.h b/components/driver_can/include/CAN_config.h new file mode 100644 index 00000000..67f61650 --- /dev/null +++ b/components/driver_can/include/CAN_config.h @@ -0,0 +1,62 @@ +/** + * @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_CFG_H__ +#define __DRIVERS_CAN_CFG_H__ + +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "driver/gpio.h" + + +/** \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. */ + uint32_t code; + uint32_t mask; + bool dual_filter; +}CAN_device_t; + +/** \brief CAN configuration reference */ +extern CAN_device_t CAN_cfg; + + +#endif /* __DRIVERS_CAN_CFG_H__ */ diff --git a/components/driver_can/include/can_regdef.h b/components/driver_can/include/can_regdef.h new file mode 100644 index 00000000..c3ce0daf --- /dev/null +++ b/components/driver_can/include/can_regdef.h @@ -0,0 +1,237 @@ +/** + * @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_ + +/** \brief Start address of CAN registers */ +#define MODULE_CAN ((volatile CAN_Module_t *)0x3ff6b000) + +/** \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 { + union{uint32_t U; /**< \brief Unsigned access */ + struct { + unsigned int DLC:4; /**< \brief [3:0] DLC, Data length container */ + unsigned int unknown_2:2; /**< \brief \internal unknown */ + unsigned int RTR:1; /**< \brief [6:6] RTR, Remote Transmission Request */ + unsigned int FF:1; /**< \brief [7:7] Frame Format */ + unsigned int reserved_24:24; /**< \brief \internal Reserved */ + } B; + } 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; + +#endif /* __DRIVERS_CAN_REGDEF_H_ */ diff --git a/components/modules/Kconfig b/components/modules/Kconfig index 381e818b..796e5d1f 100644 --- a/components/modules/Kconfig +++ b/components/modules/Kconfig @@ -67,6 +67,12 @@ config LUA_MODULE_BTHCI help Includes the simple BlueTooth HCI module. +config LUA_MODULE_CAN + bool "CAN module" + default "n" + help + Includes the can module. + config LUA_MODULE_DHT bool "DHT11/21/22/AM2301/AM2302 module" default "n" diff --git a/components/modules/can.c b/components/modules/can.c new file mode 100644 index 00000000..3768a59b --- /dev/null +++ b/components/modules/can.c @@ -0,0 +1,168 @@ +// Module for interfacing with adc hardware + +#include "module.h" +#include "lauxlib.h" +#include "platform.h" + +#include "CAN.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "esp_task.h" +#include "esp_log.h" + +#include + +#include "task/task.h" + +CAN_device_t CAN_cfg = { + .speed = CAN_SPEED_1000KBPS, // CAN Node baudrade + .tx_pin_id = -1, // CAN TX pin + .rx_pin_id = -1, // CAN RX pin + .rx_queue = NULL, // FreeRTOS queue for RX frames + .code = 0, + .mask = 0xffffffff, + .dual_filter = false +}; + +static task_handle_t can_data_task_id; +static int can_on_received = LUA_NOREF; + +static xTaskHandle xCanTaskHandle = NULL; + +// LUA +static void can_data_task( task_param_t param, task_prio_t prio ) { + CAN_frame_t *frame = (CAN_frame_t *)param; + + if(can_on_received == LUA_NOREF) { + free( frame ); + return; + } + lua_State *L = lua_getstate(); + + lua_rawgeti(L, LUA_REGISTRYINDEX, can_on_received); + lua_pushinteger(L, frame->Extended? 1 : 0); + lua_pushinteger(L, frame->MsgID); + lua_pushlstring(L, (char *)frame->data.u8, frame->DLC); + free( frame ); + lua_call(L, 3, 0); +} + +// RTOS +static void task_CAN( void *pvParameters ){ + (void)pvParameters; + + //frame buffer + CAN_frame_t frame; + + //create CAN RX Queue + CAN_cfg.rx_queue = xQueueCreate(10, sizeof(CAN_frame_t)); + + //start CAN Module + CAN_init(); + + for (;;){ + //receive next CAN frame from queue + if( xQueueReceive( CAN_cfg.rx_queue, &frame, 3 * portTICK_PERIOD_MS ) == pdTRUE ){ + CAN_frame_t *postFrame = (CAN_frame_t *)malloc( sizeof( CAN_frame_t ) ); + memcpy(postFrame, &frame, sizeof( CAN_frame_t )); + task_post_medium( can_data_task_id, (task_param_t)postFrame ); + } + } +} + +// Lua: setup( {}, callback ) +static int can_setup( lua_State *L ) +{ + if(xCanTaskHandle != NULL) + luaL_error( L, "Stop CAN before setup" ); + luaL_checkanytable (L, 1); + + luaL_checkanyfunction (L, 2); + lua_settop (L, 2); + if(can_on_received != LUA_NOREF) luaL_unref(L, LUA_REGISTRYINDEX, can_on_received); + can_on_received = luaL_ref(L, LUA_REGISTRYINDEX); + + lua_getfield (L, 1, "speed"); + CAN_cfg.speed = luaL_checkint(L, -1); + lua_getfield (L, 1, "tx"); + CAN_cfg.tx_pin_id = luaL_checkint(L, -1); + lua_getfield (L, 1, "rx"); + CAN_cfg.rx_pin_id = luaL_checkint(L, -1); + lua_getfield (L, 1, "dual_filter"); + CAN_cfg.dual_filter = lua_toboolean(L, 0); + lua_getfield (L, 1, "code"); + CAN_cfg.code = (uint32_t)luaL_optnumber(L, -1, 0); + lua_getfield (L, 1, "mask"); + CAN_cfg.mask = (uint32_t)luaL_optnumber(L, -1, 0x0ffffffff); + return 0; +} + +static int can_start( lua_State *L ) +{ + if(xCanTaskHandle != NULL) + luaL_error( L, "CAN started" ); + xTaskCreate(task_CAN, "CAN", 2048, NULL, ESP_TASK_MAIN_PRIO + 1, &xCanTaskHandle); + return 0; +} + +static int can_stop( lua_State *L ) +{ + if(xCanTaskHandle) { + vTaskDelete(xCanTaskHandle); + xCanTaskHandle = NULL; + } + CAN_stop(); + if(CAN_cfg.rx_queue) { + vQueueDelete( CAN_cfg.rx_queue ); + CAN_cfg.rx_queue = NULL; + } + return 0; +} + +static int can_send( lua_State *L ) +{ + uint32_t format = (uint8_t)luaL_checkinteger( L, 1 ); + uint32_t msg_id = luaL_checkinteger( L, 2 ); + size_t len; + const char *data = luaL_checklstring( L, 3, &len ); + uint8_t i; + CAN_frame_t frame; + + if(xCanTaskHandle == NULL) + luaL_error( L, "CAN not started" ); + + if(len > 8) + luaL_error( L, "CAN can not send more than 8 bytes" ); + + frame.Extended = format? 1 : 0; + frame.MsgID = msg_id; + frame.DLC = len; + + for(i = 0; i < len; i++) + frame.data.u8[i] = data[i]; + + CAN_write_frame(&frame); + return 0; +} + +// Module function map +static const LUA_REG_TYPE can_map[] = +{ + { LSTRKEY( "setup" ), LFUNCVAL( can_setup ) }, + { LSTRKEY( "start" ), LFUNCVAL( can_start ) }, + { LSTRKEY( "stop" ), LFUNCVAL( can_stop ) }, + { LSTRKEY( "send" ), LFUNCVAL( can_send ) }, + { LSTRKEY( "STANDARD_FRAME" ), LNUMVAL( 0 ) }, + { LSTRKEY( "EXTENDED_FRAME" ), LNUMVAL( 1 ) }, + { LNILKEY, LNILVAL } +}; + +int luaopen_can( lua_State *L ) { + can_data_task_id = task_get_id( can_data_task ); // reset CAN after sw reset + CAN_stop(); + return 0; +} + +NODEMCU_MODULE(CAN, "can", can_map, luaopen_can); diff --git a/docs/en/modules/can.md b/docs/en/modules/can.md new file mode 100644 index 00000000..68286408 --- /dev/null +++ b/docs/en/modules/can.md @@ -0,0 +1,77 @@ +# CAN Module +| Since | Origin / Contributor | Maintainer | Source | +| :----- | :-------------------- | :---------- | :------ | +| 2017-04-27 | [@ThomasBarth](https://github.com/ThomasBarth/ESP32-CAN-Driver/), zelll | | [can.c](../../../components/modules/can.c)| + +The CAN module provides access to the in-built CAN controller. + + +## can.send() +Send a frame. + +#### Syntax +`can.send(format, msg_id, data)` + +#### Parameters +- `format` Frame format. `can.STANDARD_FRAME` or `can.EXTENDED_FRAME` +- `msg_id` CAN Messge ID +- `data` CAN data, up to 8 bytes + +#### Returns +nil + + +## can.setup() +Configuration CAN controller. + +#### Syntax +```lua +can.setup({ + speed = 1000, + tx = 5, + rx = 4, + dual_filter = false, + code = 0, + mask = 0xffffffff +}, function(format, msg_id, data) end) +``` + +#### Parameters +- `config` + - `speed` kbps. One of following value: `1000`, `800`, `500`, `250`, `100`. + - `tx` Pin num for TX. + - `rx` Pin num for RX. + - `dual_filter` `true` dual filter mode, `false` single filter mode (default) + - `code` 4-bytes integer. Use this with mask to filter CAN frame. Default: `0`. See [SJA1000](http://www.nxp.com/documents/data_sheet/SJA1000.pdf) + - `mask` 4-bytes integer. Default: `0xffffffff` +- `callback` function to be called when CAN data received. + - `format` Frame format. `can.STANDARD_FRAME` or `can.EXTENDED_FRAME` + - `msg_id` CAN Messge ID + - `data` CAN data, up to 8 bytes + +#### Returns +nil + + +## can.start() +Start CAN controller. + +#### Syntax +`can.start()` + +#### Parameters + +#### Returns +nil + + +## can.stop() +Stop CAN controller. + +#### Syntax +`can.stop()` + +#### Parameters + +#### Returns +nil diff --git a/mkdocs.yml b/mkdocs.yml index 1bd5913f..f19da2e4 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -35,6 +35,7 @@ pages: - 'adc': 'en/modules/adc.md' - 'bit': 'en/modules/bit.md' - 'bthci': 'en/modules/bthci.md' + - 'can': 'en/modules/can.md' - 'dht': 'en/modules/dht.md' - 'file': 'en/modules/file.md' - 'gpio': 'en/modules/gpio.md'