STM32-14 透过SPI驱动MCP2515实现CAN(下)

对于MCP2515 DataSheet还不太清楚的话,可以看看上一篇针对DataSheet有些说明~

整理一下要如何驱动MCP2515来实现CANBus通讯:

使用SPI协定驱动初始化
2.1 设定ID
2.2 设定Mask
2.3 设定Filter
2.4 中断传送与接收透过USART将资料显示出来

.IOC设置

同样得先将PA5设为Reset_State,这样SPI1才可以开启!
http://img2.58codes.com/2024/201463253jlSJYd8AG.png点选SPI1模式选择Full-Duplex Master,并将下方NSS选择Disable(透过软体控制CS)。下方Configuration中的Data Size选择为8Bits预分频係数选择64使Baud Rate 设为1.25Mbits/s。
http://img2.58codes.com/2024/20146325ugFqU3SQ58.png
http://img2.58codes.com/2024/201463250E4CDwGyjN.png

实作

首先我们需要宣告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 Start
static void StartSPI(void){    HAL_GPIO_WritePin(GPIOB,GPIO_PIN_6,GPIO_PIN_RESET);}
SPI Stop
static void SPI_Tx(uint8_t data){  HAL_SPI_Transmit(&hspi1, &data, 1, 10);}
SPI_Tx
static void SPI_Tx(uint8_t data){  HAL_SPI_Transmit(&hspi1, &data, 1, 10);}
SPI_Rx
static 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 Byte
uint8_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 Mode
int 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 Mode
int 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 Reset
void 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显示在电脑上  }}
接着就可以接上电脑实际测试一下接收回来的数据拉!

关于作者: 网站小编

码农网专注IT技术教程资源分享平台,学习资源下载网站,58码农网包含计算机技术、网站程序源码下载、编程技术论坛、互联网资源下载等产品服务,提供原创、优质、完整内容的专业码农交流分享平台。

热门文章