MCP2518FD外部CAN FD控制器具有小尺寸和SPI接口,可实现轻松连接。可将CAN FD通道轻松添加到缺少CAN FD外设或没有所需CAN FD通道的微控制器上。MCP2518FD支持经典格式 (CAN 2.0B) 和CAN灵活数据速率 (CAN FD) 格式中的CAN帧格式,符合ISO11898-1:2015标准。
框图:
本文实验板MCU:STM32F103C8T6
CAN FD控制器:MCP2518FD
CAN FD收发器:ATA6560
开发环境:Keil uVision5.28
想了解开发板更详细信息,请点击 MCP2518FD学习评估套件
MCP2518FD学习评估套件
电路图如下:
典型应用
上图有两部分组成,一部分是CAN控制器mcp2518fd,另一部分就是CANFD收发器ATA6563,我们实验板上用的是ATA6560,和ATA6563 PIN to PIN。
对于CAN 收发器,有两个引脚要特别注意,就是STBY和NSIL引脚,这两个引脚的状态控制CAN收发器工作状态,如下图:
CAN收发器STBY接地,VIO(NSIL)接3.3v,VDD接5V,VSS接地。
1、SPI时序
我们要配置寄存器,要通过SPI总线发数据过去,相对于外设就是SDI接口的数据:Command + Address + Data.
第一个字节:4位命令+4位地址;第二个字节:8位地址;第三个字节及以后:都是数据;所以往寄存器写一个字节时序可以这么写:
// write one byte
spiTransmitBuffer[0] = (uint8_t) ((CMD << 4) + ((ADDR >> 8) & 0xF)); // 4位指令码+地址高4位
spiTransmitBuffer[1] = (uint8_t) (ADDR& 0xFF); // 地址低8位
spiTransmitBuffer[2] = txdata; // 数据位
同理,写halfWord或者one Word也可以这样定义。
指令:
注意:只有在器件进入配置模式后才能发出RESET 指令
2、MCP2518FD 驱动程序移植
2.1 下载驱动程序
从Microchip 官网下载此IC的驱动程序,一共包括4个文件: drv_canfdspi_api.c,drv_canfdspi_api.h,drv_canfdspi_defines.h,drv_canfdspi_register.h
drv_canfdspi_api.c 里面是MCP2515FD的驱动函数。
drv_canfdspi_api.h 应对.c文件函数的声明
drv_canfdspi_defines.h 寄存器的值定义,比如CAN_DLC等等
drv_canfdspi_register.h 寄存器的定义
由于这4个文件都是官方做好的,我们只管拿过来用,尽量不要去修改里面的内容。
在Keil MDK建好工程后,直接把这4个文件添加到工程中。
2.2 编写SPI驱动程序
//SPIx 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节 SPI通信可以同进读写数据
uint8_t SPI2_ReadWriteByte(uint8_t TxData)
{
u8 dat=0,i;
for(i=0;i<8;i++)
{
if(TxData & 0x80) // 如果位为1,则发送1,否则发送0
{
CAN1_SPI_MOSI_HIGH(); // 发送1
}
else{CAN1_SPI_MOSI_LOW(); // 发送0
}
TxData<<= 1;
dat <<= 1;
__NOP();
__NOP();
if(CAN1_MISO())
{dat += 1; }
CAN1_SPI_CLK_HIGH();
__NOP();
__NOP();
CAN1_SPI_CLK_LOW();
__NOP();
__NOP();
}
CAN1_SPI_MOSI_LOW();
return dat;
}
2.3 SPI通信
通过分析官方给的4个文件,我们真正关心的,其实就是drv_canfdspi_api.c里的驱动函数,那如何将stm32f103c8t6的SPI函数连接上里面的函数呢?
SPI 数据的读写:
//数据读写
int8_t DRV_SPI_TransferData(uint8_t spiSlaveDeviceIndex, uint8_t *SpiTxData, uint8_t *SpiRxData, uint16_t spiTransferSize);
没错,就是这个函数。
spiSlaveDeviceIndex: 由于SPI总线只接1个MCP2518FD,所以这个值一直定义为0
*SpiTxData:要发送的数据首地址
*SpiRxData:接收数据存放在首地址
spiTransferSize:发送数据的字节数
完整函数如下:
2.4 CAN初始化
void MCP251xFD_canfd_cfg_init(CAN_BITTIME_SETUP baud)
{
REG_CiFLTOBJ fObj;
REG_CiMASK mObj;
CAN_TX_FIFO_CONFIG txConfig;
CAN_RX_FIFO_CONFIG rxConfig;
CAN_CONFIG config;
// Reset device
DRV_CANFDSPI_Reset(CANFD_CH1); //复位MCP2518FD 所有SFR和状态机都会像上电复位期间一样复位,器件会立即进入配置模式
// Enable ECC and initialize RAM
DRV_CANFDSPI_EccEnable(CANFD_CH1); //使能ECC(ECC逻辑支持单个位错误纠正和双位错误检测)
DRV_CANFDSPI_RamInit(CANFD_CH1, 0xff); //并将RAM空间初始化为初值0xFF
// Configure device
DRV_CANFDSPI_ConfigureObjectReset(&config); //MCP2518FD配置信息复位
config.IsoCrcEnable = 1; // 使能CAN FD帧中的ISO CRC位
config.StoreInTEF = 0;// 不将发送的报文保存到TEF中,也就不在RAM中预留TEF空间
config.BitRateSwitchDisable = 0; // Depends on the BRS bit on TX msg
//CiCON->addr:0x00-03
DRV_CANFDSPI_Configure(CANFD_CH1, &config); //MCP2518FD配置
// Setup TX FIFO 发送FIFO配置
DRV_CANFDSPI_TransmitChannelConfigureObjectReset(&txConfig);
txConfig.FifoSize = 11; // 采用FIFO11作为发送FIFO
txConfig.PayLoadSize = CAN_PLSIZE_64; // 有效负载大小位64个数据字节
txConfig.TxPriority = 1; // 使能奇偶校验位
//CiTXQCON->addr:0x50-53 + CAN_FIFO_CHn*12
DRV_CANFDSPI_TransmitChannelConfigure(CANFD_CH1, CAN_TX_FIFO, &txConfig);
// Setup RX FIFO 接收FIFO配置
DRV_CANFDSPI_ReceiveChannelConfigureObjectReset(&rxConfig);
rxConfig.FifoSize = 15; // 采用FIFO15作为接收FIFO
rxConfig.PayLoadSize = CAN_PLSIZE_64; // 有效负载大小位64个数据字节
rxConfig.RxTimeStampEnable = 1; // 捕捉时间戳 (去研究下,捕捉和不捕捉的区别在哪里)
//CiFIFOCON1->addr:0x50-53 + CAN_FIFO_CHn*12
DRV_CANFDSPI_ReceiveChannelConfigure(CANFD_CH1, CAN_RX_FIFO, &rxConfig);
// set time stamp
DRV_CANFDSPI_TimeStampModeConfigure(CANFD_CH1,CAN_TS_RES);
DRV_CANFDSPI_TimeStampPrescalerSet(CANFD_CH1,40-1); //40 clk 加1,40M晶体,单位1us
DRV_CANFDSPI_TimeStampSet(CANFD_CH1,0);
DRV_CANFDSPI_TimeStampEnable(CANFD_CH1); //使能时间戳
// Setup RX Filter 接收滤波器设置 ,只接收数据侦ID为0x128的数据(前提是屏蔽寄存器有效)
fObj.word = 0;
fObj.bF.SID = 0x128; // 接收标准标识符 11bit
fObj.bF.EXIDE = 0; // 接收扩展标识符使能位 1 enable, 0 disable 1bit
fObj.bF.EID = 0; // 接收扩展标识符 18bit
//CiFLTCON0->0x1D0-0x1D3
DRV_CANFDSPI_FilterObjectConfigure(CANFD_CH1, CAN_FILTER0, &fObj.bF);
// Setup RX Mask 接收屏蔽器设置 高电平有效
mObj.word = 0; // 32bit 寄存器写法,这里不用
mObj.bF.MSID = 0x128; // 接收标准标识符屏蔽位 如果不应用于范围控制,就设置值等于 fObj.bF.SID,一对一对应
mObj.bF.MIDE = 1; // 只适用于扩展侦模式,标准侦模式时,这个位不起作用
mObj.bF.MEID = 0; // 接收扩展标识符屏蔽位
//CiMASK0
DRV_CANFDSPI_FilterMaskConfigure(CANFD_CH1, CAN_FILTER0, &mObj.bF);
// Link FIFO and Filter 将接收滤波器与接收屏蔽器与接收FIFO绑定,则满足接收滤波器和接收屏蔽器规则的报文会在相应的FIFO接收。
DRV_CANFDSPI_FilterToFifoLink(CANFD_CH1, CAN_FILTER0, CAN_RX_FIFO, true);
// Setup Bit Time 设置位时间 总线波特率 baud,这里采用自动测量发送器延时的方式实现二次采样点采集数据位
// CiNBTCFG->0x04-0x07
DRV_CANFDSPI_BitTimeConfigure(CANFD_CH1, baud, CAN_SSP_MODE_AUTO, CAN_SYSCLK_40M);
// Setup Transmit and Receive Interrupts
// IOCONN->0xE04-0xE07
DRV_CANFDSPI_GpioModeConfigure(CANFD_CH1, GPIO_MODE_INT, GPIO_MODE_INT);
//CiFOFICON0
DRV_CANFDSPI_ReceiveChannelEventEnable(CANFD_CH1, CAN_RX_FIFO, CAN_RX_FIFO_NOT_EMPTY_EVENT);
//CiINT->0x1C
DRV_CANFDSPI_ModuleEventEnable(CANFD_CH1, CAN_RX_EVENT);
// Select Normal Mode
//CiCON->0x00-0x03
DRV_CANFDSPI_OperationModeSelect(CANFD_CH1, CAN_NORMAL_MODE);
// DRV_CANFDSPI_OperationModeSelect(CANFD_CH1, CAN_INTERNAL_LOOPBACK_MODE);
}
2.5 CAN收发函数
先将数据打包,然后发送
CAN接收数据,这里用的是查询方式,先查询MCP2518FD INT引脚状态变化,如有变化,接收数据,其实,这个方法,很容易应用中断方式来接收。
数据接收的过程在 int8_t MCP251XFD_canfd_rcv_poll(void) 这个函数完成。
3 移植大概思路
3.1 先从官网下载驱动代码
工程路径:MCP2518FD_STM32F103C8T6\Driver\canfdspi
3.2 编写STM32F103 SPI函数
工程路径:MCP2518FD_STM32F103C8T6\Driver
SPI_mcp2518fd.c 和 SPI_mcp2518fd.h
使用IO模拟SPI,并把SPI驱动连接到 drv_canfdspi_api.c 里的函数
int8_t DRV_SPI_TransferData(uint8_t spiSlaveDeviceIndex, uint8_t *SpiTxData, uint8_t *SpiRxData, uint16_t spiTransferSize)
3.3 编写MCP2518FD 初始化程序
工程路径:MCP2518FD_STM32F103C8T6\User
Canfd.c 和 canfd.h
这个文件完成对MCP2518FD初始化,时钟配置,工作模式等等,都在这个函数完成:
void MCP251xFD_canfd_cfg_init(CAN_BITTIME_SETUP baud)
当然这个文件其它函数也是常用的,比如
int8_t MCP251xFD_can_transmit_msg(CANFDSPI_MODULE_ID index,CANFD_TX_MSG *tx_msg)
这个就是数据发送的。
3.4 发送数据打包 和 接收数据过程
打包数据:
接收数据,通过查询方式接收。