新唐N32926開發板移植mcp2515(一)

參考文章:http://blog.csdn.net/xiaohuangzhilin/article/details/48968207
     http://www.embedu.org/Column/Column596.htm
     https://wenku.baidu.com/view/65baea51bb68a98271fefaa7.html

硬件環境:快捷N32926開發板

Socket CAN介紹

  Socket CAN是在Linux下CAN協議實現的一種實現方法。Linux下最早使用CAN的方法是基於字符設備來實現的,與之不同的是Socket CAN使用伯克利的Socket接口和Linux網絡協議棧,這種方法使得CAN設備驅動可以通過網絡接口來調用。Socket CAN的接口被設計的儘量接近TCP/IP的協議,讓那些熟悉網絡編程的程序員能夠比較容易的學習和使用。
  使用內核自帶MCP251X驅動,通過spi連接mcp2515模塊,以SocketCAN的方式實現。

一、修改內核驅動代碼

1. 修改inux-2.6.35.4/arch/arm/mach-w55fa92/dev.c文件。

//設置晶振頻率
static struct mcp251x_platform_data mcp251x_info = {
     .oscillator_frequency = 8 * 1000 * 1000,                //頻率8M
};
... ...
static struct spi_board_info w55fa92_spi0_board_info[] __initdata = {
/*
        statement;                         //註釋掉原有內容
*/
    {   
        .modalias = "mcp2515",             //設備名稱
        .bus_num = 0,                      //連接在spi控制器0上
        .chip_select = 0,                  //片選線號
        .platform_data = &mcp251x_info,    //晶振頻率
        .irq = W55FA92_IRQ(2),             //中斷號
        .max_speed_hz =  2 * 1000 * 1000,  //SPI最大速率
        .mode = SPI_MODE_0,                //選擇模式CPOL=0, CPHA=0
    },
};

  最後w55fa92_dev_init函數中的spi_register_board_info會將該spi設備進行註冊。

2.修改linux-2.6.35.4/drivers/net/can/mcp251x.c文件,配置中斷引腳。

  在mcp251x_hw_probe函數中配置中斷引腳

writel(readl(REG_GPIOB_OMD) & ~((1 << 5)), REG_GPIOB_OMD);  // input
writel(readl(REG_GPIOB_PUEN) | ((1 << 5)), REG_GPIOB_PUEN); // pull-up
writel(readl(REG_IRQSRCGPB) & ~(0xC00), REG_IRQSRCGPB);     // GPB5 as nIRQ0 source
writel((readl(REG_IRQENGPB)& ~(0x00200000)) | 0x00000020, REG_IRQENGPB); // falling edge trigge
writel((readl(REG_AIC_SCR1)& ~(0x00C70000)) | 0x00C70000, REG_AIC_SCR1);

enable_irq(W55FA92_IRQ(2));

  在mcp251x_can_ist函數開始讀取中斷狀態,在結束清除中斷。

static irqreturn_t mcp251x_can_ist(int irq, void *dev_id)
{
    u32 src;
    src = readl(REG_IRQTGSRC0);
    ... ...
    writel(src & 0x00200000, REG_IRQTGSRC0);
    return 0;
} 

  在mcp251x_open函數中清中斷。

static int mcp251x_open(struct net_device *net) 
{
    writel((1 << W55FA92_IRQ(2)),  REG_AIC_SCCR); // force clear previous interrupt, if any. 
    writel(readl(REG_IRQTGSRC0) & 0x000001C, REG_IRQTGSRC0); // clear source 

    ret = request_threaded_irq(W55FA92_IRQ(2), NULL, mcp251x_can_ist, 
                            IRQF_TRIGGER_FALLING, DEVICE_NAME, priv);
}

3.修改menuconfig配置文件

  配置spi驅動,除下述選項外,其他不選。

[*] SPIsupport —>
   -*- Utilitiesfor Bitbanging SPImasters
  <*> NuvotonW55FA92 seriesSPI

  配置can驅動,除下述選項外,其他不選。

