基于S32K SDK FLEXCAN的通信
- 1 前言
 - 2 基本流程
 - 3 实例
 - 
- 3.1硬件资源
 - 3.2 实现功能
 - 3.3 实现
 - 
- 3.3.1 MCU配置
 - 3.3.2 定义CAN通信配置及邮箱
 - 3.3.3 初始化CAN实例及缓冲区
 - 3.3.4 定义回调函数及处理内容
 
 - 3.4 运行结果
 
 - 后记
 
1 前言
在NXP S32K SDK中FLEXCAN提供了CAN和CAN FD的驱动。本文讲解基本CAN通信的程序方法。
2 基本流程
第一步:基本硬件配置
 这一步主要配置S32Kxxx MCU的硬件资源。如时钟,引脚,CAN物理层。这一步利用Processor Expert完成。
 第二步:定义CAN通信配置及邮箱。
 第三步:初始化CAN实例及缓冲区。
 第四部:定义回调函数及处理内容。
3 实例
3.1硬件资源
MCU: S32K118
 使用引脚:
 PTC1:按键
 PTA10:LED输出
 PTB1:CAN Tx
 PTB0:CAN Rx
3.2 实现功能
a. 按下按键后以500Kbps波特率发送ID=1,长度为8,内容为0x01 02 03 04 05 06 07 08的数据。
 b. 当接收到500Kbps波特率 ID=2的帧时,点亮LED灯。当接收到ID=4的帧时,关闭LED灯。
3.3 实现
3.3.1 MCU配置
在Processor Expert中:
 a. GPIO配置
 按使用引脚将PTA10, PTC1分别设置为输出和输入如下:
 
 设置CAN通信所用引脚:
 
 b. 设置CAN硬件参数
 
 这部分设置在SDK生成代码后对应:canCom1.c的如下内容:
const flexcan_user_config_t canCom1_InitConfig0 = {
/*CAN通信配置结构体名:canCom1_InitConfig0*/
    .fd_enable = false, /*不使用CAN FD*/
    .pe_clock = FLEXCAN_CLK_SOURCE_PERIPH,  /*CAN通信使用外设时钟源*/
    .max_num_mb = 10,/*邮箱数量为10*/
    .num_id_filters = FLEXCAN_RX_FIFO_ID_FILTERS_8, /*CAN帧ID过滤器数量为8*/
    .is_rx_fifo_needed = false,   /*不使用FIFO*/
    .flexcanMode = FLEXCAN_NORMAL_MODE,    /*正常模式*/
    .payload = FLEXCAN_PAYLOAD_SIZE_8,     
    .bitrate = {  /*仲裁场位时间设置*/
        .propSeg = 7,
        .phaseSeg1 = 4,
        .phaseSeg2 = 1,
        .preDivider = 5,
        .rJumpwidth = 1
    },
    .bitrate_cbt = {/*数据场位时间设置*/
        .propSeg = 7,
        .phaseSeg1 = 4,
        .phaseSeg2 = 1,
        .preDivider = 5,
        .rJumpwidth = 1
    },
    .transfer_type = FLEXCAN_RXFIFO_USING_INTERRUPTS,/*中断方式*/
    .rxFifoDMAChannel = 0U
};
3.3.2 定义CAN通信配置及邮箱
a ,配置CAN通信格式
 在主程序设置配置结构体及接收数据缓冲区,例如接收部分如下:
flexcan_data_info_t Rx_dataInfo =
  {
          .data_length = 8U,
          .msg_id_type = FLEXCAN_MSG_ID_STD, /*使用11位标准ID格式*/
          .enable_brs  = false,  /*因为不使用CAN FD所以关闭*/
          .fd_enable   = false,  /*因为不使用CAN FD所以关闭*/
          .fd_padding  = 0U      /*因为不使用CAN FD所以关闭*/
  };
flexcan_msgbuff_t recvBuff; //接收数据缓冲区
在SDK中定义的数据缓冲区格式如下,后文将按这个结构体进行CAN报文的设置。
typedef struct {
    uint32_t cs;                        /*!< Code and Status*/
    uint32_t msgId;                     /*!< Message Buffer ID*/
    uint8_t data[64];                   /*!< Data bytes of the FlexCAN message*/
    uint8_t dataLen;                    /*!< Length of data in bytes */
} flexcan_msgbuff_t;
b. 定义通信邮箱
//定义发送邮箱,最后数字为CAN ID值 #define Tx_MB_01 (1UL) // 定义接收邮箱,最后的数字为CAN ID值 #define Rx_MB_02 (2UL) #define Rx_MB_04 (4UL)
3.3.3 初始化CAN实例及缓冲区
a. 配置CAN通信格式
 在主程序中按前面Process Expert中指定的CAN实例及配置结构体名进行CAN口的初始化。
