Transmit And Receive

This sample demonstrates the data transmission and reception capabilities of the CAN peripheral.

Connect the USB CAN analyzer to communicate with the CAN controller via the PC by sending and receiving standard data frames, extended data frames, standard remote frames, and extended remote frames.

The SoC side receives the data from the PC in the CAN interrupt and transmits the same data to the PC side, which receives the data back from the SoC.

Requirements

For requirements, please refer to the Requirements.

Wiring

The EVB is connected to a TJA1051 CAN Receiver module by connecting P3_2 to CTX, P3_4 to CRX. The EVB VCC3.3V is connected to the module VCC and NC. The EVB GND is connected to the module GND and S.

The other end of the TJA1051 CAN Receiver module is connected to the PC via a USB CAN analyzer, by connecting module CANH to analyzer CANH and module CANL to analyzer CANL.

CAN Transceiver Introduction

The module adopts TJA1051, which is a high-speed CAN transceiver that provides differential transmission and reception functions for the CAN controller, with a transmission rate of up to 1Mbit/s. Any other CAN transceivers could be used instead.

Here should be a picture of the CAN transceiver module

CAN transceiver module

Here should be a picture of the CAN transceiver module connection schematic

CAN transceiver module connection schematic

Building and Downloading

For building and downloading, please refer to the Building and Downloading.

Experimental Verification

  1. After IC reset, frames of each type are received on the CAN Tool.

  2. Use the tool to send data frames to the IC.

  3. On the Debug Analyzer, print the received frame data.

    [CAN] send standard remote frame
    [CAN HANDLER]  MB_1 tx done
    ...
    [CAN HANDLER]  MB_5 tx done
    [CAN]  waiting for rx...
    [CAN HANDLER] MB_0 rx done
    [CAN HANDLER] frame_type 1, frame_id = 0x100, ext_frame_id = 0x00000
    [CAN HANDLER] rx_data [0] 0x01
    ...
    [CAN HANDLER] rx_data [7] 0xef
    [CAN]  waiting for rx...
    [CAN HANDLER] MB_0 tx done
    
  4. The IC sends the received data frames back to the tool, and it can see the sent and received data frames on the tool:

Code Overview

This section introduces the code and process description for initialization and corresponding function implementation in the sample.

Source Code Directory

The directory for project file and source code are as follows:

  • Project directory: sdk\samples\peripheral\can\trx\proj

  • Source code directory: sdk\samples\peripheral\can\trx\src

Initialization

The initialization flow for peripherals can refer to Initialization Flow in General Introduction.

  1. Call Pad_Config() and Pinmux_Config() to configure the corresponding pin’s PAD and 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);
    }
    
  2. Call RCC_PeriphClockCmd() to enable the CAN clock.

  3. Initialize the CAN peripheral:

    1. Define a CAN_InitTypeDef type init_struct and call CAN_StructInit() to pre-fill init_struct with default values.

    2. Modify the init_struct parameters as needed. The CAN initialization parameters are configured as shown in the table below.

    3. Call CAN_Init() to initialize the parameters and the CAN peripheral.

CAN Initialization Parameters

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

  1. Call CAN_Cmd() to enable the corresponding CAN peripheral.

  2. Call CAN_INTConfig() to configure the CAN receive complete CAN_RX_INT interrupt, transmit complete CAN_TX_INT interrupt, error interrupt CAN_ERROR_INT, etc. Configure NVIC, and refer to Interrupt Configuration for NVIC-related configurations.

  3. Call CAN_GetBusState() to loop-check the CAN bus state and wait for the CAN bus to open.

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

Note

If the program is stuck at the point of waiting for the CAN bus to be on, please check if the CAN bus is connected correctly.

Functional Implementation

  1. Call can_basic_tx, send a standard data frame, an extended data frame, a standard remote frame, an extended remote frame.

    1. Configure the message buffer to tx mode using the set transmit frame type.

    2. Enable the message buffer tx interrupt. Poll the RAM status to be idle.

    void can_basic_tx(uint32_t buf_id, uint8_t frame_type, \
                  uint16_t frame_id, uint32_t ext_id, uint8_t *tx_data, uint8_t data_len)
    {
        CANError_TypeDef tx_error;
        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.auto_reply_bit = DISABLE;
        tx_frame_type.extend_frame_id = 0;
    
        switch (frame_type)
        {
            ...
        }
    
        CAN_MBTxINTConfig(tx_frame_type.msg_buf_id, ENABLE);
        tx_error = CAN_SetMsgBufTxMode(&tx_frame_type, tx_data, data_len);
    
        ...
    }
    

Note

If sending several frames using the same message buffer id, make sure to wait for the previous frame to be already sent before sending the next frame.

  1. After sending, call can_basic_rx and wait for the PC to send a frame into the receive interrupt.

  1. Use message buffer 12 to receive frame if FIFO mode is enabled.

  2. Mask the rtr, ide, and id bit filters to receive all frames.

  3. Disable Rx GDMA. Disable auto reply.

  4. Configure the message buffer to rx mode using the set receive frame type.

  5. Enable the message buffer rx interrupt. Poll the RAM status to be idle.

void can_basic_rx(void)
{
    CANError_TypeDef rx_error;
    CANRxFrame_TypeDef rx_frame_type;
#if CAN_RX_FIFO_EN
    rx_frame_type.msg_buf_id = CAN_MESSAGE_FIFO_START_ID;
#else
    rx_frame_type.msg_buf_id = 0;
#endif

    rx_frame_type.extend_frame_id = 0;
    rx_frame_type.standard_frame_id = 0;
    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);

    CAN_MBRxINTConfig(rx_frame_type.msg_buf_id, ENABLE);

    ...
}
  1. When transmission is complete, reception is complete, or when an error occurs, enter interrupt handler function CAN_Handler.

    1. Read and print the received frame data sequentially from the message buffer that generated the receive completion interrupt.

    2. Execute can_basic_tx to send back the received frame type and frame data.

    3. Execute can_basic_rx and wait for the next interrupt.

    ...
    CAN_ClearMBnRxDoneFlag(index);
    CANError_TypeDef get_error;
    CANMsgBufInfo_TypeDef mb_info;
    get_error = CAN_GetMsgBufInfo(index, &mb_info);
    ...
    CAN_GetRamData(mb_info.data_length, rx_data);
    CANDataFrameSel_TypeDef frame_type = CAN_CheckFrameType(mb_info.rtr_bit, mb_info.ide_bit,
                                                            mb_info.edl_bit);
    ...
    /* Send back frame here. */
    can_basic_tx(0, frame_type, mb_info.standard_frame_id, \
                  mb_info.extend_frame_id, rx_data, mb_info.data_length);
    
    /* Start rx next time. */
    can_basic_rx();
    ...