1. 程式人生 > >SPI,MCP2515除錯總結

SPI,MCP2515除錯總結

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來發送接收,能用示波器看到波形了,但是資料在接收那邊還沒能出來,這個在之後得後續更新原因.

6、本文主要驗證Linux-imx_share\Documentation\spi目錄下spidev_test.c的測試例程,能否正常控制SPI介面。在命令列下輸入ifconfig -a,已經出現can0資訊