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

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

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

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

.IOC设置

  1. 同样得先将PA5设为Reset_State,这样SPI1才可以开启!
    https://ithelp.ithome.com.tw/upload/images/20220318/201463253jlSJYd8AG.png
  2. 点选SPI1模式选择Full-Duplex Master,并将下方NSS选择Disable(透过软件控制CS)。下方Configuration中的Data Size选择为8Bits预分频系数选择64使Baud Rate 设为1.25Mbits/s。
    https://ithelp.ithome.com.tw/upload/images/20220318/20146325ugFqU3SQ58.png
    https://ithelp.ithome.com.tw/upload/images/20220318/201463250E4CDwGyjN.png

实作

  1. 首先我们需要宣告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
  1. 定义好相关的暂存器後接下来就可以来写相关的函数方法了
  • 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(); //Reset
	MCP2515_SetConfigMode(); //IntoConfig mode
//	Set TQ
	MCP2515_WriteByte(CNF1,0x03);
	MCP2515_WriteByte(CNF2, 0x80|PHSEG1_3TQ|PRSEG_1TQ);
	MCP2515_WriteByte(CNF3, PHSEG2_3TQ);
//	Set CANBUS ID
	MCP2515_WriteByte(TXB0SIDH, 0x11);
	MCP2515_WriteByte(TXB0SIDL, 0xE8); //bit3 EXIDE set 1
	MCP2515_WriteByte(TXB0EID8, 0xFF);
	MCP2515_WriteByte(TXB0EID0, 0xFF);
//	Clear Receive Register
	MCP2515_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 Filter
	MCP2515_WriteByte(RXF0SIDH, 0x11);
	MCP2515_WriteByte(RXF0SIDL, 0xE8);
	MCP2515_WriteByte(RXF0EID8, 0xFF);
	MCP2515_WriteByte(RXF0EID0, 0xFF);
//	Set Mask
	MCP2515_WriteByte(RXM0SIDH, 0x00);
	MCP2515_WriteByte(RXM0SIDL, 0x00);
	MCP2515_WriteByte(RXM0EID8, 0x00);
	MCP2515_WriteByte(RXM0EID0, 0x00);
//	Config Interrupt
	MCP2515_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 这边将资料长度设为8Byte
	StartSPI();
	MCP2515_WriteByte(TXB0CTRL, 0x08); //Request Send Data
	StopSPI();
}
  • 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 Buffer
	if(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 Flag
	return 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显示在电脑上
  }
}
  1. 接着就可以接上电脑实际测试一下接收回来的数据拉!

<<:  【JavaScript】"9" > "12" //true

>>:  ISO 27001 资讯安全管理系统 【解析】(十六)

[Day19] - Django-REST-Framework Viewsets 介绍

在第一个 API 中我们编写的 View,不知道大家有没有觉得非常简洁呢,好像没有写什麽代码,但是就...

使用MongoDB -- 资料库简易上手

1. 注册及登入mongo database 2. Altas 点这边 之後应该会出现这个画面 点击...

JWT实作(五)(Day9)

讲到权限之前,我们必须谈谈spring security的Filter Chain(过滤器链) Ke...

[从0到1] C#小乳牛 练成基础程序逻辑 Day 8 - 变数 运算子 运算元

变数..装啊~再装啊~ | 一元 二元 ++ -- | 算术->数值 | 逻辑->T/...

[DAY16]模板按钮

TemplateSendMessage - ButtonsTemplate buttons_temp...