打印
[STM32F0]

CAN模块,按照官方例程初始化不成功

[复制链接]
2792|12
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
renyaq|  楼主 | 2016-8-24 22:07 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
调试STM32F072的can模块,发现按照官方例程在运行到CAN_Init(CAN, &CAN_InitStructure);     时,软件可以通过对CAN_MCR寄存器的INRQ位清’0’,来请求从初始化模式进入正常模式,然后要等待硬件对CAN_MSR寄存器的INAK位置’1’的确认  ,就是 while (((CANx->MSR & CAN_MSR_INAK) == (uint16_t)CAN_MSR_INAK) && (wait_ack != INAK_TIMEOUT))
   {
     wait_ack++;   
   }
但是为什么我这里一直不成功啊?
沙发
598330983| | 2016-8-24 22:34 | 只看该作者
CAN ID值的结构分析

              在讲到代码实例之前,首先大家都弄懂一件事,当给定一个CAN ID,如0x1800f001,当然这个是扩展ID,这里要问的是,这个CAN ID的值本身包含两部分,即基本ID与扩展ID,即么你知道这个扩展ID0x1800f001的哪些位是基本ID,哪些位又是扩展ID?(在基本CANID格式下不存在这个问题)

                在回答这个问题之前我们来看看ISO11898的定义,如下图:


                                                                                     图9

如上图,基本格式不存在扩展ID,而扩展格式中ID0~ID17为Extension ID,而ID18~ID28为Base ID.

因此CAN ID值0x1800f001用二进制表示为:0b 0001 1000 0000 0000 1111 0000 0000 0001,用括号分别区别为:0b 000[1 1000 0000 00][00 1111 0000 0000 0001],红色部分为扩展ID,蓝色部分为基本ID。那么知道这些有什么用呢?接下来的代码示例中你就会有什么用了。

4.2 位宽为32位的屏蔽模式

在此种模式下中过滤多个CAN ID,此时,过滤器包含两个寄存器,屏蔽码寄存器和标识符寄存器。此模式下最多只存在一个屏蔽过滤器。

如下图所示:

                                                                                 图10

如上图,上面的ID为标识符寄存器,中间部分的MASK为屏蔽码寄存器。每个寄存器都是32位的。最下边显示的是与CAN ID各位定位的映射关系。由4.1的知识很快可以发现,上图最下边的映射关系恰好等于扩展CAN值左移3位再补上IDE(扩展帧标识),RTR(远程帧标志)。


因此,我们初步得出这样的推论:对于一个扩展CAN ID,不能单纯地将它看到的一个数,而应该将它看成两部分,基本ID和扩展ID(当然标准CAN ID只包含基本ID部分),过滤器屏蔽码寄存器和标识符寄存器也应该看成多个部分,然后问题就变成了如何将CAN ID所表示的各部分如何针对过滤器寄存器各部分对号入座的问题了。

对号入座的方法多种多样,但万变不离其心,主要是掌握其核心思想即可:1:在各种过滤器模式下,CAN ID与寄存器相应位置一定要匹配;2:在屏蔽方式下,屏蔽码寄存器某位为1表示接收到的CAN ID对应的位必须对验证码寄存器对应的位相同。


