mcp2510驱动备份

来源:互联网 发布:淘宝一次性粥杯 编辑:程序博客网 时间:2024/06/14 14:54


MCP2515 CAN控制器的使用

MCP2515最高支持SPI速率为10MHZ,本身CAN速率最高支持1MHZ(传输距离缩短),只支持SPI倆种传输模式 00,11。有些寄存器只能在配置模式修改。MCP2510的发送操作通过

三个发送缓冲器来实现。这三个发送缓冲器各占据14个字节的SRAM。第一字节是控制寄存器TXBNCTRL,该寄存器里的内容设定了信息发送的条件,且给出了信息的发送状态;第二

至第六字节用来存放标准的和扩展的标识符以及仲裁信息;最后八个字节则用来存放待发送的数据信息。

 1.引脚说明:

TXCAN—连接到 CAN 总线的发送输出引脚

RXCAN—连接到 CAN 总线的接收输入引脚

CLKOUT—带可编程预分频器的时钟输出引脚

TX0RTS—发送缓冲器 TXB0 请求发送或通用数字输入引脚。

RX0BF—接收缓冲器 RXB0 的中断引脚或通用数字输出引脚

INT—中断输出引脚

RESET—低电平有效器件复位输入引脚

SCKSPI接口时钟引脚

SISPI接口数据输入引脚

SISPI接口数据输出引脚

CSSPI接口片选输入引脚

2. CAN的特点  

(1)  多主控制 

在总线空闲时,所有的单元都可开始发送消息(多主控制)。 最先访问总线的单元可获得发送权(CSMA/CA 方式)。 

多个单元同时开始发送时,发送高优先级ID消息的单元可获得发送权。 

(2)  消息的发送 

CAN协议中,所有的消息都以固定的格式发送。ID并不是表示发送的目的地址,而是表示访问总线的消息的优先级。

仲裁获胜(被判定为优先级最高)的单元可继续发送消息,仲裁失利的单元则立刻停止发送而进行接收工作。 

(3)  系统的柔软性 

与总线相连的单元没有类似于“地址”的信息。因此在总线上增加单元时,连接在总线上的其它单元的软硬

件及应用层都不需要改变。 

(4)  通信速度 

在同一网络中,所有单元必须设定成统一的通信速度。即使有一个单元的通信速度与其它的不一样,此单元

也会输出错误信号,妨碍整个网络的通信。不同网络间则可以有不同的通信速度。 

(5)  远程数据请求 

可通过发送“遥控帧”  请求其他单元发送数据。 

(6)  错误检测功能·错误通知功能·错误恢复功能 

所有的单元都可以检测错误(错误检测功能)。 

检测出错误的单元会立即同时通知其他所有单元(错误通知功能)。 

正在发送消息的单元一旦检测出错误,会强制结束当前的发送。强制结束发送的单元会不断反复地重新发送

此消息直到成功发送为止(错误恢复功能)。 

(7)  故障封闭 

CAN可以判断出错误的类型是总线上暂时的数据错误(如外部噪声等)还是持续的数据错误(如单元内部

故障、驱动器故障、断线等)。由此功能,当总线上发生持续数据错误时,可将引起此故障的单元从总线上

隔离出去。 

单元始终处于种状态之一。 

(1)  主动错误状态 

主动错误状态是可以正常参加总线通信的状态。 

处于主动错误状态的单元检测出错误时,输出主动错误标志。 

(2)  被动错误状态 

被动错误状态是易引起错误的状态。 

处于被动错误状态的单元虽能参加总线通信,但为不妨碍其它单元通信,接收时不能积极地发送错误通知。

处于被动错误状态的单元即使检测出错误,而其它处于主动错误状态的单元如果没发现错误,整个总线也被

认为是没有错误的。 处于被动错误状态的单元检测出错误时,输出被动错误标志。 

处于被动错误状态的单元在发送结束后不能马上再次开始发送。在开始下次发送前,在间隔帧期间内

