mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
cpu/esp32: port periph/can to ESP-IDF twai HAL
This commit is contained in:
parent
1b2360e6da
commit
fccd3821dd
@ -74,19 +74,9 @@ typedef struct can {
|
||||
uint32_t rx_frames_num; /**< number of frames in ring buffer */
|
||||
uint32_t rx_filter_num; /**< number of acceptance filters */
|
||||
|
||||
bool powered_up; /**< device is powered up */
|
||||
|
||||
gpio_t tx_pin; /**< CAN transceiver TX pin */
|
||||
gpio_t rx_pin; /**< CAN transceiver RX pin */
|
||||
#ifdef ESP_CAN_CLK_OUT
|
||||
gpio_t clk_out_pin; /**< optional CLK_OUT pin */
|
||||
#endif
|
||||
#ifdef ESP_CAN_BUS_ON_OFF
|
||||
gpio_t bus_on_off_pin; /**< optional BUS_ON_OFF pin */
|
||||
#endif
|
||||
bool powered_up; /**< device is powered up */
|
||||
|
||||
uint32_t events; /**< events triggered by the last interrupt */
|
||||
|
||||
} can_t;
|
||||
|
||||
/** CAN device type can_t is redefined by ESP CAN */
|
||||
@ -99,10 +89,10 @@ typedef struct {
|
||||
uint32_t bitrate; /**< Bitrate */
|
||||
gpio_t tx_pin; /**< CAN transceiver TX pin */
|
||||
gpio_t rx_pin; /**< CAN transceiver RX pin */
|
||||
#ifdef ESP_CAN_CLK_OUT
|
||||
#ifdef CAN_CLK_OUT
|
||||
gpio_t clk_out_pin; /**< optional CLK_OUT pin */
|
||||
#endif
|
||||
#ifdef ESP_CAN_BUS_ON_OFF
|
||||
#ifdef CAN_BUS_ON_OFF
|
||||
gpio_t bus_on_off_pin; /**< optional BUS_ON_OFF pin */
|
||||
#endif
|
||||
} can_conf_t;
|
||||
|
@ -22,28 +22,17 @@
|
||||
#include "can_esp.h"
|
||||
#include "can_params.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_common.h"
|
||||
#include "gpio_arch.h"
|
||||
#include "irq_arch.h"
|
||||
#include "tools.h"
|
||||
|
||||
#include "can/common.h"
|
||||
#include "can/device.h"
|
||||
#include "driver/periph_ctrl.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_rom_gpio.h"
|
||||
#include "hal/interrupt_controller_types.h"
|
||||
#include "hal/interrupt_controller_ll.h"
|
||||
#include "hal/twai_hal.h"
|
||||
#include "log.h"
|
||||
#include "rom/ets_sys.h"
|
||||
|
||||
#include "periph/can.h"
|
||||
#include "periph/gpio.h"
|
||||
|
||||
#include "hal/twai_ll.h"
|
||||
#include "soc/gpio_sig_map.h"
|
||||
#include "soc/gpio_struct.h"
|
||||
#include "soc/twai_struct.h"
|
||||
#include "soc/soc.h"
|
||||
|
||||
#include "xtensa/xtensa_api.h"
|
||||
|
||||
#define ENABLE_DEBUG 0
|
||||
#include "debug.h"
|
||||
@ -88,28 +77,13 @@
|
||||
#define ESP_CAN_MAX_DATA_LEN 8 /* 8 data bytes at maximum */
|
||||
#define ESP_CAN_FRAME_LEN 13 /* 13 bytes at maximum */
|
||||
|
||||
typedef union esp_can_frame {
|
||||
struct {
|
||||
struct {
|
||||
uint8_t dlc :4; /* frame payload length */
|
||||
uint8_t zero:2; /* don't care, should be zero */
|
||||
uint8_t rtr :1; /* remote transmission request frame */
|
||||
uint8_t eff :1; /* extended ID frame format */
|
||||
};
|
||||
union {
|
||||
struct {
|
||||
uint8_t id[ESP_CAN_SFF_ID_LEN]; /* 11 bit standard frame identifier */
|
||||
uint8_t data[ESP_CAN_MAX_DATA_LEN]; /* data bytes */
|
||||
uint8_t reserved8[2];
|
||||
} standard;
|
||||
struct {
|
||||
uint8_t id[ESP_CAN_EFF_ID_LEN]; /* 29 bit standard frame identifier */
|
||||
uint8_t data[ESP_CAN_MAX_DATA_LEN]; /* data bytes */
|
||||
} extended;
|
||||
};
|
||||
};
|
||||
uint8_t bytes[ESP_CAN_FRAME_LEN];
|
||||
} _esp_can_frame_t;
|
||||
#if !defined(CAN_CLK_OUT) && !defined(CAN_CLK_OUT_DIV)
|
||||
/* if CAN_CLK_OUT is not used, CAN_CLKOUT_DIV is set to 0 */
|
||||
#define CAN_CLK_OUT_DIV 0
|
||||
#elif defined(CAN_CLK_OUT) && !defined(CAN_CLK_OUT_DIV)
|
||||
/* if CAN_CLK_OUT is used, CAN_CLK_OUT_DIV has to be defined */
|
||||
#error "CAN_CLK_OUT pin defined but not the CAN_CLK_OUT_DIV"
|
||||
#endif
|
||||
|
||||
/* driver interface functions */
|
||||
static int _esp_can_init(candev_t *candev);
|
||||
@ -121,13 +95,16 @@ static int _esp_can_abort(candev_t *candev, const struct can_frame *frame);
|
||||
static int _esp_can_set_filter(candev_t *candev, const struct can_filter *filter);
|
||||
static int _esp_can_remove_filter(candev_t *candev, const struct can_filter *filter);
|
||||
|
||||
/* internal function declarations, we don't need device since we habe only one */
|
||||
static void _esp_can_set_bittiming (can_t *dev);
|
||||
/* internal function declarations, we don't need the device since we have only one */
|
||||
static void _esp_can_set_bittiming(can_t *dev);
|
||||
static void _esp_can_start(can_t *dev);
|
||||
static void _esp_can_stop(can_t *dev);
|
||||
static void _esp_can_power_up(can_t *dev);
|
||||
static void _esp_can_power_down(can_t *dev);
|
||||
static void _esp_can_init_pins(can_t *dev);
|
||||
static int _esp_can_set_mode(can_t *dev, canopt_state_t state);
|
||||
static void IRAM_ATTR _esp_can_intr_handler (void *arg);
|
||||
static void _esp_can_init_pins(void);
|
||||
static void _esp_can_deinit_pins(void);
|
||||
static void IRAM_ATTR _esp_can_intr_handler(void *arg);
|
||||
|
||||
/** ESP32 CAN low level device driver data */
|
||||
static const candev_driver_t _esp_can_driver = {
|
||||
@ -221,6 +198,8 @@ static void _esp_can_isr(candev_t *candev)
|
||||
}
|
||||
}
|
||||
|
||||
twai_hal_context_t hw;
|
||||
|
||||
static int _esp_can_init(candev_t *candev)
|
||||
{
|
||||
can_t *dev = container_of(candev, can_t, candev);
|
||||
@ -229,12 +208,12 @@ static int _esp_can_init(candev_t *candev)
|
||||
|
||||
assert(dev);
|
||||
|
||||
/* initialize used GPIOs */
|
||||
_esp_can_init_pins(dev);
|
||||
|
||||
/* power up and configure the CAN controller */
|
||||
/* power up and configure the CAN controller and initialize the pins */
|
||||
_esp_can_power_up(dev);
|
||||
|
||||
/* start the CAN controller in configured operation mode */
|
||||
_esp_can_start(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -259,18 +238,18 @@ static int _esp_can_send(candev_t *candev, const struct can_frame *frame)
|
||||
dev->tx_frame = (struct can_frame*)frame;
|
||||
|
||||
/* prepare the frame as expected by ESP32 */
|
||||
_esp_can_frame_t esp_frame = {};
|
||||
twai_hal_frame_t esp_frame = { };
|
||||
|
||||
esp_frame.dlc = frame->can_dlc;
|
||||
esp_frame.rtr = (frame->can_id & CAN_RTR_FLAG);
|
||||
esp_frame.eff = (frame->can_id & CAN_EFF_FLAG);
|
||||
esp_frame.frame_format = (frame->can_id & CAN_EFF_FLAG);
|
||||
|
||||
/* esp_frame is a union that provides two views on the same memory: one
|
||||
* tailored for efficient access and the other for readable code. Likely
|
||||
* due to cppcheck not finding all headers it wrongly assumes that values
|
||||
* are assigned but never read again (unreadVariable). But the union members
|
||||
* are read via the aliases to the same memory. */
|
||||
if (esp_frame.eff) {
|
||||
if (esp_frame.frame_format) {
|
||||
uint32_t id = frame->can_id & CAN_EFF_MASK;
|
||||
/* cppcheck-suppress unreadVariable */
|
||||
esp_frame.extended.id[0] = (id >> 21) & 0xff;
|
||||
@ -289,16 +268,17 @@ static int _esp_can_send(candev_t *candev, const struct can_frame *frame)
|
||||
esp_frame.standard.id[1] = (id << 5) & 0xff;
|
||||
}
|
||||
|
||||
memcpy(esp_frame.eff ? &esp_frame.extended.data
|
||||
: &esp_frame.standard.data, frame->data, ESP_CAN_MAX_DATA_LEN);
|
||||
|
||||
/* place the frame in TX buffer of ESP32 */
|
||||
for (unsigned i = 0; i < ESP_CAN_FRAME_LEN; i++) {
|
||||
CAN.tx_rx_buffer[i].val = esp_frame.bytes[i];
|
||||
}
|
||||
/* copy data from can_frame to esp_frame */
|
||||
memcpy(esp_frame.frame_format ? &esp_frame.extended.data
|
||||
: &esp_frame.standard.data,
|
||||
frame->data, ESP_CAN_MAX_DATA_LEN);
|
||||
|
||||
/* set the single shot transmit command without self-receiption */
|
||||
CAN.command_reg.val = ESP_CMD_TX_SINGLE_SHOT;
|
||||
esp_frame.single_shot = 1;
|
||||
esp_frame.self_reception = 0;
|
||||
|
||||
/* place the frame in TX buffer and trigger the command */
|
||||
twai_hal_set_tx_buffer_and_transmit(&hw, &esp_frame);
|
||||
|
||||
critical_exit();
|
||||
|
||||
@ -322,32 +302,48 @@ static int _esp_can_set(candev_t *candev, canopt_t opt, void *value, size_t valu
|
||||
return -EOVERFLOW;
|
||||
}
|
||||
dev->candev.bittiming = *((struct can_bittiming*)value);
|
||||
_esp_can_stop(dev);
|
||||
_esp_can_set_bittiming(dev);
|
||||
_esp_can_start(dev);
|
||||
|
||||
res = sizeof(struct can_bittiming);
|
||||
break;
|
||||
|
||||
case CANOPT_STATE:
|
||||
DEBUG("%s CANOPT_STATE\n", __func__);
|
||||
DEBUG("%s CANOPT_STATE %d\n", __func__, *((canopt_state_t *)value));
|
||||
if (dev->powered_up) {
|
||||
_esp_can_stop(dev);
|
||||
}
|
||||
res = _esp_can_set_mode(dev, *((canopt_state_t *)value));
|
||||
if (dev->powered_up) {
|
||||
_esp_can_start(dev);
|
||||
}
|
||||
if (res == 0) {
|
||||
res = sizeof(uint16_t);
|
||||
}
|
||||
break;
|
||||
|
||||
case CANOPT_TEC:
|
||||
case CANOPT_REC:
|
||||
DEBUG("%s %s\n", __func__, (opt == CANOPT_TEC) ? "CANOPT_TEC" : "CANOPT_REC");
|
||||
DEBUG("%s %s\n", __func__, "CANOPT_TEC");
|
||||
if (value_len < sizeof(uint16_t)) {
|
||||
DEBUG("%s size error\n", __func__);
|
||||
return -EOVERFLOW;
|
||||
}
|
||||
if (opt == CANOPT_TEC) {
|
||||
CAN.tx_error_counter_reg.txerr = *((uint16_t*)value);
|
||||
}
|
||||
else {
|
||||
CAN.rx_error_counter_reg.rxerr = *((uint16_t*)value);
|
||||
_esp_can_stop(dev);
|
||||
twai_ll_set_tec(hw.dev, *((uint16_t*)value));
|
||||
_esp_can_start(dev);
|
||||
res = sizeof(uint16_t);
|
||||
break;
|
||||
|
||||
case CANOPT_REC:
|
||||
DEBUG("%s %s\n", __func__, "CANOPT_REC");
|
||||
if (value_len < sizeof(uint16_t)) {
|
||||
DEBUG("%s size error\n", __func__);
|
||||
return -EOVERFLOW;
|
||||
}
|
||||
_esp_can_stop(dev);
|
||||
twai_ll_set_rec(hw.dev, *((uint16_t*)value));
|
||||
_esp_can_start(dev);
|
||||
res = sizeof(uint16_t);
|
||||
break;
|
||||
|
||||
@ -406,20 +402,26 @@ static int _esp_can_get(candev_t *candev, canopt_t opt, void *value, size_t max_
|
||||
break;
|
||||
|
||||
case CANOPT_TEC:
|
||||
case CANOPT_REC:
|
||||
DEBUG("%s %s\n", __func__, (opt == CANOPT_TEC) ? "CANOPT_TEC" : "CANOPT_REC");
|
||||
DEBUG("%s %s\n", __func__, "CANOPT_TEC");
|
||||
if (max_len < sizeof(uint16_t)) {
|
||||
res = EOVERFLOW;
|
||||
break;
|
||||
}
|
||||
|
||||
if (opt == CANOPT_TEC) {
|
||||
*((uint16_t *)value) = CAN.tx_error_counter_reg.txerr;
|
||||
}
|
||||
else {
|
||||
*((uint16_t *)value) = CAN.rx_error_counter_reg.rxerr;
|
||||
*((uint16_t *)value) = twai_ll_get_tec(hw.dev);
|
||||
|
||||
res = sizeof(uint16_t);
|
||||
break;
|
||||
|
||||
case CANOPT_REC:
|
||||
DEBUG("%s %s\n", __func__, "CANOPT_REC");
|
||||
if (max_len < sizeof(uint16_t)) {
|
||||
res = EOVERFLOW;
|
||||
break;
|
||||
}
|
||||
|
||||
*((uint16_t *)value) = twai_ll_get_rec(hw.dev);
|
||||
|
||||
res = sizeof(uint16_t);
|
||||
break;
|
||||
|
||||
@ -461,7 +463,7 @@ static int _esp_can_abort(candev_t *candev, const struct can_frame *frame)
|
||||
assert(frame);
|
||||
|
||||
/* abort transmission command */
|
||||
CAN.command_reg.val = ESP_CMD_ABORT_TX;
|
||||
twai_ll_set_cmd_abort_tx(hw.dev);
|
||||
|
||||
/* mark the transmitter as free */
|
||||
dev->tx_frame = NULL;
|
||||
@ -503,6 +505,7 @@ static int _esp_can_set_filter(candev_t *candev, const struct can_filter *filter
|
||||
/* set the filter and return the filter index */
|
||||
dev->rx_filters[i] = *filter;
|
||||
dev->rx_filter_num++;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
@ -530,90 +533,52 @@ static int _esp_can_remove_filter(candev_t *candev, const struct can_filter *fil
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal functions. All these functions have no dev parameter since
|
||||
* they directly access the only one existing CAN controller.
|
||||
* Internal functions.
|
||||
*/
|
||||
|
||||
static void _esp_can_set_reset_mode(void)
|
||||
{
|
||||
DEBUG("%s\n", __func__);
|
||||
|
||||
while (CAN.mode_reg.rm != 1) {
|
||||
CAN.mode_reg.rm = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void _esp_can_set_operating_mode(void)
|
||||
{
|
||||
DEBUG("%s\n", __func__);
|
||||
|
||||
while (CAN.mode_reg.rm != 0) {
|
||||
CAN.mode_reg.rm = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int _esp_can_config(can_t *dev)
|
||||
static void _esp_can_start(can_t *dev)
|
||||
{
|
||||
DEBUG("%s dev=%p\n", __func__, dev);
|
||||
|
||||
assert(dev);
|
||||
|
||||
critical_enter();
|
||||
/* start the CAN controller in configured mode */
|
||||
switch (dev->state) {
|
||||
case CANOPT_STATE_LISTEN_ONLY:
|
||||
twai_hal_start(&hw, TWAI_MODE_LISTEN_ONLY);
|
||||
break;
|
||||
case CANOPT_STATE_SLEEP:
|
||||
/* sleep mode is not supported, the normal mode is used instead */
|
||||
case CANOPT_STATE_ON:
|
||||
twai_hal_start(&hw, TWAI_MODE_NORMAL);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* reset mode to be able to write configuration registers */
|
||||
_esp_can_set_reset_mode();
|
||||
static void _esp_can_stop(can_t *dev)
|
||||
{
|
||||
DEBUG("%s dev=%p\n", __func__, dev);
|
||||
assert(dev);
|
||||
|
||||
/* configuration is done in listen only mode */
|
||||
CAN.mode_reg.stm = 0;
|
||||
CAN.mode_reg.lom = 1;
|
||||
|
||||
/* Use SJA1000 PeliCAN mode and registers layout */
|
||||
CAN.clock_divider_reg.cm = 1;
|
||||
|
||||
#ifndef ESP_CAN_CLK_OUT
|
||||
CAN.clock_divider_reg.co = 1;
|
||||
CAN.clock_divider_reg.cd = 0;
|
||||
#else
|
||||
uint32_t clk_out_div = ESP_CAN_CLK_OUT_DIV / 2 - 1;
|
||||
clk_out_div = (clk_out_div < 7) ? clk_out_div : 7;
|
||||
CAN.clock_divider_reg.co = 0;
|
||||
CAN.clock_divider_reg.cd = clk_out_div;
|
||||
#endif
|
||||
|
||||
/* set error counter values and warning limits */
|
||||
CAN.error_warning_limit_reg.ewl = ESP_CAN_ERROR_WARNING_LIMIT;
|
||||
CAN.tx_error_counter_reg.txerr = 0;
|
||||
CAN.rx_error_counter_reg.rxerr = 0;
|
||||
|
||||
/* accept all CAN messages, filtering is done by software */
|
||||
CAN.mode_reg.afm = 0; /* single filter */
|
||||
CAN.acceptance_filter.amr[0].byte = 0xff; /* all bits masked */
|
||||
CAN.acceptance_filter.amr[1].byte = 0xff;
|
||||
CAN.acceptance_filter.amr[2].byte = 0xff;
|
||||
CAN.acceptance_filter.amr[3].byte = 0xff;
|
||||
|
||||
/* clear interrupt status register by read and enable all interrupts */
|
||||
uint32_t tmp = CAN.interrupt_reg.val; (void)tmp;
|
||||
CAN.interrupt_enable_reg.val = ESP_CAN_INTR_MASK;
|
||||
|
||||
/* route CAN interrupt source to CPU interrupt and enable it */
|
||||
intr_matrix_set(PRO_CPU_NUM, ETS_CAN_INTR_SOURCE, CPU_INUM_CAN);
|
||||
xt_set_interrupt_handler(CPU_INUM_CAN, _esp_can_intr_handler, (void*)(uintptr_t)dev);
|
||||
xt_ints_on(BIT(CPU_INUM_CAN));
|
||||
|
||||
/* set bittiming from parameters as given in device data */
|
||||
_esp_can_set_bittiming(dev);
|
||||
|
||||
/* switch to operating mode */
|
||||
_esp_can_set_operating_mode();
|
||||
|
||||
critical_exit();
|
||||
|
||||
return 0;
|
||||
/* stop the CAN controller by entering the reset mode */
|
||||
twai_hal_stop(&hw);
|
||||
}
|
||||
|
||||
static void _esp_can_power_up(can_t *dev)
|
||||
{
|
||||
/**
|
||||
* Function esp_can_power_up
|
||||
* - powers up the CAN controller,
|
||||
* - initializes the HAL context,
|
||||
* - sets the timing and the acceptance filters according to configuration
|
||||
* - resets the error counters and sets the warning limit
|
||||
*
|
||||
* The CAN controller must be started in configured mode explicitly
|
||||
* afterwards using function _esp_can_start().
|
||||
*/
|
||||
|
||||
DEBUG("%s dev=%p\n", __func__, dev);
|
||||
assert(dev);
|
||||
|
||||
/* just return when already powered up */
|
||||
@ -621,20 +586,42 @@ static void _esp_can_power_up(can_t *dev)
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG("%s dev=%p\n", __func__, dev);
|
||||
|
||||
critical_enter();
|
||||
|
||||
/* power up and (re)configure the CAN controller */
|
||||
periph_module_enable(PERIPH_CAN_MODULE);
|
||||
/* power up the peripheral */
|
||||
periph_module_reset(PERIPH_TWAI_MODULE);
|
||||
periph_module_enable(PERIPH_TWAI_MODULE);
|
||||
|
||||
/* initialize the HAL context, on return the CAN controller is in listen
|
||||
* only mode but not yet started, the error counters are reset and
|
||||
* pending interrupts cleared */
|
||||
|
||||
if (!twai_hal_init(&hw)) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
/* set bittiming from parameters as given in device data */
|
||||
_esp_can_set_bittiming(dev);
|
||||
|
||||
/* set warning limit */
|
||||
twai_ll_set_err_warn_lim(hw.dev, ESP_CAN_ERROR_WARNING_LIMIT);
|
||||
|
||||
/* route CAN interrupt source to CPU interrupt and enable it */
|
||||
intr_matrix_set(PRO_CPU_NUM, ETS_TWAI_INTR_SOURCE, CPU_INUM_CAN);
|
||||
intr_cntrl_ll_set_int_handler(CPU_INUM_CAN, _esp_can_intr_handler, (void*)(uintptr_t)dev);
|
||||
intr_cntrl_ll_enable_interrupts(BIT(CPU_INUM_CAN));
|
||||
|
||||
/* initialize used GPIOs */
|
||||
_esp_can_init_pins();
|
||||
|
||||
dev->powered_up = true;
|
||||
_esp_can_config (dev);
|
||||
|
||||
critical_exit();
|
||||
}
|
||||
|
||||
static void _esp_can_power_down(can_t *dev)
|
||||
{
|
||||
DEBUG("%s dev=%p\n", __func__, dev);
|
||||
assert(dev);
|
||||
|
||||
/* just return when already powered down */
|
||||
@ -642,43 +629,76 @@ static void _esp_can_power_down(can_t *dev)
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG("%s dev=%p\n", __func__, dev);
|
||||
/* deinitialize used GPIOs */
|
||||
_esp_can_deinit_pins();
|
||||
|
||||
/* stop the CAN controller and deinitialize the HAL context */
|
||||
twai_hal_stop(&hw);
|
||||
twai_hal_deinit(&hw);
|
||||
|
||||
/* power down the CAN controller */
|
||||
periph_module_disable(PERIPH_CAN_MODULE);
|
||||
|
||||
dev->powered_up = false;
|
||||
}
|
||||
|
||||
static void _esp_can_init_pins(can_t *dev)
|
||||
static void _esp_can_init_pins(void)
|
||||
{
|
||||
DEBUG("%s dev=%p\n", __func__, dev);
|
||||
DEBUG("%s\n", __func__);
|
||||
|
||||
assert(dev);
|
||||
const can_conf_t *cfg = &candev_conf[0];
|
||||
|
||||
/* Init TX pin */
|
||||
gpio_init(dev->tx_pin, GPIO_OUT);
|
||||
gpio_set_pin_usage(dev->tx_pin, _CAN);
|
||||
GPIO.func_out_sel_cfg[dev->tx_pin].func_sel = TWAI_TX_IDX;
|
||||
gpio_init(cfg->tx_pin, GPIO_OUT);
|
||||
esp_rom_gpio_connect_out_signal(cfg->tx_pin, TWAI_TX_IDX, false, false);
|
||||
esp_rom_gpio_pad_select_gpio(cfg->tx_pin);
|
||||
gpio_set_pin_usage(cfg->tx_pin, _CAN);
|
||||
|
||||
/* Init RX pin */
|
||||
gpio_init(dev->rx_pin, GPIO_IN);
|
||||
gpio_set_pin_usage(dev->rx_pin, _CAN);
|
||||
GPIO.func_in_sel_cfg[CAN_RX_IDX].sig_in_sel = 1;
|
||||
GPIO.func_in_sel_cfg[CAN_RX_IDX].sig_in_inv = 0;
|
||||
GPIO.func_in_sel_cfg[CAN_RX_IDX].func_sel = dev->rx_pin;
|
||||
gpio_init(cfg->rx_pin, GPIO_IN);
|
||||
esp_rom_gpio_connect_in_signal(cfg->rx_pin, TWAI_RX_IDX, false);
|
||||
esp_rom_gpio_pad_select_gpio(cfg->rx_pin);
|
||||
gpio_set_pin_usage(cfg->rx_pin, _CAN);
|
||||
|
||||
#ifdef ESP_CAN_CLK_OUT
|
||||
#ifdef CAN_CLK_OUT
|
||||
/* Init CLK_OUT pin (optional) if defined */
|
||||
gpio_init(dev->clk_out_pin, GPIO_OD);
|
||||
gpio_set_pin_usage(dev->clk_out_pin, _CAN);
|
||||
GPIO.func_out_sel_cfg[dev->clk_out_pin].func_sel = TWAI_CLKOUT_IDX;
|
||||
gpio_init(cfg->clk_out_pin, GPIO_OD);
|
||||
esp_rom_gpio_connect_out_signal(cfg->clk_out_pin, TWAI_CLKOUT_IDX, false, false);
|
||||
esp_rom_gpio_pad_select_gpio(cfg->clk_out_pin);
|
||||
gpio_set_pin_usage(cfg->clk_out_pin, _CAN);
|
||||
#endif
|
||||
|
||||
#ifdef CAN_BUS_ON_OFF
|
||||
/* Init BUS_ON_OFF pin pin (optional) if defined */
|
||||
#ifdef ESP_CAN_BUS_ON_OFF
|
||||
gpio_init(dev->bus_on_of_pin, GPIO_OD);
|
||||
gpio_set_pin_usage(dev->bus_on_of_pin, _CAN);
|
||||
GPIO.func_out_sel_cfg[dev->bus_on_of_pin].func_sel = TWAI_BUS_OFF_ON_IDX;
|
||||
gpio_init(cfg->bus_on_of_pin, GPIO_OD);
|
||||
esp_rom_gpio_connect_out_signal(cfg->bus_on_of_pin, TWAI_BUS_OFF_ON_IDX, false, false);
|
||||
esp_rom_gpio_pad_select_gpio(cfg->bus_on_of_pin);
|
||||
gpio_set_pin_usage(cfg->bus_on_of_pin, _CAN);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void _esp_can_deinit_pins(void)
|
||||
{
|
||||
const can_conf_t *cfg = &candev_conf[0];
|
||||
|
||||
/* Reset TX pin */
|
||||
gpio_set_pin_usage(cfg->tx_pin, _GPIO);
|
||||
gpio_init(cfg->tx_pin, GPIO_IN_PU);
|
||||
|
||||
/* Reset RX pin */
|
||||
gpio_set_pin_usage(cfg->rx_pin, _GPIO);
|
||||
gpio_init(cfg->rx_pin, GPIO_IN_PU);
|
||||
|
||||
#ifdef CAN_CLK_OUT
|
||||
/* Reset CLK_OUT pin (optional) if defined */
|
||||
gpio_set_pin_usage(cfg->clk_out_pin, _GPIO);
|
||||
gpio_init(cfg->clk_out_pin, GPIO_IN);
|
||||
#endif
|
||||
|
||||
#ifdef CAN_BUS_ON_OFF
|
||||
/* Reset BUS_ON_OFF pin pin (optional) if defined */
|
||||
gpio_set_pin_usage(cfg->bus_on_of_pin, _GPIO);
|
||||
gpio_init(cfg->bus_on_of_pin, GPIO_IN_PD);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -690,48 +710,30 @@ static int _esp_can_set_mode(can_t *dev, canopt_state_t state)
|
||||
|
||||
critical_enter();
|
||||
|
||||
/* set the new mode */
|
||||
dev->state = state;
|
||||
|
||||
switch (state) {
|
||||
case CANOPT_STATE_OFF:
|
||||
/* just power down the CAN controller */
|
||||
/* stop and power down the CAN controller */
|
||||
_esp_can_power_down(dev);
|
||||
break;
|
||||
|
||||
case CANOPT_STATE_LISTEN_ONLY:
|
||||
/* power up and (re)configure the CAN controller if necessary */
|
||||
_esp_can_power_up(dev);
|
||||
/* set the new mode (has to be done in reset mode) */
|
||||
_esp_can_set_reset_mode();
|
||||
CAN.mode_reg.stm = 0;
|
||||
CAN.mode_reg.lom = 1;
|
||||
_esp_can_set_operating_mode ();
|
||||
break;
|
||||
|
||||
case CANOPT_STATE_SLEEP:
|
||||
/* Sleep mode is not supported by ESP32, State On is used instead. */
|
||||
#if 0
|
||||
/* power up and (re)configure the CAN controller if necessary */
|
||||
_esp_can_power_up(dev);
|
||||
/* set the sleep mode (not necessary to set reset mode before) */
|
||||
CAN.mode_reg.sleep_mode = 1;
|
||||
break;
|
||||
#endif
|
||||
|
||||
/* sleep mode is not supported, so CAN can't be powered down */
|
||||
case CANOPT_STATE_LISTEN_ONLY:
|
||||
case CANOPT_STATE_ON:
|
||||
/* power up and (re)configure the CAN controller if necessary */
|
||||
_esp_can_power_up(dev);
|
||||
/* set the new mode (has to be done in reset mode) */
|
||||
_esp_can_set_reset_mode();
|
||||
CAN.mode_reg.stm = 0;
|
||||
CAN.mode_reg.lom = 0;
|
||||
_esp_can_set_operating_mode ();
|
||||
/* restart the CAN controller in new mode */
|
||||
_esp_can_stop(dev);
|
||||
_esp_can_start(dev);
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_TAG_ERROR ("esp_can", "state value %d not supported\n", state);
|
||||
break;
|
||||
}
|
||||
dev->state = state;
|
||||
|
||||
critical_exit();
|
||||
|
||||
return 0;
|
||||
@ -745,99 +747,71 @@ static void IRAM_ATTR _esp_can_intr_handler(void *arg)
|
||||
|
||||
critical_enter();
|
||||
|
||||
/* read the registers to clear them */
|
||||
uint32_t sta_reg = CAN.status_reg.val;
|
||||
uint32_t int_reg = CAN.interrupt_reg.val;
|
||||
uint32_t events = twai_hal_get_events(&hw);
|
||||
|
||||
DEBUG("%s int=%08x sta=%08x\n", __func__, int_reg, sta_reg);
|
||||
|
||||
/* Wake-Up Interrupt (not supported by ESP32) */
|
||||
if (int_reg & BIT(4)) {
|
||||
DEBUG("%s wake-up interrupt\n", __func__);
|
||||
dev->events |= ESP_CAN_EVENT_WAKE_UP;
|
||||
}
|
||||
DEBUG("%s events=%08"PRIx32"\n", __func__, events);
|
||||
|
||||
/* Arbitration Lost Interrupt */
|
||||
if (int_reg & TWAI_LL_INTR_ALI) {
|
||||
if (events & TWAI_HAL_EVENT_ARB_LOST) {
|
||||
DEBUG("%s arbitration lost interrupt\n", __func__);
|
||||
/* can only happen during transmission, handle it as error in single shot transmission */
|
||||
dev->events |= ESP_CAN_EVENT_TX_ERROR;
|
||||
}
|
||||
|
||||
/* bus or error status has changed, handle this first */
|
||||
if (int_reg & TWAI_LL_INTR_EI) {
|
||||
/* BUS_OFF state condition */
|
||||
if (events & TWAI_HAL_EVENT_BUS_OFF) {
|
||||
DEBUG("%s bus-off state interrupt\n", __func__);
|
||||
/* save the event */
|
||||
dev->events |= ESP_CAN_EVENT_BUS_OFF;
|
||||
}
|
||||
|
||||
/* BUS_OFF state condition */
|
||||
if ((sta_reg & TWAI_LL_STATUS_ES) && (sta_reg & TWAI_LL_STATUS_BS)) {
|
||||
DEBUG("%s bus-off state interrupt\n", __func__);
|
||||
/* switch to listen only mode to freeze the RX error counter */
|
||||
_esp_can_set_mode (dev, CANOPT_STATE_LISTEN_ONLY);
|
||||
/* save the event */
|
||||
dev->events |= ESP_CAN_EVENT_BUS_OFF;
|
||||
}
|
||||
|
||||
/* ERROR_WARNING state condition, RX/TX error counter are > 96 */
|
||||
else if ((sta_reg & TWAI_LL_STATUS_ES) && !(sta_reg & TWAI_LL_STATUS_BS)) {
|
||||
DEBUG("%s error warning interrupt\n", __func__);
|
||||
/* save the event */
|
||||
dev->events |= ESP_CAN_EVENT_ERROR_WARNING;
|
||||
}
|
||||
/* ERROR_WARNING state condition, RX/TX error counter are > 96 */
|
||||
if (events & TWAI_HAL_EVENT_ABOVE_EWL) {
|
||||
DEBUG("%s error warning interrupt\n", __func__);
|
||||
/* save the event */
|
||||
dev->events |= ESP_CAN_EVENT_ERROR_WARNING;
|
||||
}
|
||||
|
||||
/* enter to / return from ERROR_PASSIVE state */
|
||||
if (int_reg & TWAI_LL_INTR_EPI) {
|
||||
/* enter to the ERROR_PASSIVE state when one of the error counters is >= 128 */
|
||||
if (CAN.tx_error_counter_reg.txerr >= ESP_CAN_ERROR_PASSIVE_LIMIT ||
|
||||
CAN.rx_error_counter_reg.rxerr >= ESP_CAN_ERROR_PASSIVE_LIMIT) {
|
||||
DEBUG("%s error passive interrupt %d %d\n", __func__,
|
||||
CAN.tx_error_counter_reg.txerr,
|
||||
CAN.rx_error_counter_reg.rxerr);
|
||||
/* save the event */
|
||||
dev->events |= ESP_CAN_EVENT_ERROR_PASSIVE;
|
||||
}
|
||||
if (events & TWAI_HAL_EVENT_ERROR_PASSIVE) {
|
||||
DEBUG("%s error passive interrupt %"PRIu32" %"PRIu32"\n", __func__,
|
||||
twai_ll_get_tec(hw.dev), twai_ll_get_rec(hw.dev));
|
||||
/* save the event */
|
||||
dev->events |= ESP_CAN_EVENT_ERROR_PASSIVE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Bus Error Interrupt (bit, stuff, crc, form, ack), details are captured
|
||||
* in ECC register (see SJA1000 Data sheet, Table 20 and 21)
|
||||
*/
|
||||
|
||||
if (int_reg & TWAI_LL_INTR_BEI) {
|
||||
if (events & TWAI_HAL_EVENT_BUS_ERR) {
|
||||
DEBUG("%s bus error interrupt\n", __func__);
|
||||
/* save the event */
|
||||
DEBUG("%s bus error interrupt, ecc=%08x\n", __func__,
|
||||
CAN.error_code_capture_reg.val);
|
||||
dev->events |= CAN.error_code_capture_reg.dir ? ESP_CAN_EVENT_RX_ERROR
|
||||
: ESP_CAN_EVENT_TX_ERROR;
|
||||
dev->events |= ESP_CAN_EVENT_TX_ERROR;
|
||||
}
|
||||
|
||||
/* TX buffer becomes free */
|
||||
if (int_reg & TWAI_LL_INTR_TI) {
|
||||
if (events & TWAI_HAL_EVENT_TX_BUFF_FREE) {
|
||||
DEBUG("%s transmit interrupt\n", __func__);
|
||||
/* save the event */
|
||||
dev->events |= ESP_CAN_EVENT_TX_CONFIRMATION;
|
||||
}
|
||||
|
||||
/* RX buffer has one or more frames */
|
||||
if (int_reg & TWAI_LL_INTR_RI) {
|
||||
if (events & TWAI_HAL_EVENT_RX_BUFF_FRAME) {
|
||||
/* get the number of messages in receive buffer */
|
||||
uint32_t msg_cnt = CAN.rx_message_counter_reg.val;
|
||||
uint32_t msg_cnt = twai_hal_get_rx_msg_count(&hw);
|
||||
|
||||
DEBUG("%s receive interrupt, msg_cnt=%d\n", __func__, msg_cnt);
|
||||
DEBUG("%s receive interrupt, msg_cnt=%"PRIu32"\n", __func__, msg_cnt);
|
||||
|
||||
for (unsigned i = 0; i < msg_cnt; i++) {
|
||||
_esp_can_frame_t esp_frame;
|
||||
/* fetch the frame from RX buffer of ESP32 */
|
||||
for (unsigned j = 0; j < ESP_CAN_FRAME_LEN; j++) {
|
||||
esp_frame.bytes[j] = CAN.tx_rx_buffer[j].val;
|
||||
}
|
||||
/* clear RX buffer at read position */
|
||||
CAN.command_reg.val = ESP_CMD_RELEASE_RX_BUFF;
|
||||
|
||||
if (dev->rx_frames_num < ESP_CAN_MAX_RX_FRAMES) {
|
||||
twai_hal_frame_t esp_frame;
|
||||
if (twai_hal_read_rx_buffer_and_clear(&hw, &esp_frame) &&
|
||||
(dev->rx_frames_num < ESP_CAN_MAX_RX_FRAMES)) {
|
||||
/* prepare the CAN frame from ESP32 CAN frame */
|
||||
struct can_frame frame = {};
|
||||
|
||||
if (esp_frame.eff) {
|
||||
if (esp_frame.frame_format) {
|
||||
frame.can_id = esp_frame.extended.id[0];
|
||||
frame.can_id = (frame.can_id << 8) | esp_frame.extended.id[1];
|
||||
frame.can_id = (frame.can_id << 8) | esp_frame.extended.id[2];
|
||||
@ -850,7 +824,7 @@ static void IRAM_ATTR _esp_can_intr_handler(void *arg)
|
||||
memcpy(frame.data, &esp_frame.standard.data, CAN_MAX_DLEN);
|
||||
}
|
||||
frame.can_id |= esp_frame.rtr ? CAN_RTR_FLAG : 0;
|
||||
frame.can_id |= esp_frame.eff ? CAN_EFF_FLAG : 0;
|
||||
frame.can_id |= esp_frame.frame_format ? CAN_EFF_FLAG : 0;
|
||||
frame.can_dlc = esp_frame.dlc;
|
||||
|
||||
/* apply acceptance filters only if they are set */
|
||||
@ -877,27 +851,20 @@ static void IRAM_ATTR _esp_can_intr_handler(void *arg)
|
||||
dev->rx_frames_num++;
|
||||
dev->rx_frames_wptr++;
|
||||
dev->rx_frames_wptr &= ESP_CAN_MAX_RX_FRAMES-1;
|
||||
}
|
||||
|
||||
/* at least one RX frame has been saved */
|
||||
dev->events |= ESP_CAN_EVENT_RX_INDICATION;
|
||||
}
|
||||
}
|
||||
else {
|
||||
DEBUG("%s receive buffer overrun\n", __func__);
|
||||
/* we use rx error since there is no separate overrun error */
|
||||
dev->events |= ESP_CAN_EVENT_RX_ERROR;
|
||||
}
|
||||
|
||||
}
|
||||
/* save the event */
|
||||
dev->events |= ESP_CAN_EVENT_RX_INDICATION;
|
||||
}
|
||||
|
||||
/* data overrun interrupts are not handled */
|
||||
if (int_reg & BIT(3)) {
|
||||
DEBUG("%s data overrun interrupt\n", __func__);
|
||||
/* we use rx error since there is no separate overrun error */
|
||||
dev->events |= ESP_CAN_EVENT_RX_INDICATION;
|
||||
}
|
||||
|
||||
DEBUG("%s events=%08x\n", __func__, dev->events);
|
||||
DEBUG("%s events=%08"PRIx32"\n", __func__, dev->events);
|
||||
|
||||
/* inform the upper layer that there are events to be handled */
|
||||
if (dev->events && dev->candev.event_callback) {
|
||||
@ -907,34 +874,38 @@ static void IRAM_ATTR _esp_can_intr_handler(void *arg)
|
||||
critical_exit();
|
||||
}
|
||||
|
||||
/* accept all CAN messages by default, filtering is done by software */
|
||||
static const twai_filter_config_t twai_filter = {
|
||||
.single_filter = false, /* single filter */
|
||||
.acceptance_mask = UINT32_MAX, /* all bits masked */
|
||||
};
|
||||
|
||||
static void _esp_can_set_bittiming(can_t *dev)
|
||||
{
|
||||
/* sets the bus timing, the CAN controller has to stopped before using
|
||||
* function _esp_can_stop(dev) and has to be restartet using
|
||||
* function _esp_can_start() afterwards */
|
||||
|
||||
assert(dev);
|
||||
|
||||
struct can_bittiming* timing = &dev->candev.bittiming;
|
||||
|
||||
DEBUG("%s %p bitrate=%d, brp=%d, sample_point=%d, tq=%d, "
|
||||
"prop_seg=%d phase_seg1=%d, phase_seg2=%d, sjw =%d\n", __func__, dev,
|
||||
DEBUG("%s %p bitrate=%"PRIu32", brp=%"PRIu32", sample_point=%"PRIu32", "
|
||||
"tq=%"PRIu32", prop_seg=%"PRIu32" phase_seg1=%"PRIu32", "
|
||||
"phase_seg2=%"PRIu32", sjw =%"PRIu32"\n", __func__, dev,
|
||||
timing->bitrate, timing->brp, timing->sample_point, timing->tq,
|
||||
timing->prop_seg, timing->phase_seg1, timing->phase_seg2,
|
||||
timing->sjw);
|
||||
|
||||
_esp_can_set_reset_mode();
|
||||
twai_timing_config_t twai_timing = {
|
||||
.brp = timing->brp,
|
||||
.sjw = timing->sjw,
|
||||
.tseg_1 = timing->prop_seg + timing->phase_seg1,
|
||||
.tseg_2 = timing->phase_seg2,
|
||||
};
|
||||
|
||||
/* Again cppcheck gets off rails due to missing concept of union (see
|
||||
* explanation above), so we suppress false unreadVariable here */
|
||||
/* cppcheck-suppress unreadVariable */
|
||||
CAN.bus_timing_0_reg.brp = (timing->brp / 2) - 1;
|
||||
/* cppcheck-suppress unreadVariable */
|
||||
CAN.bus_timing_0_reg.sjw = timing->sjw - 1;
|
||||
/* cppcheck-suppress unreadVariable */
|
||||
CAN.bus_timing_1_reg.tseg1 = (timing->prop_seg + timing->phase_seg1) - 1;
|
||||
/* cppcheck-suppress unreadVariable */
|
||||
CAN.bus_timing_1_reg.tseg2 = timing->phase_seg2 - 1;
|
||||
/* cppcheck-suppress unreadVariable */
|
||||
CAN.bus_timing_1_reg.sam = 0;
|
||||
|
||||
_esp_can_set_operating_mode();
|
||||
twai_hal_configure(&hw, &twai_timing, &twai_filter,
|
||||
ESP_CAN_INTR_MASK, CAN_CLK_OUT_DIV);
|
||||
}
|
||||
|
||||
void can_init(can_t *dev, const can_conf_t *conf)
|
||||
@ -947,20 +918,11 @@ void can_init(can_t *dev, const can_conf_t *conf)
|
||||
dev->candev.driver = &_esp_can_driver;
|
||||
dev->candev.bittiming.bitrate = conf->bitrate;
|
||||
|
||||
dev->tx_pin = conf->tx_pin;
|
||||
dev->rx_pin = conf->rx_pin;
|
||||
#ifdef ESP_CAN_CLK_OUT
|
||||
dev->clk_out_pin = conf->clk_out_pin;
|
||||
#endif
|
||||
#ifdef ESP_CAN_BUS_ON_OFF
|
||||
dev->bus_on_off_pin = conf->bus_on_off_pin;
|
||||
#endif
|
||||
|
||||
/* determine the hardware bittiming constants */
|
||||
struct can_bittiming_const timing_const = bittiming_const;
|
||||
|
||||
/* calculate the initial bittimings from the bittiming constants */
|
||||
can_device_calc_bittiming (ESP_CAN_CLOCK, &timing_const, &dev->candev.bittiming);
|
||||
can_device_calc_bittiming(ESP_CAN_CLOCK, &timing_const, &dev->candev.bittiming);
|
||||
|
||||
/* initialize other members */
|
||||
dev->state = CAN_STATE_SLEEPING;
|
||||
@ -970,7 +932,6 @@ void can_init(can_t *dev, const can_conf_t *conf)
|
||||
dev->rx_frames_num = 0;
|
||||
dev->rx_filter_num = 0;
|
||||
dev->powered_up = false;
|
||||
|
||||
}
|
||||
|
||||
void can_print_config(void)
|
||||
|
Loading…
Reference in New Issue
Block a user