CAN通信配置过滤器和使用三个邮箱发送

RM比赛用的电机基本都使用CAN通信,但是一条CAN线上只用一个发送邮箱在挂在设备多的情况可能会导致发送不完,但其实完全可以把三个发送邮箱都用上。这里贴一下自己的CAN筛选器,接收以及发送的代码。

完整的工程可以看我开源的飞机云台程序~

项目代码开源地址: https://github.com/ittuann/RoboMaster_UAV-Gimbal_Ares2022

/**
  * @brief          CAN筛选器
  */
void Can_Filter_Init(void)
{
	/***	CAN1	***/
    CAN_FilterTypeDef sFilterConfig;
	sFilterConfig.FilterActivation = ENABLE;			// 激活过滤器
	sFilterConfig.FilterBank = 0;						// 配置主CAN筛选器组编号
	sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;	// 配置工作模式为列表模式
	sFilterConfig.FilterScale = CAN_FILTERSCALE_16BIT;	// 配置筛选器的尺度为16位长
	sFilterConfig.FilterIdHigh = 0x0000;				// CAN_FxR1寄存器
	sFilterConfig.FilterIdLow = 0x0000;
	sFilterConfig.FilterMaskIdHigh = 0x0000;			// CAN_FxR2寄存器
	sFilterConfig.FilterMaskIdLow = 0x0000;
	sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0;	// 筛选器接筛选报文关联到FIFO0
	
	if (HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK) {						// 配置CAN1接收筛选过滤器
		Error_Handler();
	}
	if (HAL_CAN_Start(&hcan1) != HAL_OK) {												// 开启CAN1
		Error_Handler();
	}
	if (HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK) {	// 开启CAN1的FIFO0接收中断
		Error_Handler();
	}
}
  • 过滤的方法分为以下两种模式:

    • 标识符列表模式(CAN_FILTERMODE_IDLIST):它把要接收报文的ID列成一个表, 要求报文ID与列表中的某一个标识符完全相同才可以接收,可以理解为白名单管理。
    • 掩码模式(CAN_FILTERMODE_IDMASK):它把可接收报文ID的某几位作为列表,这几位被称为掩码, 可以把它理解成关键字搜索,只要掩码(关键字)相同,就符合要求,报文就会被保存到接收FIFO。
  • CAN_FilterNumber 用于设置筛选器的编号,即本过滤器结构体配置的是哪一组筛选器,CAN一共有28个筛选器,所以它的可输入参数范围为0-27。如果是双CAN的情况,一般CAN1为0-13,CAN2为14-27。

  • CAN_FilterFIFOAssignment 用于设置当报文通过筛选器的匹配后,该报文会被存储到哪一个接收FIFO

  • 每组筛选器包含2个32位的寄存器,分别为CAN_FxR1CAN_FxR2,它们用来存储要筛选的ID或掩码

模式CAN FilterldHighCAN FilterldLowCAN FilterMaskIdHighCAN FilterMaskIdLow
32位列表ID1 的高16位ID1 的低16位ID2 的高16位ID2 的低16位
16位列表ID1 的完整数值ID2 的完整数值ID3 的完整数值ID4 的完整数值
32位掩码ID1 的高16位ID1 的低16位ID1 掩码的高16位ID1 掩码的低16位
16位掩码模ID1 的完整数值ID2 的完整数值ID1 掩码的完整数值ID2 掩码完整数值

CAN通信的发送函数如下,以RM比赛用的M3508无刷电机使用的C620电调的通信协议为例

#include <stdbool.h>

