LoopBack

该示例使用 CAN 的回环测试模式发送和接收帧数据。

使用 CAN 发送邮箱分别发送标准数据帧、扩展数据帧、标准远程帧、扩展远程帧。 在 Debug Analyzer 工具上打印 CAN 邮箱接收到的帧数据。

环境需求

该示例的环境需求,可参考 环境需求

编译和下载

该示例的编译和下载流程,可参考 编译和下载

测试验证

  1. 执行 CAN 初始化,初始化前后分别读取总线状态。

    [CAN]  BUS state: 0, waiting...
    [CAN]  BUS ON 1
    
  2. 发送邮箱分别发送标准数据帧、扩展数据帧、标准远程帧、扩展远程帧,接收邮箱收到数据后打印数据帧信息 log。

    [CAN]  rx frame_type x
    [CAN]  rx frame_id = xxx , ext_frame_id = xxx
    [CAN]  rx_data [0] xxx
    ...
    [CAN]  BUS RX done
    ...
    

代码介绍

该章节主要介绍示例中的初始化和相应功能实现的代码和流程说明。

源码路径

工程文件和源码路径如下:

  • 工程路径: sdk\samples\peripheral\can\loopback\proj

  • 源码路径: sdk\samples\peripheral\can\loopback\src

初始化

外设的初始化流程可参考 General Introduction 中的 初始化流程 部分。

  1. 在 CAN 回环测试下,无需配置 PAD 与 PINMUX。

  2. 调用 RCC_PeriphClockCmd() ,开启 CAN 时钟。

  3. 对 CAN 外设进行初始化:

    1. 定义 CAN_InitTypeDef 类型 init_struct ,调用 CAN_StructInit()init_struct 预填默认值。

    2. 根据需求修改 init_struct 参数,CAN 的初始化参数配置如下表。

    3. 调用 CAN_Init(),初始化 CAN 外设。

参数,CAN 初始化参数

CAN Hardware Parameters

Setting in the init_struct

CAN

Auto Re-transmit Enable

CAN_InitTypeDef::CAN_AutoReTxEn

DISABLE

CAN speed parameter - brp

CAN_BIT_TIMING_TYPE_TypeDef::can_brp

3

CAN speed parameter - sjw

CAN_BIT_TIMING_TYPE_TypeDef::can_sjw

3

CAN speed parameter - tseg1

CAN_BIT_TIMING_TYPE_TypeDef::can_tseg1

13

CAN speed parameter - tseg2

CAN_BIT_TIMING_TYPE_TypeDef::can_tseg2

4

Test Mode

CAN_InitTypeDef::CAN_TestModeSel

CAN_TEST_MODE_INT_LOOPBACK

  1. 调用 CAN_Cmd(),使能相应 CAN 外设。

  2. 调用 CAN_GetBusState(),循环检查 CAN 总线状态,等待 CAN 总线打开。

    while (CAN_GetBusState() != CAN_BUS_STATE_ON)
    {
        __asm volatile
        (
            "nop    \n"
        );
    }
    

备注

注意:如果程序卡在等待 CAN 总线打开处,请检查 CAN bus 是否连接准确。

功能实现

  1. 依次以回环方式发送标准数据帧,扩展数据帧,标准远程帧,扩展远程帧。

备注

注意:如果使用同一个邮箱号连续发送多个帧,请务必保证前一帧发送完成后再发送下一帧。

void can_demo(void)
{
    ...
    /* Send standard data frame. */
    can_loopback(0, CAN_STD_DATA_FRAME, 0x123, 0, tx_data, 8);
    /* Send extend data frame. */
    can_loopback(1, CAN_EXT_DATA_FRAME, 0x123, 0x4567, tx_data, 8);
    /* Send standard remote frame. */
    can_loopback(2, CAN_STD_REMOTE_FRAME, 0x7ff, 0, tx_data, 0);
    /* Send extend remote frame. */
    can_loopback(3, CAN_EXT_REMOTE_FRAME, 0x7ff, 0x3ffff, tx_data, 0);
    ...
}
  1. 执行 can_loopback 函数,以回环方式发送和接收指定类型的帧。

    1. 调用 CAN_SetMsgBufRxMode() 设置接收邮箱的接收帧类型。使用邮箱 15 进行接收,屏蔽接收帧 rtr,ide,id 过滤,即接收所有帧,失能 Rx GDMA,失能自动回复,使用设置的接收帧类型将邮箱配置为接收模式。

    2. 调用 CAN_GetRamState() 等待 RAM 状态空闲。

    3. 调用 CAN_SetMsgBufTxMode() 设置发送邮箱的发送帧类型。使用传入参数邮箱 id,帧类型,标准帧 id,扩展帧 id,失能自动回复,使用设置的发送帧类型将邮箱配置为发送模式。调用 CAN_GetRamState() 等待 RAM 状态空闲。

    4. 轮询 CAN_GetMBnTxDoneFlag()CAN_GetMBnRxDoneFlag() 等待发送与接收完成。

    5. 调用 CAN_GetRamData()CAN_CheckFrameType() 读取接收邮箱信息,通过接收邮箱信息读取 RAM 中接收到的帧数据并打印。

    CANError_TypeDef rx_error;
    CANRxFrame_TypeDef rx_frame_type;
    
    rx_frame_type.msg_buf_id = 15;
    /* Set 0 to filter related bit, and set 1 to mask related filter. */
    rx_frame_type.frame_rtr_mask = SET;
    rx_frame_type.frame_ide_mask = SET;
    rx_frame_type.frame_id_mask = CAN_FRAME_ID_MASK_MAX_VALUE;
    rx_frame_type.rx_dma_en = RESET;
    rx_frame_type.auto_reply_bit = RESET;
    
    rx_error = CAN_SetMsgBufRxMode(&rx_frame_type);
    
    while (CAN_GetRamState() != CAN_RAM_STATE_IDLE)
    ...
    
    /* Set tx message buffer. */
    CANError_TypeDef tx_error = CAN_NO_ERR;
    CANTxFrame_TypeDef tx_frame_type;
    
    tx_frame_type.msg_buf_id = buf_id;
    tx_frame_type.frame_type = frame_type;
    tx_frame_type.standard_frame_id = frame_id;
    tx_frame_type.extend_frame_id = 0;
    tx_frame_type.frame_brs_bit = CAN_BRS_NO_SWITCH_BIT_TIMING;
    tx_frame_type.auto_reply_bit = DISABLE;
    
    tx_error = CAN_SetMsgBufTxMode(&tx_frame_type, tx_data, data_len);
    while (CAN_GetRamState() != CAN_RAM_STATE_IDLE)
    ...
    
    /* Polling tx done. */
    while (SET != CAN_GetMBnTxDoneFlag(tx_frame_type.msg_buf_id))
    ...
    
    /* Polling rx done. */
    while (SET != CAN_GetMBnRxDoneFlag(rx_frame_type.msg_buf_id))
    ...
    
    /* Receive rx data. */
    ...
    get_error = CAN_GetMsgBufInfo(rx_frame_type.msg_buf_id, &mb_info);
    ...
    CAN_GetRamData(mb_info.data_length, rx_data);
    CANDataFrameSel_TypeDef get_frame_type = CAN_CheckFrameType(mb_info.rtr_bit, mb_info.ide_bit, mb_info.edl_bit);
    ...