IMX RT FlexCan调试手册

本文描述的FlexCan主要指的是NXP公司的I.MX RT系列提供的FlexCan低速总线,主要内容是个人根据在调试FlexCan接口时碰到的一些问题而总结出的调试心得,希望对后续使用到此类接口的朋友有所帮助。

1、 FlexCan内部结构

IMX RT FlexCan调试手册_第1张图片
其中比较重要的结构就是提供Message Buffers去临时存储发送或者接收到的CAN Frame数据,这也是后续会使用到的很重要的一个功能。CAN收发数据时,MBs会存储有仲裁域、数据域的数据(含ID),控制器将从这一块的空间去取得收发的数据,进而完成收发功能。
此处不做详细介绍,感兴趣的朋友直接参照手册即可,下面直接进入正题,关于FlexCan的初始化和不同的功能实现。
**

2、 FlexCan的初始化

**
第一步,配置CAN的总线时钟,配置如下:

/*Clock setting for FLEXCAN*/
clock_root_config_t rootCfg = {0};
rootCfg.mux = kCLOCK_CAN1_ClockRoot_MuxSysPll2Pfd3; //FLEXCAN_CLOCK_SOURCE_SELECT
rootCfg.div = 5; //CAN ROOT CLK:60M
CLOCK_SetRootClock(kCLOCK_Root_Can1, &rootCfg);

此处我配置的总线时钟为60M,主要是为了后续配置CANFD总线波特率方便分频,如数据域的几个常用波特率:1Mbps,2 Mbps,3 Mbps,4 Mbps,5Mbps等,具体波特率可以参照产品的实际需求去配置。
第二步,在配置完时钟之后,配置波特率的实际分频参数,注意这里为了更贴合CAN总线对Bit数据采样的需求(实际测试过程中也发现如果使用默认配置,板子当前的硬件链路高波特率下无法完成正常传输:3Mbps以上,收发器支持5Mbps),下图是1BIT的CAN数据的组成:
IMX RT FlexCan调试手册_第2张图片

代码如下:

if (FLEXCAN_FDCalculateImprovedTimingValues(flexcanConfig.baudRate, flexcanConfig.baudRateFD, CAN_CLK_FREQ,
                                            &timing_config))
{
    /* Update the improved timing configuration*/
    memcpy(&(flexcanConfig.timingConfig), &timing_config, sizeof(flexcan_timing_config_t));
}
else
{
    PRINTF("No found Improved Timing Configuration. Just used default configuration\r\n\r\n");
}

第三步,设置完波特率之后,就是初始化CAN控制器,使用的函数如下:

/*!
 * brief Initializes a FlexCAN instance.
 *
 * This function initializes the FlexCAN module with user-defined settings.
 * This example shows how to set up the flexcan_config_t parameters and how
 * to call the FLEXCAN_FDInit function by passing in these parameters.
 *  code
 *   flexcan_config_t flexcanConfig;
 *   flexcanConfig.clkSrc               = kFLEXCAN_ClkSrc0;
 *   flexcanConfig.baudRate             = 1000000U;
 *   flexcanConfig.baudRateFD           = 2000000U;
 *   flexcanConfig.maxMbNum             = 16;
 *   flexcanConfig.enableLoopBack       = false;
 *   flexcanConfig.enableSelfWakeup     = false;
 *   flexcanConfig.enableIndividMask    = false;
 *   flexcanConfig.disableSelfReception = false;
 *   flexcanConfig.enableListenOnlyMode = false;
 *   flexcanConfig.enableDoze           = false;
 *   flexcanConfig.timingConfig         = timingConfig;
 *   FLEXCAN_FDInit(CAN0, &flexcanConfig, 60000000UL, kFLEXCAN_8BperMB, false);
 *   endcode
 *
 * param base FlexCAN peripheral base address.
 * param pConfig Pointer to the user-defined configuration structure.
 * param sourceClock_Hz FlexCAN Protocol Engine clock source frequency in Hz.
 * param dataSize FlexCAN FD frame payload size.
 * param brs If bitrate switch is enabled in FD mode.
 */
void FLEXCAN_FDInit(
    CAN_Type *base, const flexcan_config_t *pConfig, uint32_t sourceClock_Hz, flexcan_mb_size_t dataSize, bool brs)

