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

  • 3
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个基于STM32的CAN通信实例,使用了HAL库和标准外设库。 首先,需要在CubeMX中配置CAN控制器。选择相应的引脚和时钟源,然后生成代码。 接下来是主要的代码实现。 1. 初始化CAN控制器 ```c CAN_HandleTypeDef hcan; void MX_CAN_Init(void) { hcan.Instance = CAN1; hcan.Init.Prescaler = 4; //CAN总线波特率=时钟频率/((Prescaler+1)*(BS1+1)*(BS2+1)) hcan.Init.Mode = CAN_MODE_NORMAL; hcan.Init.SyncJumpWidth = CAN_SJW_1TQ; hcan.Init.TimeSeg1 = CAN_BS1_13TQ; hcan.Init.TimeSeg2 = CAN_BS2_2TQ; hcan.Init.TimeTriggeredMode = DISABLE; hcan.Init.AutoBusOff = DISABLE; hcan.Init.AutoWakeUp = DISABLE; hcan.Init.AutoRetransmission = ENABLE; hcan.Init.ReceiveFifoLocked = DISABLE; hcan.Init.TransmitFifoPriority = DISABLE; if (HAL_CAN_Init(&hcan) != HAL_OK) { Error_Handler(); } } ``` 2. 配置CAN过滤器 ```c CAN_FilterTypeDef sFilterConfig; void MX_CAN_Filter_Config(void) { sFilterConfig.FilterBank = 0; sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; sFilterConfig.FilterIdHigh = 0x0000; sFilterConfig.FilterIdLow = 0x0000; sFilterConfig.FilterMaskIdHigh = 0x0000; sFilterConfig.FilterMaskIdLow = 0x0000; sFilterConfig.FilterFIFOAssignment = CAN_FILTER_FIFO0; sFilterConfig.FilterActivation = ENABLE; sFilterConfig.SlaveStartFilterBank = 14; if (HAL_CAN_ConfigFilter(&hcan, &sFilterConfig) != HAL_OK) { Error_Handler(); } } ``` 3. 发送CAN消息 ```c void CAN_Transmit(uint32_t id, uint8_t* data, uint8_t length) { CAN_TxHeaderTypeDef TxHeader; TxHeader.StdId = id; TxHeader.ExtId = 0; TxHeader.RTR = CAN_RTR_DATA; TxHeader.IDE = CAN_ID_STD; TxHeader.DLC = length; uint32_t TxMailbox; if (HAL_CAN_AddTxMessage(&hcan, &TxHeader, data, &TxMailbox) != HAL_OK) { Error_Handler(); } } ``` 4. 接收CAN消息 ```c void CAN_Receive(void) { CAN_RxHeaderTypeDef RxHeader; uint8_t data[8]; if (HAL_CAN_GetRxMessage(&hcan, CAN_RX_FIFO0, &RxHeader, data) == HAL_OK) { //处理接收到的数据 } } ``` 最后,在main函数中调用相关函数即可完成CAN通信的初始化和使用。 ```c int main(void) { //初始化CAN控制器和过滤器 MX_CAN_Init(); MX_CAN_Filter_Config(); while(1) { //发送CAN消息 uint8_t data[8] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; CAN_Transmit(0x123, data, 8); //接收CAN消息 CAN_Receive(); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值