树莓派利用MCP2515实现SPI转CAN通信(C语言)

树莓派利用MCP2515实现SPI转CAN通信(C)

1、主要硬件

树莓派3b、ubuntu16.04、RS485_CAN_HAT、CAN分析仪

2、主要实现方法

之前使用PYTHON库工具直接实现了CAN的通信,但项目上层使用C++,也因为不知道如何去得到PYTHON接收到的数据,套接字的方法还是了解的少,故重新使用C来实现基于BCM2835库的CAN通信协议。

3、主要函数

协议首先需要封装基于BCM2835的SPI的读和写,一次操作一个字节的数据。基于这两个读写,再封装读写命令。
MCP2515的读写之前,都需要向SPI总线发送CAN_READ或者CAN_WRITE命令,再提供地址、数据来完成读写。MCP2515的初始化,首先调用BCM2835来初始化SPI,树莓派3B的spi接口固定,片选引脚可选,按照原理图的设计来选择是CE0还是CE1。
发送缓冲器标准标志符的高低位为16位,前11位对应于CAN的ID,在初始化MCP2515时不对其进行限制。初始化验收屏蔽寄存器为0,可以不启用验收滤波器,接收所有ID的CAN报文。验收屏蔽寄存器与验收滤波器位对应,屏蔽寄存器对应位为1,为启用该位的滤波,滤波位相同接收,详细见MCP2515手册。
发送函数需要提供数据的长度、数据地址、报文ID。接收函数在存储数组的最后一位存放该报文的ID。
#include <stdlib.h>
#include <stdio.h>
#include <bcm2835.h>
#include "mcp2515_new.h"
#include "MCP2515.h"
#define MCP2515_CS 8
unsigned char SPI_ReadByte(void)
{
    return bcm2835_spi_transfer(0xFF);
}
void SPI_SendByte(unsigned char dt)
{
    bcm2835_spi_transfer(dt);
}
void MCP2515_WriteByte(unsigned char addr,unsigned char dat)
{
    bcm2835_gpio_write(MCP2515_CS,LOW);//置MCP2515的CS为低电平
    SPI_SendByte(CAN_WRITE); //发送写命令
    SPI_SendByte(addr); //发送地址
    SPI_SendByte(dat); //写入数据
    bcm2835_gpio_write(MCP2515_CS,HIGH);//置MCP2515的CS为高电平
}
unsigned char MCP2515_ReadByte(unsigned char addr)
{
    unsigned char rByte;
    bcm2835_gpio_write(MCP2515_CS,LOW);             //置MCP2515的CS为低电平
    SPI_SendByte(CAN_READ);     //发送读命令
    SPI_SendByte(addr);         //发送地址
    rByte=SPI_ReadByte();       //读取数据
    bcm2835_gpio_write(MCP2515_CS,HIGH);                //置MCP2515的CS为高电平
    return rByte;               //返回读到的一个字节数据
}
void MCP2515_Reset(void)
{
    bcm2835_gpio_write(MCP2515_CS,LOW); //置MCP2515的CS为低电平
    SPI_SendByte(CAN_RESET); //发送寄存器复位命令
    bcm2835_gpio_write(MCP2515_CS,HIGH); //置MCP2515的CS为高电平
}
int MCP2515_Init(void)
{
    unsigned char temp=0;
    if (!bcm2835_init())
    {
        printf("bcm2835_init failed. Are you running as root??\n");
        return 1;
    }

    if (!bcm2835_spi_begin())
    {
        printf("bcm2835_spi_begin failedg. Are you running as root??\n");
        return 1;
    }
    bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_MSBFIRST);      // The default
    bcm2835_spi_setDataMode(BCM2835_SPI_MODE0);                   // The default 上跳沿或下跳沿的选择
    bcm2835_spi_setClockDivider(BCM2835_SPI_CLOCK_DIVIDER_65536); // The default  时钟速率
    bcm2835_spi_chipSelect(BCM2835_SPI_CS0);                      // The default  片选信号
    bcm2835_spi_setChipSelectPolarity(BCM2835_SPI_CS0, LOW);      // the default

    bcm2835_gpio_fsel(MCP2515_CS, BCM2835_GPIO_FSEL_OUTP);

    MCP2515_Reset();    //发送复位指令软件复位MCP2515
    bcm2835_delay(5);

