iT邦幫忙

0

STM32-14 透過SPI驅動MCP2515實現CAN(下)

  • 分享至 

  • xImage
  •  

對於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. 接著就可以接上電腦實際測試一下接收回來的數據拉!

圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言