[*]Networkingsupport->
   <*>CAN bus subsystemsupport->
     <*>RawCAN Protocal
    <*>BroadcastManage CAN Protocal
    CANDevice Drivers->
      <*>Platform CAN driver withNetlink support
        [*]CANbit-timing calculation
      <*>Microchip MCP251x SPI CANcontrollers

  通過以上步驟完成mcp2515驅動移植。驅動註冊成功在內核啓動時會提示mcp251xspi0.0: probed。使用ifconfig -a命令可以看到can0設備。

二、通信測試

1、移植iprout2,測試和使用SocketCAN。

  (1)下載iproute2的源碼http://www.kernel.org/pub/linux/utils/net/iproute2/。我這裏下載的是iproute2 3.6.0。
  (2)解壓iproute2-3.6.0.tar.xz,修改Makefile。

CC = arm-linux-gcc
SUBDIRS=lib ip

  (3)修改完成執行make命令,生成ip命令,拷貝到開發板文件/bin目錄。
  (4)一些ip命令

ifconfig can0 down //關閉can0,以便配置
ip link set can0 up type can bitrate 250000 //設置can0波特率
ip -details link show can0 //顯示can0信息

遇到的問題:
  (1)修改iprout目錄下的ip/ipnetns.c文件,註釋掉所有unshare函數相關的語句。快捷提供的交叉編譯工具中沒有定義這個函數。
  (2)修改iprout目錄下的ip/ip6tunnel.c文件,添加struct in6_addr in6addr_any定義。
  (3)缺少libresolv.so.0等庫,在開發板中添加相應的庫。

2、移植canutils

  (1)下載canutils的最新源碼http://www.pengutronix.de/software/socket-can/download/canutils
  (2)因爲canutils編譯需要libsocketcan庫的支持,需要下載libsocketcan。http://www.pengutronix.de/software/libsocketcan/download/ 筆者下載的是libsocketcan 0.0.9。
  (3)解壓libsocketcan-0.0.9.tar.bz2。執行configure命令。
    ./configure –host=arm-none-linux-gnueabi –prefix=/home/…
  (4)執行make編譯庫;
  (5)執行make install 生成庫。至此,libsocketcan編譯完畢。
  (6)解壓canutils-4.0.6.tar.bz2,執行configure命令。
    ./configure –host=arm-none-linux-gnueabi –prefix=/home/… libsocketcan_LIBS=-lsocketcan LDFLAGS=-L/home/…/socketcan/lib CPPFLAGS=-I/home/…/socketcan/include
  (7)修改完成執行make命令,生成四個目錄,分別拷貝到開發板文件系統的相應目錄。
  (8)使用canutils工具:
    配置CAN的總線通訊波特率:
      canconfig canX bitrate + 波特率
    開啓 / 重啓 / 關閉CAN總線
      canconfig canX start
      canconfig canX restart
      canconfig canX stop
    發送信息
      cansend canX –-identifier=ID + 數據
    接收數據
      candump canX
    使用濾波器接收ID匹配的數據
      candump canX –-filter=ID:mask

3、通信測試

  通過CAN連接兩個開發板。
  開發板A執行命令candump canX
  開發板B執行命令cansend canX –-identifier=0x123 0x12
  開發板A終端收到信息<0x123> [1] 12,表示數據接收成功。

三、SocketCAN編程

  (1)初始化

    SocketCAN 中大部分的數據結構和函數在頭文件 linux/can.h 中進行了定義。 CAN 總線套接字的創建採用標準的網絡套接字操作來完成。網絡套接字在頭文件 sys/socket.h 中定義。

int s;
struct sockaddr_can addr;
struct ifreq ifr;
s = socket(PF_CAN, SOCK_RAW, CAN_RAW);//創建 SocketCAN 套接字
strcpy(ifr.ifr_name, "can0" );
ioctl(s, SIOCGIFINDEX, &ifr);//指定 can0 設備
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
bind(s, (struct sockaddr *)&addr, sizeof(addr)); //將套接字與 can0 綁定

  (2)數據發送

    CAN幀結構體定義

struct can_frame {
canid_t can_id;//CAN 標識符
__u8 can_dlc;  //數據場的長度
__u8 data[8];  //數據
};

    can_id 爲幀的標識符