//设置波特率为125Kbps
//set CNF1,SJW=00,长度为1TQ,BRP=49,TQ=[2*(BRP+1)]/Fsoc=2*50/8M=12.5us
    MCP2515_WriteByte(CNF1,CAN_500Kbps);
//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,0x09);//发送缓冲器0标准标识符高位
   // MCP2515_WriteByte(TXB0SIDL,0x20);//发送缓冲器0标准标识符低位

    MCP2515_WriteByte(RXB0SIDH,0x00);//清空接收缓冲器0的标准标识符高位
    MCP2515_WriteByte(RXB0SIDL,0x00);//清空接收缓冲器0的标准标识符低位
    MCP2515_WriteByte(RXB0CTRL,0x20);//仅仅接收标准标识符的有效信息
    MCP2515_WriteByte(RXB0DLC,DLC_8);//设置接收数据的长度为8个字节

    MCP2515_WriteByte(RXF0SIDH,0x00);//配置验收滤波寄存器n标准标识符高位
    MCP2515_WriteByte(RXF0SIDL,0x00);//配置验收滤波寄存器n标准标识符低位
    MCP2515_WriteByte(RXM0SIDH,0x00);//配置验收屏蔽寄存器n标准标识符高位
    MCP2515_WriteByte(RXM0SIDL,0x00);//配置验收屏蔽寄存器n标准标识符低位

    MCP2515_WriteByte(CANINTF,0x00);//清空CAN中断标志寄存器的所有位(必须由MCU清空)
    MCP2515_WriteByte(CANINTE,0x01);//配置CAN中断使能寄存器的接收缓冲器0满中断使能,其它位禁止中断

    MCP2515_WriteByte(CANCTRL,REQOP_NORMAL|CLKOUT_ENABLED);//将MCP2515设置为环回模式REQOP_LOOPBACK,退出配置模式;normal module:REQOP_NORMAL

    temp=MCP2515_ReadByte(CANSTAT);//读取CAN状态寄存器的值
    if(OPMODE_NORMAL!=(temp&&0xE0))//判断MCP2515是否已经进入环回模式
    {
        MCP2515_WriteByte(CANCTRL,REQOP_NORMAL|CLKOUT_ENABLED);//再次将MCP2515设置为正常模式,退出配置模式
    }
    return 0;
}
void MCP2515_End(void)
{
    bcm2835_spi_end();
    bcm2835_close();
}
void CAN_Send_Buffer(unsigned char *CAN_TX_Buf,unsigned char len, unsigned char msgID)
{
    unsigned char j,dly,count;
    count=0;
    MCP2515_WriteByte(TXB0SIDH, (msgID>>3)&0x1F);
    MCP2515_WriteByte(TXB0SIDL, (msgID<<5)&0xE0);
    while(count<len)
    {
        dly=0;
        while((MCP2515_ReadByte(TXB0CTRL)&0x08) && (dly<50))//快速读某些状态指令,等待TXREQ标志清零
        {
            bcm2835_delay(1);//通过软件延时约nms(不准确)
            dly++;
        }

        for(j=0;j<8;)
        {
            MCP2515_WriteByte(TXB0D0+j,CAN_TX_Buf[count++]);//将待发送的数据写入发送缓冲寄存器
            j++;
            if(count>=len) break;
        }
        MCP2515_WriteByte(TXB0DLC,j);//将本帧待发送的数据长度写入发送缓冲器0的发送长度寄存器
        bcm2835_gpio_write(MCP2515_CS,LOW);
        MCP2515_WriteByte(TXB0CTRL,0x08);//请求发送报文
        bcm2835_gpio_write(MCP2515_CS,HIGH);
    }
}
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++;
        }
    CAN_RX_Buf[i]=(((MCP2515_ReadByte(RXB0SIDH))<<3) | ((MCP2515_ReadByte(RXB0SIDL))>>5));
    }
    MCP2515_WriteByte(CANINTF,0);//清除中断标志位(中断标志寄存器必须由MCU清零)
    return len;
}

4、总结

树莓派基于库的操作相对都是简单的。

5、参考

https://blog.csdn.net/eric_lmy/article/details/51946156
http://www.waveshare.net/wiki/RS485_CAN_HAT
MCP2515数据手册http://www.usr.cn/Uploads/Attach/201012/4cf65dd9de775.pdf
https://blog.csdn.net/tylr2005/article/details/52468433
http://www.geek-workshop.com/thread-33355-1-1.html

©️2020 CSDN 皮肤主题: 大白 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值