必须插入“延迟传送”(8 个位的隐性位。 

(3)  总线关闭态 

总线关闭态是不能参加总线上通信的状态。 

信息的接收和发送均被禁止。 

这些状态依靠发送错误计数和接收错误计数来管理,根据计数值决定进入何种状态。错误状态和计数值的关

系如表及图所示。 

 

8.2  数据帧 

数据帧由个段构成。 

数据帧的构成如图16所示。 

(1)  帧起始 

表示数据帧开始的段。 

(2)  仲裁段 

表示该帧优先级的段。 

(3)  控制段 

表示数据的字节数及保留位的段。 

(4)  数据段 

数据的内容,可发送个字节的数据。 

(5) CRC 段 

检查帧的传输错误的段。 

(6) ACK 段 

表示确认正常接收的段。 

(7)  帧结束 

表示数据帧结束的段

MCP2515有配置模式、正常模式、休眠模式、监听模式、环回模式五种工作模式,通过CANCTRL.REQOP位的设置才能进入相应工作模式。

CAN的初始化只有在配置模式下才能进行:

a.设置CANCTRL,进入CAN配置模式;读CANSTAT状态,判断是否进入配置状态。

b.设置CAN总线的波特率---CNF1/CNF2/CNF3

c.设置TXRTSCTRL(发送请求控制寄存器),确定TX0RTS~TX2RTS引脚的适用状态;

d.设置TXBnCTRL(发送邮箱控制寄存器),确定邮箱0、邮箱1、邮箱2的优先级,以及

ABTF/MLOA/TXERR/TXREQ位清零

报文写入邮箱之前,清零TXBnCTRL.TXREQ,表明发送邮箱无旧报文要发送;

e.设置TXBnSIDH/TXBnSIDL(邮箱n的标准标识符)

TXBnEID8/TXBnEID0(邮箱n的扩展标识符);并将TXBnSIDL.EXIDE置一(扩展符

状态)。

f.设置TXBnDLC(邮箱长度码),确定数据长度(0-8字节)

g设置TXBnDn, 发送邮箱n的数据字节;例TXB0D0TXB0D1........TXB0D7

h.设置CANINTE.TXnIE初始化,以便报文发送时,禁止或使能中断。

I.设置CANCTRL,进入CAN巡回/正常模式;

3、报文发送与接受

在进行MCP2510的“读”操作时,发送完读指令及其地址码之后,仍然需要向MCP2510提供时钟,以接收“读”到的数据。可以通过向MCP2510发送一个“0”字节来实现。

MCP2510支持6SPI命令。现将要用到的读指令、写指令、复位指令、请求发送指令、位修改指令这五种一一cp2510_send_frame()mcp2510_read_frame()两个函数实现了CAN

帧的装载以及CAN帧的读取。因为MCP2510的写指令工作方式是:发送写指令、发送寄存器地址、要写入的值、MCP2510内部寄存器指针加1从而可进行下一次写入。读指令

也是类似。因而,对于CAN帧装载、读取这种需要对MCP2510一段连续寄存器进行读写的操作,我们只需要在最开始发送一次读/写指令和寄存器段起始

地址,然后直接就可以连续读/写一连串的数据。与原来驱动中要反复发读/写指令和地址另外还有mcp2510_hw_reset()函数。这个函数用于将MCP2510RESET

脚拉低、对芯片进行硬复位。在MCP2510芯片跑死的时候,将会用到这个函数。

MCP2515有三个发送缓冲器。每个发送缓冲器占用14字节的SRAM,并映射到器件存

储器中。其中第一个字节TXBnCTRL是与报文缓冲器相关的控制寄存器。该寄存器中的信

息决定了报文在何种条件下发送,并在报文发送时指示其状态。

启动发送:设置TXBnCTRL.TXREQ为一,表明数据准备好,等待总线空闲时发送。发送

的三种方式:

a、利用SPI写命令写寄存器,启动;

b、发送SPIRTS命令,启动;

c、置TXnRTS引脚为低电平,启动;

报文发送函数:

write_MCP2515(TXB0CTRL,0x03); //设置为发送最高优先级

write_MCP2515(TXB0SIDH,0xFF); //SID10--SID3

write_MCP2515(TXB0SIDL,0xE0); //SID2--SID0

write_MCP2515(TXB0DLC,0x08); //发送数据长度为8字节

write_MCP2515(TXB0D0,0x88); //发送的数据88

send_TXB0(); //请求发送

while((read_MCP2515(TXB0CTRL)&0x08)==0x08); //等待发送完毕

4MCP2515的报文接收

MCP2515具有两个全接收缓冲器,应接收不同的缓冲器有两个验收屏蔽寄存器和六个验收滤波寄

存器。除上述专用接收缓冲器外,MCP2515还具有单独的报文集成缓冲器(MessageAssemblyBuffer

MAB),可作为第三个接收缓冲器。报文接收开始时首先检测起始帧,清接收缓存对应的中断标

志,进入接收状态,数据进入报文过滤和屏蔽处理。

报文接收函数:

bit_modify_MCP2515(CANINTF, 0x01,0x00);//接 收 缓 冲 器满 中 断 标 志 位 清 零

read_MCP2515(RXB0SIDH); //接收到的标准标识符高字节

read_MCP2515(RXB0SIDL); //接收到的标准标识符低字节

read_MCP2515(RXB0EID8); //接收到的扩展标识符高字节(RXB0EXID而定)

read_MCP2515(RXB0EID0); //接收到的扩展标识符低字节(RXB0EXID而定)

read_MCP2515(RXB0DLC); //接收到的数据长度

read_MCP2515(RXB0D0); //接收到的数据

。。。。。。数据处理。。。。。。

CAN协议规定:收发报文帧长为10个字节,其数据 

长度为08个字节;工作时通过报文标识符确定总 

线访问优先权,高优先级报文具有较低延迟时间;发 

送期间若丢失仲裁,或由于出错而被破坏的报文可 

自动重发;具有广播和成组报文的能力。

收发缓冲区一般都采用环形FIFO队列,使得 

读写可以并发执行。每一次读写操作都要判断队列 

是否可操作。依据CAN通信协议,CAN控制器每 

次只接收或发送一帧数据,一帧CAN数据是10个 

字节,它由一个字节的标识位、一个字节的RTR和 

DLC位、8个字节的数据区组成。所以我们在每次 

需要内存缓冲区时,直接分配长度为10个字节数据 

块,这10个字节地址是线性连续的。在向缓冲区读 

写数据时,只要判断一次缓冲区可操作否,并获得块 

首地址,就可以一次读写1O个字节的数据,减少了 

重复性条件判断,提高了效率。在CAN设备驱动 

程序里,设立这样一个CANFIFObuffer数据结 

构作为收发数据缓冲区: 

usedbytes代表当前缓冲区有多少字节被占用, 

使用usedbytes可以很方便判断缓冲区满或空, 

usedbytes=0空,usedbytes=CANBUFFERSIZE 

为满。

static ssize_t MCP2515_read(struct file *file,const char *buffer,size_t count,loff_t *ppos) 

       uchar i; 

       uchar receive_data[11]; 

       //使读进程阻塞于此,等待中断发生时唤醒。 

       interruptible_sleep_on(&my_wait);  

       //打印调试信息 

       printk("sleep over\n"); 

       //读取 CAN 数据帧的标识符高位    

receive_data[0]=spi_read_2510(RXB0SIDH);  

// 读取CAN数据帧的标识符低位 

       receive_data[1]=spi_read_2510(RXB0SIDL);  

       //读取CAN数据帧的数据长度 

       receive_data[2]=spi_read_2510(RXB0DLC);  

       //片选信号拉低 

     s3c2410_gpio_setpin(S3C2410_GPB0,0); 

     //延时1s 确保片选完成 

     mdelay(1000); 

//发读命令字 

     spi_tx(cmd_read); 

     //发接收缓冲器地址 

     spi_tx(RXB0D0);  

     for(i=0;i<8;i++) 

     { 

           //提供S3C2410 SPI读数据所需时钟脉冲信号 

spi_tx(0xff); 

           //等待SPI读一字节数据完成 

            while(!(SPI_STAT0&0x1)); 

           //读取CAN数据帧的数据字段 

receive_data[i+3]=SPI_RDAT0;  

// 片选信号拉高 

     s3c2410_gpio_setpin(S3C2410_GPB0,1);  

     //延时1s 

mdelay(1000); 

// 将收到的CAN数据帧传递到用户空间  

copy_to_user(buffer,receive_data,sizeo f(receive_data); printk("receive ok\n"); 

return 0; 



4.can驱动备份

static int dev_open(struct inode *inode,struct file *filp){

   MCP2510_BitModi(0x0F,0xE0,0x80); //设置MCP2510为配置模式

   //注册接收中断服务程序

   request_irq(IRQ_EINT21,proc_Interrupt,IRQF_SAMPLE_RANDOM|IRQF_TRIGGER_LOW,"MCP2510",NULL);

    MCP2510_Write(0x2B,0x03);      //设置中断使能寄存器,使能接收缓冲中断

    MCP2510_CS_L;          //设置接收屏蔽寄存器RXMn都为0

    iowrite8(0x02,(void *)SPTDAT0);

    while(!SPI0_READY);

    iowrite8(0x20,(void *)SPTDAT0);

    while(!SPI0_READY);

    for(tmp=0;tmp<8;tmp++){

     iowrite8(0x0,(void *)SPTDAT0);

     while(!SPI0_READY);

    }

    MCP2510_CS_H;

    //设置接收控制寄存器

    MCP2510_BitModi(TXBnCTRL[0],0x0B,0x03); //设置TXB0CTRL -发送缓冲器0

有最高优先级

    MCP2510_BitModi(TXBnCTRL[1],0x0B,0x02); //设置TXB1CTRL -发送缓冲器1

有次高优先级

    MCP2510_BitModi(TXBnCTRL[2],0x0B,0x01); //设置TXB2CTRL -发送缓冲器2

有低优先级

    

    MCP2510_BitModi(0x60,0x64,0x04); //设置RXB0CTRL -接收缓冲器0控制寄存器-

                  //接收符合滤波器条件的所有带扩展标识符或标准标识符的有效报文

                  //如果RXB0 满, RXB0 接收到的报文将被滚存至RXB1

    MCP2510_BitModi(0x70,0x60,0x00); //设置RXB1CTRL -接收缓冲器 1 控制寄存器-

                  //接收符合滤波器条件的所有带扩展标识符或标准标识符的有效报文

    

    MCP2510_Write(0x2A,0x00);    //设置CNF1

    MCP2510_Write(0x29,0x9E);    //设置CNF2

    MCP2510_Write(0x28,0x03);    //设置CNF3,设置波特率为500Kbps/s(1,7,4,4)

    MCP2510_BitModi(0x0F,0xE0,0x00); //设置MCP2510为正常模式

    spiOpened = 1;

  }

}

static ssize_t dev_read(struct file *filp,char __user *buf,size_t size,loff_t 

*ppos){

 struct CANBus_Message *recMsg;


 recMsg = kcalloc(1,size,GFP_KERNEL);

 kfifo_out(&CanBuff,recMsg,size);

 copy_to_user(buf,(void *)recMsg,size);

}

static ssize_t dev_write(struct file *filp,const char __user *buf,size_t size,

loff_t *ppos){

 unsigned char reg;

 struct CANBus_Message *SendMsg;

 

  copy_from_user((char *)SendMsg,buf,size);

  //读三个发送缓冲器寄存器,以确定哪个发送缓冲器可用。

  for(result=0;result<3;result++){

   if(!(MCP2510_Read(TXBnCTRL[result]) & 0x08))

    break;

  }

  MCP2510_CS_L;

  iowrite8(0x02,(void *)SPTDAT0);

  while(!SPI0_READY);

  iowrite8(TXBnCTRL[result]+1,(void *)SPTDAT0);

  while(!SPI0_READY);

  reg = SendMsg->SID>>3;

  iowrite8(reg,(void *)SPTDAT0);

  while(!SPI0_READY);

  reg = (unsigned char)(SendMsg->SID<<5);

  reg =reg |(SendMsg->EID>>16);

  if(SendMsg->EID)

   reg |= (1<<3);

  iowrite8(reg,(void *)SPTDAT0);

  while(!SPI0_READY);

  reg = (unsigned char)(SendMsg->EID>>8);

  iowrite8(reg,(void *)SPTDAT0);

  while(!SPI0_READY);

  reg = (unsigned char)SendMsg->EID;

  iowrite8(reg,(void *)SPTDAT0);

  while(!SPI0_READY);

  if(SendMsg->Length>8)

   SendMsg->Length = 8;

  iowrite8(SendMsg->Length | (SendMsg->RTR<<6),(void *)SPTDAT0);

 

  for(reg = 0;reg<SendMsg->Length;reg++){

   while(!SPI0_READY);

   iowrite8(SendMsg->Messages[reg],(void *)SPTDAT0);

  }

  while(!SPI0_READY);

  MCP2510_CS_H;

  MCP2510_BitModi(TXBnCTRL[result],0x08,0x08);

  kfree(SendMsg);

  result = 0;

  spin_unlock_irq(spinLock);

 }

 else{

  result = -EBUSY;

 }

 return result;

}



3.1.MCP2515设备结构体

首先需要定义MCP2515的设备结构体,包含MCP2515驱动所需要的相关属性。

struct mcp2515_chip

        {

                canid_t own_id; // CAN ID

                canid_t broadcast_id; // Broadcase ID

                CanBandRate bandrate; // 波特率 

                struct cdev cdev; // 字符设备结构体

                struct spi_device *spi; // SPI 设备结构体

                struct class *class; // Class类 

                struct work_struct irq_work; // 工作队列 

                uint32_t count; // CAN 设备计数

                uint8_t *spi_transfer_buf; // SPI传输缓冲区

                /* SPI输入输出缓冲区 */

                struct can_frame spi_tx_buf[MCP2515_BUF_LEN];

                struct can_frame spi_rx_buf[MCP2515_BUF_LEN];

                uint32_t rxbin; // 接收报文计数

                uint32_t rxbout; // 读报文计数

                uint32_t txbin; // 发送报文计数

                uint32_t txbout; // 发送报文计数

                wait_queue_head_t rwq; // 读报文等待队列头

        };

在整个驱动中我们都需要使用这个设备结构体,所以在驱动注册成功后在probe

函数中要创建这个设备结构体,并且对这个结构体相应的初始化。

static int __devinit mcp2515_probe(struct spi_device *spi)

        {

                struct mcp2515_chip *chip;

                /* 为设备结构体申请空间 */

                chip = kmalloc(sizeof(struct mcp2515_chip), GFP_KERNEL);

                 /* 初始化设备结构体 */

                dev_set_drvdata(&spi->dev, chip); 

                /* 初始化工作队列 */

                INIT_WORK(&chip->irq_work, mcp2515_irq_handler);

                 /* 申请中断 */

                ret = request_irq(IRQ_EINT(1), mcp2515_irq,IRQF_DISABLED | 

IRQF_TRIGGER_FALLING, DEVICE_NAME, spi);

                /* 初始化等待队列头 */

                init_waitqueue_head(&chip->rwq);

                /* 注册设备 */

  

static void mcp251x_hw_tx_frame(struct spi_device *spi, u8 *buf,  

                int len, int tx_buf_idx)  

{  

    struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);  

//如果是2510,还需要指定使用那个发送缓冲区发送数据  

    if (mcp251x_is_2510(spi)) {  

        int i;  

        for (i = 1; i < TXBDAT_OFF + len; i++)  

            mcp251x_write_reg(spi, TXBCTRL(tx_buf_idx) + i,  

                      buf[i]);  

    } else {  

        memcpy(priv->spi_tx_buf, buf, TXBDAT_OFF + len);  

        mcp251x_spi_trans(spi, TXBDAT_OFF + len);  

    }  

}  

static void mcp251x_hw_tx(struct spi_device *spi, struct can_frame *frame,  

              int tx_buf_idx)  

{  

    u32 sid, eid, exide, rtr;  

    u8 buf[SPI_TRANSFER_BUF_LEN];  

    exide = (frame->can_id & CAN_EFF_FLAG) ? 1 : 0; /* Extended ID Enable */  

DBG("打印是否扩展帧\n");  

    if (exide)  

        {  

        sid = (frame->can_id & CAN_EFF_MASK) >> 18;  

        DBG("是扩展帧\n");  

        }  

    else  

        {  

        sid = frame->can_id & CAN_SFF_MASK; /* Standard ID */  

        DBG("是标准帧\n");  

        }  

    eid = frame->can_id & CAN_EFF_MASK; /* Extended ID */  

    rtr = (frame->can_id & CAN_RTR_FLAG) ? 1 : 0; /* Remote transmission */  

//INSTRUCTION_LOAD_TXB(0)=0x40,即装载tx0缓冲器  

    buf[TXBCTRL_OFF] = INSTRUCTION_LOAD_TXB(tx_buf_idx);  

    buf[TXBSIDH_OFF] = sid >> SIDH_SHIFT;  

    buf[TXBSIDL_OFF] = ((sid & SIDL_SID_MASK) << SIDL_SID_SHIFT) |  

        (exide << SIDL_EXIDE_SHIFT) |  

        ((eid >> SIDL_EID_SHIFT) & SIDL_EID_MASK);  

    buf[TXBEID8_OFF] = GET_BYTE(eid, 1);  

    buf[TXBEID0_OFF] = GET_BYTE(eid, 0);  

    buf[TXBDLC_OFF] = (rtr << DLC_RTR_SHIFT) | frame->can_dlc;  

    memcpy(buf + TXBDAT_OFF, frame->data, frame->can_dlc);  

DBG("打印送给spi的数据\n");  

    mcp251x_hw_tx_frame(spi, buf, frame->can_dlc, tx_buf_idx);//装载到tx0

缓冲器  

    mcp251x_write_reg(spi, TXBCTRL(tx_buf_idx), TXBCTRL_TXREQ);//请求发送tx0  

}  

  

static void mcp251x_hw_rx_frame(struct spi_device *spi, u8 *buf,  

                int buf_idx)  

{  

    struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);  

  

//        DBG("mcp251x_hw_rx_frame\n");  

DBG("打印是否是mcp2515\n");  

    if (mcp251x_is_2510(spi)) {  

DBG("是mcp2510\n");  

        int i, len;  

        for (i = 1; i < RXBDAT_OFF; i++)  

            buf[i] = mcp251x_read_reg(spi, RXBCTRL(buf_idx) + i);  

  

        len = get_can_dlc(buf[RXBDLC_OFF] & RXBDLC_LEN_MASK);  

        for (; i < (RXBDAT_OFF + len); i++)  

            buf[i] = mcp251x_read_reg(spi, RXBCTRL(buf_idx) + i);  

    } else {  

DBG("是mcp2515\n");  

//INSTRUCTION_READ_RXB(0)=90,即读取rx0缓冲器  

        priv->spi_tx_buf[RXBCTRL_OFF] = INSTRUCTION_READ_RXB(buf_idx);  

/*SPI_TRANSFER_BUF_LEN=14, 

即spi的发送和接收缓冲区都设为14 

因为mcp2515共返回14个字节,假如是读rxbuf0,则 

RXBOCTRL 

RXB0SIDH 

RXB0SIDL 

RXB0EID8 

RXB0EID0 

RXB0DLC 

RXB0D0 

... 

RXB0D7 

*/  

        mcp251x_spi_trans(spi, SPI_TRANSFER_BUF_LEN);  

        memcpy(buf, priv->spi_rx_buf, SPI_TRANSFER_BUF_LEN);  

    }  

}  


