Merge branch 'refactor/can_driver' into 'master'

Refactor CAN to use HAL and LowLevel layers

Closes IDF-617

See merge request espressif/esp-idf!7019
This commit is contained in:
Angus Gratton 2020-01-10 14:22:02 +08:00
commit 782fc0df5f
17 changed files with 1665 additions and 783 deletions

View file

@ -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;

View file

@ -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_*/

View file

@ -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()

View file

@ -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 <stdint.h>
#include <stdbool.h>
#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

View file

@ -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

View file

@ -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 <stdint.h>
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/* -------------------------- Register Definitions -------------------------- */
#include <stdint.h>
/* ---------------------------- 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_ */

View file

@ -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
#define SOC_SDIO_SLAVE_SUPPORTED 1
#define SOC_CAN_SUPPORTED 1

View file

@ -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 <stddef.h>
#include <stdbool.h>
#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

View file

@ -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 <stdint.h>
#include <stdbool.h>
/**
* @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

View file

@ -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

View file

@ -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 <stddef.h>
#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);
}
}

View file

@ -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 \

View file

@ -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

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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++) {