diff --git a/components/driver/can.c b/components/driver/can.c index b08829d15..4ff728b80 100644 --- a/components/driver/can.c +++ b/components/driver/can.c @@ -11,6 +11,10 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. + +#include "soc/soc_caps.h" +#ifdef SOC_CAN_SUPPORTED + #include "sdkconfig.h" #include "freertos/FreeRTOS.h" #include "freertos/portmacro.h" @@ -21,11 +25,11 @@ #include "esp_log.h" #include "esp_intr_alloc.h" #include "esp_pm.h" -#include "soc/can_periph.h" #include "driver/gpio.h" #include "driver/periph_ctrl.h" #include "driver/can.h" -#if CONFIG_IDF_TARGET_ESP32 +#include "soc/can_periph.h" +#include "hal/can_hal.h" /* ---------------------------- Definitions --------------------------------- */ //Internal Macros @@ -44,36 +48,7 @@ #define CAN_RESET_FLAG(var, mask) ((var) &= ~(mask)) #define CAN_TAG "CAN" -/* - * Baud Rate Prescaler Divider config/values. The BRP_DIV bit is located in the - * CAN interrupt enable register, and is only available in ESP32 Revision 2 or - * later. Setting this bit will cause the APB clock to be prescaled (divided) by - * a factor 2, before having the BRP applied. This will allow for lower bit rates - * to be achieved. - */ -#define BRP_DIV_EN_THRESH 128 //A BRP config value large this this will need to enable brp_div -#define BRP_DIV_EN_BIT 0x10 //Bit mask for brp_div in the interrupt register -//When brp_div is enabled, the BRP config value must be any multiple of 4 between 132 and 256 -#define BRP_CHECK_WITH_DIV(brp) ((brp) >= 132 && (brp) <= 256 && ((brp) & 0x3) == 0) -//When brp_div is disabled, the BRP config value must be any even number between 2 to 128 -#define BRP_CHECK_NO_DIV(brp) ((brp) >= 2 && (brp) <= 128 && ((brp) & 0x1) == 0) - -//Driver default config/values -#define DRIVER_DEFAULT_EWL 96 //Default Error Warning Limit value -#define DRIVER_DEFAULT_TEC 0 //TX Error Counter starting value -#define DRIVER_DEFAULT_REC 0 //RX Error Counter starting value -#define DRIVER_DEFAULT_CLKOUT_DIV 14 //APB CLK divided by two #define DRIVER_DEFAULT_INTERRUPTS 0xE7 //Exclude data overrun (bit[3]) and brp_div (bit[4]) -#define DRIVER_DEFAULT_ERR_PASS_CNT 128 //Error counter threshold for error passive - -//Command Bit Masks -#define CMD_TX_REQ 0x01 //Transmission Request -#define CMD_ABORT_TX 0x02 //Abort Transmission -#define CMD_RELEASE_RX_BUFF 0x04 //Release Receive Buffer -#define CMD_CLR_DATA_OVRN 0x08 //Clear Data Overrun -#define CMD_SELF_RX_REQ 0x10 //Self Reception Request -#define CMD_TX_SINGLE_SHOT 0x03 //Single Shot Transmission -#define CMD_SELF_RX_SINGLE_SHOT 0x12 //Single Shot Self Reception //Control flags #define CTRL_FLAG_STOPPED 0x001 //CAN peripheral in stopped state @@ -82,53 +57,17 @@ #define CTRL_FLAG_ERR_PASSIVE 0x008 //TEC or REC is >= 128 #define CTRL_FLAG_BUS_OFF 0x010 //Bus-off due to TEC >= 256 #define CTRL_FLAG_TX_BUFF_OCCUPIED 0x020 //Transmit buffer is occupied -#define CTRL_FLAG_SELF_TEST 0x040 //Configured to Self Test Mode -#define CTRL_FLAG_LISTEN_ONLY 0x080 //Configured to Listen Only Mode - -//Constants use for frame formatting and parsing -#define FRAME_MAX_LEN 13 //EFF with 8 bytes of data -#define FRAME_MAX_DATA_LEN 8 //Max data bytes allowed in CAN2.0 -#define FRAME_EXTD_ID_LEN 4 //EFF ID requires 4 bytes (29bit) -#define FRAME_STD_ID_LEN 2 //SFF ID requires 2 bytes (11bit) -#define FRAME_INFO_LEN 1 //Frame info requires 1 byte #define ALERT_LOG_LEVEL_WARNING CAN_ALERT_ARB_LOST //Alerts above and including this level use ESP_LOGW #define ALERT_LOG_LEVEL_ERROR CAN_ALERT_TX_FAILED //Alerts above and including this level use ESP_LOGE /* ------------------ Typedefs, structures, and variables ------------------- */ -/* Formatted frame structure has identical layout as TX/RX buffer registers. - This allows for direct copy to/from TX/RX buffer. The two reserved bits in TX - buffer are used in the frame structure to store the self_reception and - single_shot flags. */ -typedef union { - struct { - struct { - uint8_t dlc: 4; //Data length code (0 to 8) of the frame - uint8_t self_reception: 1; //This frame should be transmitted using self reception command - uint8_t single_shot: 1; //This frame should be transmitted using single shot command - uint8_t rtr: 1; //This frame is a remote transmission request - uint8_t frame_format: 1; //Format of the frame (1 = extended, 0 = standard) - }; - union { - struct { - uint8_t id[FRAME_STD_ID_LEN]; //11 bit standard frame identifier - uint8_t data[FRAME_MAX_DATA_LEN]; //Data bytes (0 to 8) - uint8_t reserved8[2]; - } standard; - struct { - uint8_t id[FRAME_EXTD_ID_LEN]; //29 bit extended frame identifier - uint8_t data[FRAME_MAX_DATA_LEN]; //Data bytes (0 to 8) - } extended; - }; - }; - uint8_t bytes[FRAME_MAX_LEN]; -} can_frame_t; - //Control structure for CAN driver typedef struct { //Control and status members uint32_t control_flags; + can_mode_t mode; uint32_t rx_missed_count; uint32_t tx_failed_count; uint32_t arb_lost_count; @@ -156,199 +95,7 @@ static portMUX_TYPE can_spinlock = portMUX_INITIALIZER_UNLOCKED; #define CAN_ENTER_CRITICAL() portENTER_CRITICAL(&can_spinlock) #define CAN_EXIT_CRITICAL() portEXIT_CRITICAL(&can_spinlock) -/* ------------------- Configuration Register Functions---------------------- */ - -static inline esp_err_t can_enter_reset_mode(void) -{ - /* Enter reset mode (required to write to configuration registers). Reset mode - also prevents all CAN activity on the current module and is automatically - set upon entering a BUS-OFF condition. */ - CAN.mode_reg.reset = 1; //Set reset mode bit - CAN_CHECK(CAN.mode_reg.reset == 1, ESP_ERR_INVALID_STATE); //Check bit was set - return ESP_OK; -} - -static inline esp_err_t can_exit_reset_mode(void) -{ - /* Exiting reset mode will return the CAN module to operating mode. Reset mode - must also be exited in order to trigger BUS-OFF recovery sequence. */ - CAN.mode_reg.reset = 0; //Exit reset mode - CAN_CHECK(CAN.mode_reg.reset == 0, ESP_ERR_INVALID_STATE); //Check bit was reset - return ESP_OK; -} - -static inline void can_config_pelican(void) -{ - //Use PeliCAN address layout. Exposes extra registers - CAN.clock_divider_reg.can_mode = 1; -} - -static inline void can_config_mode(can_mode_t mode) -{ - //Configure CAN mode of operation - can_mode_reg_t mode_reg; - mode_reg.val = CAN.mode_reg.val; //Get current value of mode register - if (mode == CAN_MODE_NO_ACK) { - mode_reg.self_test = 1; - mode_reg.listen_only = 0; - } else if (mode == CAN_MODE_LISTEN_ONLY) { - mode_reg.self_test = 0; - mode_reg.listen_only = 1; - } else { - //Default to normal operating mode - mode_reg.self_test = 0; - mode_reg.listen_only = 0; - } - CAN.mode_reg.val = mode_reg.val; //Write back modified value to register -} - -static inline void can_config_interrupts(uint32_t interrupts) -{ - //Enable interrupt sources - CAN.interrupt_enable_reg.val = interrupts; -} - -static inline void can_config_bus_timing(uint32_t brp, uint32_t sjw, uint32_t tseg_1, uint32_t tseg_2, bool triple_sampling) -{ - /* Configure bus/bit timing of CAN peripheral. - - BRP (even from 2 to 128) divide APB to CAN system clock (T_scl) - - SJW (1 to 4) is number of T_scl to shorten/lengthen for bit synchronization - - TSEG_1 (1 to 16) is number of T_scl in a bit time before sample point - - TSEG_2 (1 to 8) is number of T_scl in a bit time after sample point - - triple_sampling will cause each bit time to be sampled 3 times */ - can_bus_tim_0_reg_t timing_reg_0; - can_bus_tim_1_reg_t timing_reg_1; - timing_reg_0.baud_rate_prescaler = (brp / 2) - 1; - timing_reg_0.sync_jump_width = sjw - 1; - timing_reg_1.time_seg_1 = tseg_1 - 1; - timing_reg_1.time_seg_2 = tseg_2 - 1; - timing_reg_1.sampling = triple_sampling; - CAN.bus_timing_0_reg.val = timing_reg_0.val; - CAN.bus_timing_1_reg.val = timing_reg_1.val; -} - -static inline void can_config_error(int err_warn_lim, int rx_err_cnt, int tx_err_cnt) -{ - /* Set error warning limit, RX error counter, and TX error counter. Note that - forcibly setting RX/TX error counters will incur the expected status changes - and interrupts as soon as reset mode exits. */ - if (err_warn_lim >= 0 && err_warn_lim <= UINT8_MAX) { - //Defaults to 96 after hardware reset. - CAN.error_warning_limit_reg.byte = err_warn_lim; - } - if (rx_err_cnt >= 0 && rx_err_cnt <= UINT8_MAX) { - //Defaults to 0 after hardware reset. - CAN.rx_error_counter_reg.byte = rx_err_cnt; - } - if (tx_err_cnt >= 0 && tx_err_cnt <= UINT8_MAX) { - //Defaults to 0 after hardware reset, and 127 after BUS-OFF event - CAN.tx_error_counter_reg.byte = tx_err_cnt; - } -} - -static inline void can_config_acceptance_filter(uint32_t code, uint32_t mask, bool single_filter) -{ - //Set filter mode - CAN.mode_reg.acceptance_filter = (single_filter) ? 1 : 0; - //Swap code and mask to match big endian registers - uint32_t code_swapped = __builtin_bswap32(code); - uint32_t mask_swapped = __builtin_bswap32(mask); - for (int i = 0; i < 4; i++) { - CAN.acceptance_filter.code_reg[i].byte = ((code_swapped >> (i * 8)) & 0xFF); - CAN.acceptance_filter.mask_reg[i].byte = ((mask_swapped >> (i * 8)) & 0xFF); - } -} - -static inline void can_config_clk_out(uint32_t divider) -{ - /* Configure CLKOUT. CLKOUT is a pre-scaled version of APB CLK. Divider can be - 1, or any even number from 2 to 14. Set to out of range value (0) to disable - CLKOUT. */ - can_clk_div_reg_t clock_divider_reg; - clock_divider_reg.val = CAN.clock_divider_reg.val; - if (divider >= 2 && divider <= 14) { - clock_divider_reg.clock_off = 0; - clock_divider_reg.clock_divider = (divider / 2) - 1; - } else if (divider == 1) { - clock_divider_reg.clock_off = 0; - clock_divider_reg.clock_divider = 7; - } else { - clock_divider_reg.clock_off = 1; - clock_divider_reg.clock_divider = 0; - } - CAN.clock_divider_reg.val = clock_divider_reg.val; -} - -/* ---------------------- Runtime Register Functions------------------------- */ - -static inline void can_set_command(uint8_t commands) -{ - CAN.command_reg.val = commands; -} - -static void can_set_tx_buffer_and_transmit(can_frame_t *frame) -{ - //Copy frame structure into TX buffer registers - for (int i = 0; i < FRAME_MAX_LEN; i++) { - CAN.tx_rx_buffer[i].val = frame->bytes[i]; - } - - //Set correct transmit command - uint8_t command; - if (frame->self_reception) { - command = (frame->single_shot) ? CMD_SELF_RX_SINGLE_SHOT : CMD_SELF_RX_REQ; - } else { - command = (frame->single_shot) ? CMD_TX_SINGLE_SHOT : CMD_TX_REQ; - } - can_set_command(command); -} - -static inline uint32_t can_get_status(void) -{ - return CAN.status_reg.val; -} - -static inline uint32_t can_get_interrupt_reason(void) -{ - return CAN.interrupt_reg.val; -} - -static inline uint32_t can_get_arbitration_lost_capture(void) -{ - return CAN.arbitration_lost_captue_reg.val; - //Todo: ALC read only to re-arm arb lost interrupt. Add function to decode ALC -} - -static inline uint32_t can_get_error_code_capture(void) -{ - return CAN.error_code_capture_reg.val; - //Todo: ECC read only to re-arm bus error interrupt. Add function to decode ECC -} - -static inline void can_get_error_counters(uint32_t *tx_error_cnt, uint32_t *rx_error_cnt) -{ - if (tx_error_cnt != NULL) { - *tx_error_cnt = CAN.tx_error_counter_reg.byte; - } - if (rx_error_cnt != NULL) { - *rx_error_cnt = CAN.rx_error_counter_reg.byte; - } -} - -static inline void can_get_rx_buffer_and_clear(can_frame_t *frame) -{ - //Copy RX buffer registers into frame structure - for (int i = 0; i < FRAME_MAX_LEN; i++) { - frame->bytes[i] = CAN.tx_rx_buffer[i].val; - } - //Clear RX buffer - can_set_command(CMD_RELEASE_RX_BUFF); -} - -static inline uint32_t can_get_rx_message_counter(void) -{ - return CAN.rx_message_counter_reg.val; -} +static can_hal_context_t can_context; /* -------------------- Interrupt and Alert Handlers ------------------------ */ @@ -370,83 +117,87 @@ static void can_alert_handler(uint32_t alert_code, int *alert_req) } } -static void can_intr_handler_err_warn(can_status_reg_t *status, int *alert_req) +static inline void can_handle_bus_off(int *alert_req) { - if (status->bus) { - if (status->error) { - //Bus-Off condition. TEC should set and held at 127, REC should be 0, reset mode entered - CAN_SET_FLAG(p_can_obj->control_flags, CTRL_FLAG_BUS_OFF); - /* Note: REC is still allowed to increase during bus-off. REC > err_warn - can prevent "bus recovery complete" interrupt from occurring. Set to - listen only mode to freeze REC. */ - can_config_mode(CAN_MODE_LISTEN_ONLY); - can_alert_handler(CAN_ALERT_BUS_OFF, alert_req); - } else { - //Bus-recovery in progress. TEC has dropped below error warning limit - can_alert_handler(CAN_ALERT_RECOVERY_IN_PROGRESS, alert_req); - } - } else { - if (status->error) { - //TEC or REC surpassed error warning limit - CAN_SET_FLAG(p_can_obj->control_flags, CTRL_FLAG_ERR_WARN); - can_alert_handler(CAN_ALERT_ABOVE_ERR_WARN, alert_req); - } else if (p_can_obj->control_flags & CTRL_FLAG_RECOVERING) { - //Bus recovery complete. - esp_err_t err = can_enter_reset_mode(); - assert(err == ESP_OK); - //Reset and set flags to the equivalent of the stopped state - CAN_RESET_FLAG(p_can_obj->control_flags, CTRL_FLAG_RECOVERING | CTRL_FLAG_ERR_WARN | - CTRL_FLAG_ERR_PASSIVE | CTRL_FLAG_BUS_OFF | - CTRL_FLAG_TX_BUFF_OCCUPIED); - CAN_SET_FLAG(p_can_obj->control_flags, CTRL_FLAG_STOPPED); - can_alert_handler(CAN_ALERT_BUS_RECOVERED, alert_req); - } else { - //TEC and REC are both below error warning - CAN_RESET_FLAG(p_can_obj->control_flags, CTRL_FLAG_ERR_WARN); - can_alert_handler(CAN_ALERT_BELOW_ERR_WARN, alert_req); - } - } + //Bus-Off condition. TEC should set and held at 127, REC should be 0, reset mode entered + CAN_SET_FLAG(p_can_obj->control_flags, CTRL_FLAG_BUS_OFF); + /* Note: REC is still allowed to increase during bus-off. REC > err_warn + can prevent "bus recovery complete" interrupt from occurring. Set to + listen only mode to freeze REC. */ + can_hal_handle_bus_off(&can_context); + can_alert_handler(CAN_ALERT_BUS_OFF, alert_req); } -static void can_intr_handler_err_passive(int *alert_req) +static inline void can_handle_recovery_complete(int *alert_req) { - uint32_t tec, rec; - can_get_error_counters(&tec, &rec); - if (tec >= DRIVER_DEFAULT_ERR_PASS_CNT || rec >= DRIVER_DEFAULT_ERR_PASS_CNT) { - //Entered error passive - CAN_SET_FLAG(p_can_obj->control_flags, CTRL_FLAG_ERR_PASSIVE); - can_alert_handler(CAN_ALERT_ERR_PASS, alert_req); - } else { - //Returned to error active - CAN_RESET_FLAG(p_can_obj->control_flags, CTRL_FLAG_ERR_PASSIVE); - can_alert_handler(CAN_ALERT_ERR_ACTIVE, alert_req); - } + //Bus recovery complete. + assert(can_hal_handle_bus_recov_cplt(&can_context)); + + //Reset and set flags to the equivalent of the stopped state + CAN_RESET_FLAG(p_can_obj->control_flags, CTRL_FLAG_RECOVERING | CTRL_FLAG_ERR_WARN | + CTRL_FLAG_ERR_PASSIVE | CTRL_FLAG_BUS_OFF | + CTRL_FLAG_TX_BUFF_OCCUPIED); + CAN_SET_FLAG(p_can_obj->control_flags, CTRL_FLAG_STOPPED); + can_alert_handler(CAN_ALERT_BUS_RECOVERED, alert_req); } -static void can_intr_handler_bus_err(int *alert_req) +static inline void can_handle_recovery_in_progress(int * alert_req) +{ + //Bus-recovery in progress. TEC has dropped below error warning limit + can_alert_handler(CAN_ALERT_RECOVERY_IN_PROGRESS, alert_req); +} + +static inline void can_handle_above_ewl(int *alert_req) +{ + //TEC or REC surpassed error warning limit + CAN_SET_FLAG(p_can_obj->control_flags, CTRL_FLAG_ERR_WARN); + can_alert_handler(CAN_ALERT_ABOVE_ERR_WARN, alert_req); +} + +static inline void can_handle_below_ewl(int *alert_req) +{ + //TEC and REC are both below error warning + CAN_RESET_FLAG(p_can_obj->control_flags, CTRL_FLAG_ERR_WARN); + can_alert_handler(CAN_ALERT_BELOW_ERR_WARN, alert_req); +} + +static inline void can_handle_error_passive(int *alert_req) +{ + //Entered error passive + CAN_SET_FLAG(p_can_obj->control_flags, CTRL_FLAG_ERR_PASSIVE); + can_alert_handler(CAN_ALERT_ERR_PASS, alert_req); +} + +static inline void can_handle_error_active(int *alert_req) +{ + //Returned to error active + CAN_RESET_FLAG(p_can_obj->control_flags, CTRL_FLAG_ERR_PASSIVE); + can_alert_handler(CAN_ALERT_ERR_ACTIVE, alert_req); +} + +static inline void can_handle_bus_error(int *alert_req) { // ECC register is read to re-arm bus error interrupt. ECC is not used - (void) can_get_error_code_capture(); + can_hal_handle_bus_error(&can_context); p_can_obj->bus_error_count++; can_alert_handler(CAN_ALERT_BUS_ERROR, alert_req); } -static void can_intr_handler_arb_lost(int *alert_req) +static inline void can_handle_arb_lost(int *alert_req) { //ALC register is read to re-arm arb lost interrupt. ALC is not used - (void) can_get_arbitration_lost_capture(); + can_hal_handle_arb_lost(&can_context); p_can_obj->arb_lost_count++; can_alert_handler(CAN_ALERT_ARB_LOST, alert_req); } -static void can_intr_handler_rx(BaseType_t *task_woken, int *alert_req) +static inline void can_handle_rx_buffer_frames(BaseType_t *task_woken, int *alert_req) { - can_rx_msg_cnt_reg_t msg_count_reg; - msg_count_reg.val = can_get_rx_message_counter(); + uint32_t msg_count = can_hal_get_rx_msg_count(&can_context); - for (int i = 0; i < msg_count_reg.rx_message_counter; i++) { - can_frame_t frame; - can_get_rx_buffer_and_clear(&frame); + for (int i = 0; i < msg_count; i++) { + can_hal_frame_t frame; + can_hal_read_rx_buffer_and_clear(&can_context, &frame); //Copy frame into RX Queue if (xQueueSendFromISR(p_can_obj->rx_queue, &frame, task_woken) == pdTRUE) { p_can_obj->rx_msg_count++; @@ -459,10 +210,10 @@ static void can_intr_handler_rx(BaseType_t *task_woken, int *alert_req) //Todo: Check for data overrun of RX FIFO, then trigger alert } -static void can_intr_handler_tx(can_status_reg_t *status, int *alert_req) +static inline void can_handle_tx_buffer_frame(BaseType_t *task_woken, int *alert_req) { //Handle previously transmitted frame - if (status->tx_complete) { + if (can_hal_check_last_tx_successful(&can_context)) { can_alert_handler(CAN_ALERT_TX_SUCCESS, alert_req); } else { p_can_obj->tx_failed_count++; @@ -475,10 +226,10 @@ static void can_intr_handler_tx(can_status_reg_t *status, int *alert_req) //Check if there are more frames to transmit if (p_can_obj->tx_msg_count > 0 && p_can_obj->tx_queue != NULL) { - can_frame_t frame; - int res = xQueueReceiveFromISR(p_can_obj->tx_queue, &frame, NULL); + can_hal_frame_t frame; + int res = xQueueReceiveFromISR(p_can_obj->tx_queue, &frame, task_woken); if (res == pdTRUE) { - can_set_tx_buffer_and_transmit(&frame); + can_hal_set_tx_buffer_and_transmit(&can_context, &frame); } else { assert(false && "failed to get a frame from TX queue"); } @@ -493,49 +244,49 @@ static void can_intr_handler_main(void *arg) { BaseType_t task_woken = pdFALSE; int alert_req = 0; - can_status_reg_t status; - can_intr_reg_t intr_reason; - + uint32_t event; CAN_ENTER_CRITICAL_ISR(); - status.val = can_get_status(); - intr_reason.val = (p_can_obj != NULL) ? can_get_interrupt_reason() : 0; //Incase intr occurs whilst driver is being uninstalled + if (p_can_obj == NULL) { //Incase intr occurs whilst driver is being uninstalled + CAN_EXIT_CRITICAL_ISR(); + return; + } + event = can_hal_decode_interrupt_events(&can_context, p_can_obj->control_flags & CTRL_FLAG_RECOVERING); -#ifdef __clang_analyzer__ - if (intr_reason.val == 0) { // Teach clang-tidy that all bitfields are zero if a register is zero; othewise it warns about p_can_obj null dereference - intr_reason.err_warn = intr_reason.err_passive = intr_reason.bus_err = intr_reason.arb_lost = intr_reason.rx = intr_reason.tx = 0; + if (event & CAN_HAL_EVENT_BUS_OFF) { + can_handle_bus_off(&alert_req); } -#endif - //Handle error counter related interrupts - if (intr_reason.err_warn) { - //Triggers when Bus-Status or Error-status bits change - can_intr_handler_err_warn(&status, &alert_req); + if (event & CAN_HAL_EVENT_BUS_RECOV_CPLT) { + can_handle_recovery_complete(&alert_req); } - if (intr_reason.err_passive) { - //Triggers when entering/returning error passive/active state - can_intr_handler_err_passive(&alert_req); + if (event & CAN_HAL_EVENT_BUS_RECOV_PROGRESS) { + can_handle_recovery_in_progress(&alert_req); } - - //Handle other error interrupts - if (intr_reason.bus_err) { - //Triggers when an error (Bit, Stuff, CRC, Form, ACK) occurs on the CAN bus - can_intr_handler_bus_err(&alert_req); + if (event & CAN_HAL_EVENT_ABOVE_EWL) { + can_handle_above_ewl(&alert_req); } - if (intr_reason.arb_lost) { - //Triggers when arbitration is lost - can_intr_handler_arb_lost(&alert_req); + if (event & CAN_HAL_EVENT_BELOW_EWL) { + can_handle_below_ewl(&alert_req); } - - //Handle TX/RX interrupts - if (intr_reason.rx) { - //Triggers when RX buffer has one or more frames. Disabled if RX Queue length = 0 - can_intr_handler_rx(&task_woken, &alert_req); + if (event & CAN_HAL_EVENT_ERROR_PASSIVE) { + can_handle_error_passive(&alert_req); } - if (intr_reason.tx) { - //Triggers when TX buffer becomes free after a transmission - can_intr_handler_tx(&status, &alert_req); + if (event & CAN_HAL_EVENT_ERROR_ACTIVE) { + can_handle_error_active(&alert_req); + } + if (event & CAN_HAL_EVENT_BUS_ERR) { + can_handle_bus_error(&alert_req); + } + if (event & CAN_HAL_EVENT_ARB_LOST) { + can_handle_arb_lost(&alert_req); + } + if (event & CAN_HAL_EVENT_RX_BUFF_FRAME) { + can_handle_rx_buffer_frames(&task_woken, &alert_req); + } + //TX command related handlers should be called last, so that other commands + //do not overwrite the TX command related bits in the command register. + if (event & CAN_HAL_EVENT_TX_BUFF_FREE) { + can_handle_tx_buffer_frame(&task_woken, &alert_req); } - /* Todo: Check possible bug where transmitting self reception request then - clearing rx buffer will cancel the transmission. */ CAN_EXIT_CRITICAL_ISR(); if (p_can_obj->alert_semphr != NULL && alert_req) { @@ -547,70 +298,7 @@ static void can_intr_handler_main(void *arg) } } -/* ---------------------- Frame and GPIO functions ------------------------- */ - -static void can_format_frame(uint32_t id, uint8_t dlc, const uint8_t *data, uint32_t flags, can_frame_t *tx_frame) -{ - /* This function encodes a message into a frame structure. The frame structure has - an identical layout to the TX buffer, allowing the frame structure to be directly - copied into TX buffer. */ - //Set frame information - tx_frame->dlc = dlc; - tx_frame->rtr = (flags & CAN_MSG_FLAG_RTR) ? 1 : 0; - tx_frame->frame_format = (flags & CAN_MSG_FLAG_EXTD) ? 1 : 0; - tx_frame->self_reception = (flags & CAN_MSG_FLAG_SELF) ? 1 : 0; - tx_frame->single_shot = (flags & CAN_MSG_FLAG_SS) ? 1 : 0; - - //Set ID - int id_len = (flags & CAN_MSG_FLAG_EXTD) ? FRAME_EXTD_ID_LEN : FRAME_STD_ID_LEN; - uint8_t *id_buffer = (flags & CAN_MSG_FLAG_EXTD) ? tx_frame->extended.id : tx_frame->standard.id; - //Split ID into 4 or 2 bytes, and turn into big-endian with left alignment (<< 3 or 5) - uint32_t id_temp = (flags & CAN_MSG_FLAG_EXTD) ? __builtin_bswap32((id & CAN_EXTD_ID_MASK) << 3) : //((id << 3) >> 8*(3-i)) - __builtin_bswap16((id & CAN_STD_ID_MASK) << 5); //((id << 5) >> 8*(1-i)) - for (int i = 0; i < id_len; i++) { - id_buffer[i] = (id_temp >> (8 * i)) & 0xFF; //Copy big-endian ID byte by byte - } - - //Set Data. - uint8_t *data_buffer = (flags & CAN_MSG_FLAG_EXTD) ? tx_frame->extended.data : tx_frame->standard.data; - for (int i = 0; (i < dlc) && (i < FRAME_MAX_DATA_LEN); i++) { //Handle case where dlc is > 8 - data_buffer[i] = data[i]; - } -} - -static void can_parse_frame(can_frame_t *rx_frame, uint32_t *id, uint8_t *dlc, uint8_t *data, uint32_t *flags) -{ - //This function decodes a frame structure into it's constituent components. - - //Copy frame information - *dlc = rx_frame->dlc; - *flags = 0; - *flags |= (rx_frame->dlc > FRAME_MAX_DATA_LEN) ? CAN_MSG_FLAG_DLC_NON_COMP : 0; - *flags |= (rx_frame->rtr) ? CAN_MSG_FLAG_RTR : 0; - *flags |= (rx_frame->frame_format) ? CAN_MSG_FLAG_EXTD : 0; - - //Copy ID - int id_len = (rx_frame->frame_format) ? FRAME_EXTD_ID_LEN : FRAME_STD_ID_LEN; - uint8_t *id_buffer = (rx_frame->frame_format) ? rx_frame->extended.id : rx_frame->standard.id; - uint32_t id_temp = 0; - for (int i = 0; i < id_len; i++) { - id_temp |= id_buffer[i] << (8 * i); //Copy big-endian ID byte by byte - } - //Revert endianness of 4 or 2 byte ID, and shift into 29 or 11 bit ID - id_temp = (rx_frame->frame_format) ? (__builtin_bswap32(id_temp) >> 3) : //((byte[i] << 8*(3-i)) >> 3) - (__builtin_bswap16(id_temp) >> 5); //((byte[i] << 8*(1-i)) >> 5) - *id = id_temp & ((rx_frame->frame_format) ? CAN_EXTD_ID_MASK : CAN_STD_ID_MASK); - - //Copy data - uint8_t *data_buffer = (rx_frame->frame_format) ? rx_frame->extended.data : rx_frame->standard.data; - for (int i = 0; (i < rx_frame->dlc) && (i < FRAME_MAX_DATA_LEN); i++) { - data[i] = data_buffer[i]; - } - //Set remaining bytes of data to 0 - for (int i = rx_frame->dlc; i < FRAME_MAX_DATA_LEN; i++) { - data[i] = 0; - } -} +/* --------------------------- GPIO functions ------------------------------ */ static void can_configure_gpio(gpio_num_t tx, gpio_num_t rx, gpio_num_t clkout, gpio_num_t bus_status) { @@ -651,12 +339,7 @@ esp_err_t can_driver_install(const can_general_config_t *g_config, const can_tim CAN_CHECK(g_config->rx_queue_len > 0, ESP_ERR_INVALID_ARG); CAN_CHECK(g_config->tx_io >= 0 && g_config->tx_io < GPIO_NUM_MAX, ESP_ERR_INVALID_ARG); CAN_CHECK(g_config->rx_io >= 0 && g_config->rx_io < GPIO_NUM_MAX, ESP_ERR_INVALID_ARG); -#if (CONFIG_ESP32_REV_MIN >= 2) - //ESP32 revision 2 or later chips have a brp_div bit. Check that the BRP config value is valid when brp_div is enabled or disabled - CAN_CHECK(BRP_CHECK_WITH_DIV(t_config->brp) || BRP_CHECK_NO_DIV(t_config->brp), ESP_ERR_INVALID_ARG); -#else - CAN_CHECK(BRP_CHECK_NO_DIV(t_config->brp), ESP_ERR_INVALID_ARG); -#endif + CAN_CHECK(CAN_BRP_IS_VALID(t_config->brp), ESP_ERR_INVALID_ARG); esp_err_t ret; can_obj_t *p_can_obj_dummy; @@ -666,8 +349,8 @@ esp_err_t can_driver_install(const can_general_config_t *g_config, const can_tim CAN_CHECK(p_can_obj_dummy != NULL, ESP_ERR_NO_MEM); //Initialize queues, semaphores, and power management locks - p_can_obj_dummy->tx_queue = (g_config->tx_queue_len > 0) ? xQueueCreate(g_config->tx_queue_len, sizeof(can_frame_t)) : NULL; - p_can_obj_dummy->rx_queue = xQueueCreate(g_config->rx_queue_len, sizeof(can_frame_t)); + p_can_obj_dummy->tx_queue = (g_config->tx_queue_len > 0) ? xQueueCreate(g_config->tx_queue_len, sizeof(can_hal_frame_t)) : NULL; + p_can_obj_dummy->rx_queue = xQueueCreate(g_config->rx_queue_len, sizeof(can_hal_frame_t)); p_can_obj_dummy->alert_semphr = xSemaphoreCreateBinary(); if ((g_config->tx_queue_len > 0 && p_can_obj_dummy->tx_queue == NULL) || p_can_obj_dummy->rx_queue == NULL || p_can_obj_dummy->alert_semphr == NULL) { @@ -682,18 +365,10 @@ esp_err_t can_driver_install(const can_general_config_t *g_config, const can_tim } #endif - //Initialize flags and variables + //Initialize flags and variables. All other members are 0 initialized by calloc() p_can_obj_dummy->control_flags = CTRL_FLAG_STOPPED; - p_can_obj_dummy->control_flags |= (g_config->mode == CAN_MODE_NO_ACK) ? CTRL_FLAG_SELF_TEST : 0; - p_can_obj_dummy->control_flags |= (g_config->mode == CAN_MODE_LISTEN_ONLY) ? CTRL_FLAG_LISTEN_ONLY : 0; - p_can_obj_dummy->tx_msg_count = 0; - p_can_obj_dummy->rx_msg_count = 0; - p_can_obj_dummy->tx_failed_count = 0; - p_can_obj_dummy->rx_missed_count = 0; - p_can_obj_dummy->arb_lost_count = 0; - p_can_obj_dummy->bus_error_count = 0; + p_can_obj_dummy->mode = g_config->mode; p_can_obj_dummy->alerts_enabled = g_config->alerts_enabled; - p_can_obj_dummy->alerts_triggered = 0; //Initialize CAN peripheral registers, and allocate interrupt CAN_ENTER_CRITICAL(); @@ -707,24 +382,8 @@ esp_err_t can_driver_install(const can_general_config_t *g_config, const can_tim } periph_module_reset(PERIPH_CAN_MODULE); periph_module_enable(PERIPH_CAN_MODULE); //Enable APB CLK to CAN peripheral - esp_err_t err = can_enter_reset_mode(); //Must enter reset mode to write to config registers - assert(err == ESP_OK); - can_config_pelican(); //Use PeliCAN addresses - /* Note: REC is allowed to increase even in reset mode. Listen only mode - will freeze REC. The desired mode will be set when can_start() is called. */ - can_config_mode(CAN_MODE_LISTEN_ONLY); -#if (CONFIG_ESP32_REV_MIN >= 2) - //If the BRP config value is large enough, the brp_div bit must be enabled to achieve the same effective baud rate prescaler - can_config_interrupts((t_config->brp > BRP_DIV_EN_THRESH) ? DRIVER_DEFAULT_INTERRUPTS | BRP_DIV_EN_BIT : DRIVER_DEFAULT_INTERRUPTS); - can_config_bus_timing((t_config->brp > BRP_DIV_EN_THRESH) ? t_config->brp/2 : t_config->brp, t_config->sjw, t_config->tseg_1, t_config->tseg_2, t_config->triple_sampling); -#else - can_config_interrupts(DRIVER_DEFAULT_INTERRUPTS); - can_config_bus_timing(t_config->brp, t_config->sjw, t_config->tseg_1, t_config->tseg_2, t_config->triple_sampling); -#endif - can_config_error(DRIVER_DEFAULT_EWL, DRIVER_DEFAULT_REC, DRIVER_DEFAULT_TEC); - can_config_acceptance_filter(f_config->acceptance_code, f_config->acceptance_mask, f_config->single_filter); - can_config_clk_out(g_config->clkout_divider); - (void) can_get_interrupt_reason(); //Read interrupt reg to clear it before allocating ISR + assert(can_hal_init(&can_context)); + can_hal_configure(&can_context, t_config, f_config, DRIVER_DEFAULT_INTERRUPTS, g_config->clkout_divider); //Todo: Allow interrupt to be registered to specified CPU CAN_EXIT_CRITICAL(); @@ -770,12 +429,9 @@ esp_err_t can_driver_uninstall(void) //Check state CAN_CHECK_FROM_CRIT(p_can_obj != NULL, ESP_ERR_INVALID_STATE); CAN_CHECK_FROM_CRIT(p_can_obj->control_flags & (CTRL_FLAG_STOPPED | CTRL_FLAG_BUS_OFF), ESP_ERR_INVALID_STATE); - esp_err_t err = can_enter_reset_mode(); //Enter reset mode to stop any CAN bus activity - assert(err == ESP_OK); + //Todo: Add check to see if in reset mode. //Enter reset mode to stop any CAN bus activity //Clear registers by reading - (void) can_get_interrupt_reason(); - (void) can_get_arbitration_lost_capture(); - (void) can_get_error_code_capture(); + can_hal_deinit(&can_context); periph_module_disable(PERIPH_CAN_MODULE); //Disable CAN peripheral p_can_obj_dummy = p_can_obj; //Use dummy to shorten critical section p_can_obj = NULL; @@ -809,22 +465,10 @@ esp_err_t can_start(void) //Reset RX queue, and RX message count xQueueReset(p_can_obj->rx_queue); p_can_obj->rx_msg_count = 0; - esp_err_t err = can_enter_reset_mode(); //Should already be in bus-off mode, set again to make sure - assert(err == ESP_OK); + //Todo: Add assert to see if in reset mode. //Should already be in bus-off mode, set again to make sure //Currently in listen only mode, need to set to mode specified by configuration - can_mode_t mode; - if (p_can_obj->control_flags & CTRL_FLAG_SELF_TEST) { - mode = CAN_MODE_NO_ACK; - } else if (p_can_obj->control_flags & CTRL_FLAG_LISTEN_ONLY) { - mode = CAN_MODE_LISTEN_ONLY; - } else { - mode = CAN_MODE_NORMAL; - } - can_config_mode(mode); //Set mode - (void) can_get_interrupt_reason(); //Clear interrupt register - err = can_exit_reset_mode(); - assert(err == ESP_OK); + assert(can_hal_start(&can_context, p_can_obj->mode)); CAN_RESET_FLAG(p_can_obj->control_flags, CTRL_FLAG_STOPPED); CAN_EXIT_CRITICAL(); @@ -838,11 +482,8 @@ esp_err_t can_stop(void) CAN_CHECK_FROM_CRIT(p_can_obj != NULL, ESP_ERR_INVALID_STATE); CAN_CHECK_FROM_CRIT(!(p_can_obj->control_flags & (CTRL_FLAG_STOPPED | CTRL_FLAG_BUS_OFF)), ESP_ERR_INVALID_STATE); - //Clear interrupts and reset flags - esp_err_t err = can_enter_reset_mode(); - assert(err == ESP_OK); - (void) can_get_interrupt_reason(); //Read interrupt register to clear interrupts - can_config_mode(CAN_MODE_LISTEN_ONLY); //Set to listen only mode to freeze REC + assert(can_hal_stop(&can_context)); + CAN_RESET_FLAG(p_can_obj->control_flags, CTRL_FLAG_TX_BUFF_OCCUPIED); CAN_SET_FLAG(p_can_obj->control_flags, CTRL_FLAG_STOPPED); @@ -862,20 +503,21 @@ esp_err_t can_transmit(const can_message_t *message, TickType_t ticks_to_wait) //Check arguments CAN_CHECK(p_can_obj != NULL, ESP_ERR_INVALID_STATE); CAN_CHECK(message != NULL, ESP_ERR_INVALID_ARG); - CAN_CHECK((message->data_length_code <= FRAME_MAX_DATA_LEN) || (message->flags & CAN_MSG_FLAG_DLC_NON_COMP), ESP_ERR_INVALID_ARG); + CAN_CHECK((message->data_length_code <= CAN_FRAME_MAX_DLC) || message->dlc_non_comp, ESP_ERR_INVALID_ARG); CAN_ENTER_CRITICAL(); //Check State - CAN_CHECK_FROM_CRIT(!(p_can_obj->control_flags & CTRL_FLAG_LISTEN_ONLY), ESP_ERR_NOT_SUPPORTED); + CAN_CHECK_FROM_CRIT(!(p_can_obj->mode == CAN_MODE_LISTEN_ONLY), ESP_ERR_NOT_SUPPORTED); CAN_CHECK_FROM_CRIT(!(p_can_obj->control_flags & (CTRL_FLAG_STOPPED | CTRL_FLAG_BUS_OFF)), ESP_ERR_INVALID_STATE); //Format frame esp_err_t ret = ESP_FAIL; - can_frame_t tx_frame; - can_format_frame(message->identifier, message->data_length_code, message->data, message->flags, &tx_frame); + can_hal_frame_t tx_frame; + can_hal_format_frame(message, &tx_frame); + //Check if frame can be sent immediately if ((p_can_obj->tx_msg_count == 0) && !(p_can_obj->control_flags & CTRL_FLAG_TX_BUFF_OCCUPIED)) { //No other frames waiting to transmit. Bypass queue and transmit immediately - can_set_tx_buffer_and_transmit(&tx_frame); + can_hal_set_tx_buffer_and_transmit(&can_context, &tx_frame); p_can_obj->tx_msg_count++; CAN_SET_FLAG(p_can_obj->control_flags, CTRL_FLAG_TX_BUFF_OCCUPIED); ret = ESP_OK; @@ -898,7 +540,7 @@ esp_err_t can_transmit(const can_message_t *message, TickType_t ticks_to_wait) //TX buffer was freed during copy, manually trigger transmission int res = xQueueReceive(p_can_obj->tx_queue, &tx_frame, 0); assert(res == pdTRUE); - can_set_tx_buffer_and_transmit(&tx_frame); + can_hal_set_tx_buffer_and_transmit(&can_context, &tx_frame); p_can_obj->tx_msg_count++; CAN_SET_FLAG(p_can_obj->control_flags, CTRL_FLAG_TX_BUFF_OCCUPIED); ret = ESP_OK; @@ -923,7 +565,7 @@ esp_err_t can_receive(can_message_t *message, TickType_t ticks_to_wait) CAN_CHECK(message != NULL, ESP_ERR_INVALID_ARG); //Get frame from RX Queue or RX Buffer - can_frame_t rx_frame; + can_hal_frame_t rx_frame; if (xQueueReceive(p_can_obj->rx_queue, &rx_frame, ticks_to_wait) != pdTRUE) { return ESP_ERR_TIMEOUT; } @@ -933,7 +575,7 @@ esp_err_t can_receive(can_message_t *message, TickType_t ticks_to_wait) CAN_EXIT_CRITICAL(); //Decode frame - can_parse_frame(&rx_frame, &(message->identifier), &(message->data_length_code), message->data, &(message->flags)); + can_hal_parse_frame(&rx_frame, message); return ESP_OK; } @@ -989,8 +631,7 @@ esp_err_t can_initiate_recovery(void) CAN_SET_FLAG(p_can_obj->control_flags, CTRL_FLAG_RECOVERING); //Trigger start of recovery process - esp_err_t err = can_exit_reset_mode(); - assert(err == ESP_OK); + assert(can_hal_start_bus_recovery(&can_context)); CAN_EXIT_CRITICAL(); return ESP_OK; @@ -1003,10 +644,8 @@ esp_err_t can_get_status_info(can_status_info_t *status_info) CAN_CHECK(status_info != NULL, ESP_ERR_INVALID_ARG); CAN_ENTER_CRITICAL(); - uint32_t tec, rec; - can_get_error_counters(&tec, &rec); - status_info->tx_error_counter = tec; - status_info->rx_error_counter = rec; + status_info->tx_error_counter = can_hal_get_tec(&can_context); + status_info->rx_error_counter = can_hal_get_rec(&can_context); status_info->msgs_to_tx = p_can_obj->tx_msg_count; status_info->msgs_to_rx = p_can_obj->rx_msg_count; status_info->tx_failed_count = p_can_obj->tx_failed_count; diff --git a/components/driver/include/driver/can.h b/components/driver/include/driver/can.h index 45b829c40..16b88f98a 100644 --- a/components/driver/include/driver/can.h +++ b/components/driver/include/driver/can.h @@ -12,18 +12,24 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef _DRIVER_CAN_H_ -#define _DRIVER_CAN_H_ +#pragma once #ifdef __cplusplus extern "C" { #endif +#include "soc/soc_caps.h" +#ifndef SOC_CAN_SUPPORTED +#error CAN is not supported in this chip target +#endif + #include "freertos/FreeRTOS.h" #include "esp_types.h" #include "esp_intr_alloc.h" #include "esp_err.h" #include "gpio.h" +#include "soc/can_caps.h" +#include "hal/can_types.h" /* -------------------- Default initializers and flags ---------------------- */ /** @cond */ //Doxy command to hide preprocessor definitions from docs @@ -39,33 +45,6 @@ extern "C" { .tx_queue_len = 5, .rx_queue_len = 5, \ .alerts_enabled = CAN_ALERT_NONE, .clkout_divider = 0, } -/** - * @brief Initializer macros for timing configuration structure - * - * The following initializer macros offer commonly found bit rates. - * - * @note These timing values are based on the assumption APB clock is at 80MHz - * @note The 20K, 16K and 12.5K bit rates are only available from ESP32 Revision 2 onwards - */ -#if (CONFIG_ESP32_REV_MIN >= 2) -#define CAN_TIMING_CONFIG_12_5KBITS() {.brp = 256, .tseg_1 = 16, .tseg_2 = 8, .sjw = 3, .triple_sampling = false} -#define CAN_TIMING_CONFIG_16KBITS() {.brp = 200, .tseg_1 = 16, .tseg_2 = 8, .sjw = 3, .triple_sampling = false} -#define CAN_TIMING_CONFIG_20KBITS() {.brp = 200, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} -#endif -#define CAN_TIMING_CONFIG_25KBITS() {.brp = 128, .tseg_1 = 16, .tseg_2 = 8, .sjw = 3, .triple_sampling = false} -#define CAN_TIMING_CONFIG_50KBITS() {.brp = 80, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} -#define CAN_TIMING_CONFIG_100KBITS() {.brp = 40, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} -#define CAN_TIMING_CONFIG_125KBITS() {.brp = 32, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} -#define CAN_TIMING_CONFIG_250KBITS() {.brp = 16, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} -#define CAN_TIMING_CONFIG_500KBITS() {.brp = 8, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} -#define CAN_TIMING_CONFIG_800KBITS() {.brp = 4, .tseg_1 = 16, .tseg_2 = 8, .sjw = 3, .triple_sampling = false} -#define CAN_TIMING_CONFIG_1MBITS() {.brp = 4, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} - -/** - * @brief Initializer macro for filter configuration to accept all IDs - */ -#define CAN_FILTER_CONFIG_ACCEPT_ALL() {.acceptance_code = 0, .acceptance_mask = 0xFFFFFFFF, .single_filter = true} - /** * @brief Alert flags * @@ -93,38 +72,11 @@ extern "C" { #define CAN_ALERT_NONE 0x0000 /**< Bit mask to disable all alerts during configuration */ #define CAN_ALERT_AND_LOG 0x2000 /**< Bit mask to enable alerts to also be logged when they occur */ -/** - * @brief Message flags - * - * The message flags are used to indicate the type of message transmitted/received. - * Some flags also specify the type of transmission. - */ -#define CAN_MSG_FLAG_NONE 0x00 /**< No message flags (Standard Frame Format) */ -#define CAN_MSG_FLAG_EXTD 0x01 /**< Extended Frame Format (29bit ID) */ -#define CAN_MSG_FLAG_RTR 0x02 /**< Message is a Remote Transmit Request */ -#define CAN_MSG_FLAG_SS 0x04 /**< Transmit as a Single Shot Transmission */ -#define CAN_MSG_FLAG_SELF 0x08 /**< Transmit as a Self Reception Request */ -#define CAN_MSG_FLAG_DLC_NON_COMP 0x10 /**< Message's Data length code is larger than 8. This will break compliance with CAN2.0B */ - -/** - * @brief Miscellaneous macros - */ -#define CAN_EXTD_ID_MASK 0x1FFFFFFF /**< Bit mask for 29 bit Extended Frame Format ID */ -#define CAN_STD_ID_MASK 0x7FF /**< Bit mask for 11 bit Standard Frame Format ID */ -#define CAN_MAX_DATA_LEN 8 /**< Maximum number of data bytes in a CAN2.0B frame */ -#define CAN_IO_UNUSED ((gpio_num_t) -1) /**< Marks GPIO as unused in CAN configuration */ /** @endcond */ -/* ----------------------- Enum and Struct Definitions ---------------------- */ +#define CAN_IO_UNUSED ((gpio_num_t) -1) /**< Marks GPIO as unused in CAN configuration */ -/** - * @brief CAN driver operating modes - */ -typedef enum { - CAN_MODE_NORMAL, /**< Normal operating mode where CAN controller can send/receive/acknowledge messages */ - CAN_MODE_NO_ACK, /**< Transmission does not require acknowledgment. Use this mode for self testing */ - CAN_MODE_LISTEN_ONLY, /**< The CAN controller will not influence the bus (No transmissions or acknowledgments) but can receive messages */ -} can_mode_t; +/* ----------------------- Enum and Struct Definitions ---------------------- */ /** * @brief CAN driver states @@ -153,31 +105,6 @@ typedef struct { uint32_t clkout_divider; /**< CLKOUT divider. Can be 1 or any even number from 2 to 14 (optional, set to 0 if unused) */ } can_general_config_t; -/** - * @brief Structure for bit timing configuration of the CAN driver - * - * @note Macro initializers are available for this structure - */ -typedef struct { - uint32_t brp; /**< Baudrate prescaler (i.e., APB clock divider) can be any even number from 2 to 128. - For ESP32 Rev 2 or later, multiples of 4 from 132 to 256 are also supported */ - uint8_t tseg_1; /**< Timing segment 1 (Number of time quanta, between 1 to 16) */ - uint8_t tseg_2; /**< Timing segment 2 (Number of time quanta, 1 to 8) */ - uint8_t sjw; /**< Synchronization Jump Width (Max time quanta jump for synchronize from 1 to 4) */ - bool triple_sampling; /**< Enables triple sampling when the CAN controller samples a bit */ -} can_timing_config_t; - -/** - * @brief Structure for acceptance filter configuration of the CAN driver (see documentation) - * - * @note Macro initializers are available for this structure - */ -typedef struct { - uint32_t acceptance_code; /**< 32-bit acceptance code */ - uint32_t acceptance_mask; /**< 32-bit acceptance mask */ - bool single_filter; /**< Use Single Filter Mode (see documentation) */ -} can_filter_config_t; - /** * @brief Structure to store status information of CAN driver */ @@ -193,19 +120,6 @@ typedef struct { uint32_t bus_error_count; /**< Number of instances a bus error has occurred */ } can_status_info_t; -/** - * @brief Structure to store a CAN message - * - * @note The flags member is used to control the message type, and transmission - * type (see documentation for message flags) - */ -typedef struct { - uint32_t flags; /**< Bit field of message flags indicates frame/transmission type (see documentation) */ - uint32_t identifier; /**< 11 or 29 bit identifier */ - uint8_t data_length_code; /**< Data length code */ - uint8_t data[CAN_MAX_DATA_LEN]; /**< Data bytes (not relevant in RTR frame) */ -} can_message_t; - /* ----------------------------- Public API -------------------------------- */ /** @@ -431,6 +345,3 @@ esp_err_t can_clear_receive_queue(void); #ifdef __cplusplus } #endif - -#endif /*_DRIVER_CAN_H_*/ - diff --git a/components/soc/CMakeLists.txt b/components/soc/CMakeLists.txt index 845cfd047..4386c7277 100644 --- a/components/soc/CMakeLists.txt +++ b/components/soc/CMakeLists.txt @@ -40,6 +40,7 @@ list(APPEND srcs if(IDF_TARGET STREQUAL "esp32") list(APPEND srcs "src/hal/mcpwm_hal.c" "src/hal/sdio_slave_hal.c" + "src/hal/can_hal.c" ) endif() diff --git a/components/soc/esp32/include/hal/can_ll.h b/components/soc/esp32/include/hal/can_ll.h new file mode 100644 index 000000000..c498ed59a --- /dev/null +++ b/components/soc/esp32/include/hal/can_ll.h @@ -0,0 +1,703 @@ +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/******************************************************************************* + * NOTICE + * The ll is not public api, don't use in application code. + * See readme.md in soc/include/hal/readme.md + ******************************************************************************/ + +// The Lowlevel layer for CAN + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include "hal/can_types.h" +#include "soc/can_periph.h" + +/* ------------------------- Defines and Typedefs --------------------------- */ + +#define CAN_LL_STATUS_RBS (0x1 << 0) +#define CAN_LL_STATUS_DOS (0x1 << 1) +#define CAN_LL_STATUS_TBS (0x1 << 2) +#define CAN_LL_STATUS_TCS (0x1 << 3) +#define CAN_LL_STATUS_RS (0x1 << 4) +#define CAN_LL_STATUS_TS (0x1 << 5) +#define CAN_LL_STATUS_ES (0x1 << 6) +#define CAN_LL_STATUS_BS (0x1 << 7) + +#define CAN_LL_INTR_RI (0x1 << 0) +#define CAN_LL_INTR_TI (0x1 << 1) +#define CAN_LL_INTR_EI (0x1 << 2) +//Data overrun interrupt not supported in SW due to HW peculiarities +#define CAN_LL_INTR_EPI (0x1 << 5) +#define CAN_LL_INTR_ALI (0x1 << 6) +#define CAN_LL_INTR_BEI (0x1 << 7) + +/* + * The following frame structure has an NEARLY identical bit field layout to + * each byte of the TX buffer. This allows for formatting and parsing frames to + * be done outside of time critical regions (i.e., ISRs). All the ISR needs to + * do is to copy byte by byte to/from the TX/RX buffer. The two reserved bits in + * TX buffer are used in the frame structure to store the self_reception and + * single_shot flags which in turn indicate the type of transmission to execute. + */ +typedef union { + struct { + struct { + uint8_t dlc: 4; //Data length code (0 to 8) of the frame + uint8_t self_reception: 1; //This frame should be transmitted using self reception command + uint8_t single_shot: 1; //This frame should be transmitted using single shot command + uint8_t rtr: 1; //This frame is a remote transmission request + uint8_t frame_format: 1; //Format of the frame (1 = extended, 0 = standard) + }; + union { + struct { + uint8_t id[2]; //11 bit standard frame identifier + uint8_t data[8]; //Data bytes (0 to 8) + uint8_t reserved8[2]; + } standard; + struct { + uint8_t id[4]; //29 bit extended frame identifier + uint8_t data[8]; //Data bytes (0 to 8) + } extended; + }; + }; + uint8_t bytes[13]; +} __attribute__((packed)) can_ll_frame_buffer_t; + +/* ---------------------------- Mode Register ------------------------------- */ + +/** + * @brief Enter reset mode + * + * When in reset mode, the CAN controller is effectively disconnected from the + * CAN bus and will not participate in any bus activates. Reset mode is required + * in order to write the majority of configuration registers. + * + * @param hw Start address of the CAN registers + * @return true if reset mode was entered successfully + * + * @note Reset mode is automatically entered on BUS OFF condition + */ +static inline bool can_ll_enter_reset_mode(can_dev_t *hw) +{ + hw->mode_reg.rm = 1; + return hw->mode_reg.rm; +} + +/** + * @brief Exit reset mode + * + * When not in reset mode, the CAN controller will take part in bus activities + * (e.g., send/receive/acknowledge messages and error frames) depending on the + * operating mode. + * + * @param hw Start address of the CAN registers + * @return true if reset mode was exit successfully + * + * @note Reset mode must be exit to initiate BUS OFF recovery + */ +static inline bool can_ll_exit_reset_mode(can_dev_t *hw) +{ + hw->mode_reg.rm = 0; + return !(hw->mode_reg.rm); +} + +/** + * @brief Check if in reset mode + * @param hw Start address of the CAN registers + * @return true if in reset mode + */ +static inline bool can_ll_is_in_reset_mode(can_dev_t *hw) +{ + return hw->mode_reg.rm; +} + +/** + * @brief Set operating mode of CAN controller + * + * @param hw Start address of the CAN registers + * @param mode Operating mode + * + * @note Must be called in reset mode + */ +static inline void can_ll_set_mode(can_dev_t *hw, can_mode_t mode) +{ + if (mode == CAN_MODE_NORMAL) { //Normal Operating mode + hw->mode_reg.lom = 0; + hw->mode_reg.stm = 0; + } else if (mode == CAN_MODE_NO_ACK) { //Self Test Mode (No Ack) + hw->mode_reg.lom = 0; + hw->mode_reg.stm = 1; + } else if (mode == CAN_MODE_LISTEN_ONLY) { //Listen Only Mode + hw->mode_reg.lom = 1; + hw->mode_reg.stm = 0; + } +} + +/* --------------------------- Command Register ----------------------------- */ + +/** + * @brief Set TX command + * + * Setting the TX command will cause the CAN controller to attempt to transmit + * the frame stored in the TX buffer. The TX buffer will be occupied (i.e., + * locked) until TX completes. + * + * @param hw Start address of the CAN registers + * + * @note Transmit commands should be called last (i.e., after handling buffer + * release and clear data overrun) in order to prevent the other commands + * overwriting this latched TX bit with 0. + */ +static inline void can_ll_set_cmd_tx(can_dev_t *hw) +{ + hw->command_reg.tr = 1; +} + +/** + * @brief Set single shot TX command + * + * Similar to setting TX command, but the CAN controller will not automatically + * retry transmission upon an error (e.g., due to an acknowledgement error). + * + * @param hw Start address of the CAN registers + * + * @note Transmit commands should be called last (i.e., after handling buffer + * release and clear data overrun) in order to prevent the other commands + * overwriting this latched TX bit with 0. + */ +static inline void can_ll_set_cmd_tx_single_shot(can_dev_t *hw) +{ + hw->command_reg.val = 0x03; //Writing to TR and AT simultaneously +} + +/** + * @brief Aborts TX + * + * Frames awaiting TX will be aborted. Frames already being TX are not aborted. + * Transmission Complete Status bit is automatically set to 1. + * Similar to setting TX command, but the CAN controller will not automatically + * retry transmission upon an error (e.g., due to acknowledge error). + * + * @param hw Start address of the CAN registers + * + * @note Transmit commands should be called last (i.e., after handling buffer + * release and clear data overrun) in order to prevent the other commands + * overwriting this latched TX bit with 0. + */ +static inline void can_ll_set_cmd_abort_tx(can_dev_t *hw) +{ + hw->command_reg.at = 1; +} + +/** + * @brief Release RX buffer + * + * Rotates RX buffer to the next frame in the RX FIFO. + * + * @param hw Start address of the CAN registers + */ +static inline void can_ll_set_cmd_release_rx_buffer(can_dev_t *hw) +{ + hw->command_reg.rrb = 1; +} + +/** + * @brief Clear data overrun + * + * Clears the data overrun status bit + * + * @param hw Start address of the CAN registers + */ +static inline void can_ll_set_cmd_clear_data_overrun(can_dev_t *hw) +{ + hw->command_reg.cdo = 1; +} + +/** + * @brief Set self reception single shot command + * + * Similar to setting TX command, but the CAN controller also simultaneously + * receive the transmitted frame and is generally used for self testing + * purposes. The CAN controller will not ACK the received message, so consider + * using the NO_ACK operating mode. + * + * @param hw Start address of the CAN registers + * + * @note Transmit commands should be called last (i.e., after handling buffer + * release and clear data overrun) in order to prevent the other commands + * overwriting this latched TX bit with 0. + */ +static inline void can_ll_set_cmd_self_rx_request(can_dev_t *hw) +{ + hw->command_reg.srr = 1; +} + +/** + * @brief Set self reception request command + * + * Similar to setting the self reception request, but the CAN controller will + * not automatically retry transmission upon an error (e.g., due to and + * acknowledgement error). + * + * @param hw Start address of the CAN registers + * + * @note Transmit commands should be called last (i.e., after handling buffer + * release and clear data overrun) in order to prevent the other commands + * overwriting this latched TX bit with 0. + */ +static inline void can_ll_set_cmd_self_rx_single_shot(can_dev_t *hw) +{ + hw->command_reg.val = 0x12; +} + +/* --------------------------- Status Register ------------------------------ */ + +/** + * @brief Get all status bits + * + * @param hw Start address of the CAN registers + * @return Status bits + */ +static inline uint32_t can_ll_get_status(can_dev_t *hw) +{ + return hw->status_reg.val; +} + +/** + * @brief Check if RX FIFO overrun status bit is set + * + * @param hw Start address of the CAN registers + * @return Overrun status bit + */ +static inline bool can_ll_is_fifo_overrun(can_dev_t *hw) +{ + return hw->status_reg.dos; +} + +/** + * @brief Check if previously TX was successful + * + * @param hw Start address of the CAN registers + * @return Whether previous TX was successful + */ +static inline bool can_ll_is_last_tx_successful(can_dev_t *hw) +{ + return hw->status_reg.tcs; +} + +//Todo: Add stand alone status bit check functions when necessary + +/* -------------------------- Interrupt Register ---------------------------- */ + +/** + * @brief Get currently set interrupts + * + * Reading the interrupt registers will automatically clear all interrupts + * except for the Receive Interrupt. + * + * @param hw Start address of the CAN registers + * @return Bit mask of set interrupts + */ +static inline uint32_t can_ll_get_and_clear_intrs(can_dev_t *hw) +{ + return hw->interrupt_reg.val; +} + +/* ----------------------- Interrupt Enable Register ------------------------ */ + +/** + * @brief Set which interrupts are enabled + * + * @param hw Start address of the CAN registers + * @param Bit mask of interrupts to enable + * + * @note Must be called in reset mode + */ +static inline void can_ll_set_enabled_intrs(can_dev_t *hw, uint32_t intr_mask) +{ +#ifdef CAN_BRP_DIV_SUPPORTED + //ESP32 Rev 2 has brp div. Need to mask when setting + hw->interrupt_enable_reg.val = (hw->interrupt_enable_reg.val & 0x10) | intr_mask; +#else + hw->interrupt_enable_reg.val = intr_mask; +#endif +} + +/* ------------------------ Bus Timing Registers --------------------------- */ + +/** + * @brief Set bus timing + * + * @param hw Start address of the CAN registers + * @param brp Baud Rate Prescaler + * @param sjw Synchronization Jump Width + * @param tseg1 Timing Segment 1 + * @param tseg2 Timing Segment 2 + * @param triple_sampling Triple Sampling enable/disable + * + * @note Must be called in reset mode + * @note ESP32 rev 2 or later can support a x2 brp by setting a brp_div bit, + * allowing the brp to go from a maximum of 128 to 256. + */ +static inline void can_ll_set_bus_timing(can_dev_t *hw, uint32_t brp, uint32_t sjw, uint32_t tseg1, uint32_t tseg2, bool triple_sampling) +{ +#ifdef CAN_BRP_DIV_SUPPORTED + if (brp > CAN_BRP_DIV_THRESH) { + //Need to set brp_div bit + hw->interrupt_enable_reg.brp_div = 1; + brp /= 2; + } +#endif + hw->bus_timing_0_reg.brp = (brp / 2) - 1; + hw->bus_timing_0_reg.sjw = sjw - 1; + hw->bus_timing_1_reg.tseg1 = tseg1 - 1; + hw->bus_timing_1_reg.tseg2 = tseg2 - 1; + hw->bus_timing_1_reg.sam = triple_sampling; +} + +/* ----------------------------- ALC Register ------------------------------- */ + +/** + * @brief Clear Arbitration Lost Capture Register + * + * Reading the ALC register rearms the Arbitration Lost Interrupt + * + * @param hw Start address of the CAN registers + */ +static inline void can_ll_clear_arb_lost_cap(can_dev_t *hw) +{ + (void)hw->arbitration_lost_captue_reg.val; + //Todo: Decode ALC register +} + +/* ----------------------------- ECC Register ------------------------------- */ + +/** + * @brief Clear Error Code Capture register + * + * Reading the ECC register rearms the Bus Error Interrupt + * + * @param hw Start address of the CAN registers + */ +static inline void can_ll_clear_err_code_cap(can_dev_t *hw) +{ + (void)hw->error_code_capture_reg.val; + //Todo: Decode error code capture +} + +/* ----------------------------- EWL Register ------------------------------- */ + +/** + * @brief Set Error Warning Limit + * + * @param hw Start address of the CAN registers + * @param ewl Error Warning Limit + * + * @note Must be called in reset mode + */ +static inline void can_ll_set_err_warn_lim(can_dev_t *hw, uint32_t ewl) +{ + hw->error_warning_limit_reg.ewl = ewl; +} + +/** + * @brief Get Error Warning Limit + * + * @param hw Start address of the CAN registers + * @return Error Warning Limit + */ +static inline uint32_t can_ll_get_err_warn_lim(can_dev_t *hw) +{ + return hw->error_warning_limit_reg.val; +} + +/* ------------------------ RX Error Count Register ------------------------- */ + +/** + * @brief Get RX Error Counter + * + * @param hw Start address of the CAN registers + * @return REC value + * + * @note REC is not frozen in reset mode. Listen only mode will freeze it. A BUS + * OFF condition automatically sets the REC to 0. + */ +static inline uint32_t can_ll_get_rec(can_dev_t *hw) +{ + return hw->rx_error_counter_reg.val; +} + +/** + * @brief Set RX Error Counter + * + * @param hw Start address of the CAN registers + * @param rec REC value + * + * @note Must be called in reset mode + */ +static inline void can_ll_set_rec(can_dev_t *hw, uint32_t rec) +{ + hw->rx_error_counter_reg.rxerr = rec; +} + +/* ------------------------ TX Error Count Register ------------------------- */ + +/** + * @brief Get TX Error Counter + * + * @param hw Start address of the CAN registers + * @return TEC value + * + * @note A BUS OFF condition will automatically set this to 128 + */ +static inline uint32_t can_ll_get_tec(can_dev_t *hw) +{ + return hw->tx_error_counter_reg.val; +} + +/** + * @brief Set TX Error Counter + * + * @param hw Start address of the CAN registers + * @param tec TEC value + * + * @note Must be called in reset mode + */ +static inline void can_ll_set_tec(can_dev_t *hw, uint32_t tec) +{ + hw->tx_error_counter_reg.txerr = tec; +} + +/* ---------------------- Acceptance Filter Registers ----------------------- */ + +/** + * @brief Set Acceptance Filter + * @param hw Start address of the CAN registers + * @param code Acceptance Code + * @param mask Acceptance Mask + * @param single_filter Whether to enable single filter mode + * + * @note Must be called in reset mode + */ +static inline void can_ll_set_acc_filter(can_dev_t* hw, uint32_t code, uint32_t mask, bool single_filter) +{ + uint32_t code_swapped = __builtin_bswap32(code); + uint32_t mask_swapped = __builtin_bswap32(mask); + for (int i = 0; i < 4; i++) { + hw->acceptance_filter.acr[i].byte = ((code_swapped >> (i * 8)) & 0xFF); + hw->acceptance_filter.amr[i].byte = ((mask_swapped >> (i * 8)) & 0xFF); + } + hw->mode_reg.afm = single_filter; +} + +/* ------------------------- TX/RX Buffer Registers ------------------------- */ + +/** + * @brief Copy a formatted CAN frame into TX buffer for transmission + * + * @param hw Start address of the CAN registers + * @param tx_frame Pointer to formatted frame + * + * @note Call can_ll_format_frame_buffer() to format a frame + */ +static inline void can_ll_set_tx_buffer(can_dev_t *hw, can_ll_frame_buffer_t *tx_frame) +{ + //Copy formatted frame into TX buffer + for (int i = 0; i < 13; i++) { + hw->tx_rx_buffer[i].val = tx_frame->bytes[i]; + } +} + +/** + * @brief Copy a received frame from the RX buffer for parsing + * + * @param hw Start address of the CAN registers + * @param rx_frame Pointer to store formatted frame + * + * @note Call can_ll_prase_frame_buffer() to parse the formatted frame + */ +static inline void can_ll_get_rx_buffer(can_dev_t *hw, can_ll_frame_buffer_t *rx_frame) +{ + //Copy RX buffer registers into frame + for (int i = 0; i < 13; i++) { + rx_frame->bytes[i] = hw->tx_rx_buffer[i].byte; + } +} + +/** + * @brief Format contents of a CAN frame into layout of TX Buffer + * + * @param[in] id 11 or 29bit ID + * @param[in] dlc Data length code + * @param[in] data Pointer to an 8 byte array containing data. NULL if no data + * @param[in] format Type of CAN frame + * @param[in] single_shot Frame will not be retransmitted on failure + * @param[in] self_rx Frame will also be simultaneously received + * @param[out] tx_frame Pointer to store formatted frame + */ +static inline void can_ll_format_frame_buffer(uint32_t id, uint8_t dlc, const uint8_t *data, + uint32_t flags, can_ll_frame_buffer_t *tx_frame) +{ + /* This function encodes a message into a frame structure. The frame structure has + an identical layout to the TX buffer, allowing the frame structure to be directly + copied into TX buffer. */ + bool is_extd = flags & CAN_MSG_FLAG_EXTD; + bool is_rtr = flags & CAN_MSG_FLAG_RTR; + + //Set frame information + tx_frame->dlc = dlc; + tx_frame->frame_format = is_extd; + tx_frame->rtr = is_rtr; + tx_frame->self_reception = (flags & CAN_MSG_FLAG_SELF) ? 1 : 0; + tx_frame->single_shot = (flags & CAN_MSG_FLAG_SS) ? 1 : 0; + + //Set ID + if (is_extd) { + uint32_t id_temp = __builtin_bswap32((id & CAN_EXTD_ID_MASK) << 3); //((id << 3) >> 8*(3-i)) + for (int i = 0; i < 4; i++) { + tx_frame->extended.id[i] = (id_temp >> (8 * i)) & 0xFF; + } + } else { + uint32_t id_temp = __builtin_bswap16((id & CAN_STD_ID_MASK) << 5); //((id << 5) >> 8*(1-i)) + for (int i = 0; i < 2; i++) { + tx_frame->standard.id[i] = (id_temp >> (8 * i)) & 0xFF; + } + } + + //Set Data + uint8_t *data_buffer = (is_extd) ? tx_frame->extended.data : tx_frame->standard.data; + if (!is_rtr) { + for (int i = 0; (i < dlc) && (i < CAN_FRAME_MAX_DLC); i++) { + data_buffer[i] = data[i]; + } + } +} + +/** + * @brief Parse formatted CAN frame (RX Buffer Layout) into its contents + * + * @param[in] rx_frame Pointer to formatted frame + * @param[out] id 11 or 29bit ID + * @param[out] dlc Data length code + * @param[out] data Data. Left over bytes set to 0. + * @param[out] format Type of CAN frame + */ +static inline void can_ll_prase_frame_buffer(can_ll_frame_buffer_t *rx_frame, uint32_t *id, uint8_t *dlc, + uint8_t *data, uint32_t *flags) +{ + //This function decodes a frame structure into it's constituent components. + + //Copy frame information + *dlc = rx_frame->dlc; + uint32_t flags_temp = 0; + flags_temp |= (rx_frame->frame_format) ? CAN_MSG_FLAG_EXTD : 0; + flags_temp |= (rx_frame->rtr) ? CAN_MSG_FLAG_RTR : 0; + flags_temp |= (rx_frame->dlc > CAN_FRAME_MAX_DLC) ? CAN_MSG_FLAG_DLC_NON_COMP : 0; + *flags = flags_temp; + + //Copy ID + if (rx_frame->frame_format) { + uint32_t id_temp = 0; + for (int i = 0; i < 4; i++) { + id_temp |= rx_frame->extended.id[i] << (8 * i); + } + id_temp = __builtin_bswap32(id_temp) >> 3; //((byte[i] << 8*(3-i)) >> 3) + *id = id_temp & CAN_EXTD_ID_MASK; + } else { + uint32_t id_temp = 0; + for (int i = 0; i < 2; i++) { + id_temp |= rx_frame->standard.id[i] << (8 * i); + } + id_temp = __builtin_bswap16(id_temp) >> 5; //((byte[i] << 8*(1-i)) >> 5) + *id = id_temp & CAN_STD_ID_MASK; + } + + //Copy data + uint8_t *data_buffer = (rx_frame->frame_format) ? rx_frame->extended.data : rx_frame->standard.data; + int data_length = (rx_frame->rtr) ? 0 : ((rx_frame->dlc > CAN_FRAME_MAX_DLC) ? CAN_FRAME_MAX_DLC : rx_frame->dlc); + for (int i = 0; i < data_length; i++) { + data[i] = data_buffer[i]; + } + //Set remaining bytes of data to 0 + for (int i = data_length; i < CAN_FRAME_MAX_DLC; i++) { + data[i] = 0; + } +} + +/* ----------------------- RX Message Count Register ------------------------ */ + +/** + * @brief Get RX Message Counter + * + * @param hw Start address of the CAN registers + * @return RX Message Counter + */ +static inline uint32_t can_ll_get_rx_msg_count(can_dev_t *hw) +{ + return hw->rx_message_counter_reg.val; +} + +/* ------------------------- Clock Divider Register ------------------------- */ + +/** + * @brief Set CLKOUT Divider and enable/disable + * + * @param hw Start address of the CAN registers + * @param divider Divider for CLKOUT. Set to 0 to disable CLKOUT + */ +static inline void can_ll_set_clkout(can_dev_t *hw, uint32_t divider) +{ + /* Configure CLKOUT. CLKOUT is a pre-scaled version of APB CLK. Divider can be + 1, or any even number from 2 to 14. Set to out of range value (0) to disable + CLKOUT. */ + + if (divider >= 2 && divider <= 14) { + CAN.clock_divider_reg.co = 0; + CAN.clock_divider_reg.cd = (divider / 2) - 1; + } else if (divider == 1) { + CAN.clock_divider_reg.co = 0; + CAN.clock_divider_reg.cd = 7; + } else { + CAN.clock_divider_reg.co = 1; + CAN.clock_divider_reg.cd = 0; + } +} + +/** + * @brief Set register address mapping to extended mode + * + * Extended mode register address mapping consists of more registers and extra + * features. + * + * @param hw Start address of the CAN registers + * + * @note Must be called before setting any configuration + * @note Must be called in reset mode + */ +static inline void can_ll_enable_extended_reg_layout(can_dev_t *hw) +{ + hw->clock_divider_reg.cm = 1; +} + +#ifdef __cplusplus +} +#endif diff --git a/components/soc/esp32/include/soc/can_caps.h b/components/soc/esp32/include/soc/can_caps.h new file mode 100644 index 000000000..68073a336 --- /dev/null +++ b/components/soc/esp32/include/soc/can_caps.h @@ -0,0 +1,37 @@ +// Copyright 2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#if (CONFIG_ESP32_REV_MIN >= 2) +#define CAN_BRP_DIV_SUPPORTED 1 +#define CAN_BRP_DIV_THRESH 128 +//Any even number from 2 to 128, or multiples of 4 from 132 to 256 +#define CAN_BRP_IS_VALID(brp) (((brp) >= 2 && (brp) <= 128 && ((brp) & 0x1) == 0) || ((brp) >= 132 && (brp) <= 256 && ((brp) & 0x3) == 0)) +#else +//Any even number from 2 to 128 +#define CAN_BRP_IS_VALID(brp) ((brp) >= 2 && (brp) <= 128 && ((brp) & 0x1) == 0) +#endif + +//Todo: Add FIFO overrun errata workaround +//Todo: Add ECC decode capabilities +//Todo: Add ALC decode capability + +#ifdef __cplusplus +} +#endif diff --git a/components/soc/esp32/include/soc/can_struct.h b/components/soc/esp32/include/soc/can_struct.h index e19196454..7325cd5a3 100644 --- a/components/soc/esp32/include/soc/can_struct.h +++ b/components/soc/esp32/include/soc/can_struct.h @@ -11,194 +11,194 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -#ifndef _SOC_CAN_STRUCT_H_ -#define _SOC_CAN_STRUCT_H_ -#include +#pragma once #ifdef __cplusplus extern "C" { #endif -/* -------------------------- Register Definitions -------------------------- */ +#include + +/* ---------------------------- Register Layout ------------------------------ */ /* The CAN peripheral's registers are 8bits, however the ESP32 can only access * peripheral registers every 32bits. Therefore each CAN register is mapped to * the least significant byte of every 32bits. */ -typedef union { - struct { - uint32_t byte: 8; /* LSB */ - uint32_t reserved24: 24; /* Internal Reserved */ - }; - uint32_t val; -} can_reg_t; - -typedef union { - struct { - uint32_t reset: 1; /* MOD.0 Reset Mode */ - uint32_t listen_only: 1; /* MOD.1 Listen Only Mode */ - uint32_t self_test: 1; /* MOD.2 Self Test Mode */ - uint32_t acceptance_filter: 1; /* MOD.3 Acceptance Filter Mode */ - uint32_t reserved28: 28; /* Internal Reserved. MOD.4 Sleep Mode not supported */ - }; - uint32_t val; -} can_mode_reg_t; - -typedef union { - struct { - uint32_t tx_req: 1; /* CMR.0 Transmission Request */ - uint32_t abort_tx: 1; /* CMR.1 Abort Transmission */ - uint32_t release_rx_buff: 1; /* CMR.2 Release Receive Buffer */ - uint32_t clear_data_overrun: 1; /* CMR.3 Clear Data Overrun */ - uint32_t self_rx_req: 1; /* CMR.4 Self Reception Request */ - uint32_t reserved27: 27; /* Internal Reserved */ - }; - uint32_t val; -} can_cmd_reg_t; - -typedef union { - struct { - uint32_t rx_buff: 1; /* SR.0 Receive Buffer Status */ - uint32_t data_overrun: 1; /* SR.1 Data Overrun Status */ - uint32_t tx_buff: 1; /* SR.2 Transmit Buffer Status */ - uint32_t tx_complete: 1; /* SR.3 Transmission Complete Status */ - uint32_t rx: 1; /* SR.4 Receive Status */ - uint32_t tx: 1; /* SR.5 Transmit Status */ - uint32_t error: 1; /* SR.6 Error Status */ - uint32_t bus: 1; /* SR.7 Bus Status */ - uint32_t reserved24: 24; /* Internal Reserved */ - }; - uint32_t val; -} can_status_reg_t; - -typedef union { - struct { - uint32_t rx: 1; /* IR.0 Receive Interrupt */ - uint32_t tx: 1; /* IR.1 Transmit Interrupt */ - uint32_t err_warn: 1; /* IR.2 Error Interrupt */ - uint32_t data_overrun: 1; /* IR.3 Data Overrun Interrupt */ - uint32_t reserved1: 1; /* Internal Reserved (Wake-up not supported) */ - uint32_t err_passive: 1; /* IR.5 Error Passive Interrupt */ - uint32_t arb_lost: 1; /* IR.6 Arbitration Lost Interrupt */ - uint32_t bus_err: 1; /* IR.7 Bus Error Interrupt */ - uint32_t reserved24: 24; /* Internal Reserved */ - }; - uint32_t val; -} can_intr_reg_t; - -typedef union { - struct { - uint32_t rx: 1; /* IER.0 Receive Interrupt Enable */ - uint32_t tx: 1; /* IER.1 Transmit Interrupt Enable */ - uint32_t err_warn: 1; /* IER.2 Error Interrupt Enable */ - uint32_t data_overrun: 1; /* IER.3 Data Overrun Interrupt Enable */ - uint32_t brp_div: 1; /* THIS IS NOT AN INTERRUPT. brp_div will prescale BRP by 2. Only available on ESP32 Revision 2 or later. Reserved otherwise */ - uint32_t err_passive: 1; /* IER.5 Error Passive Interrupt Enable */ - uint32_t arb_lost: 1; /* IER.6 Arbitration Lost Interrupt Enable */ - uint32_t bus_err: 1; /* IER.7 Bus Error Interrupt Enable */ - uint32_t reserved24: 24; /* Internal Reserved */ - }; - uint32_t val; -} can_intr_en_reg_t; - -typedef union { - struct { - uint32_t baud_rate_prescaler: 6; /* BTR0[5:0] Baud Rate Prescaler */ - uint32_t sync_jump_width: 2; /* BTR0[7:6] Synchronization Jump Width*/ - uint32_t reserved24: 24; /* Internal Reserved */ - }; - uint32_t val; -} can_bus_tim_0_reg_t; - -typedef union { - struct { - uint32_t time_seg_1: 4; /* BTR1[3:0] Timing Segment 1 */ - uint32_t time_seg_2: 3; /* BTR1[6:4] Timing Segment 2 */ - uint32_t sampling: 1; /* BTR1.7 Sampling*/ - uint32_t reserved24: 24; /* Internal Reserved */ - }; - uint32_t val; -} can_bus_tim_1_reg_t; - -typedef union { - struct { - uint32_t arbitration_lost_capture: 5; /* ALC[4:0] Arbitration lost capture */ - uint32_t reserved27: 27; /* Internal Reserved */ - }; - uint32_t val; -} can_arb_lost_cap_reg_t; - -typedef union { - struct { - uint32_t segment: 5; /* ECC[4:0] Error Code Segment 0 to 5 */ - uint32_t direction: 1; /* ECC.5 Error Direction (TX/RX) */ - uint32_t error_code: 2; /* ECC[7:6] Error Code */ - uint32_t reserved24: 24; /* Internal Reserved */ - }; - uint32_t val; -} can_err_code_cap_reg_t; - -typedef struct can_acc_filter_s { - can_reg_t code_reg[4]; - can_reg_t mask_reg[4]; - uint32_t reserved32[5]; -} can_acc_filter_t; - -typedef union { - struct { - uint32_t rx_message_counter: 5; /* RMC[4:0] RX Message Counter */ - uint32_t reserved27: 27; /* Internal Reserved */ - }; - uint32_t val; -} can_rx_msg_cnt_reg_t; - -typedef union { - struct { - uint32_t clock_divider: 3; /* CDR[2:0] CLKOUT frequency selector based of fOSC */ - uint32_t clock_off: 1; /* CDR.3 CLKOUT enable/disable */ - uint32_t reserved3: 3; /* Internal Reserved. RXINTEN and CBP not supported */ - uint32_t can_mode: 1; /* CDR.7 BasicCAN:0 PeliCAN:1 */ - uint32_t reserved24: 24; /* Internal Reserved */ - }; - uint32_t val; -} can_clk_div_reg_t; - -/* ---------------------------- Register Layout ------------------------------ */ typedef volatile struct can_dev_s { //Configuration and Control Registers - can_mode_reg_t mode_reg; /* Address 0 */ - can_cmd_reg_t command_reg; /* Address 1 */ - can_status_reg_t status_reg; /* Address 2 */ - can_intr_reg_t interrupt_reg; /* Address 3 */ - can_intr_en_reg_t interrupt_enable_reg; /* Address 4 */ - uint32_t reserved_05; /* Address 5 */ - can_bus_tim_0_reg_t bus_timing_0_reg; /* Address 6 */ - can_bus_tim_1_reg_t bus_timing_1_reg; /* Address 7 */ - uint32_t reserved_08; /* Address 8 (Output control not supported) */ - uint32_t reserved_09; /* Address 9 (Test Register not supported) */ - uint32_t reserved_10; /* Address 10 */ + union { + struct { + uint32_t rm: 1; /* MOD.0 Reset Mode */ + uint32_t lom: 1; /* MOD.1 Listen Only Mode */ + uint32_t stm: 1; /* MOD.2 Self Test Mode */ + uint32_t afm: 1; /* MOD.3 Acceptance Filter Mode */ + uint32_t reserved28: 28; /* Internal Reserved. MOD.4 Sleep Mode not supported */ + }; + uint32_t val; + } mode_reg; /* Address 0 */ + union { + struct { + uint32_t tr: 1; /* CMR.0 Transmission Request */ + uint32_t at: 1; /* CMR.1 Abort Transmission */ + uint32_t rrb: 1; /* CMR.2 Release Receive Buffer */ + uint32_t cdo: 1; /* CMR.3 Clear Data Overrun */ + uint32_t srr: 1; /* CMR.4 Self Reception Request */ + uint32_t reserved27: 27; /* Internal Reserved */ + }; + uint32_t val; + } command_reg; /* Address 1 */ + union { + struct { + uint32_t rbs: 1; /* SR.0 Receive Buffer Status */ + uint32_t dos: 1; /* SR.1 Data Overrun Status */ + uint32_t tbs: 1; /* SR.2 Transmit Buffer Status */ + uint32_t tcs: 1; /* SR.3 Transmission Complete Status */ + uint32_t rs: 1; /* SR.4 Receive Status */ + uint32_t ts: 1; /* SR.5 Transmit Status */ + uint32_t es: 1; /* SR.6 Error Status */ + uint32_t bs: 1; /* SR.7 Bus Status */ + uint32_t reserved24: 24; /* Internal Reserved */ + }; + uint32_t val; + } status_reg; /* Address 2 */ + union { + struct { + uint32_t ri: 1; /* IR.0 Receive Interrupt */ + uint32_t ti: 1; /* IR.1 Transmit Interrupt */ + uint32_t ei: 1; /* IR.2 Error Interrupt */ + uint32_t reserved2: 2; /* Internal Reserved (Data Overrun interrupt and Wake-up not supported) */ + uint32_t epi: 1; /* IR.5 Error Passive Interrupt */ + uint32_t ali: 1; /* IR.6 Arbitration Lost Interrupt */ + uint32_t bei: 1; /* IR.7 Bus Error Interrupt */ + uint32_t reserved24: 24; /* Internal Reserved */ + }; + uint32_t val; + } interrupt_reg; /* Address 3 */ + union { + struct { + uint32_t rie: 1; /* IER.0 Receive Interrupt Enable */ + uint32_t tie: 1; /* IER.1 Transmit Interrupt Enable */ + uint32_t eie: 1; /* IER.2 Error Interrupt Enable */ + uint32_t doie: 1; /* IER.3 Data Overrun Interrupt Enable */ + uint32_t brp_div: 1; /* THIS IS NOT AN INTERRUPT. brp_div will prescale BRP by 2. Only available on ESP32 Revision 2 or later. Reserved otherwise */ + uint32_t epie: 1; /* IER.5 Error Passive Interrupt Enable */ + uint32_t alie: 1; /* IER.6 Arbitration Lost Interrupt Enable */ + uint32_t beie: 1; /* IER.7 Bus Error Interrupt Enable */ + uint32_t reserved24: 24; /* Internal Reserved */ + }; + uint32_t val; + } interrupt_enable_reg; /* Address 4 */ + uint32_t reserved_05; /* Address 5 */ + union { + struct { + uint32_t brp: 6; /* BTR0[5:0] Baud Rate Prescaler */ + uint32_t sjw: 2; /* BTR0[7:6] Synchronization Jump Width*/ + uint32_t reserved24: 24; /* Internal Reserved */ + }; + uint32_t val; + } bus_timing_0_reg; /* Address 6 */ + union { + struct { + uint32_t tseg1: 4; /* BTR1[3:0] Timing Segment 1 */ + uint32_t tseg2: 3; /* BTR1[6:4] Timing Segment 2 */ + uint32_t sam: 1; /* BTR1.7 Sampling*/ + uint32_t reserved24: 24; /* Internal Reserved */ + }; + uint32_t val; + } bus_timing_1_reg; /* Address 7 */ + uint32_t reserved_08; /* Address 8 (Output control not supported) */ + uint32_t reserved_09; /* Address 9 (Test Register not supported) */ + uint32_t reserved_10; /* Address 10 */ //Capture and Counter Registers - can_arb_lost_cap_reg_t arbitration_lost_captue_reg; /* Address 11 */ - can_err_code_cap_reg_t error_code_capture_reg; /* Address 12 */ - can_reg_t error_warning_limit_reg; /* EWLR[7:0] Error Warning Limit: Address 13 */ - can_reg_t rx_error_counter_reg; /* RXERR[7:0] Receive Error Counter: Address 14 */ - can_reg_t tx_error_counter_reg; /* TXERR[7:0] Transmit Error Counter: Address 15 */ + union { + struct { + uint32_t alc: 5; /* ALC[4:0] Arbitration lost capture */ + uint32_t reserved27: 27; /* Internal Reserved */ + }; + uint32_t val; + } arbitration_lost_captue_reg; /* Address 11 */ + union { + struct { + uint32_t seg: 5; /* ECC[4:0] Error Code Segment 0 to 5 */ + uint32_t dir: 1; /* ECC.5 Error Direction (TX/RX) */ + uint32_t errc: 2; /* ECC[7:6] Error Code */ + uint32_t reserved24: 24; /* Internal Reserved */ + }; + uint32_t val; + } error_code_capture_reg; /* Address 12 */ + union { + struct { + uint32_t ewl: 8; /* EWL[7:0] Error Warning Limit */ + uint32_t reserved24: 24; /* Internal Reserved */ + }; + uint32_t val; + } error_warning_limit_reg; /* EWLR[7:0] Error Warning Limit: Address 13 */ + union { + struct { + uint32_t rxerr: 8; /* RXERR[7:0] Receive Error Counter */ + uint32_t reserved24: 24; /* Internal Reserved */ + }; + uint32_t val; + } rx_error_counter_reg; /* Address 12 */ + union { + struct { + uint32_t txerr: 8; /* TXERR[7:0] Receive Error Counter */ + uint32_t reserved24: 24; /* Internal Reserved */ + }; + uint32_t val; + } tx_error_counter_reg; /* Address 15 */ //Shared Registers (TX Buff/RX Buff/Acc Filter) union { - can_acc_filter_t acceptance_filter; - can_reg_t tx_rx_buffer[13]; - }; /* Address 16-28 TX/RX Buffer and Acc Filter*/; + struct { + union { + struct { + uint32_t byte: 8; /* ACRx[7:0] Acceptance Code */ + uint32_t reserved24: 24; /* Internal Reserved */ + }; + uint32_t val; + } acr[4]; + union { + struct { + uint32_t byte: 8; /* AMRx[7:0] Acceptance Mask */ + uint32_t reserved24: 24; /* Internal Reserved */ + }; + uint32_t val; + } amr[4]; + uint32_t reserved32[5]; + } acceptance_filter; + union { + struct { + uint32_t byte: 8; + uint32_t reserved24: 24; + }; + uint32_t val; + } tx_rx_buffer[13]; + }; /* Address 16-28 TX/RX Buffer and Acc Filter*/; //Misc Registers - can_rx_msg_cnt_reg_t rx_message_counter_reg; /* Address 29 */ - can_reg_t reserved_30; /* Address 30 (RX Buffer Start Address not supported) */ - can_clk_div_reg_t clock_divider_reg; /* Address 31 */ - - //Start of RX FIFO + union { + struct { + uint32_t rmc: 5; /* RMC[4:0] RX Message Counter */ + uint32_t reserved27: 27; /* Internal Reserved */ + }; + uint32_t val; + } rx_message_counter_reg; /* Address 29 */ + uint32_t reserved_30; /* Address 30 (RX Buffer Start Address not supported) */ + union { + struct { + uint32_t cd: 3; /* CDR[2:0] CLKOUT frequency selector based of fOSC */ + uint32_t co: 1; /* CDR.3 CLKOUT enable/disable */ + uint32_t reserved3: 3; /* Internal Reserved. RXINTEN and CBP not supported */ + uint32_t cm: 1; /* CDR.7 BasicCAN:0 PeliCAN:1 */ + uint32_t reserved24: 24; /* Internal Reserved */ + }; + uint32_t val; + } clock_divider_reg; /* Address 31 */ } can_dev_t; _Static_assert(sizeof(can_dev_t) == 128, "CAN registers should be 32 * 4 bytes"); @@ -208,6 +208,3 @@ extern can_dev_t CAN; #ifdef __cplusplus } #endif - -#endif /* _SOC_CAN_STRUCT_H_ */ - diff --git a/components/soc/esp32/include/soc/soc_caps.h b/components/soc/esp32/include/soc/soc_caps.h index 6bfd62180..bbc8e8f19 100644 --- a/components/soc/esp32/include/soc/soc_caps.h +++ b/components/soc/esp32/include/soc/soc_caps.h @@ -8,4 +8,5 @@ #define SOC_MCPWM_SUPPORTED 1 #define SOC_SDMMC_HOST_SUPPORTED 1 #define SOC_BT_SUPPORTED 1 -#define SOC_SDIO_SLAVE_SUPPORTED 1 \ No newline at end of file +#define SOC_SDIO_SLAVE_SUPPORTED 1 +#define SOC_CAN_SUPPORTED 1 diff --git a/components/soc/include/hal/can_hal.h b/components/soc/include/hal/can_hal.h new file mode 100644 index 000000000..e72316072 --- /dev/null +++ b/components/soc/include/hal/can_hal.h @@ -0,0 +1,305 @@ +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/******************************************************************************* + * NOTICE + * The hal is not public api, don't use in application code. + * See readme.md in soc/include/hal/readme.md + ******************************************************************************/ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include "hal/can_types.h" +#include "hal/can_ll.h" + +/* ------------------------- Defines and Typedefs --------------------------- */ + +//Error active interrupt related +#define CAN_HAL_EVENT_BUS_OFF (1 << 0) +#define CAN_HAL_EVENT_BUS_RECOV_CPLT (1 << 1) +#define CAN_HAL_EVENT_BUS_RECOV_PROGRESS (1 << 2) +#define CAN_HAL_EVENT_ABOVE_EWL (1 << 3) +#define CAN_HAL_EVENT_BELOW_EWL (1 << 4) +#define CAN_HAL_EVENT_ERROR_PASSIVE (1 << 5) +#define CAN_HAL_EVENT_ERROR_ACTIVE (1 << 6) +#define CAN_HAL_EVENT_BUS_ERR (1 << 7) +#define CAN_HAL_EVENT_ARB_LOST (1 << 8) +#define CAN_HAL_EVENT_RX_BUFF_FRAME (1 << 9) +#define CAN_HAL_EVENT_TX_BUFF_FREE (1 << 10) + +typedef struct { + can_dev_t *dev; +} can_hal_context_t; + +typedef can_ll_frame_buffer_t can_hal_frame_t; + +/* ---------------------------- Init and Config ----------------------------- */ + +/** + * @brief Initialize CAN peripheral and HAL context + * + * Sets HAL context, puts CAN peripheral into reset mode, then sets some + * registers with default values. + * + * @param hal_ctx Context of the HAL layer + * @return True if successfully initialized, false otherwise. + */ +bool can_hal_init(can_hal_context_t *hal_ctx); + +/** + * @brief Deinitialize the CAN peripheral and HAL context + * + * Clears any unhandled interrupts and unsets HAL context + * + * @param hal_ctx Context of the HAL layer + */ +void can_hal_deinit(can_hal_context_t *hal_ctx); + +/** + * @brief Configure the CAN peripheral + * + * @param hal_ctx Context of the HAL layer + * @param t_config Pointer to timing configuration structure + * @param f_config Pointer to filter configuration structure + * @param intr_mask Mask of interrupts to enable + * @param clkout_divider Clock divider value for CLKOUT. Set to -1 to disable CLKOUT + */ +void can_hal_configure(can_hal_context_t *hal_ctx, const can_timing_config_t *t_config, const can_filter_config_t *f_config, uint32_t intr_mask, uint32_t clkout_divider); + +/* -------------------------------- Actions --------------------------------- */ + +/** + * @brief Start the CAN peripheral + * + * Start the CAN peripheral by configuring its operating mode, then exiting + * reset mode so that the CAN peripheral can participate in bus activities. + * + * @param hal_ctx Context of the HAL layer + * @param mode Operating mode + * @return True if successfully started, false otherwise. + */ +bool can_hal_start(can_hal_context_t *hal_ctx, can_mode_t mode); + +/** + * @brief Stop the CAN peripheral + * + * Stop the CAN peripheral by entering reset mode to stop any bus activity, then + * setting the operating mode to Listen Only so that REC is frozen. + * + * @param hal_ctx Context of the HAL layer + * @return True if successfully stopped, false otherwise. + */ +bool can_hal_stop(can_hal_context_t *hal_ctx); + +/** + * @brief Start bus recovery + * + * @param hal_ctx Context of the HAL layer + * @return True if successfully started bus recovery, false otherwise. + */ +static inline bool can_hal_start_bus_recovery(can_hal_context_t *hal_ctx) +{ + return can_ll_exit_reset_mode(hal_ctx->dev); +} + +/** + * @brief Get the value of the TX Error Counter + * + * @param hal_ctx Context of the HAL layer + * @return TX Error Counter Value + */ +static inline uint32_t can_hal_get_tec(can_hal_context_t *hal_ctx) +{ + return can_ll_get_tec((hal_ctx)->dev); +} + +/** + * @brief Get the value of the RX Error Counter + * + * @param hal_ctx Context of the HAL layer + * @return RX Error Counter Value + */ +static inline uint32_t can_hal_get_rec(can_hal_context_t *hal_ctx) +{ + return can_ll_get_rec((hal_ctx)->dev); +} + +/** + * @brief Get the RX message count register + * + * @param hal_ctx Context of the HAL layer + * @return RX message count + */ +static inline uint32_t can_hal_get_rx_msg_count(can_hal_context_t *hal_ctx) +{ + return can_ll_get_rx_msg_count((hal_ctx)->dev); +} + +/** + * @brief Check if the last transmitted frame was successful + * + * @param hal_ctx Context of the HAL layer + * @return True if successful + */ +static inline bool can_hal_check_last_tx_successful(can_hal_context_t *hal_ctx) +{ + return can_ll_is_last_tx_successful((hal_ctx)->dev); +} + +/* ----------------------------- Event Handling ----------------------------- */ + +/** + * @brief Decode current events that triggered an interrupt + * + * This function should be called on every CAN interrupt. It will read (and + * thereby clear) the interrupt register, then determine what events have + * occurred to trigger the interrupt. + * + * @param hal_ctx Context of the HAL layer + * @param bus_recovering Whether the CAN peripheral was previous undergoing bus recovery + * @return Bit mask of events that have occurred + */ +uint32_t can_hal_decode_interrupt_events(can_hal_context_t *hal_ctx, bool bus_recovering); + +/** + * @brief Handle bus recovery complete + * + * This function should be called on an bus recovery complete event. It simply + * enters reset mode to stop bus activity. + * + * @param hal_ctx Context of the HAL layer + * @return True if successfully handled bus recovery completion, false otherwise. + */ +static inline bool can_hal_handle_bus_recov_cplt(can_hal_context_t *hal_ctx) +{ + return can_ll_enter_reset_mode((hal_ctx)->dev); +} + +/** + * @brief Handle arbitration lost + * + * This function should be called on an arbitration lost event. It simply clears + * the clears the ALC register. + * + * @param hal_ctx Context of the HAL layer + */ +static inline void can_hal_handle_arb_lost(can_hal_context_t *hal_ctx) +{ + can_ll_clear_arb_lost_cap((hal_ctx)->dev); +} + +/** + * @brief Handle bus error + * + * This function should be called on an bus error event. It simply clears + * the clears the ECC register. + * + * @param hal_ctx Context of the HAL layer + */ +static inline void can_hal_handle_bus_error(can_hal_context_t *hal_ctx) +{ + can_ll_clear_err_code_cap((hal_ctx)->dev); +} + +/** + * @brief Handle BUS OFF + * + * This function should be called on a BUS OFF event. It simply changes the + * mode to LOM to freeze REC + * + * @param hal_ctx Context of the HAL layer + */ +static inline void can_hal_handle_bus_off(can_hal_context_t *hal_ctx) +{ + can_ll_set_mode((hal_ctx)->dev, CAN_MODE_LISTEN_ONLY); +} + +/* ------------------------------- TX and RX -------------------------------- */ + +/** + * @brief Format a CAN Frame + * + * This function takes a CAN message structure (containing ID, DLC, data, and + * flags) and formats it to match the layout of the TX frame buffer. + * + * @param message Pointer to CAN message + * @param frame Pointer to empty frame structure + */ +static inline void can_hal_format_frame(const can_message_t *message, can_hal_frame_t *frame) +{ + //Direct call to ll function + can_ll_format_frame_buffer(message->identifier, message->data_length_code, message->data, + message->flags, frame); +} + +/** + * @brief Parse a CAN Frame + * + * This function takes a CAN frame (in the format of the RX frame buffer) and + * parses it to a CAN message (containing ID, DLC, data and flags). + * + * @param frame Pointer to frame structure + * @param message Pointer to empty message structure + */ +static inline void can_hal_parse_frame(can_hal_frame_t *frame, can_message_t *message) +{ + //Direct call to ll function + can_ll_prase_frame_buffer(frame, &message->identifier, &message->data_length_code, + message->data, &message->flags); +} + +/** + * @brief Copy a frame into the TX buffer and transmit + * + * This function copies a formatted TX frame into the TX buffer, and the + * transmit by setting the correct transmit command (e.g. normal, single shot, + * self RX) in the command register. + * + * @param hal_ctx Context of the HAL layer + * @param tx_frame Pointer to structure containing formatted TX frame + */ +void can_hal_set_tx_buffer_and_transmit(can_hal_context_t *hal_ctx, can_hal_frame_t *tx_frame); + +/** + * @brief Copy a frame from the RX buffer and release + * + * This function copies a frame from the RX buffer, then release the buffer (so + * that it loads the next frame in the RX FIFO). + * + * @param hal_ctx Context of the HAL layer + * @param rx_frame Pointer to structure to store RX frame + */ +static inline void can_hal_read_rx_buffer_and_clear(can_hal_context_t *hal_ctx, can_hal_frame_t *rx_frame) +{ + can_ll_get_rx_buffer(hal_ctx->dev, rx_frame); + can_ll_set_cmd_release_rx_buffer(hal_ctx->dev); + /* + * Todo: Support overrun handling by: + * - Check overrun status bit. Return false if overrun + */ +} + + +//Todo: Decode ALC register +//Todo: Decode error code capture + +#ifdef __cplusplus +} +#endif diff --git a/components/soc/include/hal/can_types.h b/components/soc/include/hal/can_types.h new file mode 100644 index 000000000..d7947e65c --- /dev/null +++ b/components/soc/include/hal/can_types.h @@ -0,0 +1,137 @@ +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/** + * @brief CAN2.0B Constants + */ +#define CAN_EXTD_ID_MASK 0x1FFFFFFF /**< Bit mask for 29 bit Extended Frame Format ID */ +#define CAN_STD_ID_MASK 0x7FF /**< Bit mask for 11 bit Standard Frame Format ID */ +#define CAN_FRAME_MAX_DLC 8 /**< Max data bytes allowed in CAN2.0 */ +#define CAN_FRAME_EXTD_ID_LEN_BYTES 4 /**< EFF ID requires 4 bytes (29bit) */ +#define CAN_FRAME_STD_ID_LEN_BYTES 2 /**< SFF ID requires 2 bytes (11bit) */ +#define CAN_ERR_PASS_THRESH 128 /**< Error counter threshold for error passive */ + +/** @cond */ //Doxy command to hide preprocessor definitions from docs +/** + * @brief CAN Message flags + * + * The message flags are used to indicate the type of message transmitted/received. + * Some flags also specify the type of transmission. + */ +#define CAN_MSG_FLAG_NONE 0x00 /**< No message flags (Standard Frame Format) */ +#define CAN_MSG_FLAG_EXTD 0x01 /**< Extended Frame Format (29bit ID) */ +#define CAN_MSG_FLAG_RTR 0x02 /**< Message is a Remote Transmit Request */ +#define CAN_MSG_FLAG_SS 0x04 /**< Transmit as a Single Shot Transmission. Unused for received. */ +#define CAN_MSG_FLAG_SELF 0x08 /**< Transmit as a Self Reception Request. Unused for received. */ +#define CAN_MSG_FLAG_DLC_NON_COMP 0x10 /**< Message's Data length code is larger than 8. This will break compliance with CAN2.0B */ + +/** + * @brief Initializer macros for timing configuration structure + * + * The following initializer macros offer commonly found bit rates. + * + * @note These timing values are based on the assumption APB clock is at 80MHz + * @note The 20K, 16K and 12.5K bit rates are only available from ESP32 Revision 2 onwards + */ +#ifdef CAN_BRP_DIV_SUPPORTED +#define CAN_TIMING_CONFIG_12_5KBITS() {.brp = 256, .tseg_1 = 16, .tseg_2 = 8, .sjw = 3, .triple_sampling = false} +#define CAN_TIMING_CONFIG_16KBITS() {.brp = 200, .tseg_1 = 16, .tseg_2 = 8, .sjw = 3, .triple_sampling = false} +#define CAN_TIMING_CONFIG_20KBITS() {.brp = 200, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} +#endif +#define CAN_TIMING_CONFIG_25KBITS() {.brp = 128, .tseg_1 = 16, .tseg_2 = 8, .sjw = 3, .triple_sampling = false} +#define CAN_TIMING_CONFIG_50KBITS() {.brp = 80, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} +#define CAN_TIMING_CONFIG_100KBITS() {.brp = 40, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} +#define CAN_TIMING_CONFIG_125KBITS() {.brp = 32, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} +#define CAN_TIMING_CONFIG_250KBITS() {.brp = 16, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} +#define CAN_TIMING_CONFIG_500KBITS() {.brp = 8, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} +#define CAN_TIMING_CONFIG_800KBITS() {.brp = 4, .tseg_1 = 16, .tseg_2 = 8, .sjw = 3, .triple_sampling = false} +#define CAN_TIMING_CONFIG_1MBITS() {.brp = 4, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} + +/** + * @brief Initializer macro for filter configuration to accept all IDs + */ +#define CAN_FILTER_CONFIG_ACCEPT_ALL() {.acceptance_code = 0, .acceptance_mask = 0xFFFFFFFF, .single_filter = true} +/** @endcond */ + +/** + * @brief CAN Controller operating modes + */ +typedef enum { + CAN_MODE_NORMAL, /**< Normal operating mode where CAN controller can send/receive/acknowledge messages */ + CAN_MODE_NO_ACK, /**< Transmission does not require acknowledgment. Use this mode for self testing */ + CAN_MODE_LISTEN_ONLY, /**< The CAN controller will not influence the bus (No transmissions or acknowledgments) but can receive messages */ +} can_mode_t; + +/** + * @brief Structure to store a CAN message + * + * @note + * @note The flags member is deprecated + */ +typedef struct { + union { + struct { + //The order of these bits must match deprecated message flags for compatibility reasons + uint32_t extd: 1; /**< Extended Frame Format (29bit ID) */ + uint32_t rtr: 1; /**< Message is a Remote Transmit Request */ + uint32_t ss: 1; /**< Transmit as a Single Shot Transmission. Unused for received. */ + uint32_t self: 1; /**< Transmit as a Self Reception Request. Unused for received. */ + uint32_t dlc_non_comp: 1; /**< Message's Data length code is larger than 8. This will break compliance with CAN2.0B. */ + uint32_t reserved: 27; /**< Reserved bits */ + }; + //Todo: Deprecate flags + uint32_t flags; /**< Alternate way to set message flags using message flag macros (see documentation) */ + }; + uint32_t identifier; /**< 11 or 29 bit identifier */ + uint8_t data_length_code; /**< Data length code */ + uint8_t data[CAN_FRAME_MAX_DLC]; /**< Data bytes (not relevant in RTR frame) */ +} can_message_t; + +/** + * @brief Structure for bit timing configuration of the CAN driver + * + * @note Macro initializers are available for this structure + */ +typedef struct { + uint32_t brp; /**< Baudrate prescaler (i.e., APB clock divider) can be any even number from 2 to 128. + For ESP32 Rev 2 or later, multiples of 4 from 132 to 256 are also supported */ + uint8_t tseg_1; /**< Timing segment 1 (Number of time quanta, between 1 to 16) */ + uint8_t tseg_2; /**< Timing segment 2 (Number of time quanta, 1 to 8) */ + uint8_t sjw; /**< Synchronization Jump Width (Max time quanta jump for synchronize from 1 to 4) */ + bool triple_sampling; /**< Enables triple sampling when the CAN controller samples a bit */ +} can_timing_config_t; + +/** + * @brief Structure for acceptance filter configuration of the CAN driver (see documentation) + * + * @note Macro initializers are available for this structure + */ +typedef struct { + uint32_t acceptance_code; /**< 32-bit acceptance code */ + uint32_t acceptance_mask; /**< 32-bit acceptance mask */ + bool single_filter; /**< Use Single Filter Mode (see documentation) */ +} can_filter_config_t; + +#ifdef __cplusplus +} +#endif diff --git a/components/soc/include/soc/can_periph.h b/components/soc/include/soc/can_periph.h index c64519fc1..f00a15d00 100644 --- a/components/soc/include/soc/can_periph.h +++ b/components/soc/include/soc/can_periph.h @@ -13,6 +13,10 @@ // limitations under the License. #pragma once + +#include "sdkconfig.h" + #if CONFIG_IDF_TARGET_ESP32 #include "soc/can_struct.h" +#include "soc/can_caps.h" #endif diff --git a/components/soc/src/hal/can_hal.c b/components/soc/src/hal/can_hal.c new file mode 100644 index 000000000..4a0da8025 --- /dev/null +++ b/components/soc/src/hal/can_hal.c @@ -0,0 +1,144 @@ +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//Todo: Place the implementation of all common HAL functions here + +#include +#include "hal/can_hal.h" + +//Default values written to various registers on initialization +#define CAN_HAL_INIT_TEC 0 +#define CAN_HAL_INIT_REC 0 +#define CAN_HAL_INIT_EWL 96 + +bool can_hal_init(can_hal_context_t *hal_ctx) +{ + //Initialize HAL context + hal_ctx->dev = &CAN; + //Initialize CAN controller, and set default values to registers + if (!can_ll_enter_reset_mode(hal_ctx->dev)) { //Must enter reset mode to write to config registers + return false; + } + can_ll_enable_extended_reg_layout(hal_ctx->dev); //Set PeliCAN address layout + can_ll_set_mode(hal_ctx->dev, CAN_MODE_LISTEN_ONLY); //Freeze REC by changing to LOM mode + //Both TEC and REC should start at 0 + can_ll_set_tec(hal_ctx->dev, CAN_HAL_INIT_TEC); + can_ll_set_rec(hal_ctx->dev, CAN_HAL_INIT_REC); + can_ll_set_err_warn_lim(hal_ctx->dev, CAN_HAL_INIT_EWL); //Set default value of for EWL + return true; +} + +void can_hal_deinit(can_hal_context_t *hal_ctx) +{ + //Clear any pending registers + (void) can_ll_get_and_clear_intrs(hal_ctx->dev); + can_ll_set_enabled_intrs(hal_ctx->dev, 0); + can_ll_clear_arb_lost_cap(hal_ctx->dev); + can_ll_clear_err_code_cap(hal_ctx->dev); + hal_ctx->dev = NULL; +} + +void can_hal_configure(can_hal_context_t *hal_ctx, const can_timing_config_t *t_config, const can_filter_config_t *f_config, uint32_t intr_mask, uint32_t clkout_divider) +{ + //Configure bus timing, acceptance filter, CLKOUT, and interrupts + can_ll_set_bus_timing(hal_ctx->dev, t_config->brp, t_config->sjw, t_config->tseg_1, t_config->tseg_2, t_config->triple_sampling); + can_ll_set_acc_filter(hal_ctx->dev, f_config->acceptance_code, f_config->acceptance_mask, f_config->single_filter); + can_ll_set_clkout(hal_ctx->dev, clkout_divider); + can_ll_set_enabled_intrs(hal_ctx->dev, intr_mask); + (void) can_ll_get_and_clear_intrs(hal_ctx->dev); //Clear any latched interrupts +} + +bool can_hal_start(can_hal_context_t *hal_ctx, can_mode_t mode) +{ + can_ll_set_mode(hal_ctx->dev, mode); //Set operating mode + //Todo: Check if this can be removed + (void) can_ll_get_and_clear_intrs(hal_ctx->dev); //Clear any latched interrupts + return can_ll_exit_reset_mode(hal_ctx->dev); //Return false if failed to exit reset mode +} + +bool can_hal_stop(can_hal_context_t *hal_ctx) +{ + if (!can_ll_enter_reset_mode(hal_ctx->dev)) { + return false; + } + //Todo: Check if this can be removed + (void) can_ll_get_and_clear_intrs(hal_ctx->dev); + can_ll_set_mode(hal_ctx->dev, CAN_MODE_LISTEN_ONLY); //Freeze REC by changing to LOM mode + return true; +} + +uint32_t can_hal_decode_interrupt_events(can_hal_context_t *hal_ctx, bool bus_recovering) +{ + uint32_t events = 0; + //Read interrupt, status + uint32_t interrupts = can_ll_get_and_clear_intrs(hal_ctx->dev); + uint32_t status = can_ll_get_status(hal_ctx->dev); + uint32_t tec = can_ll_get_tec(hal_ctx->dev); + uint32_t rec = can_ll_get_rec(hal_ctx->dev); + + //Receive Interrupt set whenever RX FIFO is not empty + if (interrupts & CAN_LL_INTR_RI) { + events |= CAN_HAL_EVENT_RX_BUFF_FRAME; + } + //Transmit interrupt set whenever TX buffer becomes free + if (interrupts & CAN_LL_INTR_TI) { + events |= CAN_HAL_EVENT_TX_BUFF_FREE; + } + //Error Warning Interrupt set whenever Error or Bus Status bit changes + if (interrupts & CAN_LL_INTR_EI) { + if (status & CAN_LL_STATUS_BS) { + //Currently in BUS OFF state + //EWL is exceeded, thus must have entered BUS OFF + //Below EWL. Therefore TEC is counting down in bus recovery + events |= (status & CAN_LL_STATUS_ES) ? CAN_HAL_EVENT_BUS_OFF : CAN_HAL_EVENT_BUS_RECOV_PROGRESS; + } else { + //Not in BUS OFF + events |= (status & CAN_LL_STATUS_ES) ? CAN_HAL_EVENT_ABOVE_EWL : //Just Exceeded EWL + ((bus_recovering) ? //If previously undergoing bus recovery + CAN_HAL_EVENT_BUS_RECOV_CPLT : + CAN_HAL_EVENT_BELOW_EWL); + } + } + //Error Passive Interrupt on transition from error active to passive or vice versa + if (interrupts & CAN_LL_INTR_EPI) { + events |= (tec >= CAN_ERR_PASS_THRESH || rec >= CAN_ERR_PASS_THRESH) ? CAN_HAL_EVENT_ERROR_PASSIVE : CAN_HAL_EVENT_ERROR_ACTIVE; + } + //Arbitration Lost Interrupt triggered on losing arbitration + if (interrupts & CAN_LL_INTR_ALI) { + events |= CAN_HAL_EVENT_ARB_LOST; + } + //Bus error interrupt triggered on a bus error (e.g. bit, ACK, stuff etc) + if (interrupts & CAN_LL_INTR_BEI) { + events |= CAN_HAL_EVENT_BUS_ERR; + } + return events; +} + +void can_hal_set_tx_buffer_and_transmit(can_hal_context_t *hal_ctx, can_hal_frame_t *tx_frame) +{ + //Copy frame into tx buffer + can_ll_set_tx_buffer(hal_ctx->dev, tx_frame); + //Hit the send command + if (tx_frame->self_reception) { + if (tx_frame->single_shot) { + can_ll_set_cmd_self_rx_single_shot(hal_ctx->dev); + } else { + can_ll_set_cmd_self_rx_request(hal_ctx->dev); + } + } else if (tx_frame->single_shot){ + can_ll_set_cmd_tx_single_shot(hal_ctx->dev); + } else { + can_ll_set_cmd_tx(hal_ctx->dev); + } +} diff --git a/docs/Doxyfile b/docs/Doxyfile index a56f13cf4..1a066b606 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -88,6 +88,7 @@ INPUT = \ ## ../../components/driver/include/driver/adc.h \ ../../components/driver/include/driver/can.h \ + ../../components/soc/include/hal/can_types.h \ ../../components/driver/include/driver/dac.h \ ../../components/driver/include/driver/gpio.h \ ../../components/driver/include/driver/rtc_io.h \ diff --git a/docs/en/api-reference/peripherals/can.rst b/docs/en/api-reference/peripherals/can.rst index ce190de6a..ccfb87039 100644 --- a/docs/en/api-reference/peripherals/can.rst +++ b/docs/en/api-reference/peripherals/can.rst @@ -273,7 +273,7 @@ Driver States Message Flags ^^^^^^^^^^^^^ -The CAN driver distinguishes different types of CAN messages by using the message flags in the ``flags`` field of :cpp:type:`can_message_t`. These flags help distinguish whether a message is in standard or extended format, an RTR, and the type of transmission to use when transmitting such a message. The CAN driver supports the following flags: +The CAN driver distinguishes different types of CAN messages by using the various bit field members of the :cpp:type:`can_message_t` structure. These bit field members help distinguish whether a message is in standard or extended format, an RTR, and the type of transmission to use when transmitting such a message. These bit field members can also be toggled using the the `flags` member of :cpp:type:`can_message_t` and the following message flags: +-------------------------------+----------------------------------------------+ | Flag | Description | @@ -286,19 +286,19 @@ The CAN driver distinguishes different types of CAN messages by using the messag | ``CAN_MSG_FLAG_SS`` | Transmit message using Single Shot | | | Transmission (Message will not be | | | retransmitted upon error or loss of | -| | arbitration) | +| | arbitration). Unused for received message. | +-------------------------------+----------------------------------------------+ | ``CAN_MSG_FLAG_SELF`` | Transmit message using Self Reception | | | Request (Transmitted message will also | -| | received by the same node) | +| | received by the same node). Unused for | +| | received message. | +-------------------------------+----------------------------------------------+ | ``CAN_MSG_FLAG_DLC_NON_COMP`` | Message's Data length code is larger than 8. | | | This will break compliance with CAN2.0B | +-------------------------------+----------------------------------------------+ - -.. note:: - The ``CAN_MSG_FLAG_NONE`` flag can be used for Standard Frame Format messages - +| ``CAN_MSG_FLAG_NONE`` | Clears all bit fields. Equivalent to a | +| | Standard Frame Format (11bit ID) Data Frame. | ++-------------------------------+----------------------------------------------+ .. -------------------------------- Examples ----------------------------------- @@ -360,7 +360,7 @@ The following code snippet demonstrates how to transmit a message via the usage //Configure message to transmit can_message_t message; message.identifier = 0xAAAA; - message.flags = CAN_MSG_FLAG_EXTD; + message.extd = 1; message.data_length_code = 4; for (int i = 0; i < 4; i++) { message.data[i] = 0; @@ -394,13 +394,13 @@ The following code snippet demonstrates how to receive a message via the usage o } //Process received message - if (message.flags & CAN_MSG_FLAG_EXTD) { + if (message.extd) { printf("Message is in Extended Format\n"); } else { printf("Message is in Standard Format\n"); } printf("ID is %d\n", message.identifier); - if (!(message.flags & CAN_MSG_FLAG_RTR)) { + if (!(message.rtr)) { for (int i = 0; i < message.data_length_code; i++) { printf("Data byte %d = %d\n", i, message.data[i]); } @@ -485,4 +485,5 @@ Application Examples API Reference ------------- +.. include:: /_build/inc/can_types.inc .. include:: /_build/inc/can.inc \ No newline at end of file diff --git a/examples/peripherals/can/can_alert_and_recovery/main/can_alert_and_recovery_example_main.c b/examples/peripherals/can/can_alert_and_recovery/main/can_alert_and_recovery_example_main.c index ea4ac360e..eee35f327 100644 --- a/examples/peripherals/can/can_alert_and_recovery/main/can_alert_and_recovery_example_main.c +++ b/examples/peripherals/can/can_alert_and_recovery/main/can_alert_and_recovery_example_main.c @@ -41,7 +41,7 @@ static const can_filter_config_t f_config = CAN_FILTER_CONFIG_ACCEPT_ALL(); static const can_timing_config_t t_config = CAN_TIMING_CONFIG_25KBITS(); static const can_general_config_t g_config = CAN_GENERAL_CONFIG_DEFAULT(TX_GPIO_NUM, RX_GPIO_NUM, CAN_MODE_NO_ACK); -static const can_message_t tx_msg = {.identifier = 0, .data_length_code = 0, .flags = CAN_MSG_FLAG_NONE}; +static const can_message_t tx_msg = {.identifier = 0, .data_length_code = 0}; static SemaphoreHandle_t tx_task_sem; static SemaphoreHandle_t ctrl_task_sem; diff --git a/examples/peripherals/can/can_network/can_network_master/main/can_network_example_master_main.c b/examples/peripherals/can/can_network/can_network_master/main/can_network_example_master_main.c index f744072d8..24d59908c 100644 --- a/examples/peripherals/can/can_network/can_network_master/main/can_network_example_master_main.c +++ b/examples/peripherals/can/can_network/can_network_master/main/can_network_example_master_main.c @@ -67,11 +67,11 @@ static const can_filter_config_t f_config = CAN_FILTER_CONFIG_ACCEPT_ALL(); static const can_general_config_t g_config = CAN_GENERAL_CONFIG_DEFAULT(TX_GPIO_NUM, RX_GPIO_NUM, CAN_MODE_NORMAL); static const can_message_t ping_message = {.identifier = ID_MASTER_PING, .data_length_code = 0, - .flags = CAN_MSG_FLAG_SS, .data = {0, 0 , 0 , 0 ,0 ,0 ,0 ,0}}; + .ss = 1, .data = {0, 0 , 0 , 0 ,0 ,0 ,0 ,0}}; static const can_message_t start_message = {.identifier = ID_MASTER_START_CMD, .data_length_code = 0, - .flags = CAN_MSG_FLAG_NONE, .data = {0, 0 , 0 , 0 ,0 ,0 ,0 ,0}}; + .data = {0, 0 , 0 , 0 ,0 ,0 ,0 ,0}}; static const can_message_t stop_message = {.identifier = ID_MASTER_STOP_CMD, .data_length_code = 0, - .flags = CAN_MSG_FLAG_NONE, .data = {0, 0 , 0 , 0 ,0 ,0 ,0 ,0}}; + .data = {0, 0 , 0 , 0 ,0 ,0 ,0 ,0}}; static QueueHandle_t tx_task_queue; static QueueHandle_t rx_task_queue; diff --git a/examples/peripherals/can/can_network/can_network_slave/main/can_network_example_slave_main.c b/examples/peripherals/can/can_network/can_network_slave/main/can_network_example_slave_main.c index 17ad07b8b..4d2f79257 100644 --- a/examples/peripherals/can/can_network/can_network_slave/main/can_network_example_slave_main.c +++ b/examples/peripherals/can/can_network/can_network_slave/main/can_network_example_slave_main.c @@ -67,12 +67,12 @@ static const can_general_config_t g_config = CAN_GENERAL_CONFIG_DEFAULT(TX_GPIO_ static const can_timing_config_t t_config = CAN_TIMING_CONFIG_25KBITS(); static const can_filter_config_t f_config = CAN_FILTER_CONFIG_ACCEPT_ALL(); static const can_message_t ping_resp = {.identifier = ID_SLAVE_PING_RESP, .data_length_code = 0, - .flags = CAN_MSG_FLAG_NONE, .data = {0, 0 , 0 , 0 ,0 ,0 ,0 ,0}}; + .data = {0, 0 , 0 , 0 ,0 ,0 ,0 ,0}}; static const can_message_t stop_resp = {.identifier = ID_SLAVE_STOP_RESP, .data_length_code = 0, - .flags = CAN_MSG_FLAG_NONE, .data = {0, 0 , 0 , 0 ,0 ,0 ,0 ,0}}; + .data = {0, 0 , 0 , 0 ,0 ,0 ,0 ,0}}; //Data bytes of data message will be initialized in the transmit task static can_message_t data_message = {.identifier = ID_SLAVE_DATA, .data_length_code = 4, - .flags = CAN_MSG_FLAG_NONE, .data = {0, 0 , 0 , 0 ,0 ,0 ,0 ,0}}; + .data = {0, 0 , 0 , 0 ,0 ,0 ,0 ,0}}; static QueueHandle_t tx_task_queue; static QueueHandle_t rx_task_queue; diff --git a/examples/peripherals/can/can_self_test/main/can_self_test_example_main.c b/examples/peripherals/can/can_self_test/main/can_self_test_example_main.c index e3a12522c..f58059fdc 100644 --- a/examples/peripherals/can/can_self_test/main/can_self_test_example_main.c +++ b/examples/peripherals/can/can_self_test/main/can_self_test_example_main.c @@ -57,7 +57,7 @@ static SemaphoreHandle_t done_sem; static void can_transmit_task(void *arg) { - can_message_t tx_msg = {.data_length_code = 1, .identifier = MSG_ID, .flags = CAN_MSG_FLAG_SELF}; + can_message_t tx_msg = {.data_length_code = 1, .identifier = MSG_ID, .self = 1}; for (int iter = 0; iter < NO_OF_ITERS; iter++) { xSemaphoreTake(tx_sem, portMAX_DELAY); for (int i = 0; i < NO_OF_MSGS; i++) { @@ -73,6 +73,7 @@ static void can_transmit_task(void *arg) static void can_receive_task(void *arg) { can_message_t rx_message; + for (int iter = 0; iter < NO_OF_ITERS; iter++) { xSemaphoreTake(rx_sem, portMAX_DELAY); for (int i = 0; i < NO_OF_MSGS; i++) {