Transmit And Receive - GDMA
CAN 外接 USB CAN 分析仪,通过 PC 端收发标准数据帧,扩展数据帧,标准远程帧,扩展远程帧等帧类型与 CAN controller 进行通信。
芯片端通过 GDMA 接收 PC 下发的数据,并通过 GDMA 传输相同数据给 PC 端,PC 端收到芯片回的数据。
环境需求
该示例的环境需求,可参考 环境需求。
硬件连线
EVB 外接 TJA1051 CAN Receiver 模块,连接 P3_2 和 CTX,P3_4 和 CRX,连接 EVB 3.3V 和模块 VCC,EVB VCC3.3 和连接模块 NC,连接 EVB GND 和模块 GND 和 S。
TJA1051 CAN Receiver 模块另一端通过 USB CAN 分析仪连接到 PC,连接模块 CANH 和分析仪 CANH,连接模块 CANL 和分析仪 CANL。
CAN 收发模块的介绍请参考 CAN 收发模块介绍 。
编译和下载
该示例的编译和下载流程,可参考 编译和下载。
测试验证
IC 复位后,工具上会收到 IC 发送的数据帧。
IC 打印等待接收的 log:
[CAN] start can trx_gdma demo! [CAN] BUS state: 0, waiting... [CAN] BUS ON 1 [TX GDMA HANDLER] TX DONE! [CAN] waiting for rx...
使用 tool 发送一个数据帧。
IC 通过 GDMA 接收到数据帧,打印 log。
[CAN HANDLER] CAN RX [CAN HANDLER] MB_1 rx done [RX DMA HANDLER] GDMA_Channel_Handler [RX DMA HANDLER] std id = 0x00000100, ext id = 0x00000000 dlc = 8 [RX DMA HANDLER] rx_data [0] 0x01 [RX DMA HANDLER] rx_data [1] 0x23 [RX DMA HANDLER] rx_data [2] 0x45 [RX DMA HANDLER] rx_data [3] 0x67 [RX DMA HANDLER] rx_data [4] 0x89 [RX DMA HANDLER] rx_data [5] 0xab [RX DMA HANDLER] rx_data [6] 0xcd [RX DMA HANDLER] rx_data [7] 0xef [CAN] waiting for rx...
代码介绍
该章节主要介绍示例中的初始化和相应功能实现的代码和流程说明。
源码路径
工程文件和源码路径如下:
工程路径:
sdk\samples\peripheral\can\trx_dma\proj
源码路径:
sdk\samples\peripheral\can\trx_dma\src
初始化
外设的初始化流程可参考 General Introduction 中的 初始化流程 部分。
调用
Pad_Config()
与Pinmux_Config()
,配置对应引脚的 PAD 和 PINMUX。void can_board_init(void) { /* Config pinmux and pad for CAN. */ Pinmux_Config(CAN_TX_PIN, CAN_TX); Pinmux_Config(CAN_RX_PIN, CAN_RX); Pad_Config(CAN_TX_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_DISABLE, PAD_OUT_LOW); Pad_Config(CAN_RX_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_DISABLE, PAD_OUT_LOW); }
调用
RCC_PeriphClockCmd()
,开启 CAN 时钟。对 CAN 外设进行初始化:
定义
CAN_InitTypeDef
类型init_struct
,调用CAN_StructInit()
将init_struct
预填默认值。根据需求修改
init_struct
参数,CAN 的初始化参数配置如下表。调用
CAN_Init()
,初始化 CAN 外设。
CAN Hardware Parameters |
Setting in the |
CAN |
---|---|---|
Auto Re-transmit Enable |
||
CAN speed parameter - brp |
3 |
|
CAN speed parameter - sjw |
3 |
|
CAN speed parameter - tseg1 |
13 |
|
CAN speed parameter - tseg2 |
4 |
|
Rx GDMA Enable |
调用
CAN_Cmd()
,使能相应 CAN 外设。调用
CAN_INTConfig()
,配置 CAN 接收完成CAN_RX_INT
中断,发送完成CAN_TX_INT
中断,错误中断CAN_ERROR_INT
等。配置 NVIC,NVIC 相关配置可参考 中断配置。调用
CAN_GetBusState()
,循环检查 CAN 总线状态,等待 CAN 总线打开。while (CAN_GetBusState() != CAN_BUS_STATE_ON) { __asm volatile ( "nop \n" ); }
备注
注意:如果程序卡在等待 CAN 总线打开处,请检查 CAN bus 是否连接准确。
调用
RCC_PeriphClockCmd()
,开启 GDMA 时钟。对 GDMA 外设进行初始化:
定义
GDMA_InitTypeDef
类型GDMA_InitStruct
,执行GDMA_StructInit()
将GDMA_InitStruct
预填默认值。根据需求修改
GDMA_InitStruct
参数。DMA TX 和 RX 通道的初始化参数配置如下表。执行GDMA_Init()
,初始化 GDMA 外设。配置 GDMA 总传输完成中断
GDMA_INT_Transfer
和 NVIC,NVIC 相关配置可参考 中断配置。
GDMA Hardware Parameters |
Setting in the |
GDMA TX Channel |
GDMA RX Channel |
---|---|---|---|
Channel Num |
1 |
0 |
|
Transfer Direction |
|||
Buffer Size |
|
- |
|
Source Address Increment or Decrement |
|||
Destination Address Increment or Decrement |
|||
Source Data Size |
|||
Destination Data Size |
|||
Source Burst Transaction Length |
|||
Destination Burst Transaction Length |
|||
Source Address |
- |
|
|
Destination Address |
|
- |
|
Source Handshake |
- |
||
Destination Handshake |
- |
- |
备注
GDMA 外设初始化时,暂不初始化 TX GDMA 通道的源端地址和 RX GDMA 通道的目的端地址,后续动态调整。
功能实现
调用
can_dma_tx
函数,配置需要通过 TX GDMA 配置到 RAM 寄存器的值tx_can_ram_struct
。调用
can_start_tx_dma
函数。设置 TX GDMA 通道的源端地址为
tx_can_ram_struct
的地址。使能 TX GDMA 的
GDMA_INT_Transfer
中断。使能 GDMA 外设开始传输。
void can_start_tx_dma(CAN_RAM_TypeDef *p_can_ram_data) { GDMA_SetSourceAddress(TX_GDMA_Channel, (uint32_t)p_can_ram_data); GDMA_INTConfig(TX_GDMA_CHANNEL_NUM, GDMA_INT_Transfer, ENABLE); GDMA_Cmd(TX_GDMA_CHANNEL_NUM, ENABLE); }
使能 GDMA TX 通道后,DMA 将数据从
tx_can_ram_struct
搬运到(CAN->CAN_RAM_DATA)
。 当 TX 完成时,会触发 GDMA TX 中断,打印相关信息。GDMA_INTConfig(TX_GDMA_CHANNEL_NUM, GDMA_INT_Transfer, DISABLE); DBG_DIRECT("[TX DMA HANDLER] TX DONE!"); GDMA_ClearINTPendingBit(TX_GDMA_CHANNEL_NUM, GDMA_INT_Transfer);
调用
can_dma_rx
函数。屏蔽接收帧 rtr,ide,id 过滤,即接收所有帧。
使能 Rx DMA,失能自动回复。
使用设置的接收帧类型将邮箱配置为接收模式。
使能邮箱接收中断,等待 RAM 状态空闲。
... rx_frame_type.msg_buf_id = RX_DMA_BUF_ID; 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 = SET; rx_frame_type.auto_reply_bit = RESET; rx_error = CAN_SetMsgBufRxMode(&rx_frame_type); CAN_MBRxINTConfig(rx_frame_type.msg_buf_id, ENABLE); while (CAN_GetRamState() != CAN_RAM_STATE_IDLE) ...
当 message buffer 接收完成时,执行中断处理函数
CAN_Handler
。调用
CAN_GetRxDmaMsize()
函数获取 Rx GDMA buffer size。调用
can_start_rx_dma
函数使能配置 GDMA buffer size,配置 GDMA RX 通道的目的端地址,使能 RX GDMA 传输。
if (dma_en_flag == ENABLE) { uint32_t dma_buffer_size = CAN_GetRxDmaMsize(); can_start_rx_dma((uint32_t)&rx_dma_data_struct, dma_buffer_size); } ... GDMA_SetBufferSize(RX_GDMA_Channel, buffer_size); GDMA_SetDestinationAddress(RX_GDMA_Channel, des_addr); GDMA_INTConfig(RX_GDMA_CHANNEL_NUM, GDMA_INT_Transfer, ENABLE); GDMA_Cmd(RX_GDMA_CHANNEL_NUM, ENABLE);
使能 GDMA RX 通道后,当 CAN 接收到数据后,DMA 将数据从
&(CAN->CAN_RX_DMA_DATA)
搬运到rx_dma_data_struct
。当 RX GDMA 接收完成时,执行中断处理函数RX_GDMA_Channel_Handler
。失能 RX GDMA 的
GDMA_INT_Transfer
中断。使能 message buffer 的 RX GDMA 开始下一次接收。
打印接收到的帧 ID,打印接收帧数据。
清除 RX GDMA 的
GDMA_INT_Transfer
中断标志位。
GDMA_INTConfig(RX_GDMA_CHANNEL_NUM, GDMA_INT_Transfer, DISABLE); ... CAN_SetMBnRxDmaEnFlag(RX_DMA_BUF_ID, ENABLE); ... DBG_DIRECT(...); ... GDMA_ClearINTPendingBit(RX_GDMA_CHANNEL_NUM, GDMA_INT_Transfer);