对于MCP2515 DataSheet还不太清楚的话,可以看看上一篇针对DataSheet有些说明~
整理一下要如何驱动MCP2515来实现CANBus通讯:
使用SPI协定驱动初始化2.1 设定ID
2.2 设定Mask
2.3 设定Filter
2.4 中断传送与接收透过USART将资料显示出来
.IOC设置
同样得先将PA5设为Reset_State,这样SPI1才可以开启!


实作
首先我们需要宣告MCP2515当中各暂存器的位置与指令等等,这边我都使用Buffer0所以只定义相关的位置,如果要使用其他的要记得再定义。Configuration Registers#define CANSTAT 0x0E#define CANCTRL 0x0F#define BFPCTRL 0x0C#define TEC 0x1C#define REC 0x1D#define CNF3 0x28#define CNF2 0x29#define CNF1 0x2A#define CANINTE 0x2B#define CANINTF 0x2C#define EFLG 0x2D#define TXRTSCTRL 0x0D
Receive Filters#define RXF0SIDH 0x00#define RXF0SIDL 0x01#define RXF0EID8 0x02#define RXF0EID0 0x03#define RXF1SIDH 0x04#define RXF1SIDL 0x05#define RXF1EID8 0x06#define RXF1EID0 0x07#define RXF2SIDH 0x08#define RXF2SIDL 0x09#define RXF2EID8 0x0A#define RXF2EID0 0x0B#define RXF3SIDH 0x10#define RXF3SIDL 0x11#define RXF3EID8 0x12#define RXF3EID0 0x13
Receive Masks#define RXM0SIDH 0x20#define RXM0SIDL 0x21#define RXM0EID8 0x22#define RXM0EID0 0x23#define RXM1SIDH 0x24#define RXM1SIDL 0x25#define RXM1EID8 0x26#define RXM1EID0 0x27
TX Buffer 0#define TXB0CTRL 0x30#define TXB0SIDH 0x31#define TXB0SIDL 0x32#define TXB0EID8 0x33#define TXB0EID0 0x34#define TXB0DLC 0x35#define TXB0D0 0x36#define TXB0D1 0x37#define TXB0D2 0x38#define TXB0D3 0x39#define TXB0D4 0x3A#define TXB0D5 0x3B#define TXB0D6 0x3C#define TXB0D7 0x3D
Rx Buffer 0#define RXB0CTRL 0x60#define RXB0SIDH 0x61#define RXB0SIDL 0x62#define RXB0EID8 0x63#define RXB0EID0 0x64#define RXB0DLC 0x65#define RXB0D0 0x66#define RXB0D1 0x67#define RXB0D2 0x68#define RXB0D3 0x69#define RXB0D4 0x6A#define RXB0D5 0x6B#define RXB0D6 0x6C#define RXB0D7 0x6D
Bit Timing/* CNF1 */#define SJW_1TQ 0x40#define SJW_2TQ 0x80#define SJW_3TQ 0x90#define SJW_4TQ 0xC0/* CNF2 */#define BTLMODE_CNF3 0x80#define BTLMODE_PH1_IPT 0x00#define SMPL_3X 0x40#define SMPL_1X 0x00#define PHSEG1_8TQ 0x38#define PHSEG1_7TQ 0x30#define PHSEG1_6TQ 0x28#define PHSEG1_5TQ 0x20#define PHSEG1_4TQ 0x18#define PHSEG1_3TQ 0x10#define PHSEG1_2TQ 0x08#define PHSEG1_1TQ 0x00#define PRSEG_8TQ 0x07#define PRSEG_7TQ 0x06#define PRSEG_6TQ 0x05#define PRSEG_5TQ 0x04#define PRSEG_4TQ 0x03#define PRSEG_3TQ 0x02#define PRSEG_2TQ 0x01#define PRSEG_1TQ 0x00/* CNF3 */#define PHSEG2_8TQ 0x07#define PHSEG2_7TQ 0x06#define PHSEG2_6TQ 0x05#define PHSEG2_5TQ 0x04#define PHSEG2_4TQ 0x03#define PHSEG2_3TQ 0x02#define PHSEG2_2TQ 0x01#define PHSEG2_1TQ 0x00#define SOF_ENABLED 0x80#define WAKFIL_ENABLED 0x40#define WAKFIL_DISABLED 0x00
DLC #define DLC_0 0x00 #define DLC_1 0x01 #define DLC_2 0x02 #define DLC_3 0x03 #define DLC_4 0x04 #define DLC_5 0x05 #define DLC_6 0x06 #define DLC_7 0x07 #define DLC_8 0x08
SPI Commands#define CAN_RESET 0xC0#define CAN_READ 0x03#define CAN_WRITE 0x02#define CAN_RTS 0x80#define CAN_RTS_TXB0 0x81#define CAN_RTS_TXB1 0x82#define CAN_RTS_TXB2 0x84#define CAN_RD_STATUS 0xA0#define CAN_BIT_MODIFY 0x05 #define CAN_RX_STATUS 0xB0#define CAN_RD_RX_BUFF 0x90#define CAN_LOAD_TX 0X40
定义好相关的暂存器后接下来就可以来写相关的函数方法了SPI Startstatic void StartSPI(void){ HAL_GPIO_WritePin(GPIOB,GPIO_PIN_6,GPIO_PIN_RESET);}
SPI Stopstatic void SPI_Tx(uint8_t data){ HAL_SPI_Transmit(&hspi1, &data, 1, 10);}
SPI_Txstatic void SPI_Tx(uint8_t data){ HAL_SPI_Transmit(&hspi1, &data, 1, 10);}
SPI_Rxstatic uint8_t SPI_Rx(void){ uint8_t retVal; HAL_SPI_Receive(&hspi1, &retVal, 1, 10); return retVal;}
MCP2515 Write Byte : 传入值为暂存器地址与要写入资料void MCP2515_WriteByte(uint8_t address, uint8_t data){ StartSPI(); SPI_Tx(CAN_WRITE); SPI_Tx(address); SPI_Tx(data); StopSPI();}
MCP2515 Read Byteuint8_t MCP2515_ReadByte (uint8_t address){ uint8_t redata; StartSPI(); SPI_Tx(CAN_READ); SPI_Tx(address); retVal = SPI_Rx(); StopSPI(); return redata;}
MCP2515 Set Config Mode : 由于要设定ID Mask Filter等等需要先进入设置模式int MCP2515_SetConfigMode(void) //配置模式{ MCP2515_WriteByte(CANCTRL, 0x80); uint8_t t = 10; do { //检查CANSTAT是否已进入配置模式 if((MCP2515_ReadByte(CANSTAT) & 0xE0) == 0x80) return 1; t--; } while(t > 0); return 0;}
MCP2515 Set Normal Modeint MCP2515_SetNormalMode(void) //正常模式{ MCP2515_WriteByte(CANCTRL, 0x00); uint8_t t = 10; do { if((MCP2515_ReadByte(CANSTAT) & 0xE0) == 0x00) return 1; t--; } while(t > 0); return 0;}
MCP2515 Set Loop Modeint MCP2515_SetLoopMode(void) //loop mode{ MCP2515_WriteByte(CANCTRL, 0x40); uint8_t t = 10; do { if((MCP2515_ReadByte(CANSTAT) & 0xE0) == 0x40) return 1; t--; } while(t > 0); return 0;}
MCP2515 Resetvoid MCP2515_ResetBus(void){StartSPI();SPI_Tx(CAN_RESET);StopSPI();}
MCP2515 Initialize : 初始化MCP2515,在这边会去将ID、Mask、Filter、TQ、Interrupt都设定好!int MCP2515_InitSys(void){MCP2515_ResetBus(); //ResetMCP2515_SetConfigMode(); //IntoConfig mode//Set TQMCP2515_WriteByte(CNF1,0x03);MCP2515_WriteByte(CNF2, 0x80|PHSEG1_3TQ|PRSEG_1TQ);MCP2515_WriteByte(CNF3, PHSEG2_3TQ);//Set CANBUS IDMCP2515_WriteByte(TXB0SIDH, 0x11);MCP2515_WriteByte(TXB0SIDL, 0xE8); //bit3 EXIDE set 1MCP2515_WriteByte(TXB0EID8, 0xFF);MCP2515_WriteByte(TXB0EID0, 0xFF);//Clear Receive RegisterMCP2515_WriteByte(RXB0SIDH, 0x00);MCP2515_WriteByte(RXB0SIDL, 0x00);MCP2515_WriteByte(RXB0EID8, 0x00);MCP2515_WriteByte(RXB0EID0, 0x00);MCP2515_WriteByte(RXB0CTRL, 0x40);MCP2515_WriteByte(RXB0DLC, DLC_8);//Set FilterMCP2515_WriteByte(RXF0SIDH, 0x11);MCP2515_WriteByte(RXF0SIDL, 0xE8);MCP2515_WriteByte(RXF0EID8, 0xFF);MCP2515_WriteByte(RXF0EID0, 0xFF);//Set MaskMCP2515_WriteByte(RXM0SIDH, 0x00);MCP2515_WriteByte(RXM0SIDL, 0x00);MCP2515_WriteByte(RXM0EID8, 0x00);MCP2515_WriteByte(RXM0EID0, 0x00);//Config InterruptMCP2515_WriteByte(CANINTE, 0x01);MCP2515_WriteByte(CANINTF, 0x00);//Exit ConfigMode//MCP2515_SetNormalMode(); //手上有两组可以设置为正常模式MCP2515_SetLoopMode();}
CAN Bus Send Buffer : 将资料传送出去void CAN_SendBuffer(uint8_t *CANTxBUFF,uint8_t len){uint8_t j,dleaytime,count;count = 0;while(count<len) //Read TXB0CTRL State wait TxREQ clean to 0{dleaytime=0;while((MCP2515_ReadByte(TXB0CTRL)&0x08)&&(dleaytime<50)){HAL_Delay(1);dleaytime++;}for(j=0;j<8;) //Reload data to TXB0D0-D7{MCP2515_WriteByte(TXB0D0+j,CANTxBUFF[count++]);j++;if(count>=len){break;}}}MCP2515_WriteByte(TXB0DLC, 8); //Set DataDLC 这边将资料长度设为8ByteStartSPI();MCP2515_WriteByte(TXB0CTRL, 0x08); //Request Send DataStopSPI();}
CAN Bus Send Buffer : 接收Bus上的资料uint8_t CAN_ReceiveBuffer(uint8_t *CANRXBuff){unsigned char i=0,len=0,temp=0;temp = MCP2515_ReadByte(CANINTF); //Read Interrupt Register Bufferif(temp&0x01) {len=MCP2515_ReadByte(RXB0DLC);while(i<len){CANRXBuff[i] = MCP2515_ReadByte(RXB0D0+i);i++;}}MCP2515_WriteByte(CANINTF, 0x00); //Receive Data Finish , need set 0 to Clear Interrupt Flagreturn len;}
main() :uint8_t CAN_RX_BUFF[8];uint8_t CAB_TX_BUFF[8] = {0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01};//要传送的资料int main(void){ HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART2_UART_Init(); MX_SPI1_Init(); State = MCP2515_InitSys(); //初始化MCP2515 while (1) { CAN_SendBuffer(CAB_TX_BUFF, 8); HAL_Delay(1000); CAN_ReceiveBuffer(CAN_RX_BUFF); HAL_UART_Transmit(&huart2, (uint8_t *)CAN_RX_BUFF, 8, 100); //透过USART显示在电脑上 }}
接着就可以接上电脑实际测试一下接收回来的数据拉!