首页

Arduino MCP2515 CAN总线接口教程

在这个项目中,我们将学习MCP2515 CAN控制器模块,如何将MCP2515 CAN总线控制器与Arduino连接,最后如何在两个MCP2515 CAN控制器和CAN协议的帮助下实现两个Arduino板之间的通信。

Arduino MCP2515 CAN总线接口图像

简介

简单CAN控制区域网络是一种总线标准,它允许微控制器及其外围设备在不需要主机设备或计算机的情况下进行通信。

CAN协议由罗伯特·博世公司开发,主要用于汽车控制单元及其部件之间的通信。

例如,发动机控制单元是汽车上使用的主要控制装置。该装置连接到许多传感器和执行器,如空气流量,压力,温度,阀门控制,空气控制的马达等。这些模块与控制单元之间的通信是通过CAN总线实现的。

为了对CAN总线、CAN控制器等重要方面有更多的了解,MCP2515 CAN总线控制器模块是非常有帮助的。

还读:spi通信基础知识

MCP2515 CAN总线控制器模块简介

MCP2515 CAN总线控制器是一个简单的模块,支持CAN协议2.0B版本,可以用于1Mbps的通信。为了建立一个完整的通信系统,你将需要两个CAN总线模块。

项目中使用的模块如下图所示。

MCP2515 CAN模块

该模块基于MCP2515 CAN控制器IC和TJA1050 CAN收发IC, MCP2515 IC是一个独立的CAN控制器,具有集成的SPI接口,用于与微控制器通信。

对于TJA1050 IC,它充当MCP2515 CAN控制器IC和物理CAN总线之间的接口。

下图显示了典型MCP2515模块上的组件和引脚。

Arduino MCP2515 CAN总线接口MCP2515 CAN模块组件

MCP2515 CAN总线模块原理图

在看到模块的原理图之前,你需要了解一些关于这两个ic的事情,例如MCP2515和TJA1050。

MCP2515 IC是主控制器,内部由CAN模块、控制逻辑和SPI块三个主要子组件组成。

CAN模块负责CAN总线上的消息收发。控制逻辑通过连接所有块来处理MCP2515的设置和操作。SPI块负责SPI通信接口。

接下来是TJA1050 IC,由于它充当MCP2515 CAN控制器和物理CAN总线之间的接口,该IC负责从控制器获取数据并将其转发到总线上。

下图是MCP2515 CAN模块的原理图,展示了MCP2515 IC和TJA1050 IC在模块上的连接方式。

CAN总线接口MCP2515

MCP2515与Arduino接口电路图

下图为MCP2515 CAN模块与Arduino接口的电路图,以及两个Arduino之间通过CAN协议进行通信的可能性。

Arduino MCP2515 CAN总线接口电路图

如果MCP2515模块的引脚不清楚,下面的图像可能有用。

Arduino MCP2515 CAN总线接口MCP2515 CAN模块引脚

