一、调试缘由:
项目由于需要四路CAN,但是使用的MCU是STM32F105,仅有两路
CAN,于是用上微芯的SPI转CAN芯片--MCP2517。在调试过程中,查阅网上
的网友记录,不详细,在介绍的时候也无关紧要,其实也仅仅需要CAN收发接
口即可;另外也全部都是使用demo,随便移植到STM32,并且移植源码也需
要收费。在进行收发的时候使用SPI轮询方式,大大影响CAN的收发性能。故此
借助官方手册和相关源码进行修正调试。
二、资料获取
在微芯官网上可获取其数据手册和demo例子,选择pic32平台的例子即
可。
MCP2517FD | Microchip Technology
三、源码移植整改
获取的源码解压之后,一直到driver部分即可。
1、SPI驱动平台适配
需要做的就是对SPI文件的驱动文件drv_spi进行平台适配,主要就是收发和
选片选接口,这个直接用STM32的HAL即可,直接使用HAL的轮询接口,但是
如果是RTOS,可以使用信号量+中断收发方式。初始化接口直接为空,因为
cubemx生成的时候已经初始化了。
1 #include <stdint.h>
2 #include <stdbool.h>
3 #include <stddef.h>
4 #include <stdlib.h>
5 #include "drv_spi.h"
6 #include "spi.h"
7 #include "gpio.h"
8 #define DRV_CANFDSPI_INDEX_0 0
9 #define DRV_CANFDSPI_INDEX_1 1
10
11 int8_t DRV_SPI_ChipSelectAssert(uint8_t spiSlaveDeviceIndex, bool assert)
12 {
13 int8_t error = 0;
14
15 // Select Chip Select
16 switch (spiSlaveDeviceIndex) {
17 case DRV_CANFDSPI_INDEX_0:
18 if (assert) HAL_GPIO_WritePin(SPI1_NSS_CAN3_GPIO_Port,SPI1_N SS_CAN3_Pin, GPIO_PIN_RESET);
19 else HAL_GPIO_WritePin(SPI1_NSS_CAN3_GPIO_Port,SPI1_NSS_CAN3 _Pin, GPIO_PIN_SET);
20 error = 0;
21 break;
22 case DRV_CANFDSPI_INDEX_1:
23 if (assert) HAL_GPIO_WritePin(SPI2_NSS_CAN4_GPIO_Port,SPI2_N SS_CAN4_Pin, GPIO_PIN_RESET);
24 else HAL_GPIO_WritePin(SPI2_NSS_CAN4_GPIO_Port,SPI2_NSS_CAN4 _Pin, GPIO_PIN_SET);
25 error = 0;
26 break;
27 default:
28 error = ‐1;
29 break;
30 }
31 return error;
32 }
33
34 void DRV_SPI_Initialize(void)
35 {
36 return;
37 }
38
39 int8_t DRV_SPI_TransferData(uint8_t spiSlaveDeviceIndex, uint8_t *SpiTxD ata, uint8_t *SpiRxData, uint16_t spiTransferSize)
40 {
41 int8_t error = 0;
42 // Assert CS
43 error = DRV_SPI_ChipSelectAssert(spiSlaveDeviceIndex, true);
44 if (error != 0) return error;
45
46 switch (spiSlaveDeviceIndex)
47 {
48 case DRV_CANFDSPI_INDEX_0:
49 HAL_SPI_TransmitReceive(&hspi1,SpiTxData,SpiRxData,spiTransferSi ze,1000);
50 break;
51 case DRV_CANFDSPI_INDEX_1:
52 HAL_SPI_TransmitReceive(&hspi2,SpiTxData,SpiRxData,spiTransferSi ze,1000);
53 break;
54 default:
55 break;
56 }
57 // De‐assert CS
58 error = DRV_SPI_ChipSelectAssert(spiSlaveDeviceIndex, false);
59
60 return error;
61 }
另外特别需要注意的是,SPI的初始化需要根据手册的SPI章节选择对应模
式,频率没必要太高,我这里分频到9M。 这里初始化如下:
1 hspi1.Instance = SPI1;
2 hspi1.Init.Mode = SPI_MODE_MASTER;
3 hspi1.Init.Direction = SPI_DIRECTION_2LINES;
4 hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
5 hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH;
6 hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;
7 hspi1.Init.NSS = SPI_NSS_SOFT;
8 hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
9 hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
10 hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
11 hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
12 hspi1.Init.CRCPolynomial = 10;
13 if (HAL_SPI_Init(&hspi1) != HAL_OK)
14 {
15 Error_Handler();
16 }
对应的cubemx初始化
2、SPI转CAN接口修正
在移植的过程中,源码中的接口实现出现不少错误和警告,由于之前调试没
记录,只能以调试好的文件和源码进行compare比较。需要修改的仅仅是
drv_canfdspi_api.c,包含的相关头文件不需要改动,就是一些寄存器的宏定义
等。
(1)spi收发缓冲需要加大,定义的是96字节,但是实际发送的时候超过96
字节,溢出的部分覆盖了别的外设内存空间导致系统不正常。这个可以更改
SPI_DEFAULT_BUFFER_LENGTH
(2)cREGADDR_CiFIFOCON定义的是80,但是canControlResetValues
数组定义是20个,下标最大是19,80/4 = 20,会编译报错,所以需要 -1。
(3)CAN_BUS_DIAGNOSTIC的结构体成员word数组只有2,但是赋值时
下标溢出,需要注释掉。
(4)switch问题,既然return了,也没必要Break,估计是代码格式问题。
四、优化收发
按照官方以及网上的移植,大多数接收都是轮询调用接收接口,发送也是不
确定发送是否成功。这样一来存在fifo满了导致接收丢包问题,二来无法保证发送结果。
但是引脚上官方是有使用了收发中断引脚输出,这样可以根据IO引脚中断,
快速响应CAN收发,在中断内部处理收发。
这么一来,我们就可以根据三个引脚来进行外部IO中断,如果IO引脚充足可
以使用这三个引脚,对于引脚紧张的只选用INT全局中断输出引脚即可,软件上
配置相关中断使能即可。
MCP2517支持收发高达32个深度,这样也满足高速收发,在初始化的时
候,分配两路FIFO给收发,并且使能接收中断。
1 static void can_mcp2517_config(uint8_t index)
2 {
3 CAN_MODULE_EVENT flags;
4 if(index > DRV_CANFDSPI_INDEX_1)
5 return;
6 CAN_OPERATION_MODE mode;
7 // Reset device
8 DRV_CANFDSPI_Reset(index);
9 mode = DRV_CANFDSPI_OperationModeGet(index);
10 printf("[config]can_bms_mcp2517_config mode:%d %d \r\n",mode,index);
11 // Oscillator Configuration: divide by 10
12 CAN_OSC_CTRL oscCtrl;
13 DRV_CANFDSPI_OscillatorControlObjectReset(&oscCtrl);
14 oscCtrl.ClkOutDivide = OSC_CLKO_DIV10;
15 DRV_CANFDSPI_OscillatorControlSet(index, oscCtrl);
16 // Input/Output Configuration: use nINT0 and nINT1
17 // DRV_CANFDSPI_GpioModeConfigure(index, GPIO_MODE_GPIO, GPIO_MODE_GP
IO);
18 // CAN Configuration: ISO_CRC, enable TEF, enable TXQ
19 CAN_CONFIG config;
20 DRV_CANFDSPI_ConfigureObjectReset(&config);
21 config.IsoCrcEnable = 1;
22 config.StoreInTEF = 1;
23 config.TXQEnable = 1;
24 DRV_CANFDSPI_Configure(index, &config);
25 // Bit Time Configuration: 500K/2M, 80% sample point
26 DRV_CANFDSPI_BitTimeConfigure(index, CAN_1000K_4M,
CAN_SSP_MODE_AUTO, CAN_SYSCLK_20M);27 // TEF Configuration: 12 messages, time stamping enabled
28 CAN_TEF_CONFIG tefConfig;
29 tefConfig.FifoSize = 11;
30 tefConfig.TimeStampEnable = 1;
31 DRV_CANFDSPI_TefConfigure(index, &tefConfig);
32 // TXQ Configuration: 8 messages, 32 byte maximum payload, high prio
rity
33 CAN_TX_QUEUE_CONFIG txqConfig;
34 DRV_CANFDSPI_TransmitQueueConfigureObjectReset(&txqConfig);
35 txqConfig.TxPriority = 1;
36 txqConfig.FifoSize = 7;
37 txqConfig.PayLoadSize = CAN_PLSIZE_32;
38 DRV_CANFDSPI_TransmitQueueConfigure(index, &txqConfig);
39 // FIFO 1: Transmit FIFO; 5 messages, 64 byte maximum payload, low p
riority
40 CAN_TX_FIFO_CONFIG txfConfig;
41 DRV_CANFDSPI_TransmitChannelConfigureObjectReset(&txfConfig);
42 txfConfig.FifoSize = 15;
43 txfConfig.PayLoadSize = CAN_PLSIZE_8;
44 txfConfig.TxPriority = 0;
45 DRV_CANFDSPI_TransmitChannelConfigure(index, CAN_FIFO_CH1, &txfConfi
g);
46 // FIFO 2: Receive FIFO; 16 messages, 64 byte maximum payload, time
stamping enabled
47 CAN_RX_FIFO_CONFIG rxfConfig;
48 rxfConfig.FifoSize = 30;
49 rxfConfig.PayLoadSize = CAN_PLSIZE_8;
50 rxfConfig.RxTimeStampEnable = 1;
51 DRV_CANFDSPI_ReceiveChannelConfigure(index, CAN_FIFO_CH2,
&rxfConfig);
52 // Double Check RAM Usage: 2040 Bytes out of a maximum of 2048 Bytes
‐> OK.
53 // Enable ECC
54 DRV_CANFDSPI_EccEnable(index);
55 // Initialize RAM
56 DRV_CANFDSPI_RamInit(index, 0xff);
57
58 HAL_Delay(1);
59 DRV_CANFDSPI_GpioInterruptPinsOpenDrainConfigure(index,GPIO_PUSH_PUL
L);
60 // DRV_CANFDSPI_TransmitChannelEventEnable(index, CAN_FIFO_CH1, CAN_T
X_FIFO_NOT_FULL_EVENT);61 DRV_CANFDSPI_ReceiveChannelEventEnable(index, CAN_FIFO_CH2, CAN_RX_F
IFO_NOT_EMPTY_EVENT);
62 // DRV_CANFDSPI_ModuleEventEnable(index, CAN_TX_EVENT);//不使能发
送
63 DRV_CANFDSPI_ModuleEventEnable(index, CAN_RX_EVENT);
64 DRV_CANFDSPI_ModuleEventEnable(index, CAN_RX_OVERFLOW_EVENT);
65 HAL_Delay(1);
66 DRV_CANFDSPI_ModuleEventEnableGet(index,&flags);
67 printf("[config]can_bms_mcp2517_config flag:%d 0x%lx \r\n",index,fla
gs);
68 DRV_CANFDSPI_OperationModeSelect(index,CAN_CLASSIC_MODE);
69 HAL_Delay(5);
70
71 mode = DRV_CANFDSPI_OperationModeGet(index);
72 printf("[config]can_bms_mcp2517_config mode~~:%d %d
\r\n",mode,index);
73
74 if(can_mcp2517_exit_flag[index])
75 {
76 can_mcp2517_exit_flag[index] = 0;
77 DRV_CANFDSPI_ModuleEventGet(index,&flags);
78 printf("[config]can_mcp2517_exit_flag11 :%d 0x%lx \r\n",index,fl
ags);
79 DRV_CANFDSPI_ModuleEventClear(index,flags);
80 // DRV_CANFDSPI_ModuleEventClear(index,CAN_TX_EVENT);
81 DRV_CANFDSPI_ModuleEventGet(index,&flags);
82 printf("[config]can_mcp2517_exit_flag22 :%d 0x%lx \r\n",index,fl
ags);
83 }
84
85
86 }
如何确保发送成功呢,我们在数据塞入FIFO完成的时候,开启下FIFO为空
中断,进入了中断,并且该位置位了,这样就能确保发送成功。
1
2 bool can_mcp2517_transmit(uint8_t num,uint32_t id, const uint8_t* data, u
int8_t data_len)
3 {
4 CAN_TX_MSGOBJ txObj;
5 uint8_t txd[MAX_DATA_BYTES];
6 // Initialize ID and Control bits7 txObj.word[0] = 0;
8 txObj.word[1] = 0;
9 txObj.bF.id.SID = ((id >> 18) & 0x7ff); // Standard or Base ID
10 txObj.bF.id.EID = (id & 0x3ffff);
11
12 txObj.bF.ctrl.FDF = 0; // not CAN FD frame
13 txObj.bF.ctrl.BRS = 0; // Switch bit rate
14 txObj.bF.ctrl.IDE = 1; // externd frame
15 txObj.bF.ctrl.RTR = 0; // Not a remote frame request
16 txObj.bF.ctrl.DLC = (data_len & 0x0F);
17 // Sequence: doesn't get transmitted, but will be stored in TEF
18 txObj.bF.ctrl.SEQ = 1;
19
20 // Initialize transmit data
21 uint8_t i;
22 for (i = 0; i < DRV_CANFDSPI_DlcToDataBytes((CAN_DLC)txObj.bF.ctrl.D
LC); i++) {
23 txd = data;
24 }
25 // Check that FIFO is not full
26 CAN_TX_FIFO_EVENT txFlags;
27 bool flush = true;
28 DRV_CANFDSPI_TransmitChannelEventGet(num, CAN_FIFO_CH1, &txFlags);
29 if (txFlags & CAN_TX_FIFO_NOT_FULL_EVENT) {
30 // Load message and transmit
31 DRV_CANFDSPI_TransmitChannelLoad(num, CAN_FIFO_CH1, &txObj, txd,
32 DRV_CANFDSPI_DlcToDataBytes((CAN_DLC)txObj.bF.ctrl.DLC),
flush);
33 can_mcp2517_send_flag[num] = 1;
34 //使能发送FIFO为空中断
35 DRV_CANFDSPI_TransmitChannelEventEnable(num, CAN_FIFO_CH1, CAN_T
X_FIFO_NOT_FULL_EVENT);
36 DRV_CANFDSPI_ModuleEventEnable(num, CAN_TX_EVENT);
37 return TRUE;
38 }
39 else
40 {
41 return FALSE;
42 }
43
44 }
我们需要在外部IO中断处理的是,判断相关中断标志位是否置位,并作出相
对应的处理后,一定要清空标志位。
1 void can_mcp2517_rtx_handler(uint8_t index)
2 {
3 CAN_MODULE_EVENT flags;
4 can_mcp2517_exit_flag[index] = 1;
5
6 DRV_CANFDSPI_ModuleEventGet(index,&flags);
7 //发送成功
8 if(((flags & CAN_TX_EVENT) == CAN_TX_EVENT) &&
(can_mcp2517_send_flag[index]))
9 {
10 can_mcp2517_send_flag[index] = 0;
11 DRV_CANFDSPI_TransmitChannelEventDisable(index,CAN_FIFO_CH1, CAN
_TX_FIFO_EMPTY_EVENT);
12 DRV_CANFDSPI_ModuleEventDisable(index,CAN_TX_EVENT);
13 DRV_CANFDSPI_ModuleEventClear(index,CAN_TX_EVENT);
14 if(index == DRV_CANFDSPI_INDEX_0)
15 {
16 can_info.tx_cb(2);
17 }
18 else if (index == DRV_CANFDSPI_INDEX_1)
19 {
20 can_info.tx_cb(3);
21 }
22 }
23 //有接收
24 if ((flags & CAN_RX_EVENT) == CAN_RX_EVENT)
25 {
26 DRV_CANFDSPI_ModuleEventClear(index,CAN_RX_EVENT);
27 can_mcp2517_receive(index);
28 }
29 if ((flags & CAN_RX_OVERFLOW_EVENT) == CAN_RX_OVERFLOW_EVENT)30 {
31 DRV_CANFDSPI_ModuleEventClear(index,CAN_RX_OVERFLOW_EVENT);
32 can_mcp2517_receive(index);
33
DRV_CANFDSPI_ReceiveChannelEventOverflowClear(index,CAN_FIFO_CH2);
34 }
35 if ((flags & CAN_TEF_EVENT) == CAN_TEF_EVENT)
36 {
37 //清除相关中断标志
38 DRV_CANFDSPI_ModuleEventClear(index,CAN_TEF_EVENT);
39 }
40 if ((flags & CAN_TX_ATTEMPTS_EVENT) == CAN_TX_ATTEMPTS_EVENT)
41 {
42 DRV_CANFDSPI_ModuleEventClear(index,CAN_TX_ATTEMPTS_EVENT);
43 }
44 if ((flags & CAN_SYSTEM_ERROR_EVENT) == CAN_SYSTEM_ERROR_EVENT)
45 {
46 DRV_CANFDSPI_ModuleEventClear(index,CAN_SYSTEM_ERROR_EVENT);
47 }
48 if ((flags & CAN_BUS_ERROR_EVENT) == CAN_BUS_ERROR_EVENT)
49 {
50 DRV_CANFDSPI_ModuleEventClear(index,CAN_BUS_ERROR_EVENT);
51 }
52 if ((flags & CAN_BUS_WAKEUP_EVENT) == CAN_BUS_WAKEUP_EVENT)
53 {
54 DRV_CANFDSPI_ModuleEventClear(index,CAN_BUS_WAKEUP_EVENT);
55 }
56 if ((flags & CAN_RX_INVALID_MESSAGE_EVENT) == CAN_RX_INVALID_MESSAGE
_EVENT)
57 {
58
DRV_CANFDSPI_ModuleEventClear(index,CAN_RX_INVALID_MESSAGE_EVENT);
59 }
60 DRV_CANFDSPI_ModuleEventClear(index,flags);
61 }
特别注意的是,在有接收中断到来的时候,有可能FIFO有好几个深度有数
据,需要判断FIFO为空的条件。
此内容由EEWORLD论坛网友RCSN原创,如需转载或用于商业用途需征得作者同意并注明出处