/**
  * @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();
		}
	#else
		HAL_CAN_AddTxMessage(hcan, &TxHeader, TxData, (uint32_t *)TxMailboxX);
	#endif
}
  • IDE存储的是扩展标志。当它的值为宏CAN_ID_STD时表示本报文是标准帧,使用StdId成员存储报文ID; 当它的值为宏CAN_ID_EXT时表示本报文是扩展帧,使用ExtId成员存储报文ID。ExtIdStdId这两个成员根据IDE位配置,只有一个是有效的。

  • RTR是报文类型标志。当它的值为宏CAN_RTR_Data时表示本报文是数据帧;当它的值为宏CAN_RTR_Remote时表示本报文是遥控帧。 由于遥控帧没有数据段,所以当报文是遥控帧时,下面的Data[8]是无效的。

  • 可以加上判断状态是否为HAL_OK,如果CAN信息发送失败则进入Error_Handler()死循环。测试时可以打断点判断问题所在,但正式版本时不想给运行着的程序增加可能卡死循环的风险所以会关掉判断。while循环等待空邮箱也可以关掉。

/**
  * @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 (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &RxHeader, rx_data) == HAL_OK)	// 接收CAN总线上发送来的数据
		{
			// 对应电机向总线上发送的反馈的标识符和程序电机序号
            switch (RxHeader.StdId) {
                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;
				#endif
            }
            // 电机返回数据协议解析
            get_motor_measure(&motorDataTmp, rx_data);
            // 向消息队列中填充数据
            if (messageQueueCreateFlag) {
                xQueueOverwriteFromISR(messageQueue[i], (void *)&motorDataTmp, &xHigherPriorityTaskWoken);
                portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
            }
		}
	}
}
  • 7
    点赞
  • 52
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
STM32F407是一款基于ARM Cortex-M4的微控制器,可以通过CAN(Controller Area Network)总线进行通信。以下是一个基本的STM32F407 CAN通信代码示例: ```c #include "stm32f4xx.h" CAN_InitTypeDef CAN_InitStructure; CAN_FilterInitTypeDef CAN_FilterInitStructure; CanTxMsg TxMessage; CanRxMsg RxMessage; void CAN1_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure; // 使能GPIO和CAN1时钟 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE); // 配置CAN引脚 GPIO_PinAFConfig(GPIOA, GPIO_PinSource11, GPIO_AF_CAN1); GPIO_PinAFConfig(GPIOA, GPIO_PinSource12, GPIO_AF_CAN1); GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12; GPIO_Init(GPIOA, &GPIO_InitStructure); // CAN1 初始化 CAN_InitStructure.CAN_TTCM = DISABLE; CAN_InitStructure.CAN_ABOM = DISABLE; CAN_InitStructure.CAN_AWUM = DISABLE; CAN_InitStructure.CAN_NART = DISABLE; CAN_InitStructure.CAN_RFLM = DISABLE; CAN_InitStructure.CAN_TXFP = DISABLE; CAN_InitStructure.CAN_Mode = CAN_Mode_Normal; CAN_InitStructure.CAN_SJW = CAN_SJW_1tq; CAN_InitStructure.CAN_BS1 = CAN_BS1_6tq; CAN_InitStructure.CAN_BS2 = CAN_BS2_7tq; CAN_InitStructure.CAN_Prescaler = 12; // 设置波特率为500Kbps CAN_Init(CAN1, &CAN_InitStructure); // CAN筛选器初始化 CAN_FilterInitStructure.CAN_FilterNumber = 0; CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdList; CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit; CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000; CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000; CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000; CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000; CAN_FilterInitStructure.CAN_FilterFIFOAssignment = 0; CAN_FilterInitStructure.CAN_FilterActivation = ENABLE; CAN_FilterInit(&CAN_FilterInitStructure); } void CAN1_SendMessage(void) { uint8_t TransmitSuccess = 0; // 配置发送消息 TxMessage.StdId = 0x321; // 标准标识符 TxMessage.ExtId = 0x01; // 扩展标识符 TxMessage.RTR = CAN_RTR_DATA; // 数据帧 TxMessage.IDE = CAN_ID_STD; // 使用标准标识符 TxMessage.DLC = 2; // 数据长度为2字节 TxMessage.Data[0] = 0xAA; // 数据字节1 TxMessage.Data[1] = 0x55; // 数据字节2 TransmitSuccess = CAN_Transmit(CAN1, &TxMessage); // 发送消息 if (TransmitSuccess == CAN_TxStatus_Ok) { // 发送成功 // 执行其他操作 } } void CAN1_ReceiveMessage(void) { if (CAN_MessagePending(CAN1, CAN_FIFO0) != 0) { // 接收到CAN消息 CAN_Receive(CAN1, CAN_FIFO0, &RxMessage); // 读取消息 // 处理接收到的消息 if (RxMessage.StdId == 0x321 && RxMessage.RTR == CAN_RTR_DATA) { // 标识符和RTR校验 // 执行其他操作 } } } int main(void) { CAN1_Configuration(); // 配置CAN1 while (1) { CAN1_SendMessage(); // 发送CAN消息 CAN1_ReceiveMessage(); // 接收CAN消息 } } ``` 以上是一个基本的STM32F407 CAN通信代码示例,包括了CAN1的初始化、发送消息和接收消息的函数。在`CAN1_SendMessage()`函数中,可以通过设置`TxMessage`结构体的成员来配置发送的CAN消息。在`CAN1_ReceiveMessage()`函数中,通过调用`CAN_Receive()`函数来读取消息并进行处理。在`main()`函数中,循环调用发送和接收函数。请注意,此示例仅为演示基本CAN通信的代码框架,具体的功能和扩展应根据实际需求进行进一步开发。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值