一、CAN总线协议介绍
1.CAN是 Controller Area Network 的缩写(以下称为 CAN ),是 ISO 国际标准化的串行通信协议。可以用来满足“多总线通信时,线束的数量过多”、“通过多个 LAN ,进行大量数据的高速通信”的需要。它的出现为分布式控制系统实现各节点之间实时、可靠的数据通信提供了强有力的技术支持。
2.CAN总线通常由两根线组成。这两根线分别是CAN-High(CAN-H)和CAN-Low(CAN-L),它们共同实现CAN网络的通信。总线电平分为显性电平和隐性电平,二者必居其一。发送方通过使总线电平发生变化,将消息发送给接收方。利用两根线的差分电压来进行数据的传输。
3.CAN协议具有以下特点:
(1)CAN总线是一种多主总线,总线上任意节点可在任意时刻主动地向网络上其他节点发送信息而不分主次,因此可在各节点之间实现自由通信。
(2)CAN总线采用非破坏性总线仲裁技术。当多个节点同时向总线发送信息时,优先级低的节点会主动退出发送,而最高优先级的节点可以不受影响地继续传输数据,从而大大节省总线冲突的仲裁时间。即使在网络负载很重的情况下也不会发生网络瘫痪情况。
(3)CAN总线的通信介质可以是双绞线、同轴电缆或光导纤维,选择灵活。
(4)CAN总线的通信速率可达 1Mbit/s(此时通信距离最长为 40 米),通信距离最远可达 10km(速率在 5kbit/s 以下)。
(5)CAN总线上的节点信息分成不同的优先级,可以满足不同级别的实时要求,高优先级的数据可以在 134μs 内得到传输。
(6)CAN总线通过报文滤波即可实现点对点、一点对多点及全局广播等几种方式传送数据,无需专门的调度。
(7)CAN总线的数据采用短帧结构,传输时间短,受干扰概率低,具有极好的检错效果。
(8)CAN总线采用 CRC 检验并可提供相应的错误处理功能,保证了数据通信的可靠性。
(9)CAN总线上的器件可被置于无任何内部活动的睡眠方式,相当于未连接到总线上,可以有效降低系统功耗。
4.CAN总线采用差分信号传输,通常情况下只需要两根信号线就可以进行正常的通信。在差分信号中,逻辑0和逻辑1是用两根差分信号线的电压差来表示。
当处于逻辑1,CAN_H和CAN_L的电压差小于0.5V,称为隐性电平(Recessive);当处于逻辑0,CAN_H和CAN_L的电压差大于0.9V,称为显性电平(Dominant)。
5.CAN有四种帧类型:
◆ 数据帧:发送单元向接收单元传送数据的帧
◆ 远程帧:总线单元发出远程帧,请求发送具有同一识别符的数据帧
◆ 错误帧:由检测到错误的任何节点发送的帧
◆ 过载帧:在数据或远程帧之间注入延迟的帧
数据帧是唯一实际传输数据的帧,结构上由7个段组成,其中根据仲裁段ID码长度的不同,分为标准帧(CAN2.0A)和扩展帧(CAN2.0B):
◆ 标准帧格式:具有11个标识符位
◆ 扩展帧格式:具有29个标识符位
标准数据帧的构成如下图所示:
6.数据帧一般由7个段构成,即:
(1)帧起始:表示数据帧开始的段。
(2)仲裁段:表示该帧优先级的段。
(3)控制段:表示数据的字节数及保留位的段。
(4)数据段:数据的内容,一帧可发送0~8个字节的数据(标准数据帧的有效数据长度为0到8字节,而扩展数据帧的有效数据长度为0到64字节)
(5)CRC段:检查帧的传输错误的段。
(6)ACK段:表示确认正常接收的段。
(7)帧结束:表示数据帧结束的段。
(详细的CAN介绍可以参看 CAN介绍.pdf)
二.工作原理
1.CAN控制器的工作原理
CAN核心模块:收到一个报文时,该模块根据CAN规范将穿行位流转换成用于接收的并行数据,发送相反。
发送缓冲器:存储一个完整的报文。
验收滤波器:根据用户设定,过滤掉无须接收的报文。
接收FIFO:存储从CAN总线上接收的所有报文。
三.应用代码
1.CAN 总线中数据在传输时使用了大端模式,即对于长度超过 1 字节的数据,先传输低字节,再传输高字节。
2.示例:
#include "xcanps.h"
#include "xparameters.h"
#include "xil_printf.h"
/************************** Constant Definitions *****************************/
#define CAN_DEVICE_ID XPAR_XCANPS_0_DEVICE_ID
#define XCANPS_MAX_FRAME_SIZE_IN_WORDS (XCANPS_MAX_FRAME_SIZE / sizeof(u32))
#define FRAME_DATA_LENGTH 8 /* Frame Data field length */
#define TEST_MESSAGE_ID 2000
#define TEST_BTR_SYNCJUMPWIDTH 3
#define TEST_BTR_SECOND_TIMESEGMENT 2
#define TEST_BTR_FIRST_TIMESEGMENT 15
#define TEST_BRPR_BAUD_PRESCALAR 29
/************************** Function Prototypes ******************************/
int CanPsPolledExample(u16 DeviceId);
static int SendFrame(XCanPs *InstancePtr);
static int RecvFrame(XCanPs *InstancePtr);
/************************** Variable Definitions *****************************/
static u32 TxFrame[XCANPS_MAX_FRAME_SIZE_IN_WORDS];
static u32 RxFrame[XCANPS_MAX_FRAME_SIZE_IN_WORDS];
/* Driver instance */
static XCanPs Can;
/****************************************************************************/
int main(void)
{
int Status;
Status = CanPsPolledExample(CAN_DEVICE_ID);
if (Status != XST_SUCCESS) {
xil_printf("CAN Polled Mode Example Test Failed\r\n");
return XST_FAILURE;
}
Status = SendFrame(&Can);
if (Status != XST_SUCCESS) {
return Status;
}
return XST_SUCCESS;
}
/*****************************************************************************/
int CanPsPolledExample(u16 DeviceId)
{
int Status;
XCanPs *CanInstPtr = &Can;
XCanPs_Config *ConfigPtr;
/*
* Initialize the Can device.
*/
ConfigPtr = XCanPs_LookupConfig(DeviceId);
if (CanInstPtr == NULL) {
return XST_FAILURE;
}
Status = XCanPs_CfgInitialize(CanInstPtr,
ConfigPtr,
ConfigPtr->BaseAddr);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
/*
* Run self-test on the device, which verifies basic sanity of the
* device and the driver.
*/
Status = XCanPs_SelfTest(CanInstPtr);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
/*
* Enter Configuration Mode so we can setup Baud Rate Prescaler
* Register (BRPR) and Bit Timing Register (BTR).
*/
XCanPs_EnterMode(CanInstPtr, XCANPS_MODE_CONFIG);
while(XCanPs_GetMode(CanInstPtr) != XCANPS_MODE_CONFIG);
/*
* Setup Baud Rate Prescaler Register (BRPR) and
* Bit Timing Register (BTR).
*/
XCanPs_SetBaudRatePrescaler(CanInstPtr, TEST_BRPR_BAUD_PRESCALAR);
XCanPs_SetBitTiming(CanInstPtr, TEST_BTR_SYNCJUMPWIDTH,
TEST_BTR_SECOND_TIMESEGMENT,
TEST_BTR_FIRST_TIMESEGMENT);
/*
* Enter Loop Back Mode.
*/
XCanPs_EnterMode(CanInstPtr, XCANPS_MODE_LOOPBACK);
while(XCanPs_GetMode(CanInstPtr) != XCANPS_MODE_LOOPBACK);
/*
* Send a frame, receive the frame via the loop back and verify its
* contents.
*/
Status = SendFrame(CanInstPtr);
if (Status != XST_SUCCESS) {
return Status;
}
Status = RecvFrame(CanInstPtr);
return Status;
}
/*****************************************************************************/
static int SendFrame(XCanPs *InstancePtr)
{
u8 *FramePtr;
int Index;
int Status;
/*
* Create correct values for Identifier and Data Length Code Register.
*/
TxFrame[0] = (u32)XCanPs_CreateIdValue((u32)TEST_MESSAGE_ID, 0, 0, 0, 0);
TxFrame[1] = (u32)XCanPs_CreateDlcValue((u32)FRAME_DATA_LENGTH);
/*
* Now fill in the data field with known values so we can verify them
* on receive.
*/
FramePtr = (u8 *)(&TxFrame[2]);
for (Index = 0; Index < FRAME_DATA_LENGTH; Index++) {
*FramePtr++ = (u8)Index;
}
/*
* Wait until TX FIFO has room.
*/
while (XCanPs_IsTxFifoFull(InstancePtr) == TRUE);
/*
* Now send the frame.
*
* Another way to send a frame is keep calling XCanPs_Send() until it
* returns XST_SUCCESS. No check on if TX FIFO is full is needed anymore
* in that case.
*/
Status = XCanPs_Send(InstancePtr, TxFrame);
return Status;
}
/*****************************************************************************/
static int RecvFrame(XCanPs *InstancePtr)
{
u8 *FramePtr;
int Status;
int Index;
/*
* Wait until a frame is received.
*/
while (XCanPs_IsRxEmpty(InstancePtr) == TRUE);
/*
* Receive a frame and verify its contents.
*/
Status = XCanPs_Recv(InstancePtr, RxFrame);
if (Status == XST_SUCCESS) {
/*
* Verify Identifier and Data Length Code.
*/
if (RxFrame[0] !=
(u32)XCanPs_CreateIdValue((u32)TEST_MESSAGE_ID, 0, 0, 0, 0))
return XST_LOOPBACK_ERROR;
if ((RxFrame[1] & ~XCANPS_DLCR_TIMESTAMP_MASK) != TxFrame[1])
return XST_LOOPBACK_ERROR;
/*
* Verify Data field contents.
*/
FramePtr = (u8 *)(&RxFrame[2]);
for (Index = 0; Index < FRAME_DATA_LENGTH; Index++) {
if (*FramePtr++ != (u8)Index) {
return XST_LOOPBACK_ERROR;
}
}
}
return Status;
}