can: Refactor CAN to use HAL and LowLevel layers

The following commit refactors the CAN driver such that
it is split into HAL and Lowlevel layers. The following
changes have also been made:

- Added bit field members to can_message_t as alternative
  to message flags. Updated examples and docs accordingly
- Register field names and fields of can_dev_t updated
This commit is contained in:
Darian Leung 2019-12-11 17:38:54 +08:00
parent 2e7e05a79a
commit a049e02d96
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++) {