新唐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));

三、问题

  驱动代码中还有不少不足之处,之后调试时再进行补充修改。

猜你喜欢

转载自blog.csdn.net/horotororensu/article/details/78344727