下面给出一个代码例子,假设我们要接收多个ID:0x7e9,0x1800f001,前面为标准ID,后面为扩展ID,要同时能接收这两个ID,那么该如何设置这个过滤器呢?

  • CAN_FilterInitTypeDef  CAN_FilterInitStructure;
  • U16 std_id =0x7e9;
  • U32 ext_id =0x1800f001;
  • U32 mask =0;
  • CAN_FilterInit(&CAN_FilterInitStructure);     //初始化CAN_FilterInitStructrue结构体变量
  • CAN_FilterInitStructure.CAN_FilterNumber=0;      //设置过滤器组0,范围为0~13
  • CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask;    //设置过滤器组0为屏蔽模式
  • CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit;   //设置过滤器组0位宽为32位
  • //标识位寄存器的设置
  • //ext_id<<3对齐,见上图9,再>>16取高16位
  • CAN_FilterInitStructure.CAN_FilterIdHigh=((ext_id<<3) >>16) &0xffff;  //设置标识符寄存器高字节。
  • CAN_FilterInitStructure.CAN_FilterIdLow=(U16)(ext_id<<3) | CAN_ID_EXT;   //设置标识符寄存器低字节
  • //这里也可以这样设置
  • //CAN_FilterInitStructure.CAN_FilterIdHigh=std_id<<5;  //设置标识符寄存器高字节.这里为什么是左移5位呢?从上图可以看出,CAN_FilterIdHigh包含的是STD[0~10]和EXID[13~17],标准CAN ID本身是不包含扩展ID数据,因此为了要将标准CAN ID放入此寄存器,标准CAN ID首先应左移5位后才能对齐.
  • //CAN_FilterInitStructure.CAN_FilterIdLow=0|CAN_ID_EXT;   //设置标识符寄存器低字节,这里也可以设置为CAN_ID_STD
  • //屏蔽寄存器的设置
  • //这里的思路是先将标准CAN ID和扩展CAN ID对应的ID值先异或后取反,为什么?异或是为了找出两个CAN ID有哪些位是相同的,是相同的位则说明需要关心,需要关心的位对应的屏蔽码位应该设置为1,因此需要取反一下。最后再整体左移3位。
  • mask =(std_id<<18);//这里为什么左移18位?因为从ISO11898中可以看出,标准CAN ID占ID18~ID28,为了与CAN_FilterIdHigh对齐,应左移2位,接着为了与扩展CAN对应,还应该再左移16位,因此,总共应左移2+16=18位。也可以用另一个方式来理解:直接看Mapping的内容,发现STDID相对EXID[0]偏移了18位,因此左移18位.
  • mask ^=ext_id;//将对齐后的标准CAN与扩展CAN异或后取反
  • mask =~mask;
  • mask <<=3;//再整体左移3位
  • mask |=0x02; //只接收数据帧,不接收远程帧
  • CAN_FilterInitStructure.CAN_FilterMaskIdHigh=(mask>>16)&0xffff; //设置屏蔽寄存器高字节
  • CAN_FilterInitStructure.CAN_FilterMaskIdLow=mask&0xffff;   //设置屏蔽寄存器低字节
  • CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_FIFO0;  //此过滤器组关联到接收FIFO0
  • CAN_FilterInitStructure.CAN_FilterActivation=ENABLE; //激活此过滤器组
  • CAN_FilterInit(&CAN_FilterInitStructure); //设置过滤器


[color=rgb(51, 102, 153) !important]复制代码

总结可知,当过滤器为屏蔽模式时,标识符寄存器对应的ID内容可为任意一需求接收的ID值,当同时要接收标准帧和扩展帧时,标识符寄存器对应IDE位也随意设置,屏蔽寄存器的IDE位设置为0,表示不关心标准帧还是扩展帧。而屏蔽寄存器对应的ID内容为各需求接收的ID值依次异或的结果再取反。

4.3 位宽为32位的标识符列表模式