组件的要求

  • Arduino UNO x 2 [在这里买
  • MCP2515 x 2
  • USB线x 2
  • 连接电线

电路设计

如前所述,CAN控制器IC促进SPI通信协议与任何微控制器的接口。因此,将MCP2515模块的SPI引脚即SCK、MOSI (SI)、MISO (SO)和CS连接到Arduino对应的SPI引脚上(见电路图)。

Arduino MCP2515 CAN总线接口电路设计

做两个这样的连接:一对作为发射机,另一对作为接收器。现在为了这个发射器和接收器之间的通信,连接每个MCP2515模块的CANH和CANL引脚。

代码

在进入代码之前,您需要下载MCP2515模块的库。有很多库,但我用过特别的一个。

下载它,并将提取的内容放在Arduino的libraries目录中。

由于通信涉及发射模块和接收模块,因此代码也分为发射代码和接收代码。

发射机的代码

接收方代码

工作

这个项目的工作非常简单,因为所有的工作都是由库(SPI和CAN)完成的。因为CAN是基于消息的通信,所以您需要发送0到8字节之间的消息。

在这个项目中,发射机发送的消息是1 1 2 3 0 5 6 7。此消息通过CAN总线传输,接收端接收此消息并显示在其串行显示器上。

此外,0th和图4th接收端分别提取上述序列中的1、0位,并将Arduino引脚2连接的LED打开、关闭。

应用程序

正如在引言中提到的,CAN在汽车领域有着广泛的应用。其中一些应用包括:

  • 电子换挡系统
  • 自动化(如工业)的主界面
  • 医疗设备
  • 机器人
  • 汽车发动机自动启动/停止

17的反应

  1. 谢谢你提供的信息。让两个arduino +MCP相互交流是有趣和简单的。当你连接到车辆的OBDII端口时,就会遇到挑战。根据我的经验,事情会变得更加复杂:

    车辆可以使用标准的can帧或者扩展的can帧(我不太确定Seeed库是否适用于扩展的can帧)。

    -即使是使用标准CAN帧的车辆,当你发送PID请求例如发动机转速时,似乎车辆没有接收到数据。

    任何指引吗?

    谢谢,
    戴夫

    1. 如果你还没有找到答案,大多数新车都有防火墙,所以你不能从OBD端口获得某些数据,除非你真的使用OBD协议来获取DTC pid。例如,与ECU直接连接的老式汽车应该可以工作。

  2. 嘿!你举的例子似乎很好。我只是想知道我能不能把一个Arduino同时编码成发射机和接收器。

    谢谢! !
    宝拉

    1. 你好。我在找和宝拉类似的东西。在我的情况下,我感兴趣的是从一个Arduino运行两个CAN模块-要么在双总线汽车(HSCAN=500kbps, MSCAN=125kbps)或作为一个隔离CAN桥-例如,当一个来自不同型号的电液动力转向泵需要CAN转换广播工作,但必须不能看到新车的原始消息。虽然SPI适用于多个模块,但我想我很难找到同时支持多个CAN接口的库。

      路加福音

      1. 也许你已经找到了这个,但如果你需要它:

        你可以像这样通过创建两个不同的对象来连接两个MC2515 shiles:

        MCP_CAN CAN_ONE (spiCSPin1);
        MCP_CAN CAN_TWO (spiCSPin2);

        例如,你需要两个CS引脚
        spiCSPin1 = 10
        spiCSPin2 = 9
        你把这些连接到每个CAN模块上对应的CS引脚上
        其他SPI引脚(11、12、13)连接到两个模块
        SPI是一个总线,因此每个从设备只需要一个额外的引脚。

        因此,您可以用CANbus的不同速度初始化两个模块。等等,你可以分别读和写。

        如果你依赖中断引脚来通知你收到的消息,你可能需要额外的引脚。在这种情况下,您可以使用引脚2和3。否则,您可以依赖于这两个模块的频繁轮询。然而,考虑到编程更复杂的2个模块和MCU可能会过载


  3. 如果我需要发送消息,然后收到一个答案,重新打包答案到不同的数据包并发送回来-如何在一个单一的草图?

  4. 我在一个大学项目中,我们建立一个电动摩托车,通信是用CAN。我已经用了你的项目的一半来尝试和阅读BUS CAN,但它甚至不能通过开始。可以循环。从我在网上看到的,这可能是由于水晶是8ookhz,而库是16000Khz。你能帮我吗?

    1. 可能对你来说太晚了,但其他人可能会发现这很有用。函数mcp2515_configRate接受晶体频率作为第二个参数.....对于8MHz,只要通过MCP_8MHz即可

      /*********************************************************************************************************
      **函数名:mcp2515_configRate
      **描述:设置波特率
      *********************************************************************************************************/
      mcp2515_can::mcp2515_configRate(const字节canSpeed, const字节时钟){
      字节集,cfg1, cfg2, cfg3;
      = 1;
      开关(时钟){
      案例(MCP_16MHz):
      开关(canSpeed) {
      案例(CAN_5KBPS):
      cfg1 = MCP_16MHz_5kBPS_CFG1;
      cfg2 = MCP_16MHz_5kBPS_CFG2;
      cfg3 = MCP_16MHz_5kBPS_CFG3;
      打破;

      案例(CAN_10KBPS):
      cfg1 = MCP_16MHz_10kBPS_CFG1;
      cfg2 = MCP_16MHz_10kBPS_CFG2;
      cfg3 = MCP_16MHz_10kBPS_CFG3;
      打破;

      案例(CAN_20KBPS):
      cfg1 = MCP_16MHz_20kBPS_CFG1;
      cfg2 = MCP_16MHz_20kBPS_CFG2;
      cfg3 = MCP_16MHz_20kBPS_CFG3;
      打破;

      案例(CAN_25KBPS):
      cfg1 = MCP_16MHz_25kBPS_CFG1;
      cfg2 = MCP_16MHz_25kBPS_CFG2;
      cfg3 = MCP_16MHz_25kBPS_CFG3;
      打破;

      案例(CAN_31K25BPS):
      cfg1 = MCP_16MHz_31k25BPS_CFG1;
      cfg2 = MCP_16MHz_31k25BPS_CFG2;
      cfg3 = MCP_16MHz_31k25BPS_CFG3;
      打破;

      案例(CAN_33KBPS):
      cfg1 = MCP_16MHz_33kBPS_CFG1;
      cfg2 = MCP_16MHz_33kBPS_CFG2;
      cfg3 = MCP_16MHz_33kBPS_CFG3;
      打破;

      案例(CAN_40KBPS):
      cfg1 = MCP_16MHz_40kBPS_CFG1;
      cfg2 = MCP_16MHz_40kBPS_CFG2;
      cfg3 = MCP_16MHz_40kBPS_CFG3;
      打破;

      案例(CAN_50KBPS):
      cfg1 = MCP_16MHz_50kBPS_CFG1;
      cfg2 = MCP_16MHz_50kBPS_CFG2;
      cfg3 = MCP_16MHz_50kBPS_CFG3;
      打破;

      案例(CAN_80KBPS):
      cfg1 = MCP_16MHz_80kBPS_CFG1;
      cfg2 = MCP_16MHz_80kBPS_CFG2;
      cfg3 = MCP_16MHz_80kBPS_CFG3;
      打破;

      案例(CAN_83K3BPS):
      cfg1 = MCP_16MHz_83k3BPS_CFG1;
      cfg2 = MCP_16MHz_83k3BPS_CFG2;
      cfg3 = MCP_16MHz_83k3BPS_CFG3;
      打破;

      案例(CAN_95KBPS):
      cfg1 = MCP_16MHz_95kBPS_CFG1;
      cfg2 = MCP_16MHz_95kBPS_CFG2;
      cfg3 = MCP_16MHz_95kBPS_CFG3;
      打破;

      案例(CAN_100KBPS):
      cfg1 = MCP_16MHz_100kBPS_CFG1;
      cfg2 = MCP_16MHz_100kBPS_CFG2;
      cfg3 = MCP_16MHz_100kBPS_CFG3;
      打破;

      案例(CAN_125KBPS):
      cfg1 = MCP_16MHz_125kBPS_CFG1;
      cfg2 = MCP_16MHz_125kBPS_CFG2;
      cfg3 = MCP_16MHz_125kBPS_CFG3;
      打破;

      案例(CAN_200KBPS):
      cfg1 = MCP_16MHz_200kBPS_CFG1;
      cfg2 = MCP_16MHz_200kBPS_CFG2;
      cfg3 = MCP_16MHz_200kBPS_CFG3;
      打破;

      案例(CAN_250KBPS):
      cfg1 = MCP_16MHz_250kBPS_CFG1;
      cfg2 = MCP_16MHz_250kBPS_CFG2;
      cfg3 = MCP_16MHz_250kBPS_CFG3;
      打破;

      案例(CAN_500KBPS):
      cfg1 = MCP_16MHz_500kBPS_CFG1;
      cfg2 = MCP_16MHz_500kBPS_CFG2;
      cfg3 = MCP_16MHz_500kBPS_CFG3;
      打破;

      案例(CAN_666KBPS):
      cfg1 = MCP_16MHz_666kBPS_CFG1;
      cfg2 = MCP_16MHz_666kBPS_CFG2;
      cfg3 = MCP_16MHz_666kBPS_CFG3;
      打破;

      案例(CAN_1000KBPS):
      cfg1 = MCP_16MHz_1000kBPS_CFG1;
      cfg2 = MCP_16MHz_1000kBPS_CFG2;
      cfg3 = MCP_16MHz_1000kBPS_CFG3;
      打破;

      默认值:
      设置= 0;
      打破;

      打破;

      案例(MCP_8MHz):
      开关(canSpeed) {
      案例(CAN_5KBPS):
      cfg1 = MCP_8MHz_5kBPS_CFG1;
      cfg2 = MCP_8MHz_5kBPS_CFG2;
      cfg3 = MCP_8MHz_5kBPS_CFG3;
      打破;

      案例(CAN_10KBPS):
      cfg1 = MCP_8MHz_10kBPS_CFG1;
      cfg2 = MCP_8MHz_10kBPS_CFG2;
      cfg3 = MCP_8MHz_10kBPS_CFG3;
      打破;

      案例(CAN_20KBPS):
      cfg1 = MCP_8MHz_20kBPS_CFG1;
      cfg2 = MCP_8MHz_20kBPS_CFG2;
      cfg3 = MCP_8MHz_20kBPS_CFG3;
      打破;

      案例(CAN_31K25BPS):
      cfg1 = MCP_8MHz_31k25BPS_CFG1;
      cfg2 = MCP_8MHz_31k25BPS_CFG2;
      cfg3 = MCP_8MHz_31k25BPS_CFG3;
      打破;

      案例(CAN_40KBPS):
      cfg1 = MCP_8MHz_40kBPS_CFG1;
      cfg2 = MCP_8MHz_40kBPS_CFG2;
      cfg3 = MCP_8MHz_40kBPS_CFG3;
      打破;

      案例(CAN_50KBPS):
      cfg1 = MCP_8MHz_50kBPS_CFG1;
      cfg2 = MCP_8MHz_50kBPS_CFG2;
      cfg3 = MCP_8MHz_50kBPS_CFG3;
      打破;

      案例(CAN_80KBPS):
      cfg1 = MCP_8MHz_80kBPS_CFG1;
      cfg2 = MCP_8MHz_80kBPS_CFG2;
      cfg3 = MCP_8MHz_80kBPS_CFG3;
      打破;

      案例(CAN_100KBPS):
      cfg1 = MCP_8MHz_100kBPS_CFG1;
      cfg2 = MCP_8MHz_100kBPS_CFG2;
      cfg3 = MCP_8MHz_100kBPS_CFG3;
      打破;

      案例(CAN_125KBPS):
      cfg1 = MCP_8MHz_125kBPS_CFG1;
      cfg2 = MCP_8MHz_125kBPS_CFG2;
      cfg3 = MCP_8MHz_125kBPS_CFG3;
      打破;

      案例(CAN_200KBPS):
      cfg1 = MCP_8MHz_200kBPS_CFG1;
      cfg2 = MCP_8MHz_200kBPS_CFG2;
      cfg3 = MCP_8MHz_200kBPS_CFG3;
      打破;

      案例(CAN_250KBPS):
      cfg1 = MCP_8MHz_250kBPS_CFG1;
      cfg2 = MCP_8MHz_250kBPS_CFG2;
      cfg3 = MCP_8MHz_250kBPS_CFG3;
      打破;

      案例(CAN_500KBPS):
      cfg1 = MCP_8MHz_500kBPS_CFG1;
      cfg2 = MCP_8MHz_500kBPS_CFG2;
      cfg3 = MCP_8MHz_500kBPS_CFG3;
      打破;

      案例(CAN_1000KBPS):
      cfg1 = MCP_8MHz_1000kBPS_CFG1;
      cfg2 = MCP_8MHz_1000kBPS_CFG2;
      cfg3 = MCP_8MHz_1000kBPS_CFG3;
      打破;

      默认值:
      设置= 0;
      打破;

      打破;

      默认值:
      设置= 0;
      打破;

      如果(集){
      mcp2515_setRegister (MCP_CNF1 cfg1);
      mcp2515_setRegister (MCP_CNF2 cfg2);
      mcp2515_setRegister (MCP_CNF3 cfg3);
      返回MCP2515_OK;
      其他}{
      返回MCP2515_FAIL;

  5. 假设我想通过4个CAN模块连接4个Arduino。
    那么将CAN消息直接发送到一个特定的CAN接收者的首选方法是什么呢?

    1. 有地址的不是arduino,而是应用程序。当您的特定arduino运行带有修复Id的“应用程序”时,您可以解决该问题。

      因此,您需要设计您的应用程序关于您想要发送的消息。例如:打开灯,ID 0x45,关闭灯ID 0x46。打开喇叭,0x10 -如果喇叭没有关闭,每10ms发送一次。在汽车里,通常你每隔X毫秒发送一个信息给前灯,如果信息没有发送,灯就会熄灭。这取决于应用程序的安全功能。

  6. 你好,
    我已经复制了发射机和接收机代码的确切代码,并下载了上面提到的相关库。
    然而,当编译错误消息'不能声明变量CAN为抽象类型MCP_CAN '时

    有人能帮我解决这个错误吗

      1. 我在例子中找到了解。

        # include
        # include“mcp2518fd_can.h”

        const int SPI_CS_PIN = 9;
        mcp2515_can可以(SPI_CS_PIN);//设置CS引脚

        现在工作。

        1. 你能分享一下代码吗?事实上,我正在做类似的事情。
          我正试图从电池通信CAN信息。所以我只需要代码的接收端。但这并不奏效。它只是每次都显示相同的值。
          你能帮帮我吗?

  7. 嗨,谁能帮我,让我知道我需要做什么修改,以便让这个与arduino mega一起工作?
    我试过用同样的别针和密码,但还是收到了信息
    CAN总线初始化失败
    谢谢,
    汤姆

留下一个回复

您的电邮地址将不会公布。

Electronicshub图标
Baidu
map