STM32 CAN外设接收中断无法触发问题解析
本嵌入式小白最近在跟着江科大学习相关CAN的相关知识,分享一下遇到的问题和解决方法,内容为个人的浅薄理解,欢迎各位大佬多评论指正!!!
ps:如果有同样的问题,不想看我bi~bi~叨的直接跳转到目录的最后
遇到的问题:使用STLink下载代码到STM32F103的最小系统板后,环回模式发送数据,不能正常进入CAN接收中断进行数据接收。
目录
遇到的问题:使用STLink下载代码到STM32F103的最小系统板后,环回模式发送数据,不能正常进入CAN接收中断进行数据接收。
(1)首先简单总结一下标准库CAN的配置流程:
(2)主函数的逻辑实现:
(3)中断函数的逻辑实现:
(4)仿真流程:
结果实测:必须放在CAN_Init的函数的后面,仿真和最终程序的现象才正常!!!
(1)首先简单总结一下标准库CAN的配置流程:
1)初始化GPIO -> 初始化NVIC -> 初始化CAN外设 -> 初始化滤波器
这里就是STM32的标准库初始化:定义对应的结构体变量,并且按需求进行成员的赋值。
2)接收、发送函数包装 -> 编写接收CAN中断函数
这里是对于接受和发送报文的函数封装,由于库函数已经将手册中的相关寄存器的操作流程封装好了,直接调用can.c中的发送及接受报文的函数即可。
下面我将从我的视角一步步寻找问题出在哪(文章略长,希望耐心看完后够帮助到你😊)
PPPPSSSS:发现问题麻烦语气温柔点😐,作者玻璃心🤤
(2)主函数的逻辑实现:
按键按下后发送一个报文,由于初始化时CAN设置为环回模式(bxCAN内部将Tx输出回馈到Rx输入,即RX仅接受来自TX引脚的信号,TX仍可以影响总线的电平状态),同时,由于初始化时配置了CAN1RX接受中断 –> “一旦向FIFO中存入了一个报文,硬件会自动将FMP[1:0]更新,并且如果把CAN_IER寄存器的FMPIE位置1,就会产生一个中断请求”(这是手册中对于接受相关中断的描述)
对于手册中的这句“如果把CAN_IER寄存器的FMPIE位置1”的理解:在初始化时调用初始化函数CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE),进入函数内部我们可以发现,函数进行了寄存器操作,将FMPIE0位置1(CAN_IT_FMP0 –> 0x00000002对应的就是FMPIE0位)。所以CAN_ITConfig函数就实现了开启中断的作用。
while循环判断按键是否按下,按下后发送一次报文数据给自己接收,bxCAN外设检测到FIFO0存入了一个报文,自动进入中断,在中断函数中接收报文数据。主循环中检测接收标志位MyCAN_RxFlag是否置1,如果置1,逻辑内部先清零,再进行OLED显示接受的报文数据等操作。(PS:这里的按键扫描使用的是正点原子的按键逻辑)
(3)中断函数的逻辑实现:
NVIC可以告诉CPU“有地方发生中断了,我给你地址,你快去处理一下”,这是硬件实现的,所以满足“条件”后会自动进入中断函数,这里指的就是RX接收中断函数。进入函数后执行接收报文的逻辑(其实就是调用库函数接收报文)。由于需要在主函数中显示报文数据,所以需要将“接收结构体”extern外部引用。调用完接受报文函数后,设置一个标志位Rx_Flag = 1,表示中断中接收报文完成。
非常好,到这里好像一切都十分合理。
但是,实操就有问题了?!?
一开始是以为中断配置问题,反复检查发现并没有问题,后来以为是眼花了,可发现即使是江科大的源码,在我的单片机上也无法实现。
那还能怎么办,开始仿真。
(4)仿真流程:
使用STLINK调试,同时打开Keil的CAN相关的寄存器和NVIC显示列表,可以在调试的同时,检测寄存器位的变化,也可以在调试的过程中改变寄存器位同步观察程序的执行逻辑。
打开方式:Peripherals -> system view -> CAN;Peripherals -> core Peripherals -> NVIC;
然后可以找到和CAN RX相关的中断和寄存器当前状态(注意,这些窗口只能在调试模式下才能打开)可以发现这里有两个RX接收中断,我理解的是FIFO0和FIFO1各自对应一个。(由于函数中初始化的是对应的FIFO0,所以后续只关注CAN1_RX0。)
既然是中断的问题,那么断点就打在CAN_ITConfig中了,理论上,主函数执行完mycan_init()初始化后,会进行CAN_ITConfig的CAN1的CAN_IT_FMP0中断使能。
进入使能函数内部:会发现根据用户设置的中断位,对IER寄存器进行控制。
可是实际调试的过程中发现即时函数运行到了这一步,也不会对IER进行“|= CAN_IT”实际的赋值操作(表现为“运行了过了这句代码,但不做事” — FMPIE0位并没有变化),所以也就是不会进入接收中断。
继续单步运行,发现程序在NVIC_Init()中使能中断后,NVIC监控列表中对于的CAN1_RX0的中断位确实置1了。
也就是说,目前RX0的这条中断和CPU的路是“被打通了的”。此后每次按下按键发送一次报文可以发现CNA_RFR寄存器FMP0的值是确实在变化的,证明了硬件确实接收到了有效报文数据。


可是当前IER的FMPIE0位仍然是0状态,也就无法进入中断读取报文。
下面我们在主函数中直接进行寄存器操作,将“CAN1->IER |= 0x00000002”后,发现可以触发接收中断了,相应的,列表中也可以观察到寄存器位的变化。所以问题就出现在这里。
PS:这里注意的CAN_GetITStatus(CAN1, CAN_IT_FMP0) == SET,实际上FMP0不为零就会触发中断请求,调用一次CAN_Receive函数就会值减1,直到FMP0的值为0,不再触发、进入中断。
百思不得其解啊,为什么需要人为的干预呢?为什么CAN_ITConfig起不到实际的作用呢?
我以为是编译器的优化问题,优化等级拉到最低,换成其他版本的编译器也不能解决,甚至会报很多其他错误,更麻烦了。
突然灵机一动,既然问题出在CAN_ITConfig的“赋值问题”,我的CAN_ITConfig函数放在了所有CAN相关初始化的前面,会不会出现"提前初始化的情况" ?
注释掉主函数的“CAN1->IER |= 0x00000002;”
然后将CAN_ITConfig放在不同的位置后:
最终结果实测:CAN_ITConfig函数必须放在CAN_Init的函数的后面,仿真和最终程序的现象才正常!!!
CAN中断接收最终现象
这么说,CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE)的位置也会影响程序的执行?
具体为什么会影响?我这小白就不在这带着大家对比分析原函数了。
乱七八糟写了一堆,其实“最有用”就是最后几句话,希望前面的部分没有把你绕晕😵
希望看到这里,能给你带来一点帮助
也非常欢迎各位大佬补充、纠正文章中的问题😊
无限进步😁
作者:xccpa