在此种模式下,过滤器组包含的两个寄存器含义一样,此模式下只多存在两个标识符列表过滤器如下图:

                                                                                                   图11

  • CAN_FilterInitTypeDef  CAN_FilterInitStructure;
  • U16 std_id =0x7e9;
  • U32 ext_id =0x1800f001;
  • CAN_FilterInit(&CAN_FilterInitStructure); //初始化CAN_FilterInitStructrue结构体变量
  • CAN_FilterInitStructure.CAN_FilterNumber=0;     //设置过滤器组0,范围为0~13
  • CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdList;    //设置过滤器组0为标识符列表模式
  • CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit;   //设置过滤器组0位宽为32位
  • //设置屏蔽寄存器,这里当标识符寄存器用
  • CAN_FilterInitStructure.CAN_FilterIdHigh=std_id<<5) ;  //为什么左移5位?与上面相同道理,这里不再重复解释
  • CAN_FilterInitStructure.CAN_FilterIdLow=0|CAN_ID_STD; //设置标识符寄存器低字节,CAN_FilterIdLow的ID位可以随意设置,在此模式下不会有效。
  • //设置标识符寄存器
  • CAN_FilterInitStructure.CAN_FilterMaskIdHigh=((ext_id<<3)>>16) & 0xffff; //设置屏蔽寄存器高字节
  • CAN_FilterInitStructure.CAN_FilterMaskIdLow=((ext_id<<3)& 0xffff) | CAN_ID_EXT;   //设置屏蔽寄存器低字节
  • CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_FIFO0;  //此过滤器组关联到接收FIFO0
  • CAN_FilterInitStructure.CAN_FilterActivation=ENABLE; //激活此过滤器组
  • CAN_FilterInit(&CAN_FilterInitStructure); //设置过滤器


[color=rgb(51, 102, 153) !important]复制代码

4.4 位宽为16位的屏蔽码模式

在此模式下,最多存在两个屏蔽码过滤器,如下图:

                                                                                                          图12

由上图映射可知,最下面的映射只包含STDID0~ID10,因此,此模式下的两个屏蔽过滤器只能实现对标准ID的过滤。具体代码就不介绍了,参见上图的映射即可。


4.5 位宽为16位的标识符列表模式

                                                                                              图13

在此模式下,由于标识符寄存器的高16位和低16位,屏蔽寄存器的高16位和低16位都用来做标识符寄存器,因此,最多可存在4个标识符过滤器。同样,只能实现对标准帧的过滤。具体代码就不介绍了,参见上图的映射即可。


以上是别人博客转载的


下面是i我自己的代码

  • /**
  • * @brief This function config can's fiflter.
  • */
  • void Can_FilterConfig(void)
  • {
  •         CAN_FilterConfTypeDef FilterConfig;
  •         FilterConfig.FilterIdHigh         = ((ext_id<<3) >>16) &0xffff;
  •         FilterConfig.FilterIdLow          = (uint16_t)(ext_id<<3) | CAN_ID_EXT | CAN_RTR_DATA;
  •         FilterConfig.FilterMaskIdHigh     = 0xfff8;
  •         FilterConfig.FilterMaskIdLow      = 0x07ff;
  •         FilterConfig.FilterFIFOAssignment = 0x00;
  •         FilterConfig.FilterNumber         = 0x00;
  •         FilterConfig.FilterMode           = CAN_FILTERMODE_IDMASK;
  •         FilterConfig.FilterScale          = CAN_FILTERSCALE_32BIT;
  •         FilterConfig.FilterActivation     = ENABLE;
  •         hcan.Instance = CAN;
  •         HAL_CAN_ConfigFilter(&hcan, &FilterConfig);
  • }
  • /**
  • * @brief This function is can send pross.
  • */
  • void Can_Assignin_Send_Pross(uint8_t *pbuf)
  • {
  •         hcan.Instance = CAN;
  •         hcan.pTxMsg = &TxMsgStruct;
  •         hcan.pRxMsg = &RxMsgStruct;
  •         TxMsgStruct.ExtId = 0x18ff0004;
  •         TxMsgStruct.IDE   = CAN_ID_EXT  ;
  •         TxMsgStruct.RTR   = CAN_RTR_DATA;
  •         TxMsgStruct.DLC   = 6;
  •         TxMsgStruct.Data[0] = pbuf[0];
  •         TxMsgStruct.Data[1] = pbuf[1];
  •         TxMsgStruct.Data[2] = pbuf[2];
  •         TxMsgStruct.Data[3] = pbuf[3];
  •         TxMsgStruct.Data[4] = pbuf[4];
  •         TxMsgStruct.Data[5] = pbuf[5];
  •         HAL_CAN_Transmit(&hcan,0);
  •         HAL_CAN_Receive_IT(&hcan, 0x00);
  • }
  • /**
  • * @brief This function handles CEC and CAN interrupts.
  • */
  • void Can_Assignin_Rev_Pross(uint8_t *pbuf)
  • {
  •         hcan.Instance = CAN;
  •         hcan.pRxMsg = &RxMsgStruct;
  •   pbuf[0] = RxMsgStruct.Data[0];
  •         pbuf[1] = RxMsgStruct.Data[1];
  •         pbuf[2] = RxMsgStruct.Data[2];
  •         pbuf[3] = RxMsgStruct.Data[3];
  •         pbuf[4] = RxMsgStruct.Data[4];
  • }

