1340 lines
41 KiB
C
1340 lines
41 KiB
C
/*
|
|
* Copyright (C) 2022-2024, Xiaohua Semiconductor Co., Ltd.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Change Logs:
|
|
* Date Author Notes
|
|
* 2022-04-28 CDT first version
|
|
* 2022-06-07 xiaoxiaolisunny add hc32f460 series
|
|
* 2022-06-08 CDT fix a bug of RT_CAN_CMD_SET_FILTER
|
|
* 2022-06-15 lianghongquan fix bug, FILTER_COUNT, RT_CAN_CMD_SET_FILTER, interrupt setup and processing.
|
|
*/
|
|
|
|
#include "drv_can.h"
|
|
#include <drv_config.h>
|
|
#include <board_config.h>
|
|
|
|
#if defined(BSP_USING_CAN)
|
|
#define LOG_TAG "drv_can"
|
|
|
|
#if defined(BSP_USING_CAN1) || defined(BSP_USING_CAN2)
|
|
|
|
#if defined(RT_CAN_USING_CANFD) && defined(HC32F460)
|
|
#error "Selected mcu does not support canfd!"
|
|
#endif
|
|
|
|
#define TSEG1_MIN_FOR_CAN2_0 (2U)
|
|
#define TSEG1_MAX_FOR_CAN2_0 (65U)
|
|
#define TSEG1_MIN_FOR_CANFD_ARBITRATION (2U)
|
|
#define TSEG1_MAX_FOR_CANFD_ARBITRATION (65U)
|
|
#define TSEG1_MIN_FOR_CANFD_DATA (2U)
|
|
#define TSEG1_MAX_FOR_CANFD_DATA (17U)
|
|
|
|
#define TSEG2_MIN_FOR_CAN2_0 (1U)
|
|
#define TSEG2_MAX_FOR_CAN2_0 (8U)
|
|
#define TSEG2_MIN_FOR_CANFD_ARBITRATION (1U)
|
|
#define TSEG2_MAX_FOR_CANFD_ARBITRATION (32U)
|
|
#define TSEG2_MIN_FOR_CANFD_DATA (1U)
|
|
#define TSEG2_MAX_FOR_CANFD_DATA (8U)
|
|
|
|
#define TSJW_MIN_FOR_CAN2_0 (1U)
|
|
#define TSJW_MAX_FOR_CAN2_0 (16U)
|
|
#define TSJW_MIN_FOR_CANFD_ARBITRATION (1U)
|
|
#define TSJW_MAX_FOR_CANFD_ARBITRATION (16U)
|
|
#define TSJW_MIN_FOR_CANFD_DATA (1U)
|
|
#define TSJW_MAX_FOR_CANFD_DATA (8U)
|
|
|
|
#define NUM_TQ_MIN_FOR_CAN2_0 (8U)
|
|
#define NUM_TQ_MAX_FOR_CAN2_0 (TSEG1_MAX_FOR_CAN2_0 + TSEG2_MAX_FOR_CAN2_0)
|
|
#define NUM_TQ_MIN_FOR_CANFD_ARBITRATION (8U)
|
|
#define NUM_TQ_MAX_FOR_CANFD_ARBITRATION (TSEG1_MAX_FOR_CANFD_ARBITRATION + TSEG2_MAX_FOR_CANFD_ARBITRATION)
|
|
#define NUM_TQ_MIN_FOR_CANFD_DATA (8U)
|
|
#define NUM_TQ_MAX_FOR_CANFD_DATA (TSEG1_MAX_FOR_CANFD_DATA + TSEG2_MAX_FOR_CANFD_DATA)
|
|
|
|
#define NUM_PRESCALE_MAX (256U)
|
|
#define MIN_TQ_MUL_PRESCALE (4U)
|
|
|
|
#define CAN_BIT_TIMING_CAN2_0 (1U << 0)
|
|
#define CAN_BIT_TIMING_CANFD_ARBITRATION (1U << 1)
|
|
#define CAN_BIT_TIMING_CANFD_DATA (1U << 2)
|
|
|
|
#if defined(HC32F4A0)
|
|
#define FILTER_COUNT (16U)
|
|
#define CAN1_INT_SRC (INT_SRC_CAN1_HOST)
|
|
#define CAN2_INT_SRC (INT_SRC_CAN2_HOST)
|
|
#endif
|
|
|
|
#if defined (HC32F460)
|
|
#define FILTER_COUNT (8U)
|
|
#define CAN1_INT_SRC (INT_SRC_CAN_INT)
|
|
#endif
|
|
|
|
#define IS_VALID_PRIV_MODE(mode) ((mode == RT_CAN_MODE_PRIV) || (mode == RT_CAN_MODE_NOPRIV))
|
|
#define IS_VALID_WORK_MODE(mode) (mode <= RT_CAN_MODE_LOOPBACKANLISTEN)
|
|
#define IS_VALID_BAUD_RATE_CAN2_0(baud) (baud == (CAN10kBaud) \
|
|
|| baud == (CAN20kBaud) \
|
|
|| baud == (CAN50kBaud) \
|
|
|| baud == (CAN125kBaud) \
|
|
|| baud == (CAN250kBaud) \
|
|
|| baud == (CAN500kBaud) \
|
|
|| baud == (CAN1MBaud) \
|
|
)
|
|
#define IS_VALID_BAUD_RATE_CANFD_ARBITRATION(baud) IS_VALID_BAUD_RATE_CAN2_0(baud)
|
|
#define IS_VALID_BAUD_RATE_CANFD_DATA(baud) (baud == (CANFD_DATA_BAUD_1M) \
|
|
|| baud == (CANFD_DATA_BAUD_2M) \
|
|
|| baud == (CANFD_DATA_BAUD_4M) \
|
|
|| baud == (CANFD_DATA_BAUD_5M) \
|
|
|| baud == (CANFD_DATA_BAUD_8M) \
|
|
)
|
|
|
|
enum
|
|
{
|
|
#ifdef BSP_USING_CAN1
|
|
CAN1_INDEX,
|
|
#endif
|
|
#ifdef BSP_USING_CAN2
|
|
CAN2_INDEX,
|
|
#endif
|
|
CAN_INDEX_MAX,
|
|
};
|
|
|
|
struct can_baud_rate_tab
|
|
{
|
|
rt_uint32_t baud_rate;
|
|
stc_can_bit_time_config_t ll_sbt;
|
|
};
|
|
|
|
struct canfd_baud_rate_tab
|
|
{
|
|
rt_uint32_t clk_src;
|
|
rt_uint8_t phase;
|
|
rt_uint32_t baud;
|
|
stc_can_bit_time_config_t ll_bt;
|
|
};
|
|
|
|
typedef struct
|
|
{
|
|
uint8_t tq_min;
|
|
uint8_t tq_max;
|
|
uint8_t seg1_min;
|
|
uint8_t seg1_max;
|
|
uint8_t seg2_min;
|
|
uint8_t seg2_max;
|
|
uint8_t sjw_min;
|
|
uint8_t sjw_max;
|
|
uint8_t min_diff_seg1_minus_seg2;
|
|
} can_bit_timing_table_t;
|
|
|
|
#ifndef RT_CAN_USING_CANFD
|
|
static const struct can_baud_rate_tab _g_baudrate_tab[] =
|
|
{
|
|
{CAN1MBaud, CAN_BIT_TIME_CONFIG_1M_BAUD},
|
|
{CAN800kBaud, CAN_BIT_TIME_CONFIG_800K_BAUD},
|
|
{CAN500kBaud, CAN_BIT_TIME_CONFIG_500K_BAUD},
|
|
{CAN250kBaud, CAN_BIT_TIME_CONFIG_250K_BAUD},
|
|
{CAN125kBaud, CAN_BIT_TIME_CONFIG_125K_BAUD},
|
|
{CAN100kBaud, CAN_BIT_TIME_CONFIG_100K_BAUD},
|
|
{CAN50kBaud, CAN_BIT_TIME_CONFIG_50K_BAUD},
|
|
{CAN20kBaud, CAN_BIT_TIME_CONFIG_20K_BAUD},
|
|
{CAN10kBaud, CAN_BIT_TIME_CONFIG_10K_BAUD},
|
|
};
|
|
#endif
|
|
|
|
typedef struct
|
|
{
|
|
struct rt_can_device rt_can;
|
|
struct can_dev_init_params init;
|
|
CM_CAN_TypeDef *instance;
|
|
stc_can_init_t ll_init;
|
|
} can_device;
|
|
|
|
#ifdef RT_CAN_USING_CANFD
|
|
static const can_bit_timing_table_t _g_can_bit_timing_tbl[3] =
|
|
{
|
|
{
|
|
.tq_min = NUM_TQ_MIN_FOR_CAN2_0,
|
|
.tq_max = NUM_TQ_MAX_FOR_CAN2_0,
|
|
.seg1_min = TSEG1_MIN_FOR_CAN2_0,
|
|
.seg1_max = TSEG1_MAX_FOR_CAN2_0,
|
|
.seg2_min = TSEG2_MIN_FOR_CAN2_0,
|
|
.seg2_max = TSEG2_MAX_FOR_CAN2_0,
|
|
.sjw_min = TSJW_MIN_FOR_CAN2_0,
|
|
.sjw_max = TSJW_MAX_FOR_CAN2_0,
|
|
.min_diff_seg1_minus_seg2 = 2,
|
|
},
|
|
{
|
|
.tq_min = NUM_TQ_MIN_FOR_CANFD_ARBITRATION,
|
|
.tq_max = NUM_TQ_MAX_FOR_CANFD_ARBITRATION,
|
|
.seg1_min = TSEG1_MIN_FOR_CANFD_ARBITRATION,
|
|
.seg1_max = TSEG1_MAX_FOR_CANFD_ARBITRATION,
|
|
.seg2_min = TSEG2_MIN_FOR_CANFD_ARBITRATION,
|
|
.seg2_max = TSEG2_MAX_FOR_CANFD_ARBITRATION,
|
|
.sjw_min = TSJW_MIN_FOR_CANFD_ARBITRATION,
|
|
.sjw_max = TSJW_MAX_FOR_CANFD_ARBITRATION,
|
|
.min_diff_seg1_minus_seg2 = 2,
|
|
},
|
|
{
|
|
.tq_min = NUM_TQ_MIN_FOR_CANFD_DATA,
|
|
.tq_max = NUM_TQ_MAX_FOR_CANFD_DATA,
|
|
.seg1_min = TSEG1_MIN_FOR_CANFD_DATA,
|
|
.seg1_max = TSEG1_MAX_FOR_CANFD_DATA,
|
|
.seg2_min = TSEG2_MIN_FOR_CANFD_DATA,
|
|
.seg2_max = TSEG2_MAX_FOR_CANFD_DATA,
|
|
.sjw_min = TSJW_MIN_FOR_CANFD_DATA,
|
|
.sjw_max = TSJW_MAX_FOR_CANFD_DATA,
|
|
.min_diff_seg1_minus_seg2 = 1,
|
|
}
|
|
};
|
|
|
|
static const struct canfd_baud_rate_tab _g_baudrate_fd[] =
|
|
{
|
|
{CAN_CLOCK_SRC_20M, CAN_BIT_TIMING_CANFD_ARBITRATION, CANFD_ARBITRATION_BAUD_250K, 1U, 64U, 16U, 16U},
|
|
{CAN_CLOCK_SRC_20M, CAN_BIT_TIMING_CANFD_ARBITRATION, CANFD_ARBITRATION_BAUD_500K, 1U, 32U, 8U, 8U},
|
|
{CAN_CLOCK_SRC_20M, CAN_BIT_TIMING_CANFD_ARBITRATION | CAN_BIT_TIMING_CANFD_DATA, CANFD_DATA_BAUD_1M, 1U, 16U, 4U, 4U},
|
|
{CAN_CLOCK_SRC_20M, CAN_BIT_TIMING_CANFD_ARBITRATION | CAN_BIT_TIMING_CANFD_DATA, CANFD_DATA_BAUD_2M, 1U, 8U, 2U, 2U},
|
|
{CAN_CLOCK_SRC_20M, CAN_BIT_TIMING_CANFD_ARBITRATION | CAN_BIT_TIMING_CANFD_DATA, CANFD_DATA_BAUD_4M, 1U, 4U, 1U, 1U},
|
|
{CAN_CLOCK_SRC_20M, CAN_BIT_TIMING_CANFD_ARBITRATION | CAN_BIT_TIMING_CANFD_DATA, CANFD_DATA_BAUD_5M, 1U, 3U, 1U, 1U},
|
|
{CAN_CLOCK_SRC_40M, CAN_BIT_TIMING_CANFD_ARBITRATION, CANFD_ARBITRATION_BAUD_250K, 2U, 64U, 16U, 16U},
|
|
{CAN_CLOCK_SRC_40M, CAN_BIT_TIMING_CANFD_ARBITRATION, CANFD_ARBITRATION_BAUD_500K, 1U, 64U, 16U, 16U},
|
|
{CAN_CLOCK_SRC_40M, CAN_BIT_TIMING_CANFD_ARBITRATION, CANFD_DATA_BAUD_1M, 1U, 32U, 8U, 8U},
|
|
{CAN_CLOCK_SRC_40M, CAN_BIT_TIMING_CANFD_ARBITRATION | CAN_BIT_TIMING_CANFD_DATA, CANFD_DATA_BAUD_2M, 1U, 16U, 4U, 4U},
|
|
{CAN_CLOCK_SRC_40M, CAN_BIT_TIMING_CANFD_ARBITRATION | CAN_BIT_TIMING_CANFD_DATA, CANFD_DATA_BAUD_4M, 1U, 8U, 2U, 2U},
|
|
{CAN_CLOCK_SRC_40M, CAN_BIT_TIMING_CANFD_ARBITRATION | CAN_BIT_TIMING_CANFD_DATA, CANFD_DATA_BAUD_5M, 1U, 6U, 2U, 2U},
|
|
{CAN_CLOCK_SRC_40M, CAN_BIT_TIMING_CANFD_ARBITRATION | CAN_BIT_TIMING_CANFD_DATA, CANFD_DATA_BAUD_8M, 1U, 4U, 1U, 1U},
|
|
{CAN_CLOCK_SRC_80M, CAN_BIT_TIMING_CANFD_ARBITRATION, CANFD_ARBITRATION_BAUD_250K, 4U, 64U, 16U},
|
|
{CAN_CLOCK_SRC_80M, CAN_BIT_TIMING_CANFD_ARBITRATION, CANFD_ARBITRATION_BAUD_500K, 2U, 64U, 16U},
|
|
{CAN_CLOCK_SRC_80M, CAN_BIT_TIMING_CANFD_ARBITRATION, CANFD_DATA_BAUD_1M, 2U, 32U, 8U, 8U},
|
|
{CAN_CLOCK_SRC_80M, CAN_BIT_TIMING_CANFD_ARBITRATION | CAN_BIT_TIMING_CANFD_DATA, CANFD_DATA_BAUD_2M, 2U, 16U, 4U, 4U},
|
|
{CAN_CLOCK_SRC_80M, CAN_BIT_TIMING_CANFD_ARBITRATION | CAN_BIT_TIMING_CANFD_DATA, CANFD_DATA_BAUD_4M, 1U, 16U, 4U, 4U},
|
|
{CAN_CLOCK_SRC_80M, CAN_BIT_TIMING_CANFD_ARBITRATION | CAN_BIT_TIMING_CANFD_DATA, CANFD_DATA_BAUD_5M, 1U, 12U, 4U, 4U},
|
|
{CAN_CLOCK_SRC_80M, CAN_BIT_TIMING_CANFD_ARBITRATION | CAN_BIT_TIMING_CANFD_DATA, CANFD_DATA_BAUD_8M, 1U, 8U, 2U, 2U},
|
|
};
|
|
#endif
|
|
|
|
static can_device _g_can_dev_array[] =
|
|
{
|
|
#if defined(HC32F4A0)
|
|
#ifdef BSP_USING_CAN1
|
|
{
|
|
{0},
|
|
CAN1_INIT_PARAMS,
|
|
.instance = CM_CAN1,
|
|
},
|
|
#endif
|
|
#ifdef BSP_USING_CAN2
|
|
{
|
|
{0},
|
|
CAN2_INIT_PARAMS,
|
|
.instance = CM_CAN2,
|
|
},
|
|
#endif
|
|
#endif
|
|
|
|
#if defined (HC32F460)
|
|
#ifdef BSP_USING_CAN1
|
|
{
|
|
{0},
|
|
CAN1_INIT_PARAMS,
|
|
.instance = CM_CAN,
|
|
},
|
|
#endif
|
|
#endif
|
|
};
|
|
|
|
static void _init_ll_struct_filter(can_device *p_can_dev);
|
|
|
|
#ifndef RT_CAN_USING_CANFD
|
|
static rt_uint32_t _get_can_baud_index(rt_uint32_t baud)
|
|
{
|
|
rt_uint32_t len, index;
|
|
|
|
len = sizeof(_g_baudrate_tab) / sizeof(_g_baudrate_tab[0]);
|
|
for (index = 0; index < len; index++)
|
|
{
|
|
if (_g_baudrate_tab[index].baud_rate == baud)
|
|
return index;
|
|
}
|
|
|
|
return 0; /* default baud is CAN1MBaud */
|
|
}
|
|
#endif
|
|
|
|
static rt_uint32_t _get_can_work_mode(rt_uint32_t mode)
|
|
{
|
|
rt_uint32_t work_mode;
|
|
switch (mode)
|
|
{
|
|
case RT_CAN_MODE_NORMAL:
|
|
work_mode = CAN_WORK_MD_NORMAL;
|
|
break;
|
|
case RT_CAN_MODE_LISTEN:
|
|
work_mode = CAN_WORK_MD_SILENT;
|
|
break;
|
|
case RT_CAN_MODE_LOOPBACK:
|
|
work_mode = CAN_WORK_MD_ELB;
|
|
break;
|
|
case RT_CAN_MODE_LOOPBACKANLISTEN:
|
|
work_mode = CAN_WORK_MD_ELB_SILENT;
|
|
break;
|
|
default:
|
|
work_mode = CAN_WORK_MD_NORMAL;
|
|
break;
|
|
}
|
|
|
|
return work_mode;
|
|
}
|
|
|
|
static uint32_t _get_filter_idx(struct rt_can_filter_config *p_filter_in)
|
|
{
|
|
uint32_t filter_selected = 0;
|
|
|
|
for (int i = 0; i < p_filter_in->count; i++)
|
|
{
|
|
if (p_filter_in->items[i].hdr_bank != -1)
|
|
{
|
|
filter_selected |= 1 << p_filter_in->items[i].hdr_bank;
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < p_filter_in->count; i++)
|
|
{
|
|
if (p_filter_in->items[i].hdr_bank == -1)
|
|
{
|
|
for (int j = 0; j < FILTER_COUNT; j++)
|
|
{
|
|
if ((filter_selected & 1 << j) == 0)
|
|
{
|
|
p_filter_in->items[i].hdr_bank = j;
|
|
filter_selected |= 1 << p_filter_in->items[i].hdr_bank;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return filter_selected;
|
|
}
|
|
|
|
static uint8_t _get_can_data_bytes_len(uint32_t dlc)
|
|
{
|
|
uint8_t data_bytes = 0;
|
|
|
|
dlc &= 0xFU;
|
|
if (dlc <= 8U)
|
|
{
|
|
data_bytes = dlc;
|
|
}
|
|
else
|
|
{
|
|
switch (dlc)
|
|
{
|
|
case CAN_DLC12:
|
|
data_bytes = 12U;
|
|
break;
|
|
case CAN_DLC16:
|
|
data_bytes = 16U;
|
|
break;
|
|
case CAN_DLC20:
|
|
data_bytes = 20U;
|
|
break;
|
|
case CAN_DLC24:
|
|
data_bytes = 24U;
|
|
break;
|
|
case CAN_DLC32:
|
|
data_bytes = 32U;
|
|
break;
|
|
case CAN_DLC48:
|
|
data_bytes = 48U;
|
|
break;
|
|
case CAN_DLC64:
|
|
data_bytes = 64U;
|
|
break;
|
|
default:
|
|
/* Code should never touch here */
|
|
break;
|
|
}
|
|
}
|
|
|
|
return data_bytes;
|
|
}
|
|
|
|
static rt_bool_t _check_filter_params(struct rt_can_filter_config *p_filter_in)
|
|
{
|
|
RT_ASSERT(p_filter_in != NULL);
|
|
RT_ASSERT(p_filter_in->count <= FILTER_COUNT);
|
|
|
|
for (int i = 0; i < p_filter_in->count; i++)
|
|
{
|
|
if (p_filter_in->items[i].hdr_bank != -1 && p_filter_in->items[i].hdr_bank >= FILTER_COUNT)
|
|
{
|
|
RT_ASSERT(p_filter_in->items[i].hdr_bank < FILTER_COUNT);
|
|
return RT_FALSE;
|
|
}
|
|
if (p_filter_in->items[i].mode == 1)
|
|
{
|
|
RT_ASSERT(p_filter_in->items[i].mode == 0);
|
|
return RT_FALSE;
|
|
}
|
|
if (p_filter_in->items[i].rtr == 1)
|
|
{
|
|
RT_ASSERT(p_filter_in->items[i].rtr == 0);
|
|
return RT_FALSE;
|
|
}
|
|
}
|
|
|
|
return RT_TRUE;
|
|
}
|
|
|
|
#ifdef RT_CAN_USING_CANFD
|
|
static uint32_t _get_can_clk_src(CM_CAN_TypeDef *CANx)
|
|
{
|
|
uint32_t can_clk = 0;
|
|
switch ((rt_uint32_t)CANx)
|
|
{
|
|
#ifdef BSP_USING_CAN1
|
|
case (rt_uint32_t)CM_CAN1:
|
|
can_clk = CAN1_CLOCK_SEL;
|
|
break;
|
|
#endif
|
|
#ifdef BSP_USING_CAN2
|
|
case (rt_uint32_t)CM_CAN2:
|
|
can_clk = CAN2_CLOCK_SEL;
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
return can_clk;
|
|
}
|
|
|
|
static rt_bool_t _get_can_bit_timing_default(uint32_t can_clk, rt_uint32_t baud, rt_uint32_t option,
|
|
stc_can_bit_time_config_t *p_stc_bit_cfg)
|
|
{
|
|
rt_uint32_t len, index;
|
|
rt_bool_t found = RT_FALSE;
|
|
|
|
len = sizeof(_g_baudrate_fd) / sizeof(_g_baudrate_fd[0]);
|
|
for (index = 0; index < len; index++)
|
|
{
|
|
if ((_g_baudrate_fd[index].clk_src == can_clk) && \
|
|
((_g_baudrate_fd[index].phase & option) == option) \
|
|
)
|
|
{
|
|
if (_g_baudrate_fd[index].baud == baud)
|
|
{
|
|
found = RT_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (found)
|
|
{
|
|
rt_memcpy(p_stc_bit_cfg, &_g_baudrate_fd[index].ll_bt, sizeof(stc_can_bit_time_config_t));
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
static inline void _get_can_bit_timing(stc_can_bit_time_config_t *p_ll_time, struct rt_can_bit_timing *p_cfg_time)
|
|
{
|
|
p_ll_time->u32Prescaler = p_cfg_time->prescaler;
|
|
p_ll_time->u32TimeSeg1 = p_cfg_time->num_seg1;
|
|
p_ll_time->u32TimeSeg2 = p_cfg_time->num_seg2;
|
|
p_ll_time->u32SJW = p_cfg_time->num_sjw;
|
|
}
|
|
|
|
static inline void _get_can_bit_timing_fd(stc_canfd_config_t *p_ll_time, struct rt_can_bit_timing *p_cfg_time)
|
|
{
|
|
p_ll_time->stcBitCfg.u32Prescaler = p_cfg_time->prescaler;
|
|
p_ll_time->stcBitCfg.u32TimeSeg1 = p_cfg_time->num_seg1;
|
|
p_ll_time->stcBitCfg.u32TimeSeg2 = p_cfg_time->num_seg2;
|
|
p_ll_time->stcBitCfg.u32SJW = p_cfg_time->num_sjw;
|
|
p_ll_time->u8SSPOffset = p_cfg_time->num_sspoff;
|
|
if (p_cfg_time->num_sspoff)
|
|
{
|
|
p_ll_time->u8TDC = CAN_FD_TDC_ENABLE;
|
|
}
|
|
}
|
|
|
|
static rt_err_t _get_can_closest_prescaler(uint32_t num_tq_mul_prescaler, uint32_t start_prescaler,
|
|
uint32_t max_tq, uint32_t min_tq)
|
|
{
|
|
rt_bool_t has_found = RT_FALSE;
|
|
uint32_t prescaler = start_prescaler;
|
|
|
|
while (!has_found)
|
|
{
|
|
if ((num_tq_mul_prescaler / prescaler > max_tq) || (num_tq_mul_prescaler % prescaler != 0))
|
|
{
|
|
++prescaler;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
has_found = RT_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
uint32_t tq = num_tq_mul_prescaler / prescaler;
|
|
if (tq * prescaler == num_tq_mul_prescaler)
|
|
{
|
|
has_found = RT_TRUE;
|
|
}
|
|
else if (tq < min_tq)
|
|
{
|
|
has_found = RT_FALSE;
|
|
}
|
|
|
|
return has_found ? prescaler : 0U;
|
|
}
|
|
|
|
static rt_err_t _calc_can_bit_timing(CM_CAN_TypeDef *CANx, int option, uint32_t baudrate,
|
|
stc_can_bit_time_config_t *p_stc_bit_cfg)
|
|
{
|
|
rt_err_t status = -RT_ERROR;
|
|
uint32_t can_clk = _get_can_clk_src(CANx);
|
|
if (_get_can_bit_timing_default(can_clk, baudrate, option, p_stc_bit_cfg) == RT_TRUE)
|
|
{
|
|
status = RT_EOK;
|
|
return status;
|
|
}
|
|
|
|
do
|
|
{
|
|
uint8_t idx = 0;
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
if (option & (1 << i))
|
|
{
|
|
idx = (uint8_t)i;
|
|
break;
|
|
}
|
|
}
|
|
if ((idx >= 3) || (baudrate == 0U) ||
|
|
(can_clk / baudrate < MIN_TQ_MUL_PRESCALE) || (p_stc_bit_cfg == NULL))
|
|
{
|
|
break;
|
|
}
|
|
|
|
const can_bit_timing_table_t *tbl = &_g_can_bit_timing_tbl[idx];
|
|
if (can_clk / baudrate < tbl->tq_min)
|
|
{
|
|
break;
|
|
}
|
|
|
|
uint32_t num_tq_mul_prescaler = can_clk / baudrate;
|
|
uint32_t start_prescaler = 1U;
|
|
uint32_t num_seg1, num_seg2;
|
|
rt_bool_t has_found = RT_FALSE;
|
|
|
|
/* Find out the minimum prescaler */
|
|
uint32_t current_prescaler;
|
|
while (!has_found)
|
|
{
|
|
current_prescaler = _get_can_closest_prescaler(num_tq_mul_prescaler, start_prescaler,
|
|
tbl->tq_max,
|
|
tbl->tq_min);
|
|
if ((current_prescaler < start_prescaler) || (current_prescaler > NUM_PRESCALE_MAX))
|
|
{
|
|
break;
|
|
}
|
|
uint32_t num_tq = num_tq_mul_prescaler / current_prescaler;
|
|
|
|
num_seg2 = (num_tq - tbl->min_diff_seg1_minus_seg2) / 2U;
|
|
num_seg1 = num_tq - num_seg2;
|
|
while (num_seg2 > tbl->seg2_max)
|
|
{
|
|
num_seg2--;
|
|
num_seg1++;
|
|
}
|
|
|
|
/* Recommended sample point is 75% - 80% */
|
|
while ((num_seg1 * 1000U) / num_tq < CAN_SAMPLEPOINT_MIN)
|
|
{
|
|
++num_seg1;
|
|
--num_seg2;
|
|
}
|
|
|
|
if ((num_seg1 * 1000U) / num_tq > CAN_SAMPLEPOINT_MAX)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if ((num_seg2 >= tbl->seg2_min) && (num_seg1 <= tbl->seg1_max))
|
|
{
|
|
has_found = RT_TRUE;
|
|
}
|
|
else
|
|
{
|
|
start_prescaler = current_prescaler + 1U;
|
|
}
|
|
}
|
|
|
|
if (has_found)
|
|
{
|
|
uint32_t num_sjw = LL_MIN(tbl->sjw_max, num_seg2);
|
|
p_stc_bit_cfg->u32TimeSeg1 = num_seg1;
|
|
p_stc_bit_cfg->u32TimeSeg2 = num_seg2;
|
|
p_stc_bit_cfg->u32SJW = num_sjw;
|
|
p_stc_bit_cfg->u32Prescaler = current_prescaler;
|
|
status = RT_EOK;
|
|
}
|
|
}
|
|
while (RT_FALSE);
|
|
|
|
return status;
|
|
}
|
|
#else
|
|
static rt_err_t _config_can20_baud(can_device *p_can_dev, void *arg)
|
|
{
|
|
rt_uint32_t argval = (rt_uint32_t)arg;
|
|
rt_uint32_t baud_index;
|
|
rt_err_t rt_ret = RT_EOK;
|
|
|
|
RT_ASSERT(IS_VALID_BAUD_RATE_CAN2_0(argval));
|
|
if (argval == p_can_dev->rt_can.config.baud_rate)
|
|
{
|
|
return rt_ret;
|
|
}
|
|
|
|
baud_index = _get_can_baud_index(argval);
|
|
p_can_dev->ll_init.stcBitCfg = _g_baudrate_tab[baud_index].ll_sbt;
|
|
|
|
/* init can */
|
|
CAN_Init(p_can_dev->instance, &p_can_dev->ll_init);
|
|
p_can_dev->rt_can.config.baud_rate = argval;
|
|
|
|
return rt_ret;
|
|
}
|
|
#endif
|
|
|
|
static rt_err_t _config_can_filter(can_device *p_can_dev, void *arg)
|
|
{
|
|
struct rt_can_filter_config *p_filter_in = (struct rt_can_filter_config *)arg;
|
|
if (_check_filter_params(p_filter_in) == RT_FALSE)
|
|
{
|
|
return -RT_EINVAL;
|
|
}
|
|
|
|
_init_ll_struct_filter(p_can_dev);
|
|
uint32_t filter_select = _get_filter_idx(p_filter_in);
|
|
p_can_dev->ll_init.u16FilterSelect = filter_select;
|
|
for (int i = 0; i < p_filter_in->count; i++)
|
|
{
|
|
p_can_dev->ll_init.pstcFilter[i].u32ID = p_filter_in->items[i].id & 0x1FFFFFFF;
|
|
/* rt-thread CAN mask, 1 mean filer, 0 mean ignore. *
|
|
* HDSC HC32 CAN mask, 0 mean filer, 1 mean ignore. */
|
|
p_can_dev->ll_init.pstcFilter[i].u32IDMask = (~p_filter_in->items[i].mask) & 0x1FFFFFFF;
|
|
switch (p_filter_in->items[i].ide)
|
|
{
|
|
case (RT_CAN_STDID):
|
|
p_can_dev->ll_init.pstcFilter[i].u32IDType = CAN_ID_STD;
|
|
break;
|
|
case (RT_CAN_EXTID):
|
|
p_can_dev->ll_init.pstcFilter[i].u32IDType = CAN_ID_EXT;
|
|
break;
|
|
default:
|
|
p_can_dev->ll_init.pstcFilter[i].u32IDType = CAN_ID_STD_EXT;
|
|
break;
|
|
}
|
|
}
|
|
(void)CAN_Init(p_can_dev->instance, &p_can_dev->ll_init);
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
static rt_err_t _config_can_work_mode(can_device *p_can_dev, void *arg)
|
|
{
|
|
rt_err_t rt_ret = RT_EOK;
|
|
rt_uint32_t argval = (rt_uint32_t) arg;
|
|
|
|
if (argval == p_can_dev->rt_can.config.mode)
|
|
{
|
|
return rt_ret;
|
|
}
|
|
RT_ASSERT(IS_VALID_WORK_MODE(argval));
|
|
|
|
p_can_dev->ll_init.u8WorkMode = _get_can_work_mode(argval);
|
|
CAN_Init(p_can_dev->instance, &p_can_dev->ll_init);
|
|
p_can_dev->rt_can.config.mode = argval;
|
|
|
|
return rt_ret;
|
|
}
|
|
|
|
static rt_err_t _config_can_priv_mode(can_device *p_can_dev, void *arg)
|
|
{
|
|
rt_err_t rt_ret = RT_EOK;
|
|
rt_uint32_t argval = (rt_uint32_t) arg;
|
|
|
|
RT_ASSERT(IS_VALID_PRIV_MODE(argval));
|
|
p_can_dev->rt_can.config.privmode = argval;
|
|
|
|
return rt_ret;
|
|
}
|
|
|
|
static void _config_can_int(can_device *p_can_dev, int cmd, void *arg)
|
|
{
|
|
en_functional_state_t stat = ENABLE;
|
|
rt_uint32_t flag = (rt_uint32_t)arg;
|
|
|
|
if (cmd == RT_DEVICE_CTRL_CLR_INT)
|
|
{
|
|
if (flag == RT_DEVICE_CAN_INT_ERR)
|
|
{
|
|
RT_ASSERT(p_can_dev->init.single_trans_mode == RT_FALSE);
|
|
}
|
|
stat = DISABLE;
|
|
}
|
|
switch (flag)
|
|
{
|
|
case RT_DEVICE_FLAG_INT_RX:
|
|
CAN_IntCmd(p_can_dev->instance, CAN_INT_RX, stat);
|
|
CAN_IntCmd(p_can_dev->instance, CAN_INT_RX_BUF_WARN, stat);
|
|
CAN_IntCmd(p_can_dev->instance, CAN_INT_RX_BUF_FULL, stat);
|
|
CAN_IntCmd(p_can_dev->instance, CAN_INT_RX_OVERRUN, stat);
|
|
break;
|
|
case RT_DEVICE_FLAG_INT_TX:
|
|
CAN_IntCmd(p_can_dev->instance, CAN_INT_STB_TX, stat);
|
|
CAN_IntCmd(p_can_dev->instance, CAN_INT_PTB_TX, stat);
|
|
break;
|
|
case RT_DEVICE_CAN_INT_ERR:
|
|
CAN_IntCmd(p_can_dev->instance, CAN_INT_ERR_INT, stat);
|
|
CAN_IntCmd(p_can_dev->instance, CAN_INT_ARBITR_LOST, stat);
|
|
CAN_IntCmd(p_can_dev->instance, CAN_INT_ERR_PASSIVE, stat);
|
|
CAN_IntCmd(p_can_dev->instance, CAN_INT_BUS_ERR, stat);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
#ifdef RT_CAN_USING_CANFD
|
|
static void _init_ll_struct_canfd(can_device *p_can_dev)
|
|
{
|
|
if (p_can_dev->ll_init.pstcCanFd == NULL)
|
|
{
|
|
p_can_dev->ll_init.pstcCanFd = (stc_canfd_config_t *)rt_malloc(sizeof(stc_canfd_config_t));
|
|
}
|
|
RT_ASSERT((p_can_dev->ll_init.pstcCanFd != RT_NULL));
|
|
CAN_FD_StructInit(p_can_dev->ll_init.pstcCanFd);
|
|
switch ((rt_uint32_t)p_can_dev->instance)
|
|
{
|
|
#ifdef BSP_USING_CAN1
|
|
case (rt_uint32_t)CM_CAN1:
|
|
p_can_dev->ll_init.pstcCanFd->u8Mode = CAN1_CANFD_MODE;
|
|
break;
|
|
#endif
|
|
#ifdef BSP_USING_CAN2
|
|
case (rt_uint32_t)CM_CAN2:
|
|
p_can_dev->ll_init.pstcCanFd->u8Mode = CAN2_CANFD_MODE;
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static rt_err_t _config_can_bit_timing(can_device *p_can_dev, void *arg)
|
|
{
|
|
rt_err_t rt_ret = RT_EOK;
|
|
|
|
struct rt_can_bit_timing_config *timing_configs = (struct rt_can_bit_timing_config *)arg;
|
|
RT_ASSERT(timing_configs != RT_NULL);
|
|
RT_ASSERT(timing_configs->count == 1 || timing_configs->count == 2);
|
|
RT_ASSERT(timing_configs->items[0].num_sspoff == 0);
|
|
|
|
_get_can_bit_timing(&p_can_dev->ll_init.stcBitCfg, &timing_configs->items[0]);
|
|
if (timing_configs->count == 2)
|
|
{
|
|
_get_can_bit_timing_fd(p_can_dev->ll_init.pstcCanFd, &timing_configs->items[1]);
|
|
}
|
|
/* init can */
|
|
CAN_Init(p_can_dev->instance, &p_can_dev->ll_init);
|
|
p_can_dev->rt_can.config.can_timing = timing_configs->items[0];
|
|
if (timing_configs->count == 2)
|
|
{
|
|
p_can_dev->rt_can.config.canfd_timing = timing_configs->items[1];
|
|
}
|
|
return rt_ret;
|
|
}
|
|
|
|
|
|
static rt_err_t _canfd_control(can_device *p_can_dev, int cmd, void *arg)
|
|
{
|
|
rt_uint32_t argval;
|
|
rt_err_t timing_stat;
|
|
|
|
switch (cmd)
|
|
{
|
|
case RT_CAN_CMD_SET_BAUD:
|
|
argval = (rt_uint32_t) arg;
|
|
RT_ASSERT(IS_VALID_BAUD_RATE_CANFD_ARBITRATION(argval));
|
|
if (p_can_dev->rt_can.config.baud_rate == argval)
|
|
{
|
|
break;
|
|
}
|
|
timing_stat = _calc_can_bit_timing(p_can_dev->instance, \
|
|
CAN_BIT_TIMING_CANFD_ARBITRATION, \
|
|
argval, \
|
|
&p_can_dev->ll_init.stcBitCfg);
|
|
if (timing_stat != RT_EOK)
|
|
{
|
|
return timing_stat;
|
|
}
|
|
CAN_Init(p_can_dev->instance, &p_can_dev->ll_init);
|
|
p_can_dev->rt_can.config.baud_rate = argval;
|
|
break;
|
|
case RT_CAN_CMD_SET_CANFD:
|
|
if (p_can_dev->rt_can.config.enable_canfd == argval)
|
|
{
|
|
break;
|
|
}
|
|
p_can_dev->rt_can.config.enable_canfd = (rt_uint32_t) argval;
|
|
break;
|
|
case RT_CAN_CMD_SET_BAUD_FD:
|
|
argval = (rt_uint32_t) arg;
|
|
RT_ASSERT(IS_VALID_BAUD_RATE_CANFD_DATA(argval));
|
|
if (p_can_dev->rt_can.config.baud_rate_fd == argval)
|
|
{
|
|
break;
|
|
}
|
|
timing_stat = _calc_can_bit_timing(p_can_dev->instance, \
|
|
CAN_BIT_TIMING_CANFD_DATA, \
|
|
argval, \
|
|
&p_can_dev->ll_init.pstcCanFd->stcBitCfg);
|
|
if (timing_stat != RT_EOK)
|
|
{
|
|
return timing_stat;
|
|
}
|
|
p_can_dev->ll_init.pstcCanFd->u8SSPOffset = p_can_dev->ll_init.pstcCanFd->stcBitCfg.u32TimeSeg1;
|
|
CAN_Init(p_can_dev->instance, &p_can_dev->ll_init);
|
|
p_can_dev->rt_can.config.baud_rate_fd = argval;
|
|
break;
|
|
case RT_CAN_CMD_SET_BITTIMING:
|
|
return _config_can_bit_timing(p_can_dev, arg);
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return RT_EOK;
|
|
}
|
|
#endif
|
|
|
|
static rt_err_t _can_config(struct rt_can_device *can, struct can_configure *cfg)
|
|
{
|
|
can_device *p_can_dev;
|
|
rt_err_t rt_ret = RT_EOK;
|
|
|
|
RT_ASSERT(can);
|
|
RT_ASSERT(cfg);
|
|
p_can_dev = (can_device *)rt_container_of(can, can_device, rt_can);
|
|
RT_ASSERT(p_can_dev);
|
|
RT_ASSERT(IS_VALID_WORK_MODE(cfg->mode));
|
|
|
|
p_can_dev->ll_init.u8WorkMode = _get_can_work_mode(cfg->mode);
|
|
#ifdef RT_CAN_USING_CANFD
|
|
if (cfg->use_bit_timing)
|
|
{
|
|
_get_can_bit_timing(&p_can_dev->ll_init.stcBitCfg, &cfg->can_timing);
|
|
_get_can_bit_timing_fd(p_can_dev->ll_init.pstcCanFd, &cfg->canfd_timing);
|
|
}
|
|
else
|
|
{
|
|
RT_ASSERT(IS_VALID_BAUD_RATE_CANFD_ARBITRATION(cfg->baud_rate));
|
|
RT_ASSERT(IS_VALID_BAUD_RATE_CANFD_DATA(cfg->baud_rate_fd));
|
|
rt_ret = _calc_can_bit_timing(p_can_dev->instance, \
|
|
CAN_BIT_TIMING_CANFD_ARBITRATION, \
|
|
cfg->baud_rate, \
|
|
&p_can_dev->ll_init.stcBitCfg);
|
|
if (rt_ret != RT_EOK)
|
|
{
|
|
return rt_ret;
|
|
}
|
|
rt_ret = _calc_can_bit_timing(p_can_dev->instance, \
|
|
CAN_BIT_TIMING_CANFD_DATA, \
|
|
cfg->baud_rate_fd, \
|
|
&p_can_dev->ll_init.pstcCanFd->stcBitCfg);
|
|
if (rt_ret != RT_EOK)
|
|
{
|
|
return rt_ret;
|
|
}
|
|
}
|
|
p_can_dev->ll_init.pstcCanFd->u8SSPOffset = p_can_dev->ll_init.pstcCanFd->stcBitCfg.u32TimeSeg1;
|
|
#else
|
|
RT_ASSERT(IS_VALID_BAUD_RATE_CAN2_0(cfg->baud_rate));
|
|
rt_uint32_t baud_index = _get_can_baud_index(cfg->baud_rate);
|
|
p_can_dev->ll_init.stcBitCfg = _g_baudrate_tab[baud_index].ll_sbt;
|
|
#endif
|
|
|
|
/* init can */
|
|
CAN_Init(p_can_dev->instance, &p_can_dev->ll_init);
|
|
|
|
struct can_configure pre_config = p_can_dev->rt_can.config;
|
|
rt_memcpy(&p_can_dev->rt_can.config, cfg, sizeof(struct can_configure));
|
|
/* restore unmodifiable member */
|
|
if ((p_can_dev->rt_can.parent.open_flag & RT_DEVICE_OFLAG_OPEN) == RT_DEVICE_OFLAG_OPEN)
|
|
{
|
|
p_can_dev->rt_can.config.msgboxsz = pre_config.msgboxsz;
|
|
p_can_dev->rt_can.config.ticks = pre_config.ticks;
|
|
}
|
|
#ifdef RT_CAN_USING_HDR
|
|
p_can_dev->rt_can.config.maxhdr = pre_config.maxhdr;
|
|
#endif
|
|
p_can_dev->rt_can.config.sndboxnumber = pre_config.sndboxnumber;
|
|
|
|
return rt_ret;
|
|
}
|
|
|
|
static rt_err_t _can_control(struct rt_can_device *can, int cmd, void *arg)
|
|
{
|
|
can_device *p_can_dev;
|
|
|
|
RT_ASSERT(can != RT_NULL);
|
|
p_can_dev = (can_device *)rt_container_of(can, can_device, rt_can);
|
|
RT_ASSERT(p_can_dev);
|
|
|
|
switch (cmd)
|
|
{
|
|
case RT_DEVICE_CTRL_CLR_INT:
|
|
case RT_DEVICE_CTRL_SET_INT:
|
|
_config_can_int(p_can_dev, cmd, arg);
|
|
break;
|
|
case RT_CAN_CMD_SET_FILTER:
|
|
return _config_can_filter(p_can_dev, arg);
|
|
case RT_CAN_CMD_SET_MODE:
|
|
return _config_can_work_mode(p_can_dev, arg);
|
|
case RT_CAN_CMD_SET_BAUD:
|
|
#ifdef RT_CAN_USING_CANFD
|
|
return _canfd_control(p_can_dev, cmd, arg);
|
|
#else
|
|
return _config_can20_baud(p_can_dev, arg);
|
|
#endif
|
|
case RT_CAN_CMD_SET_PRIV:
|
|
return _config_can_priv_mode(p_can_dev, arg);
|
|
case RT_CAN_CMD_GET_STATUS:
|
|
{
|
|
struct rt_can_status *rt_can_stat = (struct rt_can_status *)arg;
|
|
stc_can_error_info_t stcErr = {0};
|
|
CAN_GetErrorInfo(p_can_dev->instance, &stcErr);
|
|
rt_can_stat->rcverrcnt = stcErr.u8RxErrorCount;
|
|
rt_can_stat->snderrcnt = stcErr.u8TxErrorCount;
|
|
rt_can_stat->lasterrtype = stcErr.u8ErrorType;
|
|
rt_can_stat->errcode = CAN_GetStatusValue(p_can_dev->instance);
|
|
}
|
|
break;
|
|
#ifdef RT_CAN_USING_CANFD
|
|
case RT_CAN_CMD_SET_CANFD:
|
|
case RT_CAN_CMD_SET_BAUD_FD:
|
|
case RT_CAN_CMD_SET_BITTIMING:
|
|
return _canfd_control(p_can_dev, cmd, arg);
|
|
#endif
|
|
default:
|
|
return -(RT_EINVAL);
|
|
|
|
}
|
|
return RT_EOK;
|
|
}
|
|
|
|
static rt_ssize_t _can_sendmsg(struct rt_can_device *can, const void *buf, rt_uint32_t box_num)
|
|
{
|
|
struct rt_can_msg *pmsg = (struct rt_can_msg *) buf;
|
|
stc_can_tx_frame_t stc_tx_frame = {0};
|
|
int32_t ll_ret;
|
|
|
|
RT_ASSERT(can != RT_NULL);
|
|
can_device *p_can_dev = (can_device *)rt_container_of(can, can_device, rt_can);
|
|
RT_ASSERT(p_can_dev);
|
|
|
|
stc_tx_frame.u32ID = pmsg->id;
|
|
if (RT_CAN_DTR == pmsg->rtr)
|
|
{
|
|
stc_tx_frame.RTR = 0;
|
|
}
|
|
else
|
|
{
|
|
stc_tx_frame.RTR = 1;
|
|
}
|
|
#ifdef RT_CAN_USING_CANFD
|
|
if (pmsg->fd_frame != 0)
|
|
{
|
|
RT_ASSERT(pmsg->len <= CAN_DLC64);
|
|
}
|
|
else
|
|
{
|
|
RT_ASSERT(pmsg->len <= CAN_DLC8);
|
|
}
|
|
stc_tx_frame.FDF = pmsg->fd_frame;
|
|
stc_tx_frame.BRS = pmsg->brs;
|
|
#endif
|
|
stc_tx_frame.DLC = pmsg->len & 0x0FU;
|
|
/* Set up the IDE */
|
|
stc_tx_frame.IDE = pmsg->ide;
|
|
/* Set up the data field */
|
|
uint32_t msg_len = _get_can_data_bytes_len(stc_tx_frame.DLC);
|
|
rt_memcpy(&stc_tx_frame.au8Data, pmsg->data, msg_len);
|
|
|
|
ll_ret = CAN_FillTxFrame(p_can_dev->instance, CAN_TX_BUF_PTB, &stc_tx_frame);
|
|
if (ll_ret != LL_OK)
|
|
{
|
|
return -RT_ERROR;
|
|
}
|
|
/* Request transmission */
|
|
CAN_StartTx(p_can_dev->instance, CAN_TX_REQ_PTB);
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
static rt_ssize_t _can_recvmsg(struct rt_can_device *can, void *buf, rt_uint32_t fifo)
|
|
{
|
|
int32_t ll_ret;
|
|
struct rt_can_msg *pmsg;
|
|
stc_can_rx_frame_t ll_rx_frame;
|
|
|
|
RT_ASSERT(can != RT_NULL);
|
|
can_device *p_can_dev = (can_device *)rt_container_of(can, can_device, rt_can);
|
|
RT_ASSERT(p_can_dev);
|
|
|
|
pmsg = (struct rt_can_msg *) buf;
|
|
/* get data */
|
|
ll_ret = CAN_GetRxFrame(p_can_dev->instance, &ll_rx_frame);
|
|
if (ll_ret != LL_OK)
|
|
return -RT_ERROR;
|
|
|
|
/* get id */
|
|
if (0 == ll_rx_frame.IDE)
|
|
{
|
|
pmsg->ide = RT_CAN_STDID;
|
|
}
|
|
else
|
|
{
|
|
pmsg->ide = RT_CAN_EXTID;
|
|
}
|
|
pmsg->id = ll_rx_frame.u32ID;
|
|
/* get type */
|
|
if (0 == ll_rx_frame.RTR)
|
|
{
|
|
pmsg->rtr = RT_CAN_DTR;
|
|
}
|
|
else
|
|
{
|
|
pmsg->rtr = RT_CAN_RTR;
|
|
}
|
|
/* get len */
|
|
pmsg->len = ll_rx_frame.DLC;
|
|
/* get hdr_index */
|
|
pmsg->hdr_index = 0;
|
|
pmsg->priv = 0;
|
|
#ifdef RT_CAN_USING_CANFD
|
|
pmsg->fd_frame = ll_rx_frame.FDF;
|
|
pmsg->brs = ll_rx_frame.BRS;
|
|
#endif
|
|
uint32_t msg_len = _get_can_data_bytes_len(ll_rx_frame.DLC);
|
|
rt_memcpy(pmsg->data, &ll_rx_frame.au8Data, msg_len);
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
static const struct rt_can_ops _can_ops =
|
|
{
|
|
_can_config,
|
|
_can_control,
|
|
_can_sendmsg,
|
|
_can_recvmsg,
|
|
};
|
|
|
|
rt_inline void _isr_can_rx(can_device *p_can_dev)
|
|
{
|
|
if (CAN_GetStatus(p_can_dev->instance, CAN_FLAG_RX_BUF_OVF) == SET)
|
|
{
|
|
/* RX overflow. */
|
|
rt_hw_can_isr(&p_can_dev->rt_can, RT_CAN_EVENT_RXOF_IND);
|
|
CAN_ClearStatus(p_can_dev->instance, CAN_FLAG_RX_BUF_OVF);
|
|
}
|
|
|
|
if (CAN_GetStatus(p_can_dev->instance, CAN_FLAG_RX) == SET)
|
|
{
|
|
/* Received a frame. */
|
|
CAN_ClearStatus(p_can_dev->instance, CAN_FLAG_RX);
|
|
rt_hw_can_isr(&p_can_dev->rt_can, RT_CAN_EVENT_RX_IND);
|
|
}
|
|
|
|
if (CAN_GetStatus(p_can_dev->instance, CAN_FLAG_RX_BUF_WARN) == SET)
|
|
{
|
|
/* RX buffer warning. */
|
|
CAN_ClearStatus(p_can_dev->instance, CAN_FLAG_RX_BUF_WARN);
|
|
}
|
|
|
|
if (CAN_GetStatus(p_can_dev->instance, CAN_FLAG_RX_BUF_FULL) == SET)
|
|
{
|
|
/* RX buffer full. */
|
|
CAN_ClearStatus(p_can_dev->instance, CAN_FLAG_RX_BUF_FULL);
|
|
}
|
|
|
|
if (CAN_GetStatus(p_can_dev->instance, CAN_FLAG_RX_OVERRUN) == SET)
|
|
{
|
|
/* RX buffer overrun. */
|
|
CAN_ClearStatus(p_can_dev->instance, CAN_FLAG_RX_OVERRUN);
|
|
}
|
|
}
|
|
|
|
rt_inline void _isr_can_tx(can_device *p_can_dev)
|
|
{
|
|
rt_bool_t is_tx_done = RT_FALSE;
|
|
rt_bool_t need_check_single_trans = RT_FALSE;
|
|
|
|
if (CAN_GetStatus(p_can_dev->instance, CAN_FLAG_TX_BUF_FULL) == SET)
|
|
{
|
|
/* TX buffer full. */
|
|
}
|
|
|
|
if (CAN_GetStatus(p_can_dev->instance, CAN_FLAG_TX_ABORTED) == SET)
|
|
{
|
|
/* TX aborted. */
|
|
CAN_ClearStatus(p_can_dev->instance, CAN_FLAG_TX_ABORTED);
|
|
}
|
|
|
|
if (CAN_GetStatus(p_can_dev->instance, CAN_FLAG_PTB_TX) == SET)
|
|
{
|
|
/* PTB transmitted. */
|
|
CAN_ClearStatus(p_can_dev->instance, CAN_FLAG_PTB_TX);
|
|
if (p_can_dev->ll_init.u8PTBSingleShotTx == CAN_PTB_SINGLESHOT_TX_ENABLE)
|
|
{
|
|
need_check_single_trans = RT_TRUE;
|
|
}
|
|
else
|
|
{
|
|
is_tx_done = RT_TRUE;
|
|
}
|
|
}
|
|
if (CAN_GetStatus(p_can_dev->instance, CAN_FLAG_STB_TX) == SET)
|
|
{
|
|
/* STB transmitted. */
|
|
CAN_ClearStatus(p_can_dev->instance, CAN_FLAG_STB_TX);
|
|
if (p_can_dev->ll_init.u8STBSingleShotTx == CAN_STB_SINGLESHOT_TX_ENABLE)
|
|
{
|
|
need_check_single_trans = RT_TRUE;
|
|
}
|
|
else
|
|
{
|
|
is_tx_done = RT_TRUE;
|
|
}
|
|
}
|
|
if (need_check_single_trans)
|
|
{
|
|
if ((CAN_GetStatus(p_can_dev->instance, CAN_FLAG_BUS_ERR) != SET) \
|
|
|| (CAN_GetStatus(p_can_dev->instance, CAN_FLAG_ARBITR_LOST) != SET))
|
|
{
|
|
is_tx_done = RT_TRUE;
|
|
}
|
|
}
|
|
if (is_tx_done)
|
|
{
|
|
rt_hw_can_isr(&p_can_dev->rt_can, RT_CAN_EVENT_TX_DONE);
|
|
}
|
|
|
|
|
|
if (CAN_GetStatus(p_can_dev->instance, CAN_FLAG_ARBITR_LOST) == SET)
|
|
{
|
|
rt_hw_can_isr(&p_can_dev->rt_can, RT_CAN_EVENT_TX_FAIL);
|
|
CAN_ClearStatus(p_can_dev->instance, CAN_FLAG_ARBITR_LOST);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
rt_inline void _isr_can_err(can_device *p_can_dev)
|
|
{
|
|
if (CAN_GetStatus(p_can_dev->instance, CAN_FLAG_ERR_INT) == SET)
|
|
{
|
|
/* ERROR. */
|
|
CAN_ClearStatus(p_can_dev->instance, CAN_FLAG_ERR_INT);
|
|
}
|
|
if (CAN_GetStatus(p_can_dev->instance, CAN_FLAG_BUS_ERR) == SET)
|
|
{
|
|
/* BUS ERROR. */
|
|
CAN_ClearStatus(p_can_dev->instance, CAN_FLAG_BUS_ERR);
|
|
}
|
|
if (CAN_GetStatus(p_can_dev->instance, CAN_FLAG_ERR_PASSIVE) == SET)
|
|
{
|
|
/* error-passive to error-active or error-active to error-passive. */
|
|
CAN_ClearStatus(p_can_dev->instance, CAN_FLAG_ERR_PASSIVE);
|
|
}
|
|
|
|
if (CAN_GetStatus(p_can_dev->instance, CAN_FLAG_TEC_REC_WARN) == SET)
|
|
{
|
|
/* TEC or REC reached warning limit. */
|
|
CAN_ClearStatus(p_can_dev->instance, CAN_FLAG_TEC_REC_WARN);
|
|
}
|
|
|
|
if (CAN_GetStatus(p_can_dev->instance, CAN_FLAG_BUS_OFF) == SET)
|
|
{
|
|
/* BUS OFF. */
|
|
}
|
|
}
|
|
|
|
rt_inline void _isr_ttcan(can_device *p_can_dev)
|
|
{
|
|
if (CAN_TTC_GetStatus(p_can_dev->instance, CAN_TTC_FLAG_TIME_TRIG) == SET)
|
|
{
|
|
/* Time trigger interrupt. */
|
|
CAN_TTC_ClearStatus(p_can_dev->instance, CAN_TTC_FLAG_TIME_TRIG);
|
|
}
|
|
|
|
if (CAN_TTC_GetStatus(p_can_dev->instance, CAN_TTC_FLAG_TRIG_ERR) == SET)
|
|
{
|
|
/* Trigger error interrupt. */
|
|
}
|
|
|
|
if (CAN_TTC_GetStatus(p_can_dev->instance, CAN_TTC_FLAG_WATCH_TRIG) == SET)
|
|
{
|
|
/* Watch trigger interrupt. */
|
|
CAN_TTC_ClearStatus(p_can_dev->instance, CAN_TTC_FLAG_WATCH_TRIG);
|
|
}
|
|
}
|
|
|
|
static void _isr_can(can_device *p_can_dev)
|
|
{
|
|
stc_can_error_info_t stcErr;
|
|
|
|
(void)CAN_GetErrorInfo(p_can_dev->instance, &stcErr);
|
|
_isr_can_rx(p_can_dev);
|
|
_isr_can_tx(p_can_dev);
|
|
_isr_can_err(p_can_dev);
|
|
_isr_ttcan(p_can_dev);
|
|
}
|
|
|
|
#if defined(BSP_USING_CAN1)
|
|
static void _irq_handler_can1(void)
|
|
{
|
|
rt_interrupt_enter();
|
|
_isr_can(&_g_can_dev_array[CAN1_INDEX]);
|
|
rt_interrupt_leave();
|
|
}
|
|
#endif
|
|
|
|
#if defined(BSP_USING_CAN2)
|
|
static void _irq_handler_can2(void)
|
|
{
|
|
rt_interrupt_enter();
|
|
_isr_can(&_g_can_dev_array[CAN2_INDEX]);
|
|
rt_interrupt_leave();
|
|
}
|
|
#endif
|
|
|
|
static void _enable_can_clock(void)
|
|
{
|
|
#if defined(HC32F4A0)
|
|
#if defined(BSP_USING_CAN1)
|
|
FCG_Fcg1PeriphClockCmd(FCG1_PERIPH_CAN1, ENABLE);
|
|
#endif
|
|
#if defined(BSP_USING_CAN2)
|
|
FCG_Fcg1PeriphClockCmd(FCG1_PERIPH_CAN2, ENABLE);
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(HC32F460)
|
|
#if defined(BSP_USING_CAN1)
|
|
FCG_Fcg1PeriphClockCmd(FCG1_PERIPH_CAN, ENABLE);
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
static void _config_can_irq(void)
|
|
{
|
|
struct hc32_irq_config irq_config;
|
|
#if defined(BSP_USING_CAN1)
|
|
irq_config.irq_num = BSP_CAN1_IRQ_NUM;
|
|
irq_config.int_src = CAN1_INT_SRC;
|
|
irq_config.irq_prio = BSP_CAN1_IRQ_PRIO;
|
|
/* register interrupt */
|
|
hc32_install_irq_handler(&irq_config,
|
|
_irq_handler_can1,
|
|
RT_TRUE);
|
|
#endif
|
|
#if defined(BSP_USING_CAN2)
|
|
irq_config.irq_num = BSP_CAN2_IRQ_NUM;
|
|
irq_config.int_src = CAN2_INT_SRC;
|
|
irq_config.irq_prio = BSP_CAN2_IRQ_PRIO;
|
|
/* register interrupt */
|
|
hc32_install_irq_handler(&irq_config,
|
|
_irq_handler_can2,
|
|
RT_TRUE);
|
|
#endif
|
|
}
|
|
|
|
static void _init_ll_struct_filter(can_device *p_can_dev)
|
|
{
|
|
if (p_can_dev->ll_init.pstcFilter == RT_NULL)
|
|
{
|
|
p_can_dev->ll_init.pstcFilter = (stc_can_filter_config_t *)rt_malloc(sizeof(stc_can_filter_config_t) * FILTER_COUNT);
|
|
}
|
|
RT_ASSERT((p_can_dev->ll_init.pstcFilter != RT_NULL));
|
|
|
|
rt_memset(p_can_dev->ll_init.pstcFilter, 0, sizeof(stc_can_filter_config_t) * FILTER_COUNT);
|
|
p_can_dev->ll_init.pstcFilter[0].u32ID = 0U;
|
|
p_can_dev->ll_init.pstcFilter[0].u32IDMask = 0x1FFFFFFF;
|
|
p_can_dev->ll_init.pstcFilter[0].u32IDType = CAN_ID_STD_EXT;
|
|
p_can_dev->ll_init.u16FilterSelect = CAN_FILTER1;
|
|
}
|
|
|
|
static void _init_struct_by_static_cfg(can_device *p_can_dev)
|
|
{
|
|
struct can_configure rt_can_config = CANDEFAULTCONFIG;
|
|
|
|
rt_can_config.privmode = RT_CAN_MODE_NOPRIV;
|
|
rt_can_config.ticks = 50;
|
|
#ifdef RT_CAN_USING_HDR
|
|
rt_can_config.maxhdr = FILTER_COUNT;
|
|
#endif
|
|
#ifdef RT_CAN_USING_CANFD
|
|
rt_can_config.baud_rate_fd = CANFD_DATA_BAUD_1M;
|
|
#endif
|
|
rt_can_config.sndboxnumber = 1;
|
|
p_can_dev->rt_can.config = rt_can_config;
|
|
|
|
if (p_can_dev->init.single_trans_mode)
|
|
{
|
|
p_can_dev->ll_init.u8PTBSingleShotTx = CAN_PTB_SINGLESHOT_TX_ENABLE;
|
|
}
|
|
|
|
#ifdef RT_CAN_USING_CANFD
|
|
_init_ll_struct_canfd(p_can_dev);
|
|
#endif
|
|
_init_ll_struct_filter(p_can_dev);
|
|
|
|
}
|
|
|
|
extern rt_err_t rt_hw_board_can_init(CM_CAN_TypeDef *CANx);
|
|
extern void CanPhyEnable(void);
|
|
int rt_hw_can_init(void)
|
|
{
|
|
_config_can_irq();
|
|
_enable_can_clock();
|
|
CanPhyEnable();
|
|
int result = RT_EOK;
|
|
uint32_t i = 0;
|
|
for (; i < CAN_INDEX_MAX; i++)
|
|
{
|
|
CAN_StructInit(&_g_can_dev_array[i].ll_init);
|
|
_init_struct_by_static_cfg(&_g_can_dev_array[i]);
|
|
|
|
/* register CAN device */
|
|
rt_hw_board_can_init(_g_can_dev_array[i].instance);
|
|
rt_hw_can_register(&_g_can_dev_array[i].rt_can, \
|
|
_g_can_dev_array[i].init.name,
|
|
&_can_ops,
|
|
&_g_can_dev_array[i]);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
INIT_DEVICE_EXPORT(rt_hw_can_init);
|
|
#endif
|
|
|
|
#endif /* BSP_USING_CAN */
|
|
|
|
/************************** end of file ******************/
|