#define CAN_EFF_FLAG 0x80000000U //擴展幀的標識
#define CAN_RTR_FLAG 0x40000000U //遠程幀的標識
#define CAN_ERR_FLAG 0x20000000U //錯誤幀的標識,用於錯誤檢查

    使用write函數進行數據發送

struct can_frame frame;
frame.can_id = 0x123; //如果爲擴展幀,那麼 frame.can_id = CAN_EFF_FLAG | 0x123;
frame.can_dlc = 1;    //數據長度爲 1
frame.data[0] = 0xAB; //數據內容爲 0xAB
int nbytes = write(s, &frame, sizeof(frame)); //發送數據
if(nbytes != sizeof(frame)) //如果 nbytes 不等於幀長度,就說明發送失敗
printf("Error\n!");

  (3)數據接收

    使用read函數進行數據接收

struct can_frame frame;
int nbytes = read(s, &frame, sizeof(frame));

  (4)過濾規則設置

    在數據接收時,系統可以根據預先設置的過濾規則,實現對報文的過濾。過濾規則使用 can_filter 結構體來實現,定義如下:

struct can_filter {
    canid_t can_id;
    canid_t can_mask;
};

    過濾的規則爲:接收到的數據幀的 can_id & mask == can_id & mask。
    通過這條規則可以在系統中過濾掉所有不符合規則的報文,使得應用程序不需要對無關的報文進行處理。在 can_filter 結構的 can_id 中,符號位 CAN_INV_FILTER 在置位時可以實現 can_id 在執行過濾前的位反轉。
    用戶可以爲每個打開的套接字設置多條獨立的過濾規則,使用方法如下:

struct can_filter rfilter[2];
rfilter[0].can_id = 0x123;
rfilter[0].can_mask = CAN_SFF_MASK; //#define CAN_SFF_MASK 0x000007FFU
rfilter[1].can_id = 0x200;
rfilter[1].can_mask = 0x700;
setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter));//設置規則

    在極端情況下,如果應用程序不需要接收報文,可以禁用過濾規則。這樣的話,原始套接字就會忽略所有接收到的報文。在這種僅僅發送數據的應用中,可以在內核中省略接收隊列,以此減少 CPU 資源的消耗。禁用方法如下:

setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0); //禁用過濾規則

    通過錯誤掩碼可以實現對錯誤幀的過濾, 例如:

can_err_mask_t err_mask = (CAN_ERR_TX_TIMEOUT | CAN_ERR_BUSOFF);
setsockopt(s, SOL_CAN_RAW, CAN_RAW_ERR_FILTER, err_mask, sizeof(err_mask));

  (5)迴環功能設置

    在默認情況下, 本地迴環功能是開啓的,可以使用下面的方法關閉迴環/開啓功能:

int loopback = 0; // 0 表示關閉, 1 表示開啓( 默認)
setsockopt(s, SOL_CAN_RAW, CAN_RAW_LOOPBACK, &loopback, sizeof(loopback));

    在本地迴環功能開啓的情況下,所有的發送幀都會被迴環到與 CAN 總線接口對應的套接字上。 默認情況下,發送 CAN 報文的套接字不想接收自己發送的報文,因此發送套接字上的迴環功能是關閉的。可以在需要的時候改變這一默認行爲:

int ro = 1; // 0 表示關閉( 默認), 1 表示開啓
setsockopt(s, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS, &ro, sizeof(ro));

四、問題

  (1)實際測試過程中循環不停的發送和接收數據,需要在發送之間添加延時。但是該開發板最低延時只能設置爲10ms,可修改內核HZ參數,或者將CAN隊列長度設置爲1000,執行命令echo 1000 >> /sys/class/net/can0/tx_queue_len
  (2)測試發現500Kbps速率下,10s傳輸的數據只有2KB多,由於我用CAN做的語音傳輸,實際速度不能滿足要求。使用示波器測量發現spi的傳輸速度有問題,原因未知,最後也沒有解決。最終採用了GPIO模擬SPI的方式重新編寫了驅動。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章