CAN通信是一种高效、可靠、灵活的数据传输方式,适用于各种应用场景,在工业自动化、汽车电子、医疗设备等领域有着广泛的应用。
但理解CAN通信的实际应用,也不能全部只看软件方面,还需要对硬件上也有了解。
在硬件上,CAN通信使用两条线路:一条是数据线(CAN_H),另一条是地线(CAN_L)。数据线和地线之间的电压差表示了数据的“1”或“0”。数据传输采用非连续总线唤醒(Non-Continuous Dominant State)的方式,这意味着,当有节点需要发送数据时,它会把总线电压拉高,表示“1”,其他节点就会停止发送,并等待数据传输完成。这种方式能够有效地避免数据冲突,保证了数据的可靠性。
使用CAN通信的设备需要实现CAN控制器,它负责控制总线的电压,并检测和处理总线上的数据。在软件层面,需要使用CAN驱动程序来实现对CAN控制器的控制。
以下这段代码就是实际应用的举例。
/**
* @brief HAL库CAN FIFO0接受邮箱中断(Rx0)回调函数
* @param hcan : CAN句柄指针
*/
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
static BaseType_t xHigherPriorityTaskWoken = pdFALSE; // 不请求上下文切换
CAN_RxHeaderTypeDef RxHeader; // CAN通信协议头
uint8_t rx_data[8] = {0}; // 暂存CAN接收数据
motor_measure_t motorDataTmp; // 电机数据
uint8_t i = 0;
// if (hcan == &hcan1)
if (hcan->Instance == CAN1)
{
if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &RxHeader, rx_data) == HAL_OK) // 接收CAN总线上发送来的数据
{
// 对应电机向总线上发送的反馈的标识符和程序电机序号
switch (RxHeader.StdId) {
case CAN_2006_M_ID : i = MotorID_ShootM; break;
case CAN_PITCH_MOTOR_ID : i = MotorID_GimbalPitch; break;
case CAN_YAW_MOTOR_ID : i = MotorID_GimbalYaw; break;
#if DEBUGMODE
default : i = RxHeader.StdId - CAN_3508_M1_ID; break;
#else
default : break;
#endif
}
// 电机返回数据协议解析
get_motor_measure(&motorDataTmp, rx_data);
#if DEBUGMODE
get_motor_measure(&motorData[i], rx_data);
#endif
// 向消息队列中填充数据
if (messageQueueCreateFlag) {
xQueueOverwriteFromISR(messageQueue[i], (void *)&motorDataTmp, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
}
}
对于CAN发送,相应的实例如下:
/**
* @brief 发送C620电调控制电流
* @param[in] motorID对应的电机控制电流, 范围 [-16384, 16384],对应电调输出的转矩电流范围 [-20A, 20A]
* @param[in] etcID: 控制报文标识符(电调ID)为1-4还是5-8
*/
void CAN_Cmd_C620(CAN_HandleTypeDef *hcan, int16_t motor1, int16_t motor2, int16_t motor3, int16_t motor4, bool_t etcID)
{
CAN_TxHeaderTypeDef TxHeader; // CAN通信协议头
uint8_t TxData[8] = {0}; // 发送电机指令缓存
uint32_t TxMailboxX = CAN_TX_MAILBOX0; // CAN发送邮箱
if (etcID == false) {
TxHeader.StdId = CAN_3508_ALL_ID; // 标准格式标识符ID
} else {
TxHeader.StdId = CAN_3508_ETC_ID;
}
TxHeader.ExtId = 0;
TxHeader.IDE = CAN_ID_STD; // 标准帧
TxHeader.RTR = CAN_RTR_DATA; // 传送帧类型为数据帧
TxHeader.DLC = 0x08; // 数据长度码
TxData[0] = (uint8_t)(motor1 >> 8);
TxData[1] = (uint8_t)motor1;
TxData[2] = (uint8_t)(motor2 >> 8);
TxData[3] = (uint8_t)motor2;
TxData[4] = (uint8_t)(motor3 >> 8);
TxData[5] = (uint8_t)motor3;
TxData[6] = (uint8_t)(motor4 >> 8);
TxData[7] = (uint8_t)motor4;
//找到空的发送邮箱
while (HAL_CAN_GetTxMailboxesFreeLevel(hcan) == 0); // 如果三个发送邮箱都阻塞了就等待直到其中某个邮箱空闲
if ((hcan->Instance->TSR & CAN_TSR_TME0) != RESET) {
// 检查发送邮箱0状态 如果邮箱0空闲就将待发送数据放入FIFO0
TxMailboxX = CAN_TX_MAILBOX0;
} else if ((hcan->Instance->TSR & CAN_TSR_TME1) != RESET) {
TxMailboxX = CAN_TX_MAILBOX1;
} else if ((hcan->Instance->TSR & CAN_TSR_TME2) != RESET) {
TxMailboxX = CAN_TX_MAILBOX2;
}
// 将数据通过CAN总线发送
#if DEBUGMODE
if (HAL_CAN_AddTxMessage(hcan, &TxHeader, TxData, (uint32_t *)TxMailboxX) != HAL_OK) {
Error_Handler(); // 如果CAN信息发送失败则进入死循环
}
#else
HAL_CAN_AddTxMessage(hcan, &TxHeader, TxData, (uint32_t *)TxMailboxX);
#endif
}
另外,CAN通信还具有较高的安全性。例如,它使用了校验和机制来检测数据传输中的错误,并使用了访问控制机制来限制对总线的访问。
在实际应用中,CAN通信还有许多标准,如:
CAN 2.0A: 这是最早的标准,支持11位帧ID和8字节数据。
CAN 2.0B: 与2.0A相比,它增加了29位帧ID和支持高速模式。
CAN FD (Flexible Data-rate) : 这是最新的标准,支持高达64字节的数据帧和更高的通信速率。
同时,由于其在工业、汽车等领域的广泛应用,也有许多标准化组织和协会为其制定了专门的应用标准,如:
J1939: 这是专门为汽车应用设计的标准,它定义了许多特定的应用层协议。
DeviceNet: 这是专门为工业自动化应用设计的标准,它定义了许多特定的应用层协议。
总之,CAN通信是一种高效、可靠、灵活的数据传输方式,适用于各种应用场景,并有许多标准和协议来支持其在不同领域的应用,是一种非常重要的通信技术。