一,CAN与RS485区别联系
CAN (Controller Area Network) 和 RS485 都是用于设备间通信的协议,但它们在设计、功能和应用上有一些重要的区别。
物理层:CAN 和 RS485 都使用差分信号,这使得它们在噪声环境中都有很好的抗干扰能力。然而,CAN 通常使用两线制(CAN_H 和 CAN_L),而 RS485 可以使用两线制或四线制(两个发送线和两个接收线)。
数据速率和距离:CAN 的数据速率可以达到1 Mbps,但通信距离较短,通常在40米以内。RS485 的数据速率较低,最高可达10 Mbps,但通信距离可以达到1200米。
网络拓扑:CAN 支持多主网络,这意味着任何节点都可以在总线空闲时启动数据传输。RS485 通常用于点对点或点对多点的通信,但需要一个主设备来控制数据流。
错误检测和恢复:CAN 协议包含了复杂的错误检测和错误恢复机制,包括CRC、帧校验、错误帧等。RS485 没有内置的错误检测,通常需要在应用层实现。
应用领域:由于其高速和可靠性,CAN 通常用于汽车和工业自动化等需要实时通信的场合。RS485 由于其长距离和多点能力,通常用于楼宇自动化、工业控制等应用。
总的来说,CAN 和 RS485 在物理层上有一些相似之处,但在数据速率、网络拓扑、错误处理和应用领域上有明显的区别。选择哪种协议取决于你的具体需求。
二,CAN常用协议
CAN(Controller Area Network)是一种用于汽车和工业自动化的通信协议。它被设计为在噪声环境中提供可靠的通信,同时也支持网络中设备的实时交互。CAN协议本身只定义了物理层和数据链路层的规范,而高级的通信协议则由各种不同的标准和规范定义。以下是一些常见的基于CAN的协议:
CANopen:CANopen是一个用于嵌入式系统的通信协议,它定义了设备配置、网络管理和应用数据交换的规范。CANopen广泛应用于工业自动化、医疗设备和其他领域[[1]]。
DeviceNet:DeviceNet是一种基于CAN的工业网络协议,它由罗克韦尔自动化公司开发。DeviceNet支持实时I/O数据交换,以及设备参数的配置和诊断[[2]]。
SAE J1939:SAE J1939是一种用于重型车辆和工业设备的通信协议。它定义了一套用于车辆网络的消息格式和参数,包括引擎控制、传感器数据、诊断信息等[[3]]。
ISO 15765-2 (ISO-TP):ISO 15765-2,也被称为ISO-TP,是一种用于汽车诊断和编程的通信协议。它提供了一种在CAN网络上发送大于8字节数据的方法[[4]]。
NMEA 2000:NMEA 2000是一种用于船舶电子设备的通信协议,它基于SAE J1939协议。NMEA 2000支持船舶设备之间的数据交换,包括导航、GPS、深度测量、引擎状态等[[5]]。
以上就是一些常见的基于CAN的协议,每种协议都有其特定的应用领域和功能
三,让Kernel支持CAN
如上图所示,linux内核原生支持Raw,SAE J1939,ISO-TP三种can高层协议,NMEA 2000也是通过SAE1939方式实现的.
四,STM32的CAN
从图中可以看出两个 CAN 都分别拥有自己的发送邮箱和接收 FIFO,但是他们共用 28 个滤波器。通过 CAN_FMR 寄存器的设置,可以设置滤波器的分配方式。STM32 的标识符过滤是一个比较复杂的东东,它的存在减少了 CPU 处理 CAN 通信的开销。STM32 的过滤器组最多有 28 个(互联型),但是 STM32F103ZET6 只有 14 个(增强型),每个滤波器组 x 由 2 个 32 为寄存器,CAN_FxR1 和 CAN_FxR2 组成。
STM32CAN硬件支持
互联型产品中,带有 2 个 CAN 控制器,STM32F103ZET6 属于增强型,只有CAN1
①发送缓冲:每个CAN都有3个独立的发送邮箱TransmitMailbox = CAN_Transmit(CAN1, &TxMessage);返回值是邮箱号,可以从返回值看出有没有发送成功
②接收缓冲:每个CAN都有2个FIFO,每个FIFO存储3个消息,如果没有及时读取可能会溢出
③过滤器:互联型过滤器最多有 28组,增强型过滤器最多有 14组,每个滤波器组 x由 2 个 32 为寄存器,CAN_FxR1 和 CAN_FxR2 组成
● 1 个 32 位过滤器,包括:STDID[10:0]、EXTID[17:0]、IDE 和 RTR 位
● 2 个 16 位过滤器,包括:STDID[10:0]、IDE、RTR 和 EXTID[17:15]位
此外过滤器可配置为,屏蔽位模式和标识符列表模式。
STM32CAN软件设计
①GPIO初始化
GPIO_InitTypeDef GPIO_InitStructure; /* 初始化IO口 */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; // PA12 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; // PA11 GPIO_Init(GPIOA, &GPIO_InitStructure);
②CAN初始化
CAN_InitTypeDef CAN_InitStructure;
CAN_DeInit(CAN1);
CAN_StructInit(&CAN_InitStructure);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
CAN_InitStructure.CAN_TTCM = DISABLE; // 关闭时间触发通信模式(Time-triggered communication mode)。在这个模式下,所有的CAN消息都会在预先设定的时间点发送,这可以提高总线的确定性,但是也增加了配置的复杂性。关闭它表示消息的发送是由软件触发,而不是由硬件在固定时间点自动触发。
CAN_InitStructure.CAN_ABOM = ENABLE; // 关闭自动离线管理(Automatic bus-off management)。在CAN通信中,如果某一节点发生错误超过一定次数,它将会被自动离线,以防止其干扰总线。如果设置了这个选项,硬件将会在一定时间后自动尝试恢复传输
CAN_InitStructure.CAN_AWUM = DISABLE; // 关闭通过软件唤醒的睡眠模式(Auto wake-up mode)。在这个模式中,如果总线空闲,CAN模块会进入睡眠状态以减少功耗。如果设置了这个选项,硬件将在总线活跃时自动唤醒。否则,需要通过软件发送唤醒命令
CAN_InitStructure.CAN_NART = DISABLE; // 启用禁止报文自动重传(No automatic retransmission)。在CAN协议中,如果一条消息发送失败,通常会自动重试。开启这个功能可以禁止自动重传
CAN_InitStructure.CAN_RFLM = DISABLE; // 关闭报文锁定(Receive FIFO locked mode)。如果这个选项被关闭,当接收FIFO满了之后,新的消息会覆盖旧的消息
CAN_InitStructure.CAN_TXFP = DISABLE; // 关闭发送优先级(Priority driven by request order)。如果这个选项关闭,发送优先级则由报文的标识符(ID)决定
CAN_InitStructure.CAN_Mode = CAN_Mode_LoopBack; //
设置为回环模式(LoopBack mode)。在这个模式下,所有发送的消息都会被直接送到接收缓冲区,这很适合于自我测试。 CAN_InitStructure.CAN_SJW = CAN_SJW_1tq; // 重新同步跳跃宽度 1 个时间单位 /* 波特率设置, 当APB1的时钟频率是36MHZ的时候。 波特率的公式为: */ /* 波特率(Kpbs) = 36M / ((CAN_BS1 + CAN_BS2 + 1) * CAN_Prescaler) */ CAN_InitStructure.CAN_BS1 = CAN_BS1_6tq; // 时间段 1 为8 个时间单位 CAN_InitStructure.CAN_BS2 = CAN_BS2_5tq; // 时间段 2 为7 个时间单位 CAN_InitStructure.CAN_Prescaler = 12; CAN_Init(CAN1, &CAN_InitStructure);
③CAN中断
NVIC_InitTypeDef NVIC_InitStructure; /* 使能接收的中断和中断优先级 */ NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE); // FIFO0消息挂号中断允许. CAN_ITConfig(CAN1, CAN_IT_ERR, DISABLE);
④CAN发送消息
TransmitMailbox = CAN_Transmit(CAN1, &TxMessage);//返回值为邮箱号 // 检查消息是否发送 uint32_t counter = 0xFFFF; while((CAN_TransmitStatus(CAN1, TransmitMailbox) != CAN_TxStatus_Ok) && --counter);//CAN是异步发送,这样其实在等待发送完毕,占用CPU if (counter) { // 发送成功 } else { // 发送失败 }
③CAN中断
NVIC_InitTypeDef NVIC_InitStructure; /* 使能接收的中断和中断优先级 */ NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE); // FIFO0消息挂号中断允许. CAN_ITConfig(CAN1, CAN_IT_ERR, DISABLE);
④CAN接收消息
//下面是中断方式 CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);//具体那个FIFO是由过滤器定义的, CAN_FIFORelease(CAN1, CAN_FIFO0); CAN_ClearITPendingBit(CAN1, CAN_IT_FMP0); //下面是主动轮询方式 void Poll_CAN1(void) { CanRxMsg RxMessage; if(CAN_MessagePending(CAN1, CAN_FIFO0)) // 检查是否有待处理的消息 { CAN_Receive(CAN1, CAN_FIFO0, &RxMessage); // 从FIFO中读取消息,注意这个函数没有返回值,也就是一定成功 // 在这里处理消息。譬如: // printf("Received CAN message with ID: 0x%X\n", RxMessage.StdId); } }