上次使用了SocketCAN的方式實現了MCP2515模塊驅動的移植,使用SPI與MCP2515連接。最終雖然能夠實現通信功能,但是通信速度太慢,與實際設置傳輸速度相差太大,無法滿足我傳輸語音的要求。所以使用GPIO模擬SPI的方式,參考單片機的MCP2515代碼,仍然使用開發板的SPI引腳進行接線,重新編寫了驅動。
一、編寫內核驅動代碼
在linux-2.6.35.4/drivers目錄下創建mcp2515文件夾,用於編寫MCP2515驅動。
1、頭文件
linux-2.6.35.4/drivers/mcp2515/mcp2515.h文件代碼:
#ifndef __MCP2515_H
#define __MCP2515_H
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>
struct mcp2515_st {
#define OPEN 1
#define CLOSE 0
int flag;
int status;
spinlock_t lock;
void __iomem *virt;
struct file_operations mcp2515_ops;
dev_t no;
struct miscdevice misc;
};
struct mcp_user_st {
unsigned int can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
unsigned char can_dlc; /* frame payload length in byte (0 .. CAN_MAX_DLEN) */
unsigned char __pad; /* padding */
unsigned char __res0; /* reserved / padding */
unsigned char __res1; /* reserved / padding */
unsigned char data[8] __attribute__((aligned(8)));
};
/* 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
/* Recieve 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
#define RXF4SIDH 0x14
#define RXF4SIDL 0x15
#define RXF4EID8 0x16
#define RXF4EID0 0x17
#define RXF5SIDH 0x18
#define RXF5SIDL 0x19
#define RXF5EID8 0x1A
#define RXF5EID0 0x1B
/* 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
/* Tx Buffer 1 */
#define TXB1CTRL 0x40
#define TXB1SIDH 0x41
#define TXB1SIDL 0x42
#define TXB1EID8 0x43
#define TXB1EID0 0x44
#define TXB1DLC 0x45
#define TXB1D0 0x46
#define TXB1D1 0x47
#define TXB1D2 0x48
#define TXB1D3 0x49
#define TXB1D4 0x4A
#define TXB1D5 0x4B
#define TXB1D6 0x4C
#define TXB1D7 0x4D
/* Tx Buffer 2 */
#define TXB2CTRL 0x50
#define TXB2SIDH 0x51
#define TXB2SIDL 0x52
#define TXB2EID8 0x53
#define TXB2EID0 0x54
#define TXB2DLC 0x55
#define TXB2D0 0x56
#define TXB2D1 0x57
#define TXB2D2 0x58
#define TXB2D3 0x59
#define TXB2D4 0x5A
#define TXB2D5 0x5B
#define TXB2D6 0x5C
#define TXB2D7 0x5D
/* 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
/* Rx Buffer 1 */
#define RXB1CTRL 0x70
#define RXB1SIDH 0x71
#define RXB1SIDL 0x72
#define RXB1EID8 0x73
#define RXB1EID0 0x74
#define RXB1DLC 0x75
#define RXB1D0 0x76
#define RXB1D1 0x77
#define RXB1D2 0x78
#define RXB1D3 0x79
#define RXB1D4 0x7A
#define RXB1D5 0x7B
#define RXB1D6 0x7C
#define RXB1D7 0x7D
/*******************************************************************
*
* * Bit register masks *
*
* *******************************************************************/
/* TXBnCTRL */
#define TXREQ 0x08
#define TXP 0x03
/* RXBnCTRL */
#define RXM 0x60
#define BUKT 0x04
/* CANCTRL */
#define REQOP 0xE0
#define ABAT 0x10
#define OSM 0x08
#define CLKEN 0x04
#define CLKPRE 0x03
/* CANSTAT */
#define REQOP 0xE0
#define ICOD 0x0E
/* CANINTE */
#define RX0IE 0x01
#define RX1IE 0x02
#define TX0IE 0x04
#define TX1IE 0x80
#define TX2IE 0x10
#define ERRIE 0x20
#define WAKIE 0x40
#define MERRE 0x80
/* CANINTF */
#define RX0IF 0x01
#define RX1IF 0x02
#define TX0IF 0x04
#define TX1IF 0x80
#define TX2IF 0x10
#define ERRIF 0x20
#define WAKIF 0x40
#define MERRF 0x80
/* BFPCTRL */
#define B1BFS 0x20
#define B0BFS 0x10
#define B1BFE 0x08
#define B0BFE 0x04
#define B1BFM 0x02
#define B0BFM 0x01
/* CNF1 Masks */
#define SJW 0xC0
#define BRP 0x3F
/* CNF2 Masks */
#define BTLMODE 0x80
#define SAM 0x40
#define PHSEG1 0x38
#define PRSEG 0x07
/* CNF3 Masks */
#define WAKFIL 0x40
#define PHSEG2 0x07
/* TXRTSCTRL Masks */
#define TXB2RTS 0x04
#define TXB1RTS 0x02
#define TXB0RTS 0x01
/*******************************************************************
*
* * Bit Timing Configuration *
*
* *******************************************************************/
/* 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
/*******************************************************************
*
* * Control/Configuration Registers *
*
* *******************************************************************/
/* CANINTE */
#define RX0IE_ENABLED 0x01
#define RX0IE_DISABLED 0x00
#define RX1IE_ENABLED 0x02
#define RX1IE_DISABLED 0x00
#define G_RXIE_ENABLED 0x03
#define G_RXIE_DISABLED 0x00
#define TX0IE_ENABLED 0x04
#define TX0IE_DISABLED 0x00
#define TX1IE_ENABLED 0x08
#define TX2IE_DISABLED 0x00
#define TX2IE_ENABLED 0x10
#define TX2IE_DISABLED 0x00
#define G_TXIE_ENABLED 0x1C
#define G_TXIE_DISABLED 0x00
#define ERRIE_ENABLED 0x20
#define ERRIE_DISABLED 0x00
#define WAKIE_ENABLED 0x40
#define WAKIE_DISABLED 0x00
#define IVRE_ENABLED 0x80
#define IVRE_DISABLED 0x00
/* CANINTF */
#define RX0IF_SET 0x01
#define RX0IF_RESET 0x00
#define RX1IF_SET 0x02
#define RX1IF_RESET 0x00
#define TX0IF_SET 0x04
#define TX0IF_RESET 0x00
#define TX1IF_SET 0x08
#define TX2IF_RESET 0x00
#define TX2IF_SET 0x10
#define TX2IF_RESET 0x00
#define ERRIF_SET 0x20
#define ERRIF_RESET 0x00
#define WAKIF_SET 0x40
#define WAKIF_RESET 0x00
#define IVRF_SET 0x80
#define IVRF_RESET 0x00
/* CANCTRL */
#define REQOP_CONFIG 0x80
#define REQOP_LISTEN 0x60
#define REQOP_LOOPBACK 0x40
#define REQOP_SLEEP 0x20
#define REQOP_NORMAL 0x00
#define ABORT 0x10
#define OSM_ENABLED 0x08
#define CLKOUT_ENABLED 0x04
#define CLKOUT_DISABLED 0x00
#define CLKOUT_PRE_8 0x03
#define CLKOUT_PRE_4 0x02
#define CLKOUT_PRE_2 0x01
#define CLKOUT_PRE_1 0x00
/* CANSTAT */
#define OPMODE_CONFIG 0x80
#define OPMODE_LISTEN 0x60
#define OPMODE_LOOPBACK 0x40
#define OPMODE_SLEEP 0x20
#define OPMODE_NORMAL 0x00
/* RXBnCTRL */
#define RXM_RCV_ALL 0x60
#define RXM_VALID_EXT 0x40
#define RXM_VALID_STD 0x20
#define RXM_VALID_ALL 0x00
#define RXRTR_REMOTE 0x08
#define RXRTR_NO_REMOTE 0x00
#define BUKT_ROLLOVER 0x04
#define BUKT_NO_ROLLOVER 0x00
#define FILHIT0_FLTR_1 0x01
#define FILHIT0_FLTR_0 0x00
#define FILHIT1_FLTR_5 0x05
#define FILHIT1_FLTR_4 0x04
#define FILHIT1_FLTR_3 0x03
#define FILHIT1_FLTR_2 0x02
#define FILHIT1_FLTR_1 0x01
#define FILHIT1_FLTR_0 0x00
/* TXBnCTRL */
#define TXREQ_SET 0x08
#define TXREQ_CLEAR 0x00
#define TXP_HIGHEST 0x03
#define TXP_INTER_HIGH 0x02
#define TXP_INTER_LOW 0x01
#define TXP_LOWEST 0x00
/*******************************************************************
*
* * Register Bit Masks *
*
* *******************************************************************/
#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
/*******************************************************************
*
* * CAN 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
/*******************************************************************
*
* * Miscellaneous *
*
* *******************************************************************/
#define DUMMY_BYTE 0x00
#define TXB0 0x31
#define TXB1 0x41
#define TXB2 0x51
#define RXB0 0x61
#define RXB1 0x71
#define EXIDE_SET 0x08
#define EiiiXIDE_RESET 0x00
extern void MCP2515_Init(int rate);
extern unsigned char CAN_Receive_Buffer(unsigned char *CAN_RX_Buf);
extern void CAN_Send_Buffer(unsigned char *CAN_TX_Buf,unsigned char len);
#endif
2、設備文件
linux-2.6.35.4/drivers/mcp2515/mcp2515_dev.c文件代碼:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/platform_device.h>
#include "mcp2515.h"
void device_release(struct device *dev)
{
}
struct resource mcp_res[] = {
};
struct platform_device mcp_dev = {
.name = "mcp2515",
.id = -1,
.dev = {
.init_name = "mcp2515",
.release = device_release,
},
.num_resources = ARRAY_SIZE(mcp_res),
.resource = mcp_res,
};
static __init int mcp2515_init(void)
{
return platform_device_register(&mcp_dev);
}
static __exit void mcp2515_exit(void)
{
platform_device_unregister(&mcp_dev);
}
module_init(mcp2515_init);
module_exit(mcp2515_exit);
MODULE_LICENSE("GPL");
3、驅動文件
linux-2.6.35.4/drivers/mcp2515/mcp2515_drv.c文件代碼:
/*
* CAN bus driver for Microchip 2515 CAN Controller with GPIO simulate SPI
*
* MCP2515 support and bug fixes by horotororensu
* <[email protected]>
*
* Copyright 2017 keluofeite.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the version 2 of the GNU General Public License
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <mach/w55fa92_reg.h>
#include <linux/slab.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/platform_device.h>
#include "mcp2515.h"
/* MCP2515 Pin definition */
#define MCP2515_SCK0 (writel(readl(REG_GPIOD_DOUT) & ~((1 << 12)), REG_GPIOD_DOUT))
#define MCP2515_SCK1 (writel(readl(REG_GPIOD_DOUT) | ((1 << 12)), REG_GPIOD_DOUT))
#define MCP2515_MISO ((readl(REG_GPIOD_PIN) & (1 << 14)) > 0 ? 1 : 0)
#define MCP2515_MOSI0 (writel(readl(REG_GPIOD_DOUT) & ~((1 << 15)), REG_GPIOD_DOUT))
#define MCP2515_MOSI1 (writel(readl(REG_GPIOD_DOUT) | ((1 << 15)), REG_GPIOD_DOUT))
#define MCP2515_CS0 (writel(readl(REG_GPIOD_DOUT) & ~((1 << 13)), REG_GPIOD_DOUT))
#define MCP2515_CS1 (writel(readl(REG_GPIOD_DOUT) | ((1 << 13)), REG_GPIOD_DOUT))
/* MCP2515 baud rate */
#define CAN_10Kbps 0x31
#define CAN_25Kbps 0x13
#define CAN_50Kbps 0x09
#define CAN_100Kbps 0x04
#define CAN_125Kbps 0x03
#define CAN_250Kbps 0x01
#define CAN_500Kbps 0x00
/* MCP2515 ioctl commands */
#define SETBAUD 0
#define SETMASK 1
#define SETID 3
#define STATE 4
/* set value*/
int baudrate;
/*
* function: SPI_ReadByte
* description: Read 1Byte data by SPI
* arguments: nothing
* return:
* rByte: 1Bytes data read by SPI
*/
unsigned char SPI_ReadByte(void)
{
unsigned char i, rByte = 0;
MCP2515_SCK0;
for(i=0; i<8; i++)
{
MCP2515_SCK1;
rByte <<= 1;
rByte |= MCP2515_MISO;
MCP2515_SCK0;
}
return rByte;
}
/*
* function: SPI_SendByte
* description: SPI send 1Byte data
* arguments:
* dt: send data
* return: nothing
*/
void SPI_SendByte(unsigned char dt)
{
unsigned char i;
for(i=0; i<8; i++)
{
MCP2515_SCK0;
if((dt << i) & 0x80)
MCP2515_MOSI1;
else
MCP2515_MOSI0;
MCP2515_SCK1;
}
MCP2515_SCK0;
}
/*
* function: MCP2515_WriteByte
* description: Write 1Bytes of data to the address register of MCP2515
* through SPI
* arguments:
* addr: MCP2515 register address
* dat: Data to be written
* return: nothing
*/
void MCP2515_WriteByte(unsigned char addr,unsigned char dat)
{
MCP2515_CS0; //置MCP2515的CS爲低電平
SPI_SendByte(CAN_WRITE); //發送寫命令
SPI_SendByte(addr); //發送地址
SPI_SendByte(dat); //寫入數據
MCP2515_CS1; //置MCP2515的CS爲高電平
}
/*
* function: MCP2515_ReadByte
* description: Read 1Bytes of data from the address register of MCP2515
* through SPI
* arguments:
* addr: MCP2515 register address
* return:
* rByte: 1Bytes data read
*/
unsigned char MCP2515_ReadByte(unsigned char addr)
{
unsigned char rByte;
MCP2515_CS0; //置MCP2515的CS爲低電平
SPI_SendByte(CAN_READ); //發送讀命令
SPI_SendByte(addr); //發送地址
rByte = SPI_ReadByte(); //讀取數據
MCP2515_CS1; //置MCP2515的CS爲高電平
return rByte; //返回讀到的一個字節數據
}
/*
* function: MCP2515_Reset
* description: Reset MCP2515 and set as configuration mode
* arguments: nothing
* return: nothing
*/
void MCP2515_Reset(void)
{
MCP2515_CS0; //置MCP2515的CS爲低電平
SPI_SendByte(CAN_RESET); //發送寄存器復位命令
MCP2515_CS1; //置MCP2515的CS爲高電平
}
/*
* function: MCP2515_Init
* description: Initialize MCP2515
* arguments:
* rate: The baud rate of MCP2515
* return: nothing
*/
void MCP2515_Init(int rate)
{
unsigned char temp = 0, cnf1;
MCP2515_Reset(); //發送復位指令軟件復位MCP2515
//配置引腳PD12 PD13 PD15
writel(readl(REG_GPIOD_OMD) | (1 << 12) | (1 << 13) | (1 << 15), REG_GPIOD_OMD); // output
writel(readl(REG_GPIOD_PUEN) | ((1 << 12) | (1 << 13) | (1 << 15)), REG_GPIOD_PUEN); // pull up
writel(readl(REG_GPIOD_DOUT) & ~((1 << 12) | (1 << 13) | (1 << 15)), REG_GPIOD_DOUT); // low
//配置引腳PD14
writel(readl(REG_GPIOD_OMD) & ~((1 << 14)), REG_GPIOD_OMD); // input
writel(readl(REG_GPIOD_PUEN) | ((1 << 14)), REG_GPIOD_PUEN); // pull-up
//設置波特率爲125Kbps
//set CNF1,SJW=00,長度爲1TQ,BRP=49,TQ=[2*(BRP+1)]/Fsoc=2*50/8M=12.5us
printk("MCP2515 set up\n");
MCP2515_WriteByte(CNF1,rate);
//set CNF2,SAM=0,在採樣點對總線進行一次採樣,PHSEG1=(2+1)TQ=3TQ,PRSEG=(0+1)TQ=1TQ
MCP2515_WriteByte(CNF2,0x80|PHSEG1_3TQ|PRSEG_1TQ);
//set CNF3,PHSEG2=(2+1)TQ=3TQ,同時當CANCTRL.CLKEN=1時設定CLKOUT引腳爲時間輸出使能位
MCP2515_WriteByte(CNF3,PHSEG2_3TQ);
MCP2515_WriteByte(TXB0SIDH,0xFF);//發送緩衝器0標準標識符高位
MCP2515_WriteByte(TXB0SIDL,0xE0);//發送緩衝器0標準標識符低位
MCP2515_WriteByte(RXB0SIDH,0x00);//清空接收緩衝器0的標準標識符高位
MCP2515_WriteByte(RXB0SIDL,0x00);//清空接收緩衝器0的標準標識符低位
MCP2515_WriteByte(RXB0CTRL,0x20);//僅僅接收標準標識符的有效信息
MCP2515_WriteByte(RXB0DLC,DLC_8);//設置接收數據的長度爲8個字節
MCP2515_WriteByte(RXF0SIDH,0xFF);//配置驗收濾波寄存器n標準標識符高位
MCP2515_WriteByte(RXF0SIDL,0xE0);//配置驗收濾波寄存器n標準標識符低位
MCP2515_WriteByte(RXM0SIDH,0xFF);//配置驗收屏蔽寄存器n標準標識符高位
MCP2515_WriteByte(RXM0SIDL,0xE0);//配置驗收屏蔽寄存器n標準標識符低位
MCP2515_WriteByte(CANINTF,0x00);//清空CAN中斷標誌寄存器的所有位(必須由MCU清空)
MCP2515_WriteByte(CANINTE,0x01);//配置CAN中斷使能寄存器的接收緩衝器0滿中斷使能,其它位禁止中斷
MCP2515_WriteByte(CANCTRL,REQOP_NORMAL|CLKOUT_ENABLED);//將MCP2515設置爲正常模式,退出配置模式
temp=MCP2515_ReadByte(CANSTAT);//讀取CAN狀態寄存器的值
if(OPMODE_NORMAL!=(temp&&0xE0))//判斷MCP2515是否已經進入正常模式
{
MCP2515_WriteByte(CANCTRL,REQOP_NORMAL|CLKOUT_ENABLED);//再次將MCP2515設置爲正常模式,退出配置模式
} else {
printk("MCP2515 mode: normal mode\n");
}
cnf1 = MCP2515_ReadByte(CNF1);
if (cnf1 == 0x00) {
printk("Baud rate: 500Kbps\n");
baudrate = CAN_500Kbps;
}
if (cnf1 == 0x01) {
printk("Baud rate: 250Kbps\n");
baudrate = CAN_500Kbps;
}
if (cnf1 == 0x03) {
printk("Baud rate: 125Kbps\n");
baudrate = CAN_500Kbps;
}
if (cnf1 == 0x04) {
printk("Baud rate: 100Kbps\n");
baudrate = CAN_500Kbps;
}
if (cnf1 == 0x09) {
printk("Baud rate: 50Kbps\n");
baudrate = CAN_500Kbps;
}
if (cnf1 == 0x13) {
printk("Baud rate: 25Kbps\n");
baudrate = CAN_500Kbps;
}
if (cnf1 == 0x31) {
printk("Baud rate: 10Kbps\n");
baudrate = CAN_500Kbps;
}
}
/*
* function: CAN_Send_Buffer
* description: Send data through CAN
* arguments:
* CAN_TX_Buf: data to be sent
* len: length of data
* return: nothing
*/
void CAN_Send_Buffer(unsigned char *CAN_TX_Buf, unsigned char len)
{
unsigned char j, dly, count;
count = 0;
while(count < len)
{
dly = 0;
while((MCP2515_ReadByte(TXB0CTRL) & 0x08) && (dly < 50))//快速讀某些狀態指令,等待TXREQ標誌清零
{
dly++;
}
for(j = 0;j < 8; )
{
MCP2515_WriteByte(TXB0D0 + j,CAN_TX_Buf[count++]);//將待發送的數據寫入發送緩衝寄存器
j++;
if(count>=len) break;
}
MCP2515_WriteByte(TXB0DLC, j);//將本幀待發送的數據長度寫入發送緩衝器0的發送長度寄存器
MCP2515_CS0;
MCP2515_WriteByte(TXB0CTRL, 0x08);//請求發送報文
MCP2515_CS1;
}
}
/*
* function: CAN_Receive_Buffer
* description: Recieve data from CAN
* arguments:
* CAN_RX_Buf: pointer of data buffer to be received
* return:
* len: length of data received
*/
unsigned char CAN_Receive_Buffer(unsigned char *CAN_RX_Buf)
{
unsigned char i = 0, len = 0, temp = 0;
temp = MCP2515_ReadByte(CANINTF);
if(temp & 0x01)
{
len = MCP2515_ReadByte(RXB0DLC);//讀取接收緩衝器0接收到的數據長度(0~8個字節)
while(i < len)
{
CAN_RX_Buf[i] = MCP2515_ReadByte(RXB0D0 + i);//把CAN接收到的數據放入指定緩衝區
i++;
}
MCP2515_WriteByte(CANINTF,0);//清除中斷標誌位(中斷標誌寄存器必須由MCU清零)
}
return len;
}
/* ======================================================================================= */
static int mcp2515_open(struct inode *inode, struct file *file)
{
return 0;
}
static ssize_t mcp2515_read(struct file *fp, char __user *buffer, size_t count, loff_t *off)
{
struct mcp2515_st *m;
struct mcp_user_st mu;
int ret;
m = fp->private_data;
ret = CAN_Receive_Buffer(mu.data);
mu.can_dlc = ret;
ret = copy_to_user(buffer, &mu, sizeof(mu));
if (ret) {
ret = -EFAULT;
goto copy_error;
}
return mu.can_dlc;
copy_error:
return ret;
}
static ssize_t mcp2515_write(struct file *fp, const char __user *buffer, size_t count, loff_t *off)
{
struct mcp_user_st mu;
struct mcp2515_st *m;
int ret;
m = fp->private_data;
if (count != sizeof(mu)) {
return -EINVAL;
}
ret = copy_from_user(&mu, buffer, count);
if (ret) {
ret = -EFAULT;
goto copy_error;
}
if (count > 8)
CAN_Send_Buffer(mu.data, 8);
else
CAN_Send_Buffer(mu.data, count);
return count;
copy_error:
return ret;
}
int mcp2515_close(struct inode *no, struct file *fp)
{
return 0;
}
long mcp2515_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
{
struct mcp2515_st *m;
int txidh, txidl;
int maskh, maskl;
int h, l;
int temp;
m = fp->private_data;
switch (cmd) {
//set baud rate
case SETBAUD:
MCP2515_Init(arg);
break;
//set mask
case SETMASK:
MCP2515_Reset(); //發送復位指令軟件復位MCP2515
//set CNF1,SJW=00,長度爲1TQ,BRP=49,TQ=[2*(BRP+1)]/Fsoc=2*50/8M=12.5us
printk("Set CAN mask\n");
MCP2515_WriteByte(CNF1, baudrate);
//set CNF2,SAM=0,在採樣點對總線進行一次採樣,PHSEG1=(2+1)TQ=3TQ,PRSEG=(0+1)TQ=1TQ
MCP2515_WriteByte(CNF2,0x80|PHSEG1_3TQ|PRSEG_1TQ);
//set CNF3,PHSEG2=(2+1)TQ=3TQ,同時當CANCTRL.CLKEN=1時設定CLKOUT引腳爲時間輸出使能位
MCP2515_WriteByte(CNF3,PHSEG2_3TQ);
if (arg == 0) {
//取消屏蔽
printk("set mask 0x0\n");
MCP2515_WriteByte(RXM0SIDH, 0x00);//配置驗收屏蔽寄存器n標準標識符高位
MCP2515_WriteByte(RXM0SIDL, 0x00);//配置驗收屏蔽寄存器n標準標識符低位
h = MCP2515_ReadByte(RXM0SIDH);
l = MCP2515_ReadByte(RXM0SIDL);
printk("h = %x, l = %x\n", h, l);
if (h == 0 && l == 0)
printk("Set CAN mask ID: 0x%x\n", 0);
} else {
MCP2515_WriteByte(RXM0SIDH, 0xFF);//配置驗收屏蔽寄存器n標準標識符高位
MCP2515_WriteByte(RXM0SIDL, 0xE0);//配置驗收屏蔽寄存器n標準標識符低位
maskh = (arg & 0xFF00) >> 8;
maskl = arg & 0x00FF;
MCP2515_WriteByte(RXF0SIDH, maskh);//配置驗收濾波寄存器n標準標識符高位
MCP2515_WriteByte(RXF0SIDL, maskl);//配置驗收濾波寄存器n標準標識符低位
h = MCP2515_ReadByte(RXF0SIDH);
l = MCP2515_ReadByte(RXF0SIDL);
if ((h << 8 | l) == arg)
printk("Set CAN mask ID: 0x%x\n", (unsigned int)arg >> 5);
}
MCP2515_WriteByte(CANCTRL,REQOP_NORMAL|CLKOUT_ENABLED);//將MCP2515設置爲正常模式,退出配置模式
temp=MCP2515_ReadByte(CANSTAT);//讀取CAN狀態寄存器的值
printk("temp = %x\n", temp);
if(OPMODE_NORMAL!=(temp&&0xE0))//判斷MCP2515是否已經進入正常模式
{
MCP2515_WriteByte(CANCTRL,REQOP_NORMAL|CLKOUT_ENABLED);//再次將MCP2515設置爲正常模式,退出配置模式
} else {
printk("MCP2515 mode: normal mode\n");
}
break;
//set ID
case SETID:
//set CNF1,SJW=00,長度爲1TQ,BRP=49,TQ=[2*(BRP+1)]/Fsoc=2*50/8M=12.5us
printk("Set CAN ID\n");
MCP2515_WriteByte(CNF1, baudrate);
//set CNF2,SAM=0,在採樣點對總線進行一次採樣,PHSEG1=(2+1)TQ=3TQ,PRSEG=(0+1)TQ=1TQ
MCP2515_WriteByte(CNF2,0x80|PHSEG1_3TQ|PRSEG_1TQ);
//set CNF3,PHSEG2=(2+1)TQ=3TQ,同時當CANCTRL.CLKEN=1時設定CLKOUT引腳爲時間輸出使能位
MCP2515_WriteByte(CNF3,PHSEG2_3TQ);
txidh = (arg & 0xFF00) >> 8;
txidl = arg & 0x00FF;
MCP2515_WriteByte(TXB0SIDH, txidh);//發送緩衝器0標準標識符高位
MCP2515_WriteByte(TXB0SIDL, txidl);//發送緩衝器0標準標識符低位
h = MCP2515_ReadByte(TXB0SIDH);
l = MCP2515_ReadByte(TXB0SIDL);
if ((h << 8 | l) == arg)
printk("Set CAN send ID: 0x%x\n", (unsigned int)arg >> 5);
printk("id = %x\n", (h << 8 | l) >> 5);
break;
case STATE:
temp=MCP2515_ReadByte(EFLG);//讀取CAN狀態寄存器的值
printk("EFLG = %x", temp);
break;
default:
break;
}
return 0;
}
int mcp2515_probe(struct platform_device *pdev)
{
struct mcp2515_st *mcp;
int ret;
mcp = kzalloc(sizeof(*mcp), GFP_KERNEL);
if (!mcp) {
ret = -ENOMEM;
goto alloc_led_error;
}
mcp->flag = CLOSE;
spin_lock_init(&mcp->lock);
mcp->mcp2515_ops.open = mcp2515_open;
mcp->mcp2515_ops.release = mcp2515_close;
mcp->mcp2515_ops.write = mcp2515_write;
mcp->mcp2515_ops.read = mcp2515_read;
mcp->mcp2515_ops.unlocked_ioctl = mcp2515_ioctl;
mcp->misc.minor = MISC_DYNAMIC_MINOR;
mcp->misc.name = pdev->name;
mcp->misc.fops = &mcp->mcp2515_ops;
ret = misc_register(&mcp->misc);
if (ret) {
goto register_misc_error;
}
MCP2515_Init(CAN_500Kbps);
platform_set_drvdata(pdev, mcp);
return 0;
register_misc_error:
kfree(mcp);
alloc_led_error:
return ret;
}
int mcp2515_remove(struct platform_device *pdev)
{
struct mcp2515_st *mcp;
mcp = platform_get_drvdata(pdev);
MCP2515_Reset();
misc_deregister(&mcp->misc);
kfree(mcp);
return 0;
}
struct platform_device_id id_table[] = {
{"mcp2515", 123},
{},
};
struct platform_driver mcp2515_drv = {
.probe = mcp2515_probe,
.remove = mcp2515_remove,
.driver = {
.name = "mcp2515",
},
.id_table = id_table,
};
static __init int mcp2515_init(void)
{
return platform_driver_register(&mcp2515_drv);
}
static __exit void mcp2515_exit(void)
{
platform_driver_unregister(&mcp2515_drv);
}
module_init(mcp2515_init);
module_exit(mcp2515_exit);
MODULE_AUTHOR("horotororensu <[email protected]>");
MODULE_DESCRIPTION("Microchip 2515 CAN driver for N32926");
MODULE_LICENSE("GPL");
4、Makefile
linux-2.6.35.4/drivers/mcp2515/Makefile文件代碼:
LINUX_PATH=/home/horo/arm/prj428/tmp/W55FA92BSP2.6.35_160719/linux-2.6.35.4
obj-$(CONFIG_MCP2515) += mcp2515_drv.o mcp2515_dev.o
all:
#編譯內核模塊要用到內核中的Makefile
make -C $(LINUX_PATH) M=`pwd` modules
clean:
make -C $(LINUX_PATH) M=`pwd` modules clean
linux-2.6.35.4/drivers/Makefile文件添加代碼:
obj-y += mcp2515/
5、編譯內核
配置menuconfig,不選擇內核的spi配置,然後編譯內核。
二、通信編程
定義can_frame結構體
struct can_frame {
unsigned int can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
unsigned char can_dlc; /* frame payload length in byte (0 .. CAN_MAX_DLEN) */
unsigned char __pad; /* padding */
unsigned char __res0; /* reserved / padding */
unsigned char __res1; /* reserved / padding */
unsigned char data[8] __attribute__((aligned(8)));
};
使用write和read函數對/dev/mcp2515文件進行讀寫操作。
write(fd, &frame, sizeof(frame));
read(fd, &rf, sizeof(frame));
三、問題
驅動代碼中還有不少不足之處,之後調試時再進行補充修改。