Files
tronone-h7-scaffold/User_Code/bsp/fdcan/bsp_fdcan.md
2025-11-03 22:08:14 +08:00

8.5 KiB
Raw Permalink Blame History

bsp_fdcan.c/bsp_fdcan.h

请注意使用FDCAN设备的时候务必保证总线只接入了2个终端电阻开发板一般都有一个6020电机、c620/c610电调、LK电机也都有终端电阻注意把多于2个的全部断开通过拨码

使用说明

若你希望新增一个基于FDCAN的module首先在该模块下应该有一个包含FDCANInstance指针的module结构体或当功能简单的时候可以是单独存在的 FDCANInstance,但不推荐这样做)。

代码结构

.h文件内包括了外部接口和类型定义,以及模块对应的宏。c文件内为私有函数和外部接口的定义。

类型定义

#define CAN_MX_REGISTER_CNT 16     // 最大FDCAN设备注册数量
#define MX_CAN_FILTER_CNT (3 * 14) // 最多可以使用的CAN过滤器数量支持3个CAN
#define DEVICE_CAN_CNT 3           // FDCAN1, FDCAN2, FDCAN3

#define CAN_CLASS   0              // 经典CAN模式
#define CAN_FD_BRS  1              // CAN FD模式

#define CAN_BR_125K 0              // 125Kbps
#define CAN_BR_200K 1              // 200Kbps
#define CAN_BR_250K 2              // 250Kbps
#define CAN_BR_500K 3              // 500Kbps
#define CAN_BR_1M   4              // 1Mbps
#define CAN_BR_2M   5              // 2Mbps
#define CAN_BR_2M5  6              // 2.5Mbps
#define CAN_BR_3M2  7              // 3.2Mbps
#define CAN_BR_4M   8              // 4Mbps
#define CAN_BR_5M   9              // 5Mbps

/* FDCAN实例结构体每个注册到FDCAN的模块都应该有这个变量 */
typedef struct _FDCANInstance
{
    FDCAN_HandleTypeDef *can_handle; // FDCAN句柄
    FDCAN_TxHeaderTypeDef txconf;    // FDCAN报文发送配置
    uint32_t tx_id;                  // 发送id
    uint32_t tx_mailbox;             // FDCAN消息填入的邮箱号
    uint8_t tx_buff[64];             // 发送缓存FDCAN最大支持64字节
    uint8_t rx_buff[64];             // 接收缓存FDCAN最大支持64字节
    uint32_t rx_id;                  // 接收id
    uint8_t rx_len;                  // 接收长度
    // 接收的回调函数,用于解析接收到的数据
    void (*can_module_callback)(struct _FDCANInstance *); // 回调函数需要实例来区分注册的设备
    void *id;                        // 使用FDCAN外设的模块指针
} FDCANInstance;

/* FDCAN实例初始化结构体 */
typedef struct
{
    FDCAN_HandleTypeDef *can_handle;              // FDCAN句柄
    uint32_t tx_id;                               // 发送id
    uint32_t rx_id;                               // 接收id
    void (*can_module_callback)(FDCANInstance *); // 处理接收数据的回调函数
    void *id;                                     // 拥有FDCAN实例的模块地址
} FDCAN_Init_Config_s;

typedef void (*fdcan_callback)(FDCANInstance *);
  • CAN_MX_REGISTER_CNT是最大的FDCAN设备注册数量当每个设备的发送频率都较高时设备过多会产生总线拥塞从而出现丢包和数据错误的情况。

  • MX_CAN_FILTER_CNT是最大的FDCAN接收过滤器数量三个FDCAN共享多个过滤器。当前为简单起见每个过滤器只设置一组规则用于控制一个id的过滤。

  • DEVICE_CAN_CNT是MCU拥有的FDCAN硬件数量H7有3个

  • FDCANInstance 是一个FDCAN实例。注意FDCAN作为一个总线设备一条总线上可以挂载多个设备因此多个设备可以共享同一个FDCAN硬件。其成员变量包括发送id发送邮箱发送buff以及接收buff还有接收id和接收协议解析回调函数。 FDCAN支持最大64字节的数据帧因此收发buff长度设置为64字节。

  • FDCAN_Init_Config_s是用于初始化FDCAN实例的结构在调用FDCAN实例的初始化函数时传入。

  • can_module_callback()是模块提供给FDCAN接收中断回调函数使用的协议解析函数指针。对于每个需要FDCAN的模块需要定义一个这样的函数用于解包数据。

  • 每个使用FDCAN外设的module都需要在其内部定义一个FDCANInstance*

外部接口