void Init2515(unsigned int IDF)
{
 SPIReset();
 delay_ms(1);
 
 //SPIByteWrite(CANCTRL,0x80);//CAN工作在配置模式
 
    SPIByteWrite(RXM0SIDH,0x00);
 SPIByteWrite(RXM0SIDL,0x00);
 SPIByteWrite(RXF0SIDH,0x00);
 SPIByteWrite(RXF0SIDL,0x00);
 
 SPIByteWrite(RXM1SIDH,0x00);
 SPIByteWrite(RXM1SIDL,0x00);
 SPIByteWrite(RXF2SIDH,0x00);
 SPIByteWrite(RXF2SIDL,0x00);
 
 //设置波特率为10Kbps
 //set CNF1,SJW=00,长度为1TQ,BRP=49,TQ=[2*(BRP+1)]/Fsoc=2*50/8M=12.5us
 SPIByteWrite(CNF1,CAN_125Kbps);
 //set CNF2,SAM=0,在采样点对总线进行一次采样,PHSEG1=(2+1)TQ=3TQ,PRSEG=(0+1)TQ=1TQ
 SPIByteWrite(CNF2,0x80|PHSEG1_3TQ|PRSEG_1TQ);
 //set CNF3,PHSEG2=(2+1)TQ=3TQ,同时当CANCTRL.CLKEN=1时设定CLKOUT引脚为时间输出使能位
 SPIByteWrite(CNF3,PHSEG2_3TQ);
 
 //set TXB0,设置发送缓冲器0的标识符和发送的数据,以及发送的数据长度
  SPIByteWrite(TXB0CTRL,0x03);//设置发送缓冲器0控制寄存器清零TXREQ,发送优先级最高
 //BitModiMcp2515(TXB0CTRL,0x08,0x00);
 
 SPIByteWrite(TXB0SIDH,IDF);//设置发送缓冲器0的标准标识符,待修改***
 SPIByteWrite(TXB0SIDL,0xE0);//用到标准标识符
 SPIByteWrite(TXB0DLC,DLC_8);//设置发送数据的长度为8个字节
 //SPIByteWrite(TXB0D0,0x1E);//有待修改及确定是否使用
 //SPIByteWrite(TXB0D1,0x10);//有待修改及确定是否使用
  
 
  
  //设置接收缓冲器0的标识符和初始化数据
  //SPIByteWrite(RXB0SIDH,0x55);//设置接收缓冲器0的标准标识符,待修改***
  //SPIByteWrite(RXB0SIDL,0xE0);//用到标准标识符
  SPIByteWrite(RXB0CTRL,0x20);//仅仅接收标准标识符的有效信息,FIILHIT0=0表示RXB0 ,采用FILHIT0
  SPIByteWrite(RXB0DLC,DLC_8);//设置接收数据的长度为8个字节
  
    SPIByteWrite(RXF0SIDH,0xE0);//初始化接收滤波器0,待修改***
  SPIByteWrite(RXF0SIDL,0xE0);
  SPIByteWrite(RXM0SIDH,0xFF);//初始化接收屏蔽器0,待修改***
  SPIByteWrite(RXM0SIDL,0xE0);           
  
  //设置接收缓冲器1的标识符和初始化数据
   //SPIByteWrite(RXB1SIDH,IDF); //设置接收缓冲器0的标准标识符,待修改***
     //SPIByteWrite(RXB1SIDL,0xE0);//用到标准标识符
   SPIByteWrite(RXB1CTRL,0x20);//仅仅接收标准标识符的有效信息,FIILHIT0=0表示RXB0 ,采用FILHIT0
   SPIByteWrite(RXB1DLC,DLC_8);//设置接收数据的长度为8个字节
  
   SPIByteWrite(RXF2SIDH,0x00);//初始化接收滤波器2,待修改***
   SPIByteWrite(RXF2SIDL,0xE0);
  
   SPIByteWrite(RXF3SIDH,0x01);//初始化接收滤波器3,待修改***
   SPIByteWrite(RXF3SIDL,0xE0);
  
   SPIByteWrite(RXF4SIDH,0x02);//初始化接收滤波器4,待修改***
   SPIByteWrite(RXF4SIDL,0xE0);
  
   SPIByteWrite(RXF5SIDH,0x56);//初始化接收滤波器5,待修改***
   SPIByteWrite(RXF5SIDL,0xE0);
  
   SPIByteWrite(RXM1SIDH,0xFF);//初始化接收屏蔽器1,待修改***
   SPIByteWrite(RXM1SIDL,0xE0);
  
  //设置接收缓冲器0中断
  SPIByteWrite(CANINTF,0x00);//接收完一次必须对中断标志位清0
  SPIByteWrite(CANINTE,0x02);//接收缓冲器0满中断使能位***修改成01-02
  //设置单触发模式1 使能,报文尝试发送一次;0禁止。如有需要报文会重发
  //BitModiMcp2515(CANCTRL,0x08,0x00);
  SPIByteWrite(CANCTRL,REQOP_NORMAL | CLKOUT_ENABLED);//设置正常模式
  BitModiMcp2515(CANCTRL,0xE7,0x04);
   dummy=SPIByteRead(CANSTAT);
  if (OPMODE_NORMAL != (dummy && 0xE0))
    SPIByteWrite(CANCTRL,REQOP_NORMAL | CLKOUT_ENABLED);//判断进入正常工作模式
   //SPIByteWrite(CANCTRL,0x00);//选定正常工作模式
 
}

