现在的位置: 首页 > 综合 > 正文

SPI,MCP2515调试总结

2014年08月29日 ⁄ 综合 ⁄ 共 3654字 ⁄ 字号 评论关闭

CPU:EXYNOS 4412
内核:3.0.15
Android:4.0.4

MCP2515是一SPI转CAN总线的芯片,对于CPU来说,CPU就是主设备,SPI就是从设备,所以在内核里,MCP2515就是一个SPI设备,而SPI设备在内核中的结构是对应的,是一个spi控制器对应一个SPI设备,也就是一个spi_master对应一个spi设备,而SPI设备在内核中,尤其是新的内核中(新的内核越来越注重分层和分离的结构),则是在驱动中填充spi_driver驱动信息,在板载中填充spi_board_info信息.

要移植好SPI设备,有几点是要注意的.

1,make menuconfig中,CONFIG_SPI_S3C54XX和SPI_SPIDEV这两个及其相关的选项需要打开,因为第一个是spi控制器(驱动在dev-spi.c里),第二个就是SPI设备.

2,当然作为主角的MCP251x.c的驱动选项页当然要打开,内核中,MCP251X.C这个源码已经包含,在drivers/net/can下,检查下Makefile和Kconfig,然后menuconfig里打开编译选项,这一步大概也不会有什么问题.唯一要注意的是在此源码中,需要改一下DEVICE_NAME这个宏,要跟下面提到的modalias一致,因为这是用来match用的.

3,SPI控制器好了,SPI设备驱动有了,那就还差的是SPI控制器配置,SPI的设备信息等的填充了.在/arch/arm/mach-exynos/下的machine_init函数所在源码文件中添加填充

static struct s3c64xx_spi_csinfo spi0_csi[]=
//这个结构体用来设置片选引脚的
{
[0] = {
.line = //片选引脚
.set_level = gpio_set_value,
.fb_delay = 0x2,
},
};
static struct mcp251x_platform_data mcp251x_info
//这个结构体用来设置振荡器频率的,至于剩下的参数要不要设,需要具体看原理图的接线情况,头文件在
//\include\linux\can\platform\mcp251x.h
static struct spi_board_info spi0_board_info[]=
//这个结构体用来配置SPI设备,也就是对应MCP251X.c这个设备驱动用的,注意modalias要对上.
{
.modalias = ,
//要跟mcp251x.c上的DEVICE_NAME宏对上
.max_speed_hz = ,//CPU支持最高50MHz,但MCP2515芯片最高只支持到10MHZ,这里要好好权衡一下
.bus_num = ,
//SPI总线的选择,CPU提供3条SPI总线
.chip_select = ,//SPI总线上第几个设备的选择
.mode = SPI_MODE_0,//关于CPOL CPHA这2个值得选择,有4种模式 00 01 10 11
.platform_data = &mcp251x_info,//传给mcp251x.c驱动用的参数,就是上面的一个结构体.
.controller_data = &spi0_csi[0],//上面的一结构体,片选所用
.irq = ,//mcp2515芯片有一中断脚连到CPU,把复用后的中断量复制在此
}

这三个结构体填充完后(记得加上相应的头文件),就在static struct platform_device上添加初始化用的设备的变量exynos_device_spi的地址,这是给machine_init函数在添加这三个变量的时候用的,

struct clk *sclk = NULL;
struct clk *prnt = NULL;
struct device *spi0_dev = &exynos_device_spi0.dev;
sclk = clk_get(spi0_dev, "dout_spi0");
if (IS_ERR(sclk))
dev_err(spi0_dev, "failed to get sclk for SPI-0\n");
prnt = clk_get(spi0_dev, "mout_mpll_user");
if (IS_ERR(prnt))
dev_err(spi0_dev, "failed to get prnt\n");
if (clk_set_parent(sclk, prnt))
printk(KERN_ERR "Unable to set parent %s of clock %s.\n",
prnt->name, sclk->name);

clk_set_rate(sclk, 10 * 1000 * 1000);
clk_put(sclk);
clk_put(prnt);

if (!gpio_request(片选引脚宏地址, 片选引脚名)) {
gpio_direction_output(片选引脚宏地址, 1);
s3c_gpio_cfgpin(片选引脚宏地址, S3C_GPIO_SFN(1));
s3c_gpio_setpull(片选引脚宏地址, S3C_GPIO_PULL_UP);
exynos_spi_set_info(0, EXYNOS_SPI_SRCCLK_SCLK,
ARRAY_SIZE(spi0_csi));
}
spi_register_board_info(spi0_board_info, ARRAY_SIZE(spi0_board_info));

而这里主要是注册SPI设备和SPI的时钟配置,配置好这些后正常的话就已经把mcp2515的can0设备驱动移植好了.
如果在这里出了问题,不防先设置好spidev.c的驱动,先调好spi的控制器,再把spidev替换成mcp2515.

4,CAN总线在新版本的内核中,并不是以字符设备的形式出现的,原因内核也有说明,主要是因为内核把CAN设备看成了网络设备(当然由于CAN设备的特殊性,CAN是没有IP地址可言的),需要用到socketcan协议,所以要想运行CAN总线,那么就要再menuconfig上打开can的协议(打开项在Networking support-->CAN bus subsystem support下).

5,内核部分完成后,就到Android部分了,由于不是字符设备,不能对其进行简单的读写操作,所以要用到2个工具,iproute2 和 canutil
iproute2在安卓源码上就有,external/iproute2,
而canutil则在网上可以下到,而是用canutil内的命令,还需要用到libsocketcan的库.

调试总结:
在刚开始调试的时候,spi在内核中的框架并不熟悉,困惑于spidev.c和mcp251x.c之间的关系怎么处理,spi的设备怎么设置时钟,为什么mcp251x.c驱动调出来之后再spi下的目录里没有mcp251x的字符设备可供调用等等.
其实spidev跟mcp251x是处于同一位置的spi设备,都由spi控制器控制,或许是可以在spi0_board_info结构体填充chip_select的时候填上排序的序号,可是最后没有用上,因为spidev不是一个实质的设备.而spi下没有mc251x的字符设备供调用是因为mcp251x的驱动节点在net/can0里,意味着内核把它注册为网络设备.这里在调试的时候最好还是先用spidev来先调好spi控制器,因为spidev的驱动提供了可调用的 spi下的字符设备节点,而在内核中,已经提供了spi设备的测试代码在document/spi下,在编译好后放到开发板上,并将spi发送接收的2条线接上,那么正常的情况下spi就能实现自收自发,这时再去调试mcp251x.c的驱动,就可以不用担心spi在系统上出太大问题了(不过关于时钟这一部分,需要好好地配置).
然后再测试can接口的时候,iproute2这个工具出现过bitrate这个参数无法辨认的情况,其实是在iproute2/ip目录下对iplink_can.c并没有实际调用,归根到底,是在编译android的时候,某些iplink_can.c内某些头文件没有顺利调用导致最后ip命令的参数不支持can,最后吧头文件的include写成了相对地址来调用,就可以了.
而对于canutil这个工具,要顺利编译好,也经过了一些波折,自己用的编译器比较多,环境变量不全,是个挺麻烦的问题,最后自己添加了环境变量,同时无视了伯克利相关的库后,勉强编译成功了cansend candump canecho和cansequence四个工具去调用.
现在,使用ip link set can0 type can bitrate xxx这些命令来设置波特率,用cansend和candump来发送接收,能用示波器看到波形了,但是数据在接收那边还没能出来,这个在之后得后续更新原因.

另感谢http://m.oschina.net/blog/185853 里的这位大神的知识分享

抱歉!评论已关闭.