大家好,又见面了,我是你们的朋友全栈君。
第一步:了解Can通信接口协议,这里推荐大家 <<Can入门教程>>(必读),里面详细说明的can相关知识点;另外推荐大家看有关Can协议标准书籍(选读),相关的pdf书籍下载地址:链接:https://pan.baidu.com/s/1KDtoqkm541xZhoTUpXVJaw 提取码:9dvs
第二步: 特别需要关注点,1、通信速度与传输距离关系,2、通讯接口的硬件连接方式(终端电阻)3、协议帧的种类和格式
4、位时序定义和采样点位置设置 注意:请大家务必了解该知识点,该知识点与支持can协议控制芯片MUC息息相关
第三步:学习如何使用can控制芯片(STM32系列IC)传输数据
特别说明:stm32芯片的CAN接口是Bxcan接口,芯片仅提供can控制,不支持can传输物理层,所以硬件设计上需要使用CAN收发器连接mcu与CAN总线才可以正常工作,同时需要注意终端电阻。
有关stm32的can知识点,请查看中文数据手册:下载链接如下:链接:https://pan.baidu.com/s/1hlcSPKhZI6FyUinlgQbDnw 提取码:pgo3
stm32的CAN相关知识点:
一、stm32对Can波特率与位时序定义:
● 同步段(SYNC_SEG):通常期望位的变化发生在该时间段内。其值固定为1 个时间单元(1 x tCAN)。 ● 时间段1(BS1):定义采样点的位置。它包含CAN 标准里的 PROP_SEG 和 PHASE_SEG1。其值可以编程为1 到16 个时间单元,但也可以被自动延 长,以补偿因为网络中不同节点的频率差异所造成的相位的正向漂移。 ● 时间段2(BS2) : 定义发送点的位置。它代表CAN 标准里的 PHASE_SEG2。其值可以编程为1 到8 个时间单元,但也可以被自动缩短 以补偿相位的负向漂移
BRP[9:0]: 波特率分频器
PCLK = Can在stm32外设总线上时钟大小(请查看stm23时钟树看can对应的时钟)
bps = PCLK/(BRP[9:0]*(BS1 BS2 SYNC_SEG))
其中SYNC_SEG固定为1个时间单元所以 bps = PCLK/(BRP[9:0]*(BS1 BS2 1)),此时注意跟同步跳转宽度SJW没关系,尽管他也一直设置为1.
二、stm32的CAN采样点设置
采样点位于时间段1和时间段2之间。根据CIA推荐采样点,最好设置在85%~90%。
计算公式: (BS1 1)/(1 BS1 BS2)
以下是经验值,仅供参考:
75% when 波特率 > 800K
80% when 波特率 > 500K
87.5% when 波特率 <= 500K
75% when 波特率 > 800K
80% when 波特率 > 500K
87.5% when 波特率<= 500K
三:STM32 CAN 过滤器
1)两种过滤模式定义:列表模式 掩码模式
列表模式:把我们需要关注的所有CAN报文ID写上去,开始过滤的时候只要对比这张表,如果接收到的报文ID与表上的相符,则通过,如果表上没有,则不通过。但是,这种列表方案有点缺陷,即如果我们只关注一个报文ID,则需要往列表中写入这个ID,如果需要关注两个,则需要写入两个报文ID,如果需要1万个,那么需要写入1万个,可问题是,MCU上的资源是有限的,不可能提供1万个或更多。非常明显,这种列表的方式受到列表容量大小的限制。
掩码模式:包括验证码和掩码,其中验证码即为我们需要关注的报文大致ID,掩码则是过滤验证码,验证码与掩码进行与操作得到结果值就是验证通过的报文ID。比如验证码设置为0x12345678;掩码0xff00ff00;进行与后结果为 0x12xx56xx;也就是说只要收到报文ID[31:24] = 0x12,ID[15:8] = 0x56(其他位不考虑,可为0或1)都是正确的报文ID,也就是可以通过过滤器;假如设置掩码为0x00000000,则表示任意报文ID都可以通过过滤器。
2) 列表模式与掩码模式的对比
3)can 工作模式:32位列表模式,32位掩码模式,16位列表模式,16位掩码模式
在bxCAN中,每个过滤器都存在这么两个寄存器CAN_FxR1和CAN_FxR2,这两个寄存器都是32位的,他的定义并不是固定的,针对不同的工作模式组合他的定义是不一样的,如列表模式-32位宽模式下,这两个寄存器的各位定义都是一样的,都用来存储某个具体的期望通过的CAN ID,这样就可以存入2个期望通过的CAN ID(标准CAN ID和扩展CAN ID均可);若在掩码模式-32位宽模式下时,则CAN_FxR1用做32位宽的验证码,而CAN_FxR2则用作32位宽的屏蔽码。在16位宽时,CAN_FxR1和CAN_FxR2都要各自拆分成两个16位宽的寄存器来使用,在列表模式-16位宽模式下,CAN_FxR1和CAN_FxR2定义一样,且各自拆成两个,则总共可以写入4个标准CAN ID,若在16位宽的掩码模式下,则可以当做2对验证码 屏蔽码组合来用,但它只能对标准CAN ID进行过滤。这个就是bxCAN过滤器的解决方案。 4) 运用实例:
注意:stm32官方提供的demo中,官方提供4个int16_t变量数据表示CAN_FxR1和CAN_FxR1寄存器;它们分别为FilterIdHigh,FilterIdLow,FilterMaskIdHigh, FilterMaskIdLow;在不同的位宽工作模式下组合不一样;
16位带宽模式下(寄存器在不同过滤模式下表示值请参考上图):
CAN_FxR1 = (FilterMaskIdLow << 16 | FilterIdLow)
CAN_FxR2 = (FilterMaskIdHigh<< 16 | FilterIdHigh)
可能有人会有这样疑问,为什么Mask在高16位?其实我们看会上图给出的4中带宽工作模式就可以知道,带mask都是占用寄存器高位的
32位带宽模式下(寄存器在不同过滤模式下表示值请参考上图):
CAN_FxR1 = (FilterIdHigh<< 16 | FilterIdLow)
CAN_FxR2 = (FilterMaskIdHigh<< 16 | FilterMaskIdLow )
运用实例1: 16位带宽掩码模式:
代码语言:javascript复制static void CANFilterConfig_Scale16_IdMask(void)
{
CAN_FilterConfTypeDef sFilterConfig;
uint16_t StdIdArray1[10] ={0x7D1,0x7D2,0x7D3,0x7D4,0x7D5, //定义第一组标准CAN ID
0x7D6,0x7D7,0x7D8,0x7D9,0x7DA};
uint16_t StdIdArray2[10] ={0x751,0x752,0x753,0x754,0x755, //定义第二组标准CAN ID
0x756,0x757,0x758,0x759,0x75A};
uint16_t mask,tmp,i,num;
sFilterConfig.FilterNumber = 5; //使用过滤器5
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; //配置为掩码模式
sFilterConfig.FilterScale = CAN_FILTERSCALE_16BIT; //设为16位宽
//配置第一个过滤对
sFilterConfig.FilterIdLow =StdIdArray1[0]<<5; //设置第一个验证码
mask =0x7ff;
num =sizeof(StdIdArray1)/sizeof(StdIdArray1[0]);
for(i =0; i<num; i ) //计算第一个屏蔽码
{
tmp =StdIdArray1[i] ^ (~StdIdArray1[0]);
mask &=tmp;
}
sFilterConfig.FilterMaskIdLow =(mask<<5)|0x10; //只接收数据帧
//配置第二个过滤对
sFilterConfig.FilterIdHigh = StdIdArray2[0]<<5; //设置第二个验证码
mask =0x7ff;
num =sizeof(StdIdArray2)/sizeof(StdIdArray2[0]);
for(i =0; i<num; i ) //计算第二个屏蔽码
{
tmp =StdIdArray2[i] ^ (~StdIdArray2[0]);
mask &=tmp;
}
sFilterConfig.FilterMaskIdHigh = (mask<<5)|0x10; //只接收数据帧
sFilterConfig.FilterFIFOAssignment = 0; //通过的CAN 消息放入到FIFO0中
sFilterConfig.FilterActivation = ENABLE;
sFilterConfig.BankNumber = 14;
if(HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK)
{
Error_Handler();
}
}
运用实例2:16位带宽列表模式:
代码语言:javascript复制static void CANFilterConfig_Scale16_IdList(void)
{
CAN_FilterConfTypeDef sFilterConfig;
uint32_t StdId1 =0x123; //这里采用4个标准CAN ID作为例子
uint32_t StdId2 =0x124;
uint32_t StdId3 =0x125;
uint32_t StdId4 =0x126;
sFilterConfig.FilterNumber = 1; //使用过滤器1
sFilterConfig.FilterMode = CAN_FILTERMODE_IDLIST; //设为列表模式
sFilterConfig.FilterScale = CAN_FILTERSCALE_16BIT; //位宽设置为16位
sFilterConfig.FilterIdHigh = StdId1<<5; //4个标准CAN ID分别放入到4个存储中
sFilterConfig.FilterIdLow = StdId2<<5;
sFilterConfig.FilterMaskIdHigh = StdId3<<5;
sFilterConfig.FilterMaskIdLow = StdId4<<5;
sFilterConfig.FilterFIFOAssignment = 0; //接收到的报文放入到FIFO0中
sFilterConfig.FilterActivation = ENABLE;
sFilterConfig.BankNumber = 14;
if(HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK)
{
Error_Handler();
}
}
运用实例3: 32位带宽掩码模式
代码语言:javascript复制static void CANFilterConfig_Scale32_IdMask_ExtendIdOnly(void)
{
CAN_FilterConfTypeDef sFilterConfig;
//定义一组扩展CAN ID用来测试
uint32_t ExtIdArray[10] ={0x1839f101,0x1835f102,0x1835f113,0x1835f124,0x1835f105,
0x1835f106,0x1835f107,0x1835f108,0x1835f109,0x1835f10A};
uint32_t mask,num,tmp,i;
sFilterConfig.FilterNumber = 3; //使用过滤器3
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; //配置为掩码模式
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; //设为32位宽
sFilterConfig.FilterIdHigh =((ExtIdArray[0]<<3) >>16) &0xffff;//数组任意一个成员都可以作为验证码
sFilterConfig.FilterIdLow =((ExtIdArray[0]<<3)&0xffff) | CAN_ID_EXT;
mask =0x1fffffff;
num =sizeof(ExtIdArray)/sizeof(ExtIdArray[0]);
for(i =0; i<num; i ) //屏蔽码位数组各成员相互同或的结果
{
tmp =ExtIdArray[i] ^ (~ExtIdArray[0]); //都与第一个数据成员进行同或操作
mask &=tmp;
}
mask <<=3; //对齐寄存器
sFilterConfig.FilterMaskIdHigh = (mask>>16)&0xffff;
sFilterConfig.FilterMaskIdLow = (mask&0xffff)|0x02; //只接收数据帧
sFilterConfig.FilterFIFOAssignment = 0;
sFilterConfig.FilterActivation = ENABLE;
sFilterConfig.BankNumber = 14;
if(HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK)
{
Error_Handler();
}
}
运用实例4: 32带宽列表模式
代码语言:javascript复制static void CANFilterConfig_Scale32_IdList(void)
{
CAN_FilterConfTypeDef sFilterConfig;
uint32_t StdId =0x321; //这里写入两个CAN ID,一个位标准CAN ID
uint32_t ExtId =0x1800f001; //一个位扩展CAN ID
sFilterConfig.FilterNumber = 0; //使用过滤器0
sFilterConfig.FilterMode = CAN_FILTERMODE_IDLIST; //设为列表模式
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; //配置为32位宽
sFilterConfig.FilterIdHigh = StdId<<5; //基本ID放入到STID中
sFilterConfig.FilterIdLow = 0|CAN_ID_STD; //设置IDE位为0
sFilterConfig.FilterMaskIdHigh = ((ExtId<<3)>>16)&0xffff;
sFilterConfig.FilterMaskIdLow = (ExtId<<3)&0xffff|CAN_ID_EXT; //设置IDE位为1
sFilterConfig.FilterFIFOAssignment = 0; //接收到的报文放入到FIFO0中
sFilterConfig.FilterActivation = ENABLE;
sFilterConfig.BankNumber = 14;
if(HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK)
{
Error_Handler();
}
}
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/151739.html原文链接:https://javaforall.cn