1. Reasons for debugging:
The project requires four CAN channels, but the MCU used is STM32F105, which only has two channels.
CAN, so we use the Microchip SPI to CAN chip - MCP2517. During the debugging process, we consulted the online
The netizen records are not detailed and are not important when introducing. In fact, only CAN transceiver is needed.
port; in addition, all of them use demo, which can be easily transplanted to STM32, and the transplant source code also needs
When using SPI polling mode for sending and receiving, the sending and receiving performance of CAN is greatly affected.
Correct and debug with the help of official manuals and related source codes.
2. Data Acquisition
The data sheet and demo examples are available on the Microchip official website. Select the example of the pic32 platform.
Can.
MCP2517FD | Microchip Technology
3. Source code transplantation and rectification
After decompressing the obtained source code, go to the driver part.
1. SPI driver platform adaptation
What needs to be done is to adapt the SPI driver file drv_spi to the platform, which is mainly for sending and receiving
Chip selection interface, this can be directly used with STM32 HAL, directly use the polling interface of HAL, but
If it is RTOS, you can use semaphore + interrupt receiving and sending. The initialization interface is directly empty because
Cubemx is initialized when it is generated.
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 }
In addition, it is important to note that the SPI initialization requires selecting the corresponding mode according to the SPI chapter of the manual.
The frequency does not need to be too high. I divide it to 9M. The initialization is as follows:
1hspi1.Instance=SPI1;
2hspi1.Init.Mode=SPI_MODE_MASTER;
3hspi1.Init.Direction=SPI_DIRECTION_2LINES;
4hspi1.Init.DataSize=SPI_DATASIZE_8BIT;
5hspi1.Init.CLKPolarity=SPI_POLARITY_HIGH;
6hspi1.Init.CLKPhase=SPI_PHASE_2EDGE;
7hspi1.Init.NSS=SPI_NSS_SOFT;
8hspi1.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_8;
9hspi1.Init.FirstBit=SPI_FIRSTBIT_MSB;
10hspi1.Init.TIMode=SPI_TIMODE_DISABLE;
11hspi1.Init.CRCCalculation=SPI_CRCCALCULATION_DISABLE;
12hspi1.Init.CRCPolynomial=10;
13if(HAL_SPI_Init(&hspi1)!=HAL_OK)
14{
15Error_Handler();
16}
Corresponding cubemx initialization
2. SPI to CAN interface correction
During the migration process, many errors and warnings appeared in the interface implementation in the source code.
Records can only be compared with debugged files and source code. The only thing that needs to be modified is
drv_canfdspi_api.c, the related header files included do not need to be modified, just the macro definitions of some registers
wait.
(1) The SPI send and receive buffer needs to be increased. It is defined as 96 bytes, but the actual sending exceeds 96 bytes.
Bytes, the overflow part overwrites the memory space of other peripherals, causing the system to malfunction. This can be changed
SPI_DEFAULT_BUFFER_LENGTH
(2) cREGADDR_CiFIFOCON is defined as 80, but canControlResetValues
The array definition is 20, and the maximum subscript is 19. 80/4 = 20, which will result in a compilation error, so -1 is needed.
(3) The word array of the CAN_BUS_DIAGNOSTIC structure member is only 2, but when assigning a value
Subscript overflow, needs to be commented out.
(4) Switch problem: since there is a return, there is no need to break. This is probably a code format problem.
4. Optimize sending and receiving
According to official and online transplants, most of the reception is polling to call the receiving interface, and the sending is also not
Determine whether the transmission is successful. In this way, there is a problem that the FIFO is full and the receiving packet is lost, and the transmission result cannot be guaranteed.
However, the official pins use the transmit and receive interrupt pin output, so that the IO pin interrupt can be used.
Quickly respond to CAN transmission and reception, and process transmission and reception within the interrupt.
In this way, we can use three pins to perform external IO interrupts. If the IO pins are sufficient,
You can use these three pins. If the pins are limited, just use the INT global interrupt output pin.
Just configure the relevant interrupt enable.
MCP2517 supports up to 32 bits of depth for transmission and reception, which also meets the requirements of high-speed transmission and reception.
When the FIFO is configured, two FIFOs are allocated to the transmitter and receiver, and the receive interrupt is enabled.
1static voidcan_mcp2517_config(uint8_t index)
2{
3CAN_MODULE_EVENTflags;
4if(index>DRV_CANFDSPI_INDEX_1)
5return;
6CAN_OPERATION_MODEmode;
7// Reset device
8DRV_CANFDSPI_Reset(index);
9mode=DRV_CANFDSPI_OperationModeGet(index);
10printf("[config]can_bms_mcp2517_config mode:%d %d \r\n",mode,index);
11// Oscillator Configuration: divide by 10
12CAN_OSC_CTRLoscCtrl;
13DRV_CANFDSPI_OscillatorControlObjectReset(&oscCtrl);
14oscCtrl.ClkOutDivide=OSC_CLKO_DIV10;
15DRV_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
19CAN_CONFIGconfig;
20DRV_CANFDSPI_ConfigureObjectReset(&config);
21config.IsoCrcEnable=1;
22config.StoreInTEF=1;
23config.TXQEnable=1;
24DRV_CANFDSPI_Configure(index,&config);
25// Bit Time Configuration: 500K/2M, 80% sample point
26DRV_CANFDSPI_BitTimeConfigure(index,CAN_1000K_4M,
CAN_SSP_MODE_AUTO,CAN_SYSCLK_20M);27// TEF Configuration: 12 messages, time stamping enabled
28CAN_TEF_CONFIGtefConfig;
29tefConfig.FifoSize=11;
30tefConfig.TimeStampEnable=1;
31DRV_CANFDSPI_TefConfigure(index,&tefConfig);
32// TXQ Configuration: 8 messages, 32 byte maximum payload, high prio
rity
33CAN_TX_QUEUE_CONFIGtxqConfig;
34DRV_CANFDSPI_TransmitQueueConfigureObjectReset(&txqConfig);
35txqConfig.TxPriority=1;
36txqConfig.FifoSize=7;
37txqConfig.PayLoadSize=CAN_PLSIZE_32;
38DRV_CANFDSPI_TransmitQueueConfigure(index,&txqConfig);
39// FIFO 1: Transmit FIFO; 5 messages, 64 byte maximum payload, low p
riority
40CAN_TX_FIFO_CONFIGtxfConfig;
41DRV_CANFDSPI_TransmitChannelConfigureObjectReset(&txfConfig);
42txfConfig.FifoSize=15;
43txfConfig.PayLoadSize=CAN_PLSIZE_8;
44txfConfig.TxPriority=0;
45DRV_CANFDSPI_TransmitChannelConfigure(index,CAN_FIFO_CH1,&txfConfi
g);
46// FIFO 2: Receive FIFO; 16 messages, 64 byte maximum payload, time
stamping enabled
47CAN_RX_FIFO_CONFIGrxfConfig;
48rxfConfig.FifoSize=30;
49rxfConfig.PayLoadSize=CAN_PLSIZE_8;
50rxfConfig.RxTimeStampEnable=1;
51DRV_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
54DRV_CANFDSPI_EccEnable(index);
55// Initialize RAM
56DRV_CANFDSPI_RamInit(index,0xff);
57
58HAL_Delay(1);
59DRV_CANFDSPI_GpioInterruptPinsOpenDrainConfigure(index,GPIO_PUSH_PUL
L);
60// DRV_CANFDSPI_TransmitChannelEventEnable(index, CAN_FIFO_CH1, CAN_T
X_FIFO_NOT_FULL_EVENT);61DRV_CANFDSPI_ReceiveChannelEventEnable(index,CAN_FIFO_CH2,CAN_RX_F
IFO_NOT_EMPTY_EVENT);
62// DRV_CANFDSPI_ModuleEventEnable(index, CAN_TX_EVENT);//不使能发
送
63DRV_CANFDSPI_ModuleEventEnable(index,CAN_RX_EVENT);
64DRV_CANFDSPI_ModuleEventEnable(index,CAN_RX_OVERFLOW_EVENT);
65HAL_Delay(1);
66DRV_CANFDSPI_ModuleEventEnableGet(index,&flags);
67printf("[config]can_bms_mcp2517_config flag:%d 0x%lx \r\n",index,fla
gs);
68DRV_CANFDSPI_OperationModeSelect(index,CAN_CLASSIC_MODE);
69HAL_Delay(5);
70
71mode=DRV_CANFDSPI_OperationModeGet(index);
72printf("[config]can_bms_mcp2517_config mode~~:%d %d
\r\n",mode,index);
73
74if(can_mcp2517_exit_flag[index])
75{
76can_mcp2517_exit_flag[index]=0;
77DRV_CANFDSPI_ModuleEventGet(index,&flags);
78printf("[config]can_mcp2517_exit_flag11 :%d 0x%lx \r\n",index,fl
ags);
79DRV_CANFDSPI_ModuleEventClear(index,flags);
80// DRV_CANFDSPI_ModuleEventClear(index,CAN_TX_EVENT);
81DRV_CANFDSPI_ModuleEventGet(index,&flags);
82printf("[config]can_mcp2517_exit_flag22 :%d 0x%lx \r\n",index,fl
ags);
83}
84
85
86}
How to ensure the success of the transmission? When the data is inserted into the FIFO, turn on the FIFO to empty it.
Interrupt, enter the interrupt, and the bit is set, which ensures successful transmission.
1
2boolcan_mcp2517_transmit(uint8_t num,uint32_t id,constuint8_t*data,u
int8_t data_len)
3{
4CAN_TX_MSGOBJtxObj;
5uint8_t txd[MAX_DATA_BYTES];
6// Initialize ID and Control bits7txObj.word[0]=0;
8txObj.word[1]=0;
9txObj.bF.id.SID=((id>>18)&0x7ff);// Standard or Base ID
10txObj.bF.id.EID=(id&0x3ffff);
11
12txObj.bF.ctrl.FDF=0;// not CAN FD frame
13txObj.bF.ctrl.BRS=0;// Switch bit rate
14txObj.bF.ctrl.IDE=1;// externd frame
15txObj.bF.ctrl.RTR=0;// Not a remote frame request
16txObj.bF.ctrl.DLC=(data_len&0x0F);
17// Sequence: doesn't get transmitted, but will be stored in TEF
18txObj.bF.ctrl.SEQ=1;
19
20// Initialize transmit data
21uint8_t i;
22for(i=0;i<DRV_CANFDSPI_DlcToDataBytes((CAN_DLC)txObj.bF.ctrl.D
LC);i++) {
23txd=data;
24}
25// Check that FIFO is not full
26CAN_TX_FIFO_EVENTtxFlags;
27bool flush=true;
28DRV_CANFDSPI_TransmitChannelEventGet(num,CAN_FIFO_CH1,&txFlags);
29if(txFlags&CAN_TX_FIFO_NOT_FULL_EVENT) {
30// Load message and transmit
31DRV_CANFDSPI_TransmitChannelLoad(num,CAN_FIFO_CH1,&txObj,txd,
32DRV_CANFDSPI_DlcToDataBytes((CAN_DLC)txObj.bF.ctrl.DLC),
flush);
33can_mcp2517_send_flag[num]=1;
34//使能发送FIFO为空中断
35DRV_CANFDSPI_TransmitChannelEventEnable(num,CAN_FIFO_CH1,CAN_T
X_FIFO_NOT_FULL_EVENT);
36DRV_CANFDSPI_ModuleEventEnable(num,CAN_TX_EVENT);
37returnTRUE;
38}
39else
40{
41returnFALSE;
42}
43
44}
What we need to do in external IO interrupt processing is to determine whether the relevant interrupt flag is set and make the corresponding
After the corresponding processing, the flag must be cleared.
1voidcan_mcp2517_rtx_handler(uint8_t index)
2{
3CAN_MODULE_EVENTflags;
4can_mcp2517_exit_flag[index]=1;
5
6DRV_CANFDSPI_ModuleEventGet(index,&flags);
7//发送成功
8if(((flags&CAN_TX_EVENT)==CAN_TX_EVENT)&&
(can_mcp2517_send_flag[index]))
9{
10can_mcp2517_send_flag[index]=0;
11DRV_CANFDSPI_TransmitChannelEventDisable(index,CAN_FIFO_CH1,CAN
_TX_FIFO_EMPTY_EVENT);
12DRV_CANFDSPI_ModuleEventDisable(index,CAN_TX_EVENT);
13DRV_CANFDSPI_ModuleEventClear(index,CAN_TX_EVENT);
14if(index==DRV_CANFDSPI_INDEX_0)
15{
16can_info.tx_cb(2);
17}
18else if(index==DRV_CANFDSPI_INDEX_1)
19{
20can_info.tx_cb(3);
21}
22}
23//有接收
24if((flags&CAN_RX_EVENT)==CAN_RX_EVENT)
25{
26DRV_CANFDSPI_ModuleEventClear(index,CAN_RX_EVENT);
27can_mcp2517_receive(index);
28}
29if((flags&CAN_RX_OVERFLOW_EVENT)==CAN_RX_OVERFLOW_EVENT)30{
31DRV_CANFDSPI_ModuleEventClear(index,CAN_RX_OVERFLOW_EVENT);
32can_mcp2517_receive(index);
33
DRV_CANFDSPI_ReceiveChannelEventOverflowClear(index,CAN_FIFO_CH2);
34}
35if((flags&CAN_TEF_EVENT)==CAN_TEF_EVENT)
36{
37//清除相关中断标志
38DRV_CANFDSPI_ModuleEventClear(index,CAN_TEF_EVENT);
39}
40if((flags&CAN_TX_ATTEMPTS_EVENT)==CAN_TX_ATTEMPTS_EVENT)
41{
42DRV_CANFDSPI_ModuleEventClear(index,CAN_TX_ATTEMPTS_EVENT);
43}
44if((flags&CAN_SYSTEM_ERROR_EVENT)==CAN_SYSTEM_ERROR_EVENT)
45{
46DRV_CANFDSPI_ModuleEventClear(index,CAN_SYSTEM_ERROR_EVENT);
47}
48if((flags&CAN_BUS_ERROR_EVENT)==CAN_BUS_ERROR_EVENT)
49{
50DRV_CANFDSPI_ModuleEventClear(index,CAN_BUS_ERROR_EVENT);
51}
52if((flags&CAN_BUS_WAKEUP_EVENT)==CAN_BUS_WAKEUP_EVENT)
53{
54DRV_CANFDSPI_ModuleEventClear(index,CAN_BUS_WAKEUP_EVENT);
55}
56if((flags&CAN_RX_INVALID_MESSAGE_EVENT)==CAN_RX_INVALID_MESSAGE
_EVENT)
57{
58
DRV_CANFDSPI_ModuleEventClear(index,CAN_RX_INVALID_MESSAGE_EVENT);
59}
60DRV_CANFDSPI_ModuleEventClear(index,flags);
61}
It is important to note that when a receive interrupt arrives, the FIFO may have several depths.
According to the conditions, it is necessary to determine whether the FIFO is empty.
This content is originally created by RCSN , a user of EEWORLD forum . If you want to reprint or use it for commercial purposes, you need to obtain the author's consent and indicate the source