在执行mcp2510读操作时,发送完读指令和地址码之后,仍然需要向mcp2510提供时钟,以接受独到的数据,可通过发送一个0字节实现。

5.Socket CAN测试

因为最新版BusyBox对Socket CAN的不支持,所以为了测试和使用Socket CAN,我们需要自己编译Socket CAN的工具。这里介绍两个工具,分别是iproute2 和 canutils。

5.1 iproute2

(1)下载iproute2的最新源码http://www.kernel.org/pub/linux/utils/net/iproute2/ 。笔者下载的是iproute2 3.6.0。

(2)解压iproute2-3.6.0.tar.xz,修改Makefile第33行。
        33 #CC = gcc
        34 CC = arm-none-linux-gnueabi-gcc

(3)因为我们只需要iprout2的ip命令,所以修改Makefile的第42行。
        42 #SUBDIRS=lib ip tc bridge misc netem genl man
        43 SUBDIRS=lib ip

(4)修改完成执行make命令,生成ip命令,拷贝到开发板文件系统目录。

(5)使用ip命令。
        ifconfig can0 down         //关闭can0,以便配置
        ./ip link set can0 up type can bitrate 250000         //设置can0波特率
        ./ip -details link show can0        //显示can0信息

5.2 canutils

Canutils是基于GNU GPLv2许可的开源代码,包括canconfig、canecho、cansend、candump、cansequence五个工具,用于检测和监控Socket CAN接口。

