stm32 cubemx can通讯(1)回环模式
过滤器可以说是can中最重要的东西,我也认为这个是一个相当复杂的一个东西。
一个刚刚学习的小白,以下文字只是自我学习的笔记,若有写错或不明确的地方请大佬指正。
CAN过滤器是一个非常重要的功能,它允许你只接收你关心的CAN消息,从而提高效率和性能。
在CAN总线上,每个消息都有一个唯一的标识符(ID)。这个ID可以是11位(标准ID)或29位(扩展ID)。ID不仅仅是用来标识消息来源,而且还决定了消息的优先级:ID小的消息优先级更高。
过滤器的工作原理是将传入的ID与预设的ID进行比较,然后决定是否接收这个消息。过滤器可以工作在两种模式下:
(下面只要知道大致流程就行不要知道sFilterConfig这些东西是啥,后面会说)
1.3节都是基于标准ID
首先, 我们考虑一个例子:我们只希望接收ID为0x123的消息,其他消息都忽略。
设置选择的过滤器:
sFilterConfig.FilterBank = 0; // 使用过滤器0
设置过滤器模式为掩码模式:
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
使用32位模式:
这使得我们可以处理标准ID和扩展ID。
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
指定我们关心的ID和掩码:
因为0x123
是一个标准ID,我们可以这样配置:
sFilterConfig.FilterIdHigh = 0x123 << 5; // 11位ID需要左移5位
sFilterConfig.FilterIdLow = 0x0000;
sFilterConfig.FilterMaskIdHigh = 0xFFFF; // 只关心高16位
sFilterConfig.FilterMaskIdLow = 0x0000;
这样的配置意味着:我们只接收高16位是0x123,低16位可以是任何值的消息。由于标准ID只有11位,低16位永远是0,所以这正是我们想要的。
为什么左移5位?
STM32的CAN过滤器使用了一种特殊的方式来处理标准11位和扩展29位的ID。为了在32位寄存器中放入11位的标准ID,我们需要将其左移5位。这是硬件的要求,不是我们随便决定的。
所以,如果你有一个标准ID0x123,其二进制形式是:
100100011
为了将它放入STM32的过滤器寄存器,你需要将它左移5位:
100100011 00000
这就是为什么我们使用<< 5。
为什么只关心高16位
当我们说“只关心高16位”,其实是指在32位的掩码或ID中,我们只设置或匹配高16位。STM32的CAN过滤器将32位分为了两个16位的部分:FilterIdHigh和FilterIdLow。在我们的例子中,由于我们只处理一个11位的标准ID,所以我们只需要在FilterIdHigh部分设置它,而将FilterIdLow部分设置为0。
关于11位与16位的区别:
实际上,我们并不真的关心整个16位,只关心其中的高11位。但由于简化和兼容性的考虑,我们通常设置整个16位。
如果我们想要严格限制为只匹配高11位,并且确保其他位不匹配,我们可以使用0xF800作为掩码。这表示我们关心高11位(1111 1000 0000 0000),而不关心低5位。
总之,为了简化操作,我们通常使用0xFFFF作为掩码来接收所有与指定ID匹配的标准CAN消息。但如果你想要更精确的匹配,你可以调整掩码以只匹配高11位。
设置FIFO:
sFilterConfig.FilterFIFOAssignment = CAN_FILTER_FIFO0;
这意味着匹配的消息将被放入FIFO0。
当想要监听多个标准ID的时候,有几种方法可以实现:
简而言之,掩码告诉硬件哪些位是我们关心的,而过滤器则告诉硬件我们希望这些位的值是什么。如果接收到的消息与过滤器匹配(只在我们关心的位上),那么消息将被接受。
STM32的CAN控制器提供了多个过滤器(具体数量取决于具体的STM32型号)。每个过滤器可以配置为匹配一个或多个ID。
例如,你想要监听两个标准ID:0x123和0x456。你可以设置两个过滤器,每个过滤器匹配一个ID。
// 过滤器0匹配0x123
sFilterConfig.FilterBank = 0; // 使用过滤器0
sFilterConfig.FilterIdHigh = 0x123 << 5;
sFilterConfig.FilterMaskIdHigh = 0xFFFF;
HAL_CAN_ConfigFilter(&hcan, &sFilterConfig);
// 过滤器1匹配0x456
sFilterConfig.FilterBank = 1; // 使用过滤器1
sFilterConfig.FilterIdHigh = 0x456 << 5;
sFilterConfig.FilterMaskIdHigh = 0xFFFF;
HAL_CAN_ConfigFilter(&hcan, &sFilterConfig);
这种方法取决于你想要监听的ID的特性。如果这些ID有一定的位集合,你可以使用掩码来匹配它们。
在列表模式下,过滤器会尝试匹配你所列举的任何ID。对于标准ID,一个过滤器可以匹配两个ID。对于扩展ID,过滤器只能匹配一个ID。
这里是一个如何使用列表模式来匹配四个标准ID(0x1, 0x2, 0x3, 0x4)的示例:
// 过滤器1匹配0x1和0x2
sFilterConfig.FilterBank = 0;
sFilterConfig.FilterMode = CAN_FILTERMODE_IDLIST;
sFilterConfig.FilterScale = CAN_FILTERSCALE_16BIT; // 16-bit列表模式用于标准ID
sFilterConfig.FilterIdHigh = 0x1 << 5; // ID1
sFilterConfig.FilterIdLow = 0x2 << 5; // ID2
HAL_CAN_ConfigFilter(&hcan, &sFilterConfig);
// 过滤器2匹配0x3和0x4
sFilterConfig.FilterBank = 1;
sFilterConfig.FilterIdHigh = 0x3 << 5; // ID3
sFilterConfig.FilterIdLow = 0x4 << 5; // ID4
HAL_CAN_ConfigFilter(&hcan, &sFilterConfig);
为什么使用 CAN_FILTERSCALE_16BIT 而不是 CAN_FILTERSCALE_32BIT?
当我们使用标准ID(11位)并希望用单个过滤器同时匹配多个ID时,CAN_FILTERSCALE_16BIT 是有意义的。STM32的CAN硬件允许我们在16位过滤模式下为两个标准ID设置单个过滤器。因此,每个32位寄存器可以分为两个16位部分,每部分用于一个标准ID。这使得硬件更加高效地匹配两个不同的ID,而不需要额外的过滤器。
关于 sFilterConfig.FilterIdHigh 和 sFilterConfig.FilterIdLow 的顺序:
过滤器的初始化顺序是可以互换的。你可以首先设置 FilterIdLow,然后再设置 FilterIdHigh。硬件只是看这两个值和接收的消息ID进行比较,不关心你是如何设置这两个值的。
sFilterConfig.FilterIdLow = 0x1 << 5; sFilterConfig.FilterIdHigh = 0x2 << 5;
这也是有效的,它将会匹配ID为 0x1 和 0x2 的消息。
我们接着使用回环模式类似的代码,
这时候由于使用到了usb_can调试器,所以你要有一个这个东西。emm
然后cubemx里面的回环模式给位normal模式就行。
把发送函数改成这样,改动的地方在于。现在使用的是标准id,由于标准id最大支持11位,所以就要求最大不要超过0x7ff,并把ide改为id_std
void CAN_senddata(CAN_HandleTypeDef *hcan)
{
TXHeader.StdId=0x7ff;//0X7FF因为标准id最大11位
TXHeader.ExtId=0x0000000;
TXHeader.DLC=8;
TXHeader.IDE=CAN_ID_STD;
TXHeader.RTR=CAN_RTR_DATA;
TXHeader.TransmitGlobalTime = DISABLE;
HAL_CAN_AddTxMessage(hcan,&TXHeader,TXmessage,&pTxMailbox);
}
只需要这些代码在can_init的位置就可以了。
当使用01 和 02进行发送的时候可以产生中断。其他的不行
/* USER CODE BEGIN CAN_Init 2 */
CAN_FilterTypeDef sFilterConfig;
sFilterConfig.FilterActivation = ENABLE;
sFilterConfig.FilterBank = 0;
sFilterConfig.FilterMode = CAN_FILTERMODE_IDLIST;
sFilterConfig.FilterScale = CAN_FILTERSCALE_16BIT; // 16-bit列表模式用于标准ID
sFilterConfig.FilterFIFOAssignment = CAN_FILTER_FIFO0;//采用FIFO0
sFilterConfig.FilterIdHigh = 0x1 << 5; // ID1
sFilterConfig.FilterIdLow = 0x2 << 5; // ID2
if(HAL_CAN_ConfigFilter(&hcan,&sFilterConfig) != HAL_OK)//初始化过滤器
{
Error_Handler();
}
if(HAL_CAN_Start(&hcan) != HAL_OK)//打开can
{
Error_Handler();
}
if(HAL_CAN_ActivateNotification(&hcan,CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK)//开启接受邮箱0挂起中断
{
Error_Handler();
}
/* USER CODE END CAN_Init 2 */
只需要这些代码在can_init的位置就可以了。
当使用01 和 02进行发送的时候可以产生中断。其他的不行
CAN_FilterTypeDef sFilterConfig;
sFilterConfig.FilterActivation = ENABLE;
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; // 16-bit列表模式用于标准ID
sFilterConfig.FilterFIFOAssignment = CAN_FILTER_FIFO0;//采用FIFO0
// 过滤器匹配0x1
sFilterConfig.FilterBank = 0;
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
sFilterConfig.FilterIdHigh = 0x1 << 5;
sFilterConfig.FilterMaskIdHigh = 0xFFFF;
if(HAL_CAN_ConfigFilter(&hcan,&sFilterConfig) != HAL_OK)//初始化过滤器
{
Error_Handler();
}
// 过滤器匹配0x1
sFilterConfig.FilterBank = 1;
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
sFilterConfig.FilterIdHigh = 0x2<< 5;
sFilterConfig.FilterMaskIdHigh = 0xFFFF;
if(HAL_CAN_ConfigFilter(&hcan,&sFilterConfig) != HAL_OK)//初始化过滤器
{
Error_Handler();
}
int cc =0;
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)//接受邮箱0挂起中断回调函数
{
if(hcan->Instance == CAN1)
{
// 获取数据
HAL_CAN_GetRxMessage(hcan, CAN_FILTER_FIFO0, &RXHeader, RXmessage);
// 根据接收到的ID进行处理
switch (RXHeader.StdId)
{
case 0x1:
// 对于ID 0x1的处理代码
cc=1;
break;
case 0x2:
// 对于ID 0x2的处理代码
cc=2;
break;
default:
// 其他未知ID的处理(如果需要)
break;
}
}
}