在本项目中,我们将了解MCP2515 CAN控制器模块,如何将MCP2515 CAN总线控制器与Arduino连接,最后如何借助两个MCP2515 CAN控制器和CAN协议实现两个Arduino板之间的通信。
介绍
简单CAN控制区域网络是一种总线标准,它允许微控制器与其外围设备进行通信,而不需要主机设备或计算机。
(adsense1)
CAN协议由罗伯特·博世公司开发,主要用于汽车控制单元及其组件之间的通信。
例如,发动机控制单元是汽车中使用的主要控制装置。该装置连接到许多传感器和执行器,如气流、压力、温度、阀门控制、空气控制电机等。这些模块与控制单元之间的通信通过CAN总线实现。
为了了解更多一点关于CAN总线、CAN控制器等重要方面的知识,MCP2515 CAN总线控制器模块非常有帮助。
还读:spi通信基础知识.
MCP2515 CAN总线控制模块简介
MCP2515 CAN总线控制器是一个简单的模块,支持CAN协议2.0B版本,可用于1Mbps的通信。为了建立一个完整的通信系统,你将需要两个CAN总线模块。
(adsense2)
项目中使用的模块如下图所示。
该模块基于MCP2515 CAN控制器IC和TJA1050 CAN收发器IC, MCP2515是一个独立的CAN控制器,并集成了用于与微控制器通信的SPI接口。
对于TJA1050 IC,它作为MCP2515 CAN控制器IC和物理CAN总线之间的接口。
下图显示了典型MCP2515模块上的组件和引脚。
MCP2515 CAN总线模块原理图
在查看模块的原理图之前,您需要了解关于MCP2515和TJA1050这两个ic的一些事情。
MCP2515集成电路是主要的控制器,内部由三个主要子组件组成:CAN模块、控制逻辑和SPI块。
CAN模块负责CAN总线上的消息的发送和接收。Control Logic通过连接所有模块来处理MCP2515的设置和操作。SPI块负责SPI通信接口。
对于TJA1050 IC,由于它作为MCP2515 CAN控制器和物理CAN总线之间的接口,该IC负责从控制器获取数据并将其中继到总线上。
下图显示了MCP2515 CAN模块的原理图,展示了MCP2515 IC和TJA1050 IC如何在模块上连接。
MCP2515与Arduino接口电路图
下图为MCP2515 CAN模块与Arduino接口的电路图,以及两个Arduino之间可能通过CAN协议进行的通信。
如果MCP2515模块的引脚不清楚,下面的图像可能有用。
组件的要求
- Arduino UNO x 2 [在这里买]
- 2 . MCP2515 x 2
- USB线x 2
- 连接电线
电路设计
如前所述,CAN控制器IC有助于SPI通信协议与任何微控制器的接口。因此,将MCP2515模块的SPI引脚SCK、MOSI (SI)、MISO (SO)、CS连接到Arduino相应的SPI引脚(见电路图)。
做两个这样的连接:一对作为发射器,另一对作为接收器。现在为了这个发射器和接收器之间的通信,连接每个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进行ON、OFF操作。
应用程序
正如介绍中提到的,CAN在汽车领域的应用非常广泛。其中一些应用包括:
- 电子换挡系统
- 自动化主界面(如工业)
- 医疗设备
- 机器人
- 汽车发动机自动启动/停止
17的反应
谢谢你提供的信息。让2个Arduinos+MCP相互交谈是有趣而简单的。当您连接到车辆的OBDII端口时,就会出现挑战。根据我的经验,事情会变得复杂得多:
车辆可以使用标准can帧或扩展can帧(我不太确定Seeed库是否适用于扩展can帧)。
-即使是使用标准CAN帧的车辆,当你发送PID请求例如发动机的rpm时,似乎车辆没有收到数据。
有什么指导吗?
谢谢,
戴夫
如果您还没有找到答案,大多数较新的汽车都有防火墙,因此您无法从OBD端口获得某些数据,除非您实际上使用OBD协议来获取DTC pid。例如,与ECU直接连接的老式汽车应该可以工作。
嘿!你举的例子看起来很不错。我只是想知道我是否可以同时编写一个Arduino作为发射器和接收器。
谢谢! !
宝拉
你好。我在找和宝拉类似的东西。在我的情况下,我感兴趣的是从一个Arduino运行两个CAN模块-无论是在双总线汽车(HSCAN=500kbps, MSCAN=125kbps)或作为隔离CAN桥接-例如,当来自不同型号的电液动力转向泵需要CAN广播翻译才能工作,但必须看不到新车的原始信息。虽然SPI适用于多个模块,但我想我很难找到一个同时支持多个CAN接口的库。
路加福音
也许你已经找到了这个,但以防你需要它:
您可以通过创建2个不同的对象来连接2个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可能会过载
你好,
我的项目是这样的,但是我使用lm 35 tem。传感器。但我不会写代码。你怎么能写?
嗨
如果我需要发送消息,然后收到一个答案,重新包装答案到不同的包,并发送回来-如何在一个简单的草图中做到这一点?
我在一个大学项目中,我们建造了一辆电动摩托车,通信是用CAN进行的。我已经使用了你的项目的一半,试图读取总线CAN,但它甚至不能通过开始。可以循环。从我在网上看到的,这可能是由于晶体是一个8oooKhz,图书馆是为16000Khz。你能帮我吗?
也许对你来说太晚了,但其他人可能会发现这很有用。函数mcp2515_configRate接受晶体频率作为第二个参数.....对于8MHz,只需通过MCP_8MHz
/*********************************************************************************************************
**函数名:mcp2515_configRate
**描述:设置波特率
*********************************************************************************************************/
mcp2515_can::mcp2515_configRate(const字节canSpeed, const字节clock) {
字节集,cfg1, cfg2, cfg3;
Set = 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;
打破;
默认值:
Set = 0;
打破;
}
打破;
case (MCP_8MHz):
切换(canSpeed) {
case (CAN_5KBPS):
cfg1 = MCP_8MHz_5kBPS_CFG1;
cfg2 = MCP_8MHz_5kBPS_CFG2;
cfg3 = MCP_8MHz_5kBPS_CFG3;
打破;
case (CAN_10KBPS):
cfg1 = MCP_8MHz_10kBPS_CFG1;
cfg2 = MCP_8MHz_10kBPS_CFG2;
cfg3 = MCP_8MHz_10kBPS_CFG3;
打破;
case (CAN_20KBPS):
cfg1 = MCP_8MHz_20kBPS_CFG1;
cfg2 = MCP_8MHz_20kBPS_CFG2;
cfg3 = MCP_8MHz_20kBPS_CFG3;
打破;
case (CAN_31K25BPS):
cfg1 = MCP_8MHz_31k25BPS_CFG1;
cfg2 = MCP_8MHz_31k25BPS_CFG2;
cfg3 = MCP_8MHz_31k25BPS_CFG3;
打破;
case (CAN_40KBPS):
cfg1 = MCP_8MHz_40kBPS_CFG1;
cfg2 = MCP_8MHz_40kBPS_CFG2;
cfg3 = MCP_8MHz_40kBPS_CFG3;
打破;
case (CAN_50KBPS):
cfg1 = MCP_8MHz_50kBPS_CFG1;
cfg2 = MCP_8MHz_50kBPS_CFG2;
cfg3 = MCP_8MHz_50kBPS_CFG3;
打破;
case (CAN_80KBPS):
cfg1 = MCP_8MHz_80kBPS_CFG1;
cfg2 = MCP_8MHz_80kBPS_CFG2;
cfg3 = MCP_8MHz_80kBPS_CFG3;
打破;
case (CAN_100KBPS):
cfg1 = MCP_8MHz_100kBPS_CFG1;
cfg2 = MCP_8MHz_100kBPS_CFG2;
cfg3 = MCP_8MHz_100kBPS_CFG3;
打破;
case (CAN_125KBPS):
cfg1 = MCP_8MHz_125kBPS_CFG1;
cfg2 = MCP_8MHz_125kBPS_CFG2;
cfg3 = MCP_8MHz_125kBPS_CFG3;
打破;
case (CAN_200KBPS):
cfg1 = MCP_8MHz_200kBPS_CFG1;
cfg2 = MCP_8MHz_200kBPS_CFG2;
cfg3 = MCP_8MHz_200kBPS_CFG3;
打破;
case (CAN_250KBPS):
cfg1 = MCP_8MHz_250kBPS_CFG1;
cfg2 = MCP_8MHz_250kBPS_CFG2;
cfg3 = MCP_8MHz_250kBPS_CFG3;
打破;
case (CAN_500KBPS):
cfg1 = MCP_8MHz_500kBPS_CFG1;
cfg2 = MCP_8MHz_500kBPS_CFG2;
cfg3 = MCP_8MHz_500kBPS_CFG3;
打破;
case (CAN_1000KBPS):
cfg1 = MCP_8MHz_1000kBPS_CFG1;
cfg2 = MCP_8MHz_1000kBPS_CFG2;
cfg3 = MCP_8MHz_1000kBPS_CFG3;
打破;
默认值:
Set = 0;
打破;
}
打破;
默认值:
Set = 0;
打破;
}
If (set) {
mcp2515_setRegister (MCP_CNF1 cfg1);
mcp2515_setRegister (MCP_CNF2 cfg2);
mcp2515_setRegister (MCP_CNF3 cfg3);
返回MCP2515_OK;
}其他{
返回MCP2515_FAIL;
}
}
假设我想通过4个CAN模块连接4个Arduino。
那么,将CAN消息直接发送到一个特定的CAN接收器的首选方式是什么?
有地址的不是arduino,而是应用程序。当您的特定arduino运行具有修复Id的“应用程序”时,您可以解决这个问题。
所以你需要根据你要发送的信息来设计你的应用程序。例如:打开灯,ID为0x45,关闭灯ID为0x46。打开喇叭,0x10 -如果没有喇叭关闭,每10毫秒发送一次。在汽车中,你通常每X毫秒发送一个信息到前灯上,如果信息没有发送,灯就会关闭。这取决于应用程序的安全功能。
你好,
我已经复制了发射机和接收机代码的确切代码,并下载了上面提到的相关库。
但是,在编译错误消息“不能将变量CAN声明为抽象类型MCP_CAN”时,
有人能帮我解决这个错误吗
你好,
你找到解决办法了吗?
我有同样的错误
你能分享一下怎么修吗?
我在例题中找到了解。
# include
# include“mcp2518fd_can.h”
const int SPI_CS_PIN = 9;
mcp2515_can可以(SPI_CS_PIN);//设置CS引脚
现在起作用了。
你能分享一下代码吗?事实上,我正在做类似的事情。
我试图从电池通信CAN信息。我只需要有代码的接收端。但这行不通。它只是每次都显示相同的值。
你能帮我一下吗?
它支持python编程吗?
嗨,谁能帮我一下,让我知道我需要做什么修改才能让它与arduino mega一起工作?
我试过使用相同的别针和相同的代码,但一直收到信息
CAN总线初始化失败
谢谢,
汤姆