FLEXCAN_DRV_Init(INST_CANCOM1, &canCom1_State, &canCom1_InitConfig0);
b.初始化收发缓冲区
   /*配置发送缓冲区*/
    FLEXCAN_DRV_ConfigTxMb(INST_CANCOM1, Tx_MB_01, &sendBuff, 1);//ID=1
    /*配置接收缓冲区*/
    FLEXCAN_DRV_ConfigRxMb(INST_CANCOM1, Rx_MB_02, &recvBuff, 2);  // ID=2
    FLEXCAN_DRV_ConfigRxMb(INST_CANCOM1, Rx_MB_04, &recvBuff, 4);  // ID=4
3.3.4 定义回调函数及处理内容
a. 定义回调函数
 此处回调函数在SDK中的格式为:
typedef void(* flexcan_callback_t) (uint8_t instance, flexcan_event_type_t eventType, uint32_t buffIdx, flexcan_state_t *flexcanState)
本例中只用回调函数进行CAN的接收处理。
 主程序中安装回调函数如下:
FLEXCAN_DRV_InstallEventCallback(INST_CANCOM1,CAN_CallBack,NULL); FLEXCAN_DRV_Receive(INST_CANCOM1, Rx_MB_02, &recvBuff); FLEXCAN_DRV_Receive(INST_CANCOM1, Rx_MB_04, &recvBuff);
程序中定义的回调函数为:CAN_CallBack()
 由于基本的CAN收发函数除非使用阻塞型函数外,函数之后后直接退出。CAN的操作结束时会触发回调函数。因此在安装回调函数后,马上使用CAN的接收API函数FLEXCAN_DRV_Receive()进行读操作。若有数据被接收到了,则自动进入回调函数中。
b.回调函数
void CAN_CallBack(uint8_t instance, flexcan_event_type_t eventType, uint32_t buffIdx, flexcan_state_t *flexcanState)
{
	(void) buffIdx;
	(void) flexcanState;
	if(eventType == FLEXCAN_EVENT_RX_COMPLETE)
	{/*CAN读完成事件触发时执行*/
		if(recvBuff.msgId==2)
		{  /*ID=2的帧点灯*/
			PINS_DRV_SetPins(GPIO2_PORT, (1 << LED2));
			FLEXCAN_DRV_Receive(INST_CANCOM1,Rx_MB_02, &recvBuff);
		}
		if(recvBuff.msgId==4)
		{/*ID=4的帧关灯*/
			PINS_DRV_ClearPins(GPIO2_PORT, (1 << LED2));
			FLEXCAN_DRV_Receive(INST_CANCOM1,Rx_MB_04, &recvBuff);
		}
	}
}
其中“eventType”是事件类型,FLEXCAN_EVENT_RX_COMPLETE为读CAN完成事件。而后的程序按帧ID进行不同的开关灯操作。
c.CAN的发送处理
 在按键处理程序中,当判断到有按键按下时,执行:
sendBuff.msgId=0x1;
           sendBuff.dataLen=8;
           for (int i=0;i<9;i++)
           {
        	   sendBuff.data[i]=i+1;
           }
            /* Send the information via CAN */
            SendCANData(Tx_MB_01, sendBuff.msgId, sendBuff.data, 8UL);
           
SendCANData()
void SendCANData(uint32_t mailbox, uint32_t messageId, uint8_t * data, uint32_t len)
{
    FLEXCAN_DRV_Send(INST_CANCOM1, mailbox, &Tx_dataInfo, messageId, data);
}
3.4 运行结果

 其中第一帧为硬件向上位机发送的CAN报文。第二,三帧为上位机发给硬件的开关灯报文。硬件开关灯正常,CAN通信收发成功。
后记
本文于2024.1.21日首发于“车灯电子扫地僧”。 如果你喜欢我的文章,也可以“车灯电子扫地僧”搜索微信订阅号。更多文章等待您的发掘。