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

  上次使用了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));

三、問題

  驅動代碼中還有不少不足之處,之後調試時再進行補充修改。

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