Transmit and Receive - AutoReply

This sample demonstrates the use of the auto-reply feature of the CAN peripheral for data transmission and reception.

A USB CAN analyzer is externally connected to the CAN, enabling communication between the PC and the CAN controller for sending and receiving data.

The CAN auto-reply feature is utilized to automatically receive or send data frames.

Requirements

For hardware requirements, please refer to the Requirements.

Wiring

The EVB is connected to a TJA1051 CAN Receiver module by connecting P4_0 to CTX, P4_1 to CRX. The EVB BAT/5V 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.

../../../_images/CAN_Hardware_Connection_Diagram.png

CAN TRX Demo Code Hardware Connection Diagram

The introduction of CAN transceiver can refer to CAN Transceiver Introduction .

Configurations

  1. The following macros can be configured to modify CAN pin definitions.

    #define CAN_TX_PIN          P4_0
    #define CAN_RX_PIN          P4_1
    
  2. The entry function is as follows, call this function in main() to run this sample code. For more details, please refer to the Initialization.

    can_trx_autoreply_demo();
    

Building and Downloading

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

Experimental Verification

  1. Press the Reset button on the EVB and the message will be displayed in the Debug Analyzer.

    can_trx_autoreply_demo: start!
    
  2. After the IC is reset, a remote frame sent by the IC is received on the tool.

  3. Within the Debug Analyzer, IC prints out the remote frame IDs to be replied and those that can be autoreplied.

    can_trx_autoreply_demo: start!
    can_driver_init: BUS state: 0, waiting...
    can_driver_init: BUS ON 1
    can_receive_remote_auto_tx_reply: tx auto reply remote frame id: 0x00000001
    can_send_remote_auto_receive_reply: rx auto receive data frame id: 0x00000010
    
  4. Send a 0x010 data frame on the tool, the IC will receive and print the log and send the next remote frame to be replied.

    can_trx_autoreply_handler: CAN RX INT
    can_send_remote_auto_receive_reply: rx auto receive data frame id: 0x00000001
    
  5. The IC auto reply data frame is received 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:

  • For project directory, please refer to Source Code Directory.

  • Source code directory: sdk\sample\io_demo\canbus\trx_autoreply\can_trx_autoreply.c.

Initialization

The initialization flow for peripherals can refer to Initialization Flow.

CAN initialization flow is shown in the following figure.

../../../_images/can_init_flow.png

CAN Init Flow

  1. Call Pad_Config() and Pinmux_Config() to configure the corresponding pin’s PAD and PINMUX.

    static 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_receive_remote_auto_tx_reply, enable TX autoreply function.

    1. Set the transmit frame type for the TX message buffer. Enable auto reply function. Configure frame id and frame data to auto reply.

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

    void can_receive_remote_auto_tx_reply(void)
    {
        CANError_TypeDef tx_error;
        CANTxFrame_TypeDef tx_frame_type;
    
        ...
    
        tx_frame_type.msg_buf_id = 0;
        tx_frame_type.frame_type = CAN_STD_DATA_FRAME;
        tx_frame_type.standard_frame_id = 0x01;
        tx_frame_type.extend_frame_id = 0x01;
        tx_frame_type.auto_reply_bit = SET;
        CAN_MBTxINTConfig(CAN0, tx_frame_type.msg_buf_id, ENABLE);
        tx_error = CAN_SetMsgBufTxMode(CAN0, &tx_frame_type, tx_data, 8);
    
        ...
    }
    
  2. Execute can_send_remote_auto_receive_reply, enable RX autoreply function.

    1. Enable auto reply. Unmask the rtr, ide, and id bit filters to receive specified frames.

    2. Disable RX GDMA.

    3. Enable the message buffer RX done interrupt. Poll the RAM status to be idle.

    void can_send_remote_auto_receive_reply(void)
    {
        CANError_TypeDef rx_error;
        CANRxFrame_TypeDef rx_frame_type;
    
        rx_frame_type.msg_buf_id = 10;
        rx_frame_type.extend_frame_id = 0;
        rx_frame_type.standard_frame_id = 0x10;
        rx_frame_type.frame_ide_bit = CAN_IDE_STANDARD_FORMAT;
        rx_frame_type.frame_rtr_bit = CAN_RTR_DATA_FRAME;
        rx_frame_type.frame_rtr_mask = CAN_RX_FRAME_UNMASK_RTR;
        rx_frame_type.frame_ide_mask = CAN_RX_FRAME_UNMASK_IDE;
        rx_frame_type.frame_id_mask = 0;
        rx_frame_type.rx_dma_en = RESET;
        rx_frame_type.auto_reply_bit = SET;
        rx_error = CAN_SetMsgBufRxMode(CAN0, &rx_frame_type);
    
        CAN_MBRxINTConfig(CAN0, rx_frame_type.msg_buf_id, ENABLE);
    
        ...
    }
    
  3. If remote frame with the same frame id as TX autoreply setting is received, the message buffer will automatically reply and trigger the TX interrupt. If data frame with the same frame id as RX autoreply setting is received, the message buffer will automatically receive the frame and trigger the RX interrupt. Check the interrupt source in the can_trx_autoreply_handler.

    1. If the interrupt source is the RX done interrupt, print the received frame data and call the can_send_remote_auto_receive_reply function, then wait for the next interrupt.

    2. If the interrupt source is the TX done interrupt, call the can_receive_remote_auto_tx_reply function, then wait for the next interrupt.

    ...
    if (SET == CAN_GetINTStatus(CAN0, CAN_RX_INT_FLAG))
    {
        ...
        CAN_GetMBnRxDoneFlag(CAN0, index)
        ...
        CAN_GetRamData(CAN0, mb_info.data_length, rx_data);
    
        CANDataFrameSel_TypeDef frame_type = CAN_CheckFrameType(mb_info.rtr_bit, mb_info.ide_bit,
                                                                        mb_info.edl_bit);
        can_send_remote_auto_receive_reply();
    }
    
    if (SET == CAN_GetINTStatus(CAN0, CAN_TX_INT_FLAG))
    {
        CAN_ClearINTPendingBit(CAN0, CAN_TX_INT_FLAG);
        IO_PRINT_INFO0("can_trx_autoreply_handler: CAN TX INT");
    
        /* Enable autoreply next time. */
        can_receive_remote_auto_tx_reply();
    
        /* Add user code. */
    }