在NXP S32K SDK中FLEXCAN提供了CAN和CAN FD的驱动。本文讲解基本CAN通信的程序方法。
第一步:基本硬件配置
这一步主要配置S32Kxxx MCU的硬件资源。如时钟,引脚,CAN物理层。这一步利用Processor Expert完成。
第二步:定义CAN通信配置及邮箱。
第三步:初始化CAN实例及缓冲区。
第四部:定义回调函数及处理内容。
MCU: S32K118
使用引脚:
PTC1:按键
PTA10:LED输出
PTB1:CAN Tx
PTB0:CAN Rx
a. 按下按键后以500Kbps波特率发送ID=1,长度为8,内容为0x01 02 03 04 05 06 07 08的数据。
b. 当接收到500Kbps波特率 ID=2的帧时,点亮LED灯。当接收到ID=4的帧时,关闭LED灯。
在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
};
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)
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
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);
}
其中第一帧为硬件向上位机发送的CAN报文。第二,三帧为上位机发给硬件的开关灯报文。硬件开关灯正常,CAN通信收发成功。
本文于2024.1.21日首发于“车灯电子扫地僧”。 如果你喜欢我的文章,也可以“车灯电子扫地僧”搜索微信订阅号。更多文章等待您的发掘。