这里有两个和MBs相关的参数,需要格外注意,分别是flexcanConfig.enableIndividMask和flexcanConfig.maxMbNum,enableIndividMask决定使用global mask还是使用individual mask,后面会有详细介绍,此处不细讲,maxMbNum决定最大的Message Buffer数量,由处理器决定,我们使用的处理器最大支持64个。
之后创建用户自定义的中断回调函数,并将用户中断回调注册到中断处理函数中,此处也是直接调用底层提供的API,如下:

    /* Create FlexCAN handle structure and set call back function. */
    FLEXCAN_TransferCreateHandle(CAN, &flexcanHandle, flexcan_callback, (flexcan_frame_t *)&rxframe);
    NVIC_SetPriority(CAN1_IRQn, 3);

用户中断回调函数的函数原型:

/*! @brief FlexCAN transfer callback function.
 *
 *  The FlexCAN transfer callback returns a value from the underlying layer.
 *  If the status equals to kStatus_FLEXCAN_ErrorStatus, the result parameter is the Content of
 *  FlexCAN status register which can be used to get the working status(or error status) of FlexCAN module.
 *  If the status equals to other FlexCAN Message Buffer transfer status, the result is the index of
 *  Message Buffer that generate transfer event.
 *  If the status equals to other FlexCAN Message Buffer transfer status, the result is meaningless and should be
 *  Ignored.
 */
typedef void (*flexcan_transfer_callback_t)(
    CAN_Type *base, flexcan_handle_t *handle, status_t status, uint32_t result, void *userData);

第四步,配置Message Buffers和对应的掩码,掩码有两种形式,其一是全局掩码,对全局的MBs有效,另一个是私有掩码,只对对应的MB有效,决定哪一个掩码有效就由上面描述的flexcanConfig.enableIndividMask = false/true决定。
全局掩码有三个,分别是RXMGMASK、RX14MASK、RX15MASK(RX14MASK只对MB 14有效,RX15MASK同理),全局掩码具体的设置函数如下:

    /* Set Rx Masking mechanism. */
    FLEXCAN_SetRxMbGlobalMask(CAN, FLEXCAN_RX_MB_STD_MASK(MASK, 0, 0));
	私有掩码则有64个,RXIMR0 - RXIMR63,对应MB0 – MB63,是一一对应的关系,所以私有掩码和对应的MB可以放到一起配置,这样看起来更方便,如下:
    /* Setup Rx Message Buffer. */
    mbConfig.format = kFLEXCAN_FrameFormatStandard;
    mbConfig.type = kFLEXCAN_FrameTypeData;
    rxframe.format = (uint8_t)kFLEXCAN_FrameFormatStandard;
    rxframe.type = (uint8_t)kFLEXCAN_FrameTypeData;
    rxframe.length = (uint8_t)DLC;
    
    for (uint8_t i = 0; i < MB_COUNT; i++)
    {
        if(idtable[i].id != 0x0U)
        {
            FLEXCAN_SetRxIndividualMask(CAN, i, FLEXCAN_RX_MB_STD_MASK(idtable[i].mask,0,0));
            mbConfig.id = FLEXCAN_ID_STD(idtable[i].id);
#if (defined(USE_CANFD) && USE_CANFD)
            FLEXCAN_SetFDRxMbConfig(CAN, i , &mbConfig, true);
#else
            FLEXCAN_SetRxMbConfig(CAN, i, &mbConfig, true);
#endif
            rxframe.id = FLEXCAN_ID_STD(idtable[i].id);
            rxXfer.mbIdx = (uint8_t)i;
#if (defined(USE_CANFD) && USE_CANFD)
            rxXfer.framefd = &rxframe;
            (void)FLEXCAN_TransferFDReceiveNonBlocking(CAN, &flexcanHandle, &rxXfer);
#else
            rxXfer.frame = &rxframe;
            (void)FLEXCAN_TransferReceiveNonBlocking(EXAMPLE_CAN, &flexcanHandle, &rxXfer);
#endif

        }
    }

这里的核心思想就是对于每一个MB都要配置对应的IndividualMask,同时启用该MB的接收函数,我们正常使用会让CAN接收函数常驻,在接收到数据时再通知应用,所以也要在中断回调中再次启用对应result(即MB index)的CAN接收函数,如下:

/*!
 * @brief FlexCAN Call Back function
 */
