本帖最后由 JasonLee27 于 2019-12-3 13:46 编辑
硬件环境: 通用开发板,ATC-LINK
软件环境:keil 5.23
我看很多人都不太了解CAN的硬件过滤机制以及设置方法,这里专门拿出来讲一下,其实芯片虽然不同,但过滤的原理都是大同小异,都是通过设置过滤ID以及ID掩码来选择接收的ID。
为了简单方便,我就直接用范例里面的CAN_sample来修改,首先把范例里面设置过滤的代码注释掉:
#if 0
#if (RECV_FRM_KIND_SEL == ONLY_RECV_STD_FRM)
CAN_SetFilterParam( 0, 1, CAN_FILTER_CODE_MODE, FILTER_IDE_STD_ONLY, CAN1_RECV_DATA_ID1);//ʹÓÃCODEģʽֻ½ÓÊÕSTDÖ¡
CAN_SetFilterParam( 1, 1, CAN_FILTER_CODE_MODE, FILTER_IDE_STD_ONLY, CAN1_RECV_DATA_ID2);//ʹÓÃCODEģʽֻ½ÓÊÕSTDÖ¡
#endif
#if (RECV_FRM_KIND_SEL == ONLY_RECV_EXT_FRM)
CAN_SetFilterParam( 0, 1, CAN_FILTER_CODE_MODE, FILTER_IDE_EXT_ONLY, CAN1_RECV_DATA_ID11);//ʹÓÃCODEģʽֻ½ÓÊÕEXTÖ¡
CAN_SetFilterParam( 1, 1, CAN_FILTER_CODE_MODE, FILTER_IDE_EXT_ONLY, CAN1_RECV_DATA_ID12);//ʹÓÃCODEģʽֻ½ÓÊÕEXTÖ¡
#endif
#if (RECV_FRM_KIND_SEL == RECV_STD_EXT_BOTH_FRM)
CAN_SetFilterParam( 0, 1, CAN_FILTER_MASK_MODE, FILTER_IDE_STD_EXT_BOTH, (CAN1_RECV_DATA_ID1 & CAN1_RECV_DATA_ID11));//ʹÓÃMASKģʽ½ÓÊÕÁ½ÖÖÖ¡
CAN_SetFilterParam( 1, 1, CAN_FILTER_MASK_MODE, FILTER_IDE_STD_EXT_BOTH, (CAN1_RECV_DATA_ID2 & CAN1_RECV_DATA_ID12));//ʹÓÃMASKģʽ½ÓÊÕÁ½ÖÖÖ¡
#endif
#else
///自己的过滤代码
#endif
我们将通过直接赋值的方式,更加直观的了解硬件过滤的工作原理。
首先我们要找到用于设置过滤通道的tab变量 g_canFilterTab,硬件过滤通道总共有16个,所以这个tab数组也是16。
typedef struct
{
uint8_t index; ///< Filter index
uint8_t enable; ///< Enable or disable
uint32_t code; ///< Code data
uint32_t mask; ///< Mask data
} CAN_FilterControl;
再看下这个tab的结构体,里面有4个成员,index表示对应的硬件通道号,从0~15顺序排列,enable表示对应的硬件通道是否使能。这里要注意,所有的报文接收都必须经过硬件过滤通道,所以,至少要打开一路硬件通道,否则就一个报文也收不到了,你可以测试一下再不使能任一通道的情况下,你用CAN盒发任何报文,芯片都无法接收,这些是论坛里之前有网友遇到过的问题,特此提出来。
那为什么代码直接不设置硬件过滤反而可以接收呢?
int32_t CAN_Initialize(CAN_Type *CANx, CAN_Config *config, const CAN_BaudrateConfig *baudrate)
{
uint8_t canIndex = GetCanIndex(CANx), i = 0;
CAN_FilterControl *filterList = (CAN_FilterControl *)&s_filterControl[0];
int32_t ret = ERROR_NO_EXCUTE;
我们可以看到在can驱动代码里面,can初始化函数默认有一个过滤控制列表s_filterControl,如果你没有填入自己的过滤list,驱动就会用自己的list来初始化硬件通道。
///< Filter sample setting list, or user define
static const CAN_FilterControl s_filterControl[CAN_MAX_FILTER_NUM] =
{
{0, 1, 0x00000000, 0x1FFFFFFF}, // mask mode
{1, 0, 0x00000002, 0x00000000}, // code mode
{2, 0, 0x00000013, 0x00000000},
{3, 0, 0x00000124, 0x00000000},
{4, 0, 0x00000050, 0x1FFFFF0F},
{5, 0, 0x00000060, 0x1FFFFF0F},
{6, 0, 0x00000007, 0x1FFFFFF0},
{7, 0, 0x00000008, 0x1FFFFFF0},
{8, 0, 0x00000009, 0x5FFFFFF0}, // standard frames
{9, 0, 0x0000000a, 0x7FFFFFF0}, // extended frames
{10, 0, 0x00000700, 0x1FFFF0FF}, // Both
{11, 0, 0x0000c000, 0x7FFF0FFF},
{12, 0, 0x000d0000, 0x7FF0FFFF},
{13, 0, 0x00e00000, 0x7F0FFFFF},
{14, 0, 0x0f000000, 0x70FFFFFF},
{15, 0, 0x10000000, 0x6FFFFFFF},
};
可以看到这个list默认是使能了第一个通道的。实际上硬件复位也是默认启用0通道的。
接下来讲讲另外两个成员,code和mask;
code就是你要接收的ID,只有前29个bit是有效的。
mask就是你选择要检查的bit,1表示屏蔽检查,0表示匹配相应的bit。具体怎么理解呢,举两个例子,假如我只想接收ID=0x400的报文,那可以如下设置:
#else
g_canFilterTab[0].enable = 1;
g_canFilterTab[0].code = 0x400;
g_canFilterTab[0].mask = 0x0;
#endif
把这段代码烧写进去,会发现你用can盒发送别的ID报文,led2都不会闪烁(闪烁表示接收到报文),只有ID400的报文才可以被接收
但有时候我们并不是只想接收一个ID,而是一段ID,这个时候就要用mask屏蔽部分bit,例如,我想要接收0x400到0x4FF段的报文,其余报文都不接收:
#else
g_canFilterTab[0].enable = 1;
g_canFilterTab[0].code = 0x400;
g_canFilterTab[0].mask = 0x0;
g_canFilterTab[1].enable = 1;
g_canFilterTab[1].code = 0x400;
g_canFilterTab[1].mask = 0x000000FF;
#endif
我们把这个规则用于通道1,把这段代码烧进去测试,可以看到CAN盒只接收ID400~4FF的报文,这是因为mask最后8个bit设置为1,表示屏蔽最后8个bit的检查,也就是对接收到的ID的最后8个bit是不进行匹配检查的。
除了ID匹配之外,mask的高3个bit还用于扩展帧和标准帧的检查。
这里bit5,6对应的是mask的bit29,bit30。可以通过这两个bit选择是否只接收标准帧或者扩展帧。
另外:对于任何一个ID,只要满足你使能的任何一个硬件通道要求,就可以被接收。
代码回复后可见
|
感谢分享,学习学习