[color=rgb(51, 102, 153) !important]复制代码





3.8 MB


使用特权

评论回复
板凳
zdphpn| | 2024-9-10 23:04 | 只看该作者
同样的问题,各种搜。最后发现是引脚复用的问题[注意GPIO_PinAFConfig的第二个参数],GPIO_PinAFConfig(GPIOB,GPIO_PinSource8,GPIO_AF_4);是正确的。GPIO_PinAFConfig(GPIOB,GPIO_Pin_8,GPIO_AF_4);是错误的。叨扰楼主。

使用特权

评论回复
地板
公羊子丹| | 2024-9-11 07:48 | 只看该作者
确认CAN模块的引脚与主控芯片的CAN引脚正确连接。常见引脚有CAN_H和CAN_L,这两个引脚是用于CAN总线通信的差分信号。

使用特权

评论回复
5
童雨竹| | 2024-9-11 07:50 | 只看该作者
CAN通信的波特率非常重要,发送方和接收方必须在同一波特率下工作。确认主控芯片的波特率配置和CAN模块匹配。

使用特权

评论回复
6
周半梅| | 2024-9-11 07:50 | 只看该作者
波特率公式通常与系统时钟有关,确保时钟配置没有错误。

使用特权

评论回复
7
帛灿灿| | 2024-9-11 07:51 | 只看该作者
CAN总线需要在总线的两端各放置一个120欧姆的终端电阻。没有正确的终端电阻会导致总线上的信号反射,影响通信。

使用特权

评论回复
8
万图| | 2024-9-11 07:51 | 只看该作者
如果使用的是独立CAN收发器芯片,检查该芯片是否工作正常,是否损坏。

使用特权

评论回复
9
Wordsworth| | 2024-9-11 07:52 | 只看该作者
模块上的收发器是否供电良好,并确保引脚没有松动或虚接。

使用特权

评论回复
10
Pulitzer| | 2024-9-11 07:53 | 只看该作者
确保CAN模块的时钟配置正确。STM32F072的CAN模块通常使用APB1总线时钟(PCLK1)。确认CAN模块启用了正确的时钟,并且时钟源的频率在允许范围内(一般是36MHz)。

使用特权

评论回复
11
Bblythe| | 2024-9-11 07:53 | 只看该作者
检查是否在代码中调用了RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN, ENABLE),以确保CAN模块的时钟已被启用。

使用特权

评论回复
12
Uriah| | 2024-9-11 07:54 | 只看该作者
在进行CAN初始化之前,应该对CAN模块进行复位。确保你在初始化前调用了CAN_DeInit(CANx),该函数会复位所有CAN寄存器到默认状态。

使用特权

评论回复
13
Clyde011| | 2024-9-11 07:55 | 只看该作者
在CAN模块进入正常模式之前,它首先需要进入初始化模式。检查你是否正确设置了CAN的初始化请求位 (INRQ),并确认CAN是否成功进入了初始化模式。
在设置INRQ之后,可以通过检查INAK位来确认CAN是否成功进入了初始化模式:

CANx->MCR |= CAN_MCR_INRQ;
while ((CANx->MSR & CAN_MSR_INAK) != CAN_MSR_INAK); // 等待进入初始化模式

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

700

主题

10138

帖子

4

粉丝