static void flexcan_callback(CAN_Type *base, flexcan_handle_t *handle, status_t status, uint32_t result, void *userdata)
{
    static BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    static BaseType_t outcome;
    switch (status)
    {
        case kStatus_FLEXCAN_RxIdle:
            outcome = xEventGroupSetBitsFromISR(flexcan_evengroup, BIT_1, &xHigherPriorityTaskWoken);
            if (outcome != pdFAIL)
                portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
            rxXfer.mbIdx = result;
#if (defined(USE_CANFD) && USE_CANFD)
            (void)FLEXCAN_TransferFDReceiveNonBlocking(base, &flexcanHandle, &rxXfer);
#else
            (void)FLEXCAN_TransferReceiveNonBlocking(base, &flexcanHandle, &rxXfer);
#endif

        break;

    case kStatus_FLEXCAN_TxIdle:
        if (TX_MESSAGE_BUFFER_NUM == result)
        {
            outcome = xEventGroupSetBitsFromISR(flexcan_evengroup, BIT_0, &xHigherPriorityTaskWoken);
            if (outcome != pdFAIL)
                portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
        }
        break;

    case kStatus_FLEXCAN_WakeUp:
        break;

    default:
        break;
    }
}

设置的MB ID和对应MASK的关系,如果设置MB[1]的ID是0x100,设置对应的RXIMR[1]的MASK为0x7FF,这说明MB[1]只会接收ID为0x100的CAN口数据,同理如果MASK设置为0x7FC,即最后两bit都为0,不会check,那此时MB[1]会接收ID范围为0x100-0x103的数据。
上述流程都是针对CANFD的,但如果使用CAN Classical,此时CAN将能支持CAN RXFIFO。
IMX RT FlexCan调试手册_第3张图片

当RXFIFO enable 后, RXFIFO 会占用前面几个MB, 首先MB[0:5]用来作为FIFO 缓存, 但是MB[6]之后的MB 可以用来定义 filter, 具体占用几个MB, 是通过CTRL2[RFFN] 来定义的, 那最少的定义(RFFN=0)MB6, 7 会被用做filter 的设置。
MB 6-7的空间能容纳8个 filter(4Byte存储一个filter), 这样RXIMR[0…7] 也有8个mask 与之对应, 这样 Rx FIFO global mask 就没有用途。
那么如果 RFFN = 1, MB6-9 用作filter, format C 格式的话, 那么会有16个filter, 但是 0-9 只有10个RXIMR 与之对应, 这样还有6个filter (10…15)没有mask 与其对应, 那么这样的换RX FIFO global mask 就跟这6个filter 合作,过滤收到的ID。
IMX RT FlexCan调试手册_第4张图片
CANFIFO的filtertable配置idFilterTable对应的数组即可,然后再配置对应的MASK,如下:

/*! @brief FlexCAN Rx FIFO configuration structure. */
typedef struct _flexcan_rx_fifo_config
{
    uint32_t *idFilterTable;                    /*!< Pointer to the FlexCAN Rx FIFO identifier filter table. */
    uint8_t idFilterNum;                        /*!< The quantity of filter elements. */
    flexcan_rx_fifo_filter_type_t idFilterType; /*!< The FlexCAN Rx FIFO Filter type. */
    flexcan_rx_fifo_priority_t priority;        /*!< The FlexCAN Rx FIFO receive priority. */
} flexcan_rx_fifo_config_t;

/*!
 * @brief Configures the FlexCAN Rx FIFO.
 *
 * This function configures the Rx FIFO with given Rx FIFO configuration.
 *
 * @param base FlexCAN peripheral base address.
 * @param pRxFifoConfig Pointer to the FlexCAN Rx FIFO configuration structure.
 * @param enable Enable/disable Rx FIFO.
 *               - true: Enable Rx FIFO.
 *               - false: Disable Rx FIFO.
 */
void FLEXCAN_SetRxFifoConfig(CAN_Type *base, const flexcan_rx_fifo_config_t *pRxFifoConfig, bool enable);

至此,CAN的初始化基本完成,同时CAN接收函数保持常驻后台,如有不足欢迎补充。

3、调试问题

调试过程中实际碰到的问题有,CANFD的通信在自己的测试板上正常,但和标准CANFD模块通信测试无法通过,一直显示错误,后面确认是CANFD的CRC域未配置为ISO标准模式,配置成ISO标准模式即正常,寄存器CAN->CTRL2如下:

IMX RT FlexCan调试手册_第5张图片

你可能感兴趣的:(IMX RT FlexCan调试手册)