FDCANInstance *FDCANRegister(FDCAN_Init_Config_s *config);
void FDCANSetDLC(FDCANInstance *_instance, uint8_t length);
uint8_t FDCANTransmit(FDCANInstance *_instance, float timeout);
void bsp_fdcan_set_baud(FDCAN_HandleTypeDef *hfdcan, uint8_t mode, uint8_t baud);

FDCANRegister是用于初始化FDCAN实例的接口module层的模块对象也应当为一个结构体内要包含一个FDCANInstance 。调用时传入初始化配置的指针。FDCANRegister应当在module的初始化函数内被调用推荐config采用以下的方式定义更加直观明了

FDCAN_Init_Config_s config = {
    .can_handle = &hfdcan1,
    .tx_id = 0x005,
    .rx_id = 0x200,
    .can_module_callback = MotorCallback,
    .id = (void*)motor_ptr
};

FDCANTransmit()是通过模块通过其拥有的FDCAN实例发送数据的接口调用时传入对应的instance。在发送之前应当给instance内的 tx_buff赋值。

bsp_fdcan_set_baud()用于设置FDCAN的波特率和模式经典CAN或CAN FD

私有函数和变量

在.c文件内设为static的函数和变量

static FDCANInstance *fdcan_instance[CAN_MX_REGISTER_CNT] = {NULL};

这是bsp层管理所有FDCAN实例的入口。

static void FDCANServiceInit()
static void FDCANAddFilter(FDCANInstance *_instance)
static void FDCANFIFOxCallback(FDCAN_HandleTypeDef *_hcan, uint32_t fifox)
static uint8_t FDCANDlcToLen(uint32_t dlc)
static uint32_t FDCANLenToDlc(uint8_t len)
void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs)
void HAL_FDCAN_RxFifo1Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo1ITs)
void HAL_FDCAN_ErrorStatusCallback(FDCAN_HandleTypeDef *hfdcan, uint32_t ErrorStatusITs)
  • FDCANServiceInit()会被FDCANRegister()调用对FDCAN外设进行硬件初始化并开启接收中断和消息提醒。

  • FDCANAddFilter()在每次使用FDCANRegister()的时候被调用,用于给当前注册的实例添加过滤器规则并设定处理对应rx_id 的接收FIFO。过滤器的作用是减小FDCAN收发器的压力只接收符合过滤器规则的报文否则不会产生接收中断

  • FDCANDlcToLen()FDCANLenToDlc()是FDCAN特有的数据长度码转换函数用于在数据长度码和实际字节数之间转换。

  • HAL_FDCAN_RxFifo0Callback()HAL_FDCAN_RxFifo1Callback()都是对HAL的FDCAN回调函数的重定义原本的callback是__weak 修饰的弱定义当发生FIFO0或FIFO1有新消息到达的时候对应的callback会被调用。FDCANFIFOxCallback() 随后被前两者调用并根据接收id和硬件中断来源哪一个FDCAN硬件调用对应的instance的回调函数进行协议解析。

  • HAL_FDCAN_ErrorStatusCallback()处理FDCAN的错误状态包括总线关闭和错误被动状态的恢复。

  • 当有一个模块注册了多个FDCAN实例时通过FDCANInstance.id,使用强制类型转换将其转换成对应模块的实例指针,就可以对不同的模块实例进行回调处理了。

弱定义回调函数

__weak void fdcan1_rx_callback(void);
__weak void fdcan2_rx_callback(void);
__weak void fdcan3_rx_callback(void);

这些是弱定义的FDCAN接收回调函数用户可以根据需要重写这些函数来实现特定的功能。

注意事项

  1. 终端电阻使用FDCAN设备时务必保证总线只接入了2个终端电阻

  2. FDCAN特性

    • FDCAN支持经典CAN和CAN FD模式
    • CAN FD模式下支持最高64字节的数据帧
    • 支持更高的通信速率最高5Mbps
  3. 发送超时由于FDCAN总线自带发送检测如果总线上没有挂载目标设备那么发送FIFO会被占满而无法发送。在FDCANTransmit() 中会对发送FIFO是否已满进行检查。当超出timeout之后函数会返回零,说明发送失败。

  4. 任务阻塞:由于卡在while(1)处不断检查FIFO是否空闲调用FDCANTransmit() 的任务可能无法按时挂起导致任务定时不精确。建议在没有连接FDCAN进行调试时按需注释掉有关FDCAN发送的代码部分或设定一个较小的 timeout值,防止对其他需要精确定时的任务产生影响。

  5. 模式选择:使用bsp_fdcan_set_baud()函数可以在经典CAN模式和CAN FD模式之间切换注意不同模式支持的波特率范围不同。

  6. 数据长度FDCAN支持最大64字节的数据帧使用FDCANSetDLC()函数设置数据长度时要注意不要超过64字节。