(1)下载canutils的最新源码http://www.pengutronix.de/software/socket-can/download/canutils 。笔者下载的是canutils 4.0.6。
        (2)因为canutils编译需要libsocketcan库的支持,需要下载libsocketcan。http://www.pengutronix.de/software/libsocketcan/download/ 笔者下载的是libsocketcan 0.0.9。
        (3)解压libsocketcan-0.0.9.tar.bz2。执行configure命令。(其中--host是指定交叉工具链,--prefix是指定库的生成位置)
        ./configure --host=arm-none-linux-gnueabi --prefix=/home/linux/workdir/can/tools/libsocketcan
        (4)执行make编译库;
        (5)执行make install 生成库。至此,libsocketcan编译完毕。
        (6)解压canutils-4.0.6.tar.bz2,执行configure命令。(其中--host是指定交叉工具链,--prefix是指定库的生成位置,libsocketcan_LIBS是指定canconfig需要链接的库,LDFLAGS是指定外部库的路径,CPPFLAGS是指定外部头文件的路径)
        ./configure --host=arm-none-linux-gnueabi --prefix=/home/linux/workdir/can/tools/canutils libsocketcan_LIBS=-lsocketcan LDFLAGS=-L/home/linux/workdir/can/tools/socketcan/lib CPPFLAGS=-I/home/linux/workdir/can/tools/socketcan/include
        (7)修改完成执行make命令,生成四个目录,分别拷贝到开发板文件系统的相应目录。
        (8)使用canutils工具。(可以使用[命令 + --help]的方式来查看命令的详细用法,下面只介绍一些常用的指令)

① 配置CAN的总线通讯波特率:
        canconfig canX bitrate + 波特率

② 开启 / 重启 / 关闭CAN总线
        canconfig canX start
        canconfig canX restart
        canconfig canX stop

③ 查看CAN总线状态
        canecho canX

④ 发送信息
        cansend canX –-identifier=ID + 数据

⑤ 接收数据
        candump canX

⑥ 使用滤波器接收ID匹配的数据
        candump canX –-filter=ID:mask