控制器局域网总线(CAN,Controller Area Network)是一种用于实时应用的串行通讯协议总线,它可以使用双绞线来传输信号,是世界上应用最广泛的现场总线之一。CAN协议用于汽车中各种不同元件之间的通信,以此取代昂贵而笨重的配电线束。该协议的健壮性使其用途延伸到其他自动化和工业应用。CAN协议的特性包括完整性的串行数据通讯、提供实时支持、传输速率高达1Mb/s、同时具有11位的寻址以及检错能力。
本驱动用到的是Microchip的MCP2515是一款独立控制器局域网络(Controller Area Network, CAN)协议控制器,完全支持CAN V2.0B技术规范。该器件能发送和接收标准和扩展数据帧以及远程帧。MCP2515自带的两个验收屏蔽寄存器和六个验收滤波寄存器可以过滤掉不想要的报文,因此减少了主单片机(MCU)的开销。MCP2515与MCU的连接是通过业界标准串行外设接口(SearialPeripheral Interface, SPI)来实现的。
mcp2515驱动移植于新唐的N32926,内核版本是linux-2.6.35.4。开始移植时途中也碰到了很多问题。下面我徒手解答我碰到的一一问题。
原理图先附上:
一,首先我们配置内核选项(Linux-2.6以上的内核都有自带的mcp251x驱动)
cd linux-2.6.35.4/
make menuconfig
1,选择如下配置:
[*]Networkingsupport->
<*>CAN bus subsystemsupport->
<*>RawCAN Protocal
<*>BroadcastManage CAN Protocal
CANDevice Drivers->
<*>Platform CAN driver withNetlink support
[*]CANbit-timing calculation
<*>Microchip MCP251x SPI CANcontrollers
2,配置spi驱动
因为mcp2515是依通过spi通信协议进行数据交流的配置如下
[*] SPIsupport --->
-*- Utilitiesfor Bitbanging SPImasters
<> GPIO-based bitbanging SPI Master(NEW)
<> Xilinx SPI controller common module(NEW)
<*> NuvotonW55FA92 seriesSPI
<*> SPI transfer with PDMA(NEW)
SPI CS0 pin select (enable CS0pin) --->
SPI CS1 pin select (disable CS1pin) --->
SPI CS0 device select (device for MTDflash) --->
<> DesignWare SPI controller core support(NEW)
*** SPI Protocol Masters***
<> User mode SPI device driver support(NEW)
<> Infineon TLE62X0 (for power switching)(NEW)
以上就是驱动方面的配置。
二,在内核代码增加一个spi的设备
1,根据硬件我们的mcp2515是挂在在spi0的接口中,不同的板子对应的内核实现都不一样
N32926 增加一个spi的是设备是在linux-2.6.35.4/arch/arm/mach-w55fa92/dev.c中
可以其他板子是在 mach-xxx.c中注册一个spi的设备具体可以搜索
spi_register_board_info(structspi_board_info const *info, unsigned n)
函数说明
structspi_board_info 结构体
structspi_board_info {
char modalias[SPI_NAME_SIZE];//定义的spi设备名字
const void *platform_data; //平台数据
void *controller_data;
int irq; //中断号
u32 max_speed_hz;
u16 bus_num;//spi
u16 chip_select;
u8 mode;// 模式选择
};
在 dev.c中定义了
staticstruct spi_board_info w55fa92_spi_board_info[] __initdata{
……….
};
因此我们把mcp2515所需的数据定义到这个数组中
staticstruct mcp251x_platform_data mcp251x_info = {
.oscillator_frequency = 8000000,
};
staticstruct spi_board_info w55fa92_spi_board_info[] __initdata = {
{
.modalias ="mcp2515",
.bus_num = 0,
.chip_select = 0,
.platform_data =&mcp251x_info,
.irq = W55FA92_IRQ(2),
.max_speed_hz = 2*1000*1000,
.mode = SPI_MODE_0,
},
};
最终spi注册
void__init w55fa92_dev_init()
{
platform_add_devices(w55fa92_public_dev,ARRAY_SIZE(w55fa92_public_dev));//注册平台设备
spi_register_board_info(w55fa92_spi_board_info,ARRAY_SIZE(w55fa92_spi_board_info));//注册spi设备
}
貌似驱动方面搞定了?看上去好像很简单。那我们编译一下吧!
编译成功后。
重新启动板子
发现can驱动报错:
mcp251xspi0.0: MCP251x didn't enter in conf mode after reset
mcp251xspi0.0: Probe failed
mcp251xspi0.0: probe failed
这是什么情况?于是我跟了一下代码
/* Here is OK to not lock the MCP, no oneknows about it yet */
if(!mcp251x_hw_probe(spi)) {
dev_info(&spi->dev,"Probe failed\n");
gotoerror_probe;
}
static int mcp251x_hw_probe(structspi_device *spi)
{
intst1, st2;
mcp251x_hw_reset(spi);
st1= mcp251x_read_reg(spi, CANSTAT) & 0xEE;
st2= mcp251x_read_reg(spi, CANCTRL) & 0x17;
dev_dbg(&spi->dev,"CANSTAT 0x%02x CANCTRL 0x%02x\n", st1, st2);
/*Check for power up default values */
return(st1 == 0x80 && st2 == 0x07) ? 1 : 0;
}
static int mcp251x_hw_reset(structspi_device *spi)
{
structmcp251x_priv *priv = dev_get_drvdata(&spi->dev);
intret;
unsignedlong timeout;
priv->spi_tx_buf[0]= INSTRUCTION_RESET;//¸´Î»Ö¸Áî
ret= spi_write(spi, priv->spi_tx_buf, 1);
if(ret) {
dev_err(&spi->dev,"reset failed: ret = %d\n", ret);
return-EIO;
}
/*Wait for reset to finish */
timeout= jiffies + HZ;
mdelay(10);
while((mcp251x_read_reg(spi, CANSTAT) & CANCTRL_REQOP_MASK)
!= CANCTRL_REQOP_CONF) {
schedule();
if(time_after(jiffies, timeout)) {
dev_err(&spi->dev,"MCP251x didn't"
"enter in conf mode after reset\n");
return-EBUSY;
}
}
return0;
}
原来祸根是这里,mcp251x_read_reg读寄存器出错 ,这里读的目的是判断是不是mcp2515属于配置模式因为probe函数会初始化mcp2515 寄存器。即写寄存器。怎么办继续跟咯
static u8 mcp251x_read_reg(structspi_device *spi, uint8_t reg)
{
structmcp251x_priv *priv = dev_get_drvdata(&spi->dev);
u8val = 0;
priv->spi_tx_buf[0]= INSTRUCTION_READ;
priv->spi_tx_buf[1]= reg;
mcp251x_spi_trans(spi,3);
val= priv->spi_rx_buf[2];
returnval;
}
static int mcp251x_spi_trans(structspi_device *spi, int len)
{
structmcp251x_priv *priv = dev_get_drvdata(&spi->dev);
structspi_transfer t = {
.tx_buf= priv->spi_tx_buf,
.rx_buf= priv->spi_rx_buf,
.len= len,
.cs_change= 0,
};
structspi_message m;
intret;
spi_message_add_tail(&t,&m);
ret= spi_sync(spi, &m);
if(ret)
dev_err(&spi->dev,"spi transfer failed: ret = %d\n", ret);
returnret;
}
这下明白了mcp2515 的读写都是调用了mcp251x_spi_trans()函数。而这个函数里实现的是spi协议的 读写。不懂可以参考下面的博文,看了就明白了。
http://blog.csdn.net/eastmoon502136/article/details/7921846
问题发现了是板子的spi有问题?一切的一切又回到开始了。
怎么办?于是我去找了个spi的应用把板子的SPI0_SO with SPI0_SI 进行短接。测试了一下。
待续…………….. 碰到什么问题可以给我留言,或者 发送问题到邮箱 917768817@qq.com
出现这个错误的情况,排除法:
1,保证spi 通信正常(自行下载一个通用spi 应用 进行测试)
2,和can 芯片连接 接收和发送要交叉接