STM32的HAL库开发系列 - CAN通信实例

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通信是一种高效、可靠、灵活的数据传输方式,适用于各种应用场景,并有许多标准和协议来支持其在不同领域的应用,是一种非常重要的通信技术。

你可能感兴趣的:(stm32,单片机,嵌入式硬件)