李维强-15级 发表于 2020-8-20 22:21:44

STM32+CubeMax+CAN通信配置详解

本帖最后由 李维强-15级 于 2020-8-27 22:08 编辑

本文旨在详细解释基于STM32下的CAN通信配置及其相应原理,给出简化版的个人见解,并最终给出一个固定的模板,使其标准化,以后直接按照本文的方法直接配置即可。
以下配置工具使用CubeMax(5.6.1)+STM32F407ZGT6单片机

1、CAN波特率配置
2、滤波器原理解析
3、中断配置
4、发送配置
5、cubeMAX配置与代码模板总结

李维强-15级 发表于 2020-8-20 22:42:53

本帖最后由 李维强-15级 于 2020-8-20 23:11 编辑

CAN波特率配置

CAN控制器只需进行少量设置就可以进行通信,其中较难设置部分就是波特率计算。CAN总线的波特率是一个范围。假如波特率为250KB/s,实际波特率可能为200~300KB/s.这样使得CAN总线有很强大容错性。
      CAN的底层协议里将CAN数据的每一位时间(TBit)分为许多时间段(Tscl),这些时间段包括:
A. 同步段(SYNC_SEG):位变化应该在此时间段内发生。只有一个时间片的固定长度(1 x tq)
B. 位段1(BS1):定义采样点的位置。其持续长度可以在 1 到 16 个时间片之间调整
C. 位段2(BS2):定义发送点的位置。其持续长度可以在 1 到 8 个时间片之间调整
D. 同步跳转宽度(SJW):定义位段加长或缩短的上限。它可以在 1 到 4 个时间片之间调整

通过实验,上面的A-D之内的东西都是随便设置的,最终目的就是为了通过下面的公式得到相应的波特率即可
下面给出波特率计算公式:

CAN波特率=(APB1 PERIPHERAL CLOCKS)/(Prescaler)/(SJW+BS1+BS2)


这里举例说明,配置250K的波特率:
先看下图


在图中看到,配置波特率关键点在看准APB1 PERIPHERAL CLOCKS的时钟,这里是42M,然后在CubeMAX里面CAN参数配置一栏选择如图所示
Prescaler=14
SJW=ReSynchronization Jump Width=1
BS1=Time Quanta in Bit Segment 1=5
BS2=Time Quanta in Bit Segment 2=6
所以得到CAN波特率=(42M)/(14)(1+5+6)=0.25M=250K
至此 CAN波特率配置完毕

李维强-15级 发表于 2020-8-21 09:05:26

本帖最后由 李维强-15级 于 2020-8-21 13:38 编辑

滤波器原理解析

1、帧ID(仲裁段)解释
本文所讨论的设置都是针对标准帧,首先给出一个概念,就是帧的ID另外一个解释即为仲裁段,也就是竞争总线权限的标示,下面给出图例做讲解

上图中,单元 1 和单元 2 同时开始向总线发送数据,开始部分他们的数据格式是一样的,故无法区分优先级,直到 T 时刻,单元 1 输出隐性电平,而单元 2 输出显性电平,此时单元 1仲裁失利,立刻转入接收状态工作,不再与单元 2 竞争,而单元 2 则顺利获得总线使用权,继续发送自己的数据。这就实现了仲裁,让连续发送显性电平多的单元获得总线使用权。

2、滤波设置
STM32CAN控制器每个筛选器组由两个32位的寄存器组成。
根据所需位宽的不同,各个筛选器可配置成16位或32位模式(如下图,当FSCx=1为32位模式,FSCx=0时为16位模式)。同时,筛选器的模式可配置成掩码模式和列表模式(FBMx=0为标识符屏蔽模式,FBMx=1为标识符列表模式)。
屏蔽模式:目的是为了过滤出一组ID,即一个ID段。
列表模式:目的是为了过滤出对应的ID,最多得出2个ID。

其中 RTR 位用于标识是否是远程帧(0,数据帧; 1,远程帧), IDE 位为标识符选择位(0,使用标准标识符; 1,使用扩展标识符),

下面给出例子(注意这里的RTR和IDE都是0,即表示标准帧和标准标识符)

假如要滤出的Id范围为0x010~0x01f (注意,这里标准帧ID都是11位的,所以3个16进制数足够表示了,而我们在程序代码中往往用0x1234这样的帧ID来表示,往往是错误的,实际上只取了11位)
那么就需要用屏蔽模式,这里用一个 32 位筛选器 - 标识符掩码

ID      : 0000 001x xxx0 0000 0000 0000 0000 0000//分别对应CAN_FxR1高16位、CAN_FxR1低16位   
屏蔽    : 1111 1110 0001 1111 1111 1111 1111 1111//分别对应CAN_FxR2高16位、CAN_FxR2低16位
这里注意对应上图中“一个 32 位筛选器 - 标识符掩码”
ID中的xxx表示这一位任何值都可以
屏蔽中的0表示对上面ID的对应位(ID中21~24位)不关心,也就是这几位无论什么都通过,而屏蔽中的1表示对上面ID对应的位必须一样才能通过
这样ID和屏蔽的组合下来 滤出的ID范围就是0x010~0x01f


例如要滤出ID为为0x011和0x012两个帧的
那么就需要用列表模式,两个 32 位筛选器 - 标识符列表

ID      : 0000 0010 0010 0000 0000 0000 0000 0000//分别对应CAN_FxR1高16位、CAN_FxR1低16位//0x011
ID      : 0000 0010 0100 0000 0000 0000 0000 0000//分别对应CAN_FxR2高16位、CAN_FxR2低16位//0x012
这里注意对应上图中“1个标识符列表-32位”
这里实际上就是给出两个ID,只有命中了这两个ID才能通过滤波



假如滤出范围为0x010~0x01f,0x000~0x00f这样两组ID范围内的,那么就要用两个 16 位筛选器 - 标识符掩码

ID      : 0000 001x xxx0 0000//分别对应CAN_FxR1低16位CAN_FilterIdLow
屏蔽    : 1111 1110 0001 1111//分别对应CAN_FxR1高16位CAN_FilterIdHigh

ID      : 0000 000x xxx0 0000//分别对应CAN_FxR2低16位CAN_FilterMaskIdLow
屏蔽    : 1111 1110 0001 1111//分别对应CAN_FxR2高16位CAN_FilterMaskIdHigh
ID中的xxx表示这一位任何值都可以
屏蔽中的0表示对上面ID的对应位(ID中21~24位)不关心,也就是这几位无论什么都通过,而屏蔽中的1表示对上面ID对应的位必须一样才能通过


假如要滤出的帧ID为0x010,0x01f,0x001,0x00f四个,那么就要用四个16 位筛选器 - 标识符列表

ID      : 0000 0010 0000 0000//分别对应CAN_FxR1低16位CAN_FilterIdLow//0x010
ID      : 0000 0011 1110 0000//分别对应CAN_FxR1高16位CAN_FilterIdHigh //0x01f

ID      : 0000 0000 0010 0000//分别对应CAN_FxR2低16位CAN_FilterMaskIdLow //0x001
ID      : 0000 0001 1110 0000//分别对应CAN_FxR2高16位CAN_FilterMaskIdHigh //0x00f
这里实际上就是给出4个ID,只有命中了这4个ID才能通过滤波


过滤器优先级
在过滤器中存在13个过滤器组,用户可以设定其中任意一个过滤器(也就是上面的任意两个32位寄存器)与接收FIFO做绑定,那么只要消息通过了被设定的那个过滤器,那么就会把对应的数据存在FIFO里面了。至于为什么单片机会设定这么多过滤组,是因为它本身就存在一个过滤判断的优先级。如下图所示:

根据过滤器的不同配置,有可能一个报文标识符能通过多个过滤器的过滤;在这种情况下,存放在接收邮箱中的过滤器匹配序号,根据下列优先级规则来确定:
1、位宽为32位的过滤器,优先级高于位宽为16位的过滤器
2、对于位宽相同的过滤器,标识符列表模式的优先级高于屏蔽位模式
3、位宽和模式都相同的过滤器,优先级由过滤器号决定,过滤器号小的优先级高
如上图,在接收一个报文时,其标识符首先与配置在标识符列表模式下的过滤器相比较;如果匹配上,报文就被存放到相关联的FIFO中,并且所匹配的过滤器的序号(这时为4)被存入过滤器匹配序号中。如同例子中所显示,报文标识符跟#4标识符匹配,因此报文内容和FMI4被存入FIFO。
如果没有匹配,报文标识符接着与配置在屏蔽位模式下的过滤器进行比较。
如果报文标识符没有跟过滤器中的任何标识符相匹配,那么硬件就丢弃该报文,且不会对软件有任何打扰。

这里总结一下,我们编程的时候,往往只需要配置第0个过滤器组与FIFO相匹配即可

最后给出相应代码,由于滤波设置的代码在CubeMAX里面没有自带生成(我没找到),所以需要自己写,这里我们一般写在开启CAN的函数之前,初始化CAN之后
例如我们这里要设置只接收0x001以及0x010的帧,那么就可以直接用两个 32 位筛选器 - 标识符列表,具体代码如下


#define                        ID1                (0x010)   
#define                        ID2                (0x001)

        CAN_FilterTypeDef CAN_FilterStructure;
       
        CAN_FilterStructure.FilterActivation = ENABLE;   //表示开启滤波
        CAN_FilterStructure.FilterBank = 0;                        //表示使用0组滤波器 即直接使用最大优先级的
        CAN_FilterStructure.FilterFIFOAssignment = CAN_FILTER_FIFO0;        //这里表示初始化的过滤器组
        CAN_FilterStructure.FilterIdHigh = (ID1<< 5);      //设置32位的高16位
        CAN_FilterStructure.FilterIdLow = 0X0000;               //设置32位的低16位       
        CAN_FilterStructure.FilterMaskIdHigh = (ID2<< 5);        //设置32位的高16位
        CAN_FilterStructure.FilterMaskIdLow = 0X0000;               //设置32位的低16位
        CAN_FilterStructure.FilterMode = CAN_FILTERMODE_IDLIST; // CAN_FILTERMODE_IDLIST 表示设置为标识符列表模式;   CAN_FILTERMODE_IDMASK 表示设置为标识符掩码模式
        CAN_FilterStructure.FilterScale = CAN_FILTERSCALE_32BIT;        //这里表示设置位32位模式
        CAN_FilterStructure.SlaveStartFilterBank = 0;                  //这个参数只是在使用两个CAN的时候才有效,否则这一项没有意义
       
        if(HAL_CAN_ConfigFilter(&CAN_Handle, &CAN_FilterStructure) != HAL_OK)
        {
                Error_Handler();
        }

李维强-15级 发表于 2020-8-24 23:36:41

本帖最后由 李维强-15级 于 2020-8-25 22:36 编辑

中断配置
下面需要了解HAL库带来的常用中断,以及中断配置流程
这里首先给出HAL库可以开启的所有中断

/** @defgroup CAN_Interrupts CAN Interrupts
* @{
*/
/* Transmit Interrupt */
#define CAN_IT_TX_MAILBOX_EMPTY   ((uint32_t)CAN_IER_TMEIE)   /*!< Transmit mailbox empty interrupt */

/* Receive Interrupts */
#define CAN_IT_RX_FIFO0_MSG_PENDING ((uint32_t)CAN_IER_FMPIE0)/*!< FIFO 0 message pending interrupt */
#define CAN_IT_RX_FIFO0_FULL      ((uint32_t)CAN_IER_FFIE0)   /*!< FIFO 0 full interrupt            */
#define CAN_IT_RX_FIFO0_OVERRUN   ((uint32_t)CAN_IER_FOVIE0)/*!< FIFO 0 overrun interrupt         */
#define CAN_IT_RX_FIFO1_MSG_PENDING ((uint32_t)CAN_IER_FMPIE1)/*!< FIFO 1 message pending interrupt */
#define CAN_IT_RX_FIFO1_FULL      ((uint32_t)CAN_IER_FFIE1)   /*!< FIFO 1 full interrupt            */
#define CAN_IT_RX_FIFO1_OVERRUN   ((uint32_t)CAN_IER_FOVIE1)/*!< FIFO 1 overrun interrupt         */

/* Operating Mode Interrupts */
#define CAN_IT_WAKEUP               ((uint32_t)CAN_IER_WKUIE)   /*!< Wake-up interrupt                */
#define CAN_IT_SLEEP_ACK            ((uint32_t)CAN_IER_SLKIE)   /*!< Sleep acknowledge interrupt      */

/* Error Interrupts */
#define CAN_IT_ERROR_WARNING      ((uint32_t)CAN_IER_EWGIE)   /*!< Error warning interrupt          */
#define CAN_IT_ERROR_PASSIVE      ((uint32_t)CAN_IER_EPVIE)   /*!< Error passive interrupt          */
#define CAN_IT_BUSOFF               ((uint32_t)CAN_IER_BOFIE)   /*!< Bus-off interrupt                */
#define CAN_IT_LAST_ERROR_CODE      ((uint32_t)CAN_IER_LECIE)   /*!< Last error code interrupt      */
#define CAN_IT_ERROR                ((uint32_t)CAN_IER_ERRIE)   /*!< Error Interrupt                  */
/**
* @}
*/


利用HAL_CAN_ActivateNotification函数进行相应的中断的使能:

        if(HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK)
        {
                return HAL_ERROR;
        }       


下面是整个CAN中断的截图


在一般情况下,我们都是开启的标准帧和数据帧,这里我们关心的即是发送、接收、错误中断,并且我们作为用户,关心的只是那些中断的使能和HAL库给我们的回调。

接收中断:
1:cubeMAX设置
在cubeMax里面 有CAN1 RX0 interrupts和CAN2 RX1 interrupt,这里我们使能RX0即可,两者的区别在于我们在滤波器那里使能的是哪个FIFO ,FIFO_0就是对应的RX0接收中断,FIFO_1就是对应的RX1接收中断

2:接收中断介绍
可以使能的接收中断有3个 即CAN_IT_RX_FIFO0_MSG_PENDING,CAN_IT_RX_FIFO0_FULL,CAN_IT_RX_FIFO0_OVERRUN,其中有前两个有相应的回调函数,而CAN_IT_RX_FIFO0_OVERRUN则是表示接收缓冲区上溢,在HAL库的中断里面没有相应的回调,而是中断调度函数HAL_CAN_IRQHandler中,这个溢出标志位直接被清零了。

3:接收FIFO队列的操作概念:
    在接收FIFO内,一条消息通过了滤波器后,就会进入到对应的FIFO内,FIFO具有3级缓冲,而具体提取哪一级内的数据都是硬件决定的,软件上只需要用函数HAL_CAN_GetRxMessage获取一条数据后,FIFO里面的队列数量就会相应的减少,例如3变为2,2变为1,而这个减少的计数是硬件控制。

在这里,强烈建议在获取FIFO里面数据的时候,使用函数HAL_CAN_GetRxFifoFillLevel获取当前队列内的数量,在得知数量后再进行相应的操作(在一般情况下,我们队列内的数量只存在1条数据,如果存在多条数据,那就是发送端发送数据太快,或者是单片机内被其他东西打断了,从而没有用HAL_CAN_GetRxMessage获取相应的数据。)
   
错误中断:
错误中断是需要使能的,按照上面的中断图,通常我们关心的错误中断包含BOFF,EPVF,EWGF,其中BOFF是TEC上溢时候产生的,EPVF是接收或者发送(TEC或者REC大于127)的时候产生的,EWGF是警告,即(TEC或者REC大于96)的时候产生的,由于在实际情况中,总线上面产生错误都是瞬间的,以上3个标志位用意是让用户选择某一种错误标志时,程序应该做出何种相应的处理,所以在编程的时候,在我看来,只需要开启2种错误中断就可以了,这里推荐使用BOFF和EPVF一起用即可。因为总线连接断开且只有1个节点不断发送报文时产生发送错误, 控制器进入错误认可状态, 但不进入总线关闭状态; 其它错误均使错误计数器增加, 依次进入错误认可状态、总线关闭状态, 后两种状态表明总线被严重干扰, 需要采取相应措施。为简化控制逻辑设计将错误认可和总线关闭合并为总线故障。

下面讲一下错误中断是怎么复位的,由于BOFF,EPVF,EWGF这些标志位是只读的,TEC,REC也是只读的,都是硬件来完成的,我们也管不了,唯一能够清除的,就是错误中断标志位ERRI,这个已经在HAL库CAN中断函数内自动帮我们清零了,所以不用管。
在查询了很多资料后发现REC是动态循环的,TEC是在检测到总线发送成功后递减的,如果错误次数累计到某个程度例如TEC=255的时候,硬件会强行关闭中断,而恢复也是硬件的,首先需要把CAN_MCR 寄存器的 ABOM 位置1,然后等待总线上面的128次连续11位的隐性位(也就是说总线必须进入一段时间的空闲状态,实际也就不到1秒钟吧),然后硬件自动把TEC,REC清零,再开始上线,下面给出一个错误循环的图


从以上的循环图中可以看到,我们的CAN节点始终都是处于以上3种状态之一,即如果正常,就是主动报错阶段,报了一定量的错误后就进入被动报错阶段,如果再有错误,就进入总线脱离阶段,然后再恢复到主动报错,这个里面涉及到很多具体的CAN协议基础知识,可以参见以下文章
CAN协议和帧结构https://www.cnblogs.com/pejoicen/p/3986587.html
错误状态的通俗解释:https://blog.csdn.net/weixin_30456039/article/details/98581529#t2

最后说下错误中断流程
首先在cubeMAX里面使能CAN1 SCE interrupt 中断并且调整相应的优先级(这个优先级应该比接收和发送都高)
然后在中断使能的地方添加如下代码

        if(HAL_CAN_ActivateNotification(&hcan1, CAN_IT_ERROR) != HAL_OK)        //使能错误中断
        {
                return HAL_ERROR;
        }
        if(HAL_CAN_ActivateNotification(&hcan1, CAN_IT_ERROR_PASSIVE) != HAL_OK)        //使能被动错误中断
        {
                return HAL_ERROR;
        }       
        if(HAL_CAN_ActivateNotification(&hcan1, CAN_IT_BUSOFF) != HAL_OK)        //使能BUSOFF中断
        {
                return HAL_ERROR;
        }               

最后添加错误中断回调函数

void HAL_CAN_ErrorCallback(CAN_HandleTypeDef *hcan)
{
   //hcan->ErrorCode即可获取相应的ESR寄存器的值   进入这里就表示一定产生错误中断了,而且是BUSOFF中断
}


李维强-15级 发表于 2020-8-25 21:59:03

本帖最后由 李维强-15级 于 2020-8-25 22:45 编辑

发送及其中断配置
在初始化的时候,建议配置把自动重发关闭掉。其流程如下,即先选择一个空邮箱(如果全部邮箱都是满的,那函数返回值就直接报错),然后把数据装进去,后面的流程就如图所示处理了,全部都是硬件处理的,用户不用管,处理过后最后得出结果,要么发送失败,要么发送成功,失败或者成功都会有相应的寄存器值表明,这里主要看TXOK这位的值是否为1,即可看到发送是否成功了。


由于发送有3个邮箱,在发送之初,我们不知道到底需要把数据装如哪个邮箱去发送,所以HAL库给我们提供了一个函数HAL_CAN_AddTxMessage ,我们只需要把相应的数据参数传进去就可以了,其他的一切自动。这里需要注意,在网上很多教程都是发送后,等待几毫秒再去做其他的事情,其实这样做在线路完好,通信正常的情况下没有问题,但是正确的做法应该是由系统发送中断来指示下一步的计划,而不是延迟。

HAL_CAN_AddTxMessage 这个函数的最后一个参数pTxMailbox 即是一个返回值,这个值只是指针传递的,相当于引用,其目的是为了保存当前传递的数据是进了哪一个发送邮箱,而后用户好去等待对应邮箱的发送成功/失败 中断,从而进行进一步的操作。

在这里 需要指出,错误计数是怎么来的,如下

错误计数器当检测下下述事件时将修改错误计数值: 
 当一接收节点检测到一个错误时,错误计数器将加1.有一种情况可以排外,即当检测到发送一主动错误标志或者重载标志时出现一位错误时除外。 
 当一接收节点发送一错误标志时,检测到首位为显性位时,错误计数器加8. 
 当一发送节后发送错误标志时,错误计数器加8,这时有两种情况除外:1:当这个发送节点处于主动错误状态下,且检测到由于ACK域未有显性位而造成的ACK错误,但是在发送时未检测到被动错误标志;2:当发送节点发送一错误标志时,在仲裁时检测到填充位错误(这些填充位原来应该是隐性,但检测结果为显性)。以上两种异常错误计数值保留原值不变。 
 当发送节点发送一主动错误标志或重载标志时,检测到位错误时,错误计数器加8. 
 当接收节点娄送一主动错误标志或重载标志时,检测到位错误时,错误计数器加8. 
 任何节点在发送主动错误标志,被动错误标志,或重载标志时都应都忍受连续7
个显示位。当检测到连接14个显示位,或者被动错误标志后紧跟着连接8个显示位,或者8个连续显性位后紧跟着被动错误标志时,所有发送节点发送错误计数器加8,所有接收节点接收错误计数器加8. 
 发成功发送一帧报文后,发送计数器应减1,除非当前已经为0. 
 当成功接收一帧报文后,如果当前接收计数器的值大于1且小于127,则接收计数器减1;如果接收计数器的值为0,则保持为0;如果在于127,则接收计数器的值应设置 为119~127之间的值。 

假如有一种情况,也就是总线断了(而非短路)那么就满足上面的第三条,TEC是不会改变的,所以发送是否成功还是需要用中断来做。
当然更暴力的做法就是在问答形式下,发送后不管了,等待对方回复即可,若一定时间不回复,则表示通信失败,用户可选择再发一次等等操作

李维强-15级 发表于 2020-8-27 14:22:03

本帖最后由 李维强-15级 于 2020-9-7 16:13 编辑


说明:调用前请先查看UserCan.h中的 “模块配置参数”部分,定义相应的过滤ID和波特率,其中波特率配置可参考楼上帖子

调用示例1:
第一步:
下面给出通过上面CAN总结而得到的代码
首先按照cube上面的配置,250K波特率使能,并且直接生成keil代码即可

第二步:
用下面的UserCan.h与UserCan.c文件的内容 替换cube生成的can.h与can.c即可
第三步:
1)在 main文件 内 添加#include "UserCan.h",
2)main函数初始化阶段,添加CAN_Config_User(); 即可
第四步:
在UserCan.c的回调函数“用户代码区”添加相应用户代码即可
第五步(非必要):
在中断控制文件中“stm32fXXXX_it.c”文件中,
1)添加#include "UserCan.h"
2)替代所有HAL_CAN_IRQHandler(&hcan1)为   My_HAL_CAN_IRQHandler(&hcan1)

调用示例2:
第一步:
1)在 main文件 内 添加#include "UserCan.h",
2)main函数初始化阶段,添加CAN_Config_User(); MX_CAN1_Init();
第二步:
在UserCan.c的回调函数“用户代码区”添加相应用户代码即可
第三步(非必要):
在中断控制文件中“stm32fXXXX_it.c”文件中,
1)添加#include "UserCan.h"
2)替代所有HAL_CAN_IRQHandler(&hcan1)为   My_HAL_CAN_IRQHandler(&hcan1)


源文件
   can.h


#ifndef __can_H
#define __can_H
#ifdef __cplusplus
extern "C" {
#endif

#include "main.h"

extern CAN_HandleTypeDef hcan1;

/***************************** 模块配置参数Begin********************************************/
/* 该模块采用列表滤波器,可直接命中2个标准帧ID */
#define                     ID1                (0x010)           //定义一个滤波器ID
#define                     ID2                (0x001)                //定义一个滤波器ID
//波特率设置开关 以下两个宏定义,只能设置一个
#define                                                                                                CAN_APB1_42M                                //设置波特率,对应STM32F407-168M时钟-APB1-42M时钟                               
//#define                                                                                                CAN_APB1_36M                                //设置波特率,对应STM32F302-72M时钟-APB1-36M时钟                               

/***************************** 模块配置参数End********************************************/

/***************************** 结构体设计 Begin ********************************************/
typedef struct
{
        uint8_t                         data;
        uint8_t                                dataSize;                //数据长度
        FlagStatus                 RxFlag;                        //接收标志
} CAN_RecvMSG_Struct;

typedef struct {
        uint8_t                                data;
        uint8_t                                dataSize;                //数据长度
        FlagStatus                 TxFlag;                        //接收标志
} CAN_TxMSG_Struct;

typedef struct {
        CAN_TxHeaderTypeDef   TxHandle;
        CAN_RxHeaderTypeDef   RxHandle;
       
        CAN_RecvMSG_Struct                        CAN_RxMsg;
        CAN_TxMSG_Struct                                CAN_TxMsg;
} CAN_User_Struct;
/***************************** 结构体设计 End ********************************************/


/***************************** 外部接口 Begin********************************************/
void MX_CAN1_Init(void);                                                //初始化CAN1(若需移植,则需要自行利用cube配置。当前配置APB1=42M->CAN波特率为250K)
void CAN_Config_User(void);                                        //配置CAN1(设置滤波器,使能中断,开启CAN1)
//以上两个函数在main函数中,一系列初始化开始调用
HAL_StatusTypeDef CAN_TRANSMIT(uint32_t tx_StdId, uint8_t* tx_data_buff, uint8_t data_size);        //发送函数
void My_HAL_CAN_IRQHandler(CAN_HandleTypeDef *hcan);                //自定义中断服务函数,可替换 void HAL_CAN_IRQHandler(CAN_HandleTypeDef *hcan)
/***************************** 外部接口 End********************************************/


/***************************** 内部函数 Begin********************************************/
static HAL_StatusTypeDef CAN_Start_User(void);
/***************************** 内部函数 End********************************************/



/*
==============================================================================
##### 系统中断回调函数 #####
### 以下中断回调函数在UserCan.c中,并在函数“用户代码区”处做对应操作
==============================================================================
                void HAL_CAN_ErrorCallback(CAN_HandleTypeDef *hcan)                                                        //错误中断回调
                void HAL_CAN_RxFifo0FullCallback(CAN_HandleTypeDef *hcan)                                //FIFO0->接收消息->队列已满中断
                void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)        //FIFO0->接收消息中断       
                void HAL_CAN_TxMailbox0CompleteCallback(CAN_HandleTypeDef *hcan)//发送邮箱0 发送完成中断
                void HAL_CAN_TxMailbox1CompleteCallback(CAN_HandleTypeDef *hcan)//发送邮箱1 发送完成中断
                void HAL_CAN_TxMailbox2CompleteCallback(CAN_HandleTypeDef *hcan)//发送邮箱2 发送完成中断

*/



/***************************** CAN发送调用示例 Begin********************************************/

void MyCANSend(uint32_t tx_StdId);
/***************************** CAN发送调用示例 End********************************************/

#ifdef __cplusplus
}
#endif
#endif /*__ can_H */

/************************ (C) COPYRIGHT DTWT *****END OF FILE****/



下面在can.c里面对应位置(cube给出的用户代码添加位置)添加相应代码


#include "string.h"
#include "UserCan.h"

CAN_HandleTypeDef hcan1;
CAN_User_Struct                CAN_User;                        //CAN数据结构体

/***************************** 外部接口 Begin********************************************/
/*
*********************************************************************************************************
*        功能说明: 初始化CAN1
*        形    参: 无
*        返 回 值: 无
**********************************************************************************************************/
void MX_CAN1_Init(void)
{
#ifdef CAN_APB1_42M       
hcan1.Instance = CAN1;
hcan1.Init.Prescaler = 14;
hcan1.Init.Mode = CAN_MODE_NORMAL;
hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ;
hcan1.Init.TimeSeg1 = CAN_BS1_5TQ;
hcan1.Init.TimeSeg2 = CAN_BS2_6TQ;
hcan1.Init.TimeTriggeredMode = DISABLE;
hcan1.Init.AutoBusOff = DISABLE;
hcan1.Init.AutoWakeUp = DISABLE;
hcan1.Init.AutoRetransmission = DISABLE;
hcan1.Init.ReceiveFifoLocked = DISABLE;
hcan1.Init.TransmitFifoPriority = DISABLE;
if (HAL_CAN_Init(&hcan1) != HAL_OK)
{
    Error_Handler();
}
#endif       
       
#ifdef CAN_APB1_36M
hcan.Instance = CAN;
hcan.Init.Prescaler = 12;
hcan.Init.Mode = CAN_MODE_NORMAL;
hcan.Init.SyncJumpWidth = CAN_SJW_1TQ;
hcan.Init.TimeSeg1 = CAN_BS1_5TQ;
hcan.Init.TimeSeg2 = CAN_BS2_6TQ;
hcan.Init.TimeTriggeredMode = DISABLE;
hcan.Init.AutoBusOff = DISABLE;
hcan.Init.AutoWakeUp = DISABLE;
hcan.Init.AutoRetransmission = DISABLE;
hcan.Init.ReceiveFifoLocked = DISABLE;
hcan.Init.TransmitFifoPriority = DISABLE;
if (HAL_CAN_Init(&hcan) != HAL_OK)
{
    Error_Handler();
}
#endif       
       
}

/*
*********************************************************************************************************
*        功能说明: 配置CAN1(设置滤波器,使能中断,开启CAN1)
*        形    参: 无
*        返 回 值: 无
**********************************************************************************************************/
void CAN_Config_User(void)
{
        CAN_FilterTypeDef CAN_FilterStructure;
               
        CAN_FilterStructure.FilterActivation = ENABLE;
        CAN_FilterStructure.FilterBank = 0;                                       
        CAN_FilterStructure.FilterFIFOAssignment = CAN_FILTER_FIFO0;
        CAN_FilterStructure.FilterIdHigh = (ID1 << 5);
        CAN_FilterStructure.FilterIdLow = 0X0000;
        CAN_FilterStructure.FilterMaskIdHigh = (ID2 << 5);
        CAN_FilterStructure.FilterMaskIdLow = 0X0000;
        CAN_FilterStructure.FilterMode = CAN_FILTERMODE_IDLIST; // CAN_FILTERMODE_IDLIST; CAN_FILTERMODE_IDMASK
        CAN_FilterStructure.FilterScale = CAN_FILTERSCALE_32BIT;
        CAN_FilterStructure.SlaveStartFilterBank = 0;
       
        if(HAL_CAN_ConfigFilter(&hcan1, &CAN_FilterStructure) != HAL_OK)
        {
                Error_Handler();
        }
       
        // 发送数据的配置
CAN_User.TxHandle.IDE = CAN_ID_STD;                //设置为标准帧
CAN_User.TxHandle.RTR = CAN_RTR_DATA;        //设置为数据帧
        CAN_User.CAN_TxMsg.TxFlag=RESET;
        CAN_User.CAN_RxMsg.RxFlag=RESET;
       
        HAL_Delay(1);       
       
        if(CAN_Start_User() != HAL_OK)
        {
                Error_Handler();
        }
}



/*
*********************************************************************************************************
*        功能说明: CAN发送
*        形    参: tx_StdId                发送目的帧ID
*        形    参: tx_data_buff         发送数据内容,数组BUFF指针
*        形    参: data_size                        发送数据长度数据长度
*        返 回 值: HAL_StatusTypeDef调用后,正确/错误标志
**********************************************************************************************************/
HAL_StatusTypeDef CAN_TRANSMIT(uint32_t tx_StdId, uint8_t* tx_data_buff, uint8_t data_size)
{
        uint8_t ArrSize;
        ArrSize=sizeof(tx_data_buff);
        if(ArrSize>8) return HAL_ERROR;

        uint32_t CAN_Tx_MailBox;      
        CAN_User.TxHandle.StdId = tx_StdId;      
        CAN_User.TxHandle.DLC = data_size;
        if(HAL_CAN_AddTxMessage(&hcan1, &(CAN_User.TxHandle), tx_data_buff, &CAN_Tx_MailBox) != HAL_OK)
        {
                                        //这里表示队列3级缓冲已经满了,放不进去数据
                                        return HAL_ERROR;
        }
        return HAL_OK;
      
}
/***************************** 外部接口 End********************************************/




/***************************** 回调函数 Begin********************************************/

/*
*********************************************************************************************************
*        功能说明: CAN接收消息的回调函数,用户代码添加在“用户代码段”
*        形    参: hcan                CAN1指针句柄
*        返 回 值: 无
**********************************************************************************************************/
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{

        HAL_StatusTypeDef   HAL_RetVal;
uint8_t Count,i;
        if(hcan ==&hcan1)
        {
                Count=HAL_CAN_GetRxFifoFillLevel(hcan, CAN_RX_FIFO0);
                memset(CAN_User.CAN_RxMsg.data,0,8);
                if(Count == 1)//缓存中只有一帧数据
                {
                        HAL_RetVal=HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &CAN_User.RxHandle, CAN_User.CAN_RxMsg.data);
                        if(HAL_RetVal == HAL_OK)
                        {
                                CAN_User.CAN_RxMsg.dataSize=CAN_User.RxHandle.DLC;
                                //这里获取到数据放在CAN_User.CAN_RxMsg.data内,在此做相应的操作
                                //用户代码段 开始
                               
                                //用户代码段 结束
                        }                       
                }
                else if(Count > 1)//缓存中有多帧数据,需全部提取
                {
                        for(i=0;i<Count;i++)
                        {
                                HAL_RetVal=HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &CAN_User.RxHandle, CAN_User.CAN_RxMsg.data);
                                if(HAL_RetVal == HAL_OK)
                                {
                                        CAN_User.CAN_RxMsg.dataSize=CAN_User.RxHandle.DLC;
                                        //这里获取到数据放在CAN_User.CAN_RxMsg.data内,在此做相应的操作
                                        //用户代码段 开始
                                       
                                        //用户代码段 结束
                                }                                       
                        }                       
                }
        }               
}

/*
*********************************************************************************************************
*        功能说明: CAN接收消息,FIFO队列已满中断,用户代码添加在“用户代码段”
*        形    参: hcan                CAN1指针句柄
*        返 回 值: 无
**********************************************************************************************************/
void HAL_CAN_RxFifo0FullCallback(CAN_HandleTypeDef *hcan)
{
        HAL_StatusTypeDef   HAL_RetVal;
uint8_t Count,i;       
       
        Count=HAL_CAN_GetRxFifoFillLevel(hcan, CAN_RX_FIFO0);                //判断FIFO里面有多少帧数据 正常来说是3帧数据
       
        for(i=0;i<Count;i++)                //这里表示大于1帧,故需要把全部数据都取出来
        {
                HAL_RetVal=HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &CAN_User.RxHandle, CAN_User.CAN_RxMsg.data);
                if(HAL_RetVal == HAL_OK)
                {
                        //用户代码段 开始
                       
                        //用户代码段 结束
                }                                       
        }       
       
}

/*
*********************************************************************************************************
*        功能说明: 错误中断回调,用户代码添加在“用户代码段”
*        形    参: hcan                CAN1指针句柄
*        返 回 值: 无
**********************************************************************************************************/
void HAL_CAN_ErrorCallback(CAN_HandleTypeDef *hcan)
{
        //进入此函数,就表示发送和接收肯定出错了
       

        /*发送方面的错误**********************************************/
        //发送邮箱0 仲裁/发送失败
        if(hcan->ErrorCode & HAL_CAN_ERROR_TX_ALST0 || hcan->ErrorCode & HAL_CAN_ERROR_TX_TERR0)
        {
                        //用户代码段 开始
                       
                        //用户代码段 结束
        }
        //发送邮箱1 仲裁/发送失败
        if(hcan->ErrorCode & HAL_CAN_ERROR_TX_ALST1 || hcan->ErrorCode & HAL_CAN_ERROR_TX_TERR1)
        {
                        //用户代码段 开始
                       
                        //用户代码段 结束               
        }
        //发送邮箱2 仲裁/发送失败
        if(hcan->ErrorCode & HAL_CAN_ERROR_TX_ALST2 || hcan->ErrorCode & HAL_CAN_ERROR_TX_TERR2)
        {
                        //用户代码段 开始
                       
                        //用户代码段 结束               
        }               
        /**********************************************************************************************************/
       
        /*接收方面的错误**********************************************/       
        //FIFO-0接收缓冲溢出
        if(hcan->ErrorCode & HAL_CAN_ERROR_RX_FOV0)
        {
                        //用户代码段 开始
                       
                        //用户代码段 结束                       
        }
        //FIFO-1接收缓冲溢出该溢出前期没配置
        if(hcan->ErrorCode & HAL_CAN_ERROR_RX_FOV1)
        {
       
        }
        /**********************************************************************************************************/
       
        /**错误警告超过阈值**********************************************/               
        //CAN已经进入警告模式       
        if(hcan->ErrorCode & HAL_CAN_ERROR_EWG)
        {
                        //用户代码段 开始
                       
                        //用户代码段 结束                               
        }
        //CAN已经进入被动错误模式               
        if(hcan->ErrorCode & HAL_CAN_ERROR_EPV)
        {
                        //用户代码段 开始
                       
                        //用户代码段 结束               
        }       
       
}


/*
*********************************************************************************************************
*        功能说明: 邮箱0发送完成中断,用户代码添加在“用户代码段”       
*        形    参: hcan                CAN1指针句柄
*        返 回 值: 无
**********************************************************************************************************/
//
void HAL_CAN_TxMailbox0CompleteCallback(CAN_HandleTypeDef *hcan)
{
        //用户代码段 开始
       
        //用户代码段 结束       
}
/*
*********************************************************************************************************
*        功能说明: 邮箱1发送完成中断,用户代码添加在“用户代码段”       
*        形    参: hcan                CAN1指针句柄
*        返 回 值: 无
**********************************************************************************************************/
void HAL_CAN_TxMailbox1CompleteCallback(CAN_HandleTypeDef *hcan)
{
        //用户代码段 开始
       
        //用户代码段 结束               
}
/*
*********************************************************************************************************
*        功能说明: 邮箱2发送完成中断,用户代码添加在“用户代码段”       
*        形    参: hcan                CAN1指针句柄
*        返 回 值: 无
**********************************************************************************************************/
void HAL_CAN_TxMailbox2CompleteCallback(CAN_HandleTypeDef *hcan)
{
        //用户代码段 开始
       
        //用户代码段 结束               
}



/***************************** 回调函数 End********************************************/



/***************************** CAN发送调用示例 Begin********************************************/
/*
*********************************************************************************************************
*        功能说明: CAN发送调用示例
*        形    参: tx_StdId                目的帧ID
*        返 回 值: 无
**********************************************************************************************************/
void MyCANSend(uint32_t tx_StdId)
{
        uint8_t cmd_data = {0x11, 0x22, 0x33,0x44, 0x55};
        if(HAL_ERROR == CAN_TRANSMIT(ID2,cmd_data,sizeof(cmd_data)))
        {
                //发送失败,做对应操作
        }
        else
        {
                //发送成功,做对应操作
        }
}
/***************************** CAN发送调用示例 End********************************************/

/***************************** 内部函数 Begin********************************************/
//CAN引脚配置,中断优先级配置
void HAL_CAN_MspInit(CAN_HandleTypeDef* canHandle)
{

GPIO_InitTypeDef GPIO_InitStruct = {0};
if(canHandle->Instance==CAN1)
{
/* USER CODE BEGIN CAN1_MspInit 0 */

/* USER CODE END CAN1_MspInit 0 */
    /* CAN1 clock enable */
    __HAL_RCC_CAN1_CLK_ENABLE();

    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**CAN1 GPIO Configuration   
    PA11   ------> CAN1_RX
    PA12   ------> CAN1_TX
    */
    GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_12;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF9_CAN1;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* CAN1 interrupt Init */
    HAL_NVIC_SetPriority(CAN1_TX_IRQn, 2, 3);
    HAL_NVIC_EnableIRQ(CAN1_TX_IRQn);
    HAL_NVIC_SetPriority(CAN1_RX0_IRQn, 2, 2);
    HAL_NVIC_EnableIRQ(CAN1_RX0_IRQn);
    HAL_NVIC_SetPriority(CAN1_SCE_IRQn, 2, 1);
    HAL_NVIC_EnableIRQ(CAN1_SCE_IRQn);
/* USER CODE BEGIN CAN1_MspInit 1 */

/* USER CODE END CAN1_MspInit 1 */
}
}

//取消CAN引脚初始化,取消中断使能
void HAL_CAN_MspDeInit(CAN_HandleTypeDef* canHandle)
{

if(canHandle->Instance==CAN1)
{
/* USER CODE BEGIN CAN1_MspDeInit 0 */

/* USER CODE END CAN1_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_CAN1_CLK_DISABLE();

    /**CAN1 GPIO Configuration   
    PA11   ------> CAN1_RX
    PA12   ------> CAN1_TX
    */
    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_11|GPIO_PIN_12);

    /* CAN1 interrupt Deinit */
    HAL_NVIC_DisableIRQ(CAN1_TX_IRQn);
    HAL_NVIC_DisableIRQ(CAN1_RX0_IRQn);
    HAL_NVIC_DisableIRQ(CAN1_SCE_IRQn);
/* USER CODE BEGIN CAN1_MspDeInit 1 */

/* USER CODE END CAN1_MspDeInit 1 */
}
}

// 使能CAN中断、启动CAN
static HAL_StatusTypeDef CAN_Start_User(void)
{
        if(HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK)
        {
                return HAL_ERROR;
        }       
       
        if(HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_FULL) != HAL_OK)
        {
                return HAL_ERROR;
        }
        if(HAL_CAN_ActivateNotification(&hcan1, CAN_IT_ERROR) != HAL_OK)      //使能错误中断
        {
                                        return HAL_ERROR;
        }
        if(HAL_CAN_ActivateNotification(&hcan1, CAN_IT_ERROR_PASSIVE) != HAL_OK)      //使能被动错误中断
        {
                                        return HAL_ERROR;
        }      
        if(HAL_CAN_ActivateNotification(&hcan1, CAN_IT_BUSOFF) != HAL_OK)      //使能BUSOFF中断
        {
                                        return HAL_ERROR;
        }          
        if(HAL_CAN_ActivateNotification(&hcan1, CAN_IT_TX_MAILBOX_EMPTY) != HAL_OK)      //使能发送中断
        {
                                        return HAL_ERROR;
        }          
       
        if(HAL_CAN_Start(&hcan1) != HAL_OK)
        {
                return HAL_ERROR;
        }
       
        return HAL_OK;
}



/*
*********************************************************************************************************
*        功能说明: 重写 CAN中断处理函数代替原来的 HAL_CAN_IRQHandler
*        形    参: hcan                CAN1指针句柄
*        返 回 值: 无
* 优化说明:        1、接收FIFO-1溢出中断优化
                                                2、接收FIFO-1装满中断优化
                                                3、接收FIFO-1接收回调中断优化
                                                4、CAN睡眠中断优化
                                                5、CAN唤醒中断优化
**********************************************************************************************************/
void My_HAL_CAN_IRQHandler(CAN_HandleTypeDef *hcan)
{
uint32_t errorcode = HAL_CAN_ERROR_NONE;
uint32_t interrupts = READ_REG(hcan->Instance->IER);
uint32_t msrflags = READ_REG(hcan->Instance->MSR);
uint32_t tsrflags = READ_REG(hcan->Instance->TSR);
uint32_t rf0rflags = READ_REG(hcan->Instance->RF0R);
//uint32_t rf1rflags = READ_REG(hcan->Instance->RF1R);
uint32_t esrflags = READ_REG(hcan->Instance->ESR);

/* Transmit Mailbox empty interrupt management *****************************/
if ((interrupts & CAN_IT_TX_MAILBOX_EMPTY) != 0U)
{
    /* Transmit Mailbox 0 management *****************************************/
    if ((tsrflags & CAN_TSR_RQCP0) != 0U)
    {
      /* Clear the Transmission Complete flag (and TXOK0,ALST0,TERR0 bits) */
      __HAL_CAN_CLEAR_FLAG(hcan, CAN_FLAG_RQCP0);

      if ((tsrflags & CAN_TSR_TXOK0) != 0U)
      {
      /* Transmission Mailbox 0 complete callback */
#if USE_HAL_CAN_REGISTER_CALLBACKS == 1
      /* Call registered callback*/
      hcan->TxMailbox0CompleteCallback(hcan);
#else
      /* Call weak (surcharged) callback */
      HAL_CAN_TxMailbox0CompleteCallback(hcan);
#endif /* USE_HAL_CAN_REGISTER_CALLBACKS */
      }
      else
      {
      if ((tsrflags & CAN_TSR_ALST0) != 0U)
      {
          /* Update error code */
          errorcode |= HAL_CAN_ERROR_TX_ALST0;
      }
      else if ((tsrflags & CAN_TSR_TERR0) != 0U)
      {
          /* Update error code */
          errorcode |= HAL_CAN_ERROR_TX_TERR0;
      }
      else
      {
          /* Transmission Mailbox 0 abort callback */
#if USE_HAL_CAN_REGISTER_CALLBACKS == 1
          /* Call registered callback*/
          hcan->TxMailbox0AbortCallback(hcan);
#else
          /* Call weak (surcharged) callback */
          HAL_CAN_TxMailbox0AbortCallback(hcan);
#endif /* USE_HAL_CAN_REGISTER_CALLBACKS */
      }
      }
    }

    /* Transmit Mailbox 1 management *****************************************/
    if ((tsrflags & CAN_TSR_RQCP1) != 0U)
    {
      /* Clear the Transmission Complete flag (and TXOK1,ALST1,TERR1 bits) */
      __HAL_CAN_CLEAR_FLAG(hcan, CAN_FLAG_RQCP1);

      if ((tsrflags & CAN_TSR_TXOK1) != 0U)
      {
      /* Transmission Mailbox 1 complete callback */
#if USE_HAL_CAN_REGISTER_CALLBACKS == 1
      /* Call registered callback*/
      hcan->TxMailbox1CompleteCallback(hcan);
#else
      /* Call weak (surcharged) callback */
      HAL_CAN_TxMailbox1CompleteCallback(hcan);
#endif /* USE_HAL_CAN_REGISTER_CALLBACKS */
      }
      else
      {
      if ((tsrflags & CAN_TSR_ALST1) != 0U)
      {
          /* Update error code */
          errorcode |= HAL_CAN_ERROR_TX_ALST1;
      }
      else if ((tsrflags & CAN_TSR_TERR1) != 0U)
      {
          /* Update error code */
          errorcode |= HAL_CAN_ERROR_TX_TERR1;
      }
      else
      {
          /* Transmission Mailbox 1 abort callback */
#if USE_HAL_CAN_REGISTER_CALLBACKS == 1
          /* Call registered callback*/
          hcan->TxMailbox1AbortCallback(hcan);
#else
          /* Call weak (surcharged) callback */
          HAL_CAN_TxMailbox1AbortCallback(hcan);
#endif /* USE_HAL_CAN_REGISTER_CALLBACKS */
      }
      }
    }

    /* Transmit Mailbox 2 management *****************************************/
    if ((tsrflags & CAN_TSR_RQCP2) != 0U)
    {
      /* Clear the Transmission Complete flag (and TXOK2,ALST2,TERR2 bits) */
      __HAL_CAN_CLEAR_FLAG(hcan, CAN_FLAG_RQCP2);

      if ((tsrflags & CAN_TSR_TXOK2) != 0U)
      {
      /* Transmission Mailbox 2 complete callback */
#if USE_HAL_CAN_REGISTER_CALLBACKS == 1
      /* Call registered callback*/
      hcan->TxMailbox2CompleteCallback(hcan);
#else
      /* Call weak (surcharged) callback */
      HAL_CAN_TxMailbox2CompleteCallback(hcan);
#endif /* USE_HAL_CAN_REGISTER_CALLBACKS */
      }
      else
      {
      if ((tsrflags & CAN_TSR_ALST2) != 0U)
      {
          /* Update error code */
          errorcode |= HAL_CAN_ERROR_TX_ALST2;
      }
      else if ((tsrflags & CAN_TSR_TERR2) != 0U)
      {
          /* Update error code */
          errorcode |= HAL_CAN_ERROR_TX_TERR2;
      }
      else
      {
          /* Transmission Mailbox 2 abort callback */
#if USE_HAL_CAN_REGISTER_CALLBACKS == 1
          /* Call registered callback*/
          hcan->TxMailbox2AbortCallback(hcan);
#else
          /* Call weak (surcharged) callback */
          HAL_CAN_TxMailbox2AbortCallback(hcan);
#endif /* USE_HAL_CAN_REGISTER_CALLBACKS */
      }
      }
    }
}

/* Receive FIFO 0 overrun interrupt management *****************************/
if ((interrupts & CAN_IT_RX_FIFO0_OVERRUN) != 0U)
{
    if ((rf0rflags & CAN_RF0R_FOVR0) != 0U)
    {
      /* Set CAN error code to Rx Fifo 0 overrun error */
      errorcode |= HAL_CAN_ERROR_RX_FOV0;

      /* Clear FIFO0 Overrun Flag */
      __HAL_CAN_CLEAR_FLAG(hcan, CAN_FLAG_FOV0);
    }
}

/* Receive FIFO 0 full interrupt management ********************************/
if ((interrupts & CAN_IT_RX_FIFO0_FULL) != 0U)
{
    if ((rf0rflags & CAN_RF0R_FULL0) != 0U)
    {
      /* Clear FIFO 0 full Flag */
      __HAL_CAN_CLEAR_FLAG(hcan, CAN_FLAG_FF0);

      /* Receive FIFO 0 full Callback */
#if USE_HAL_CAN_REGISTER_CALLBACKS == 1
      /* Call registered callback*/
      hcan->RxFifo0FullCallback(hcan);
#else
      /* Call weak (surcharged) callback */
      HAL_CAN_RxFifo0FullCallback(hcan);
#endif /* USE_HAL_CAN_REGISTER_CALLBACKS */
    }
}

/* Receive FIFO 0 message pending interrupt management *********************/
if ((interrupts & CAN_IT_RX_FIFO0_MSG_PENDING) != 0U)
{
    /* Check if message is still pending */
    if ((hcan->Instance->RF0R & CAN_RF0R_FMP0) != 0U)
    {
      /* Receive FIFO 0 mesage pending Callback */
#if USE_HAL_CAN_REGISTER_CALLBACKS == 1
      /* Call registered callback*/
      hcan->RxFifo0MsgPendingCallback(hcan);
#else
      /* Call weak (surcharged) callback */
      HAL_CAN_RxFifo0MsgPendingCallback(hcan);
#endif /* USE_HAL_CAN_REGISTER_CALLBACKS */
    }
}

/* Error interrupts management *********************************************/
if ((interrupts & CAN_IT_ERROR) != 0U)
{
    if ((msrflags & CAN_MSR_ERRI) != 0U)
    {
      /* Check Error Warning Flag */
      if (((interrupts & CAN_IT_ERROR_WARNING) != 0U) &&
          ((esrflags & CAN_ESR_EWGF) != 0U))
      {
      /* Set CAN error code to Error Warning */
      errorcode |= HAL_CAN_ERROR_EWG;

      /* No need for clear of Error Warning Flag as read-only */
      }

      /* Check Error Passive Flag */
      if (((interrupts & CAN_IT_ERROR_PASSIVE) != 0U) &&
          ((esrflags & CAN_ESR_EPVF) != 0U))
      {
      /* Set CAN error code to Error Passive */
      errorcode |= HAL_CAN_ERROR_EPV;

      /* No need for clear of Error Passive Flag as read-only */
      }

      /* Check Bus-off Flag */
      if (((interrupts & CAN_IT_BUSOFF) != 0U) &&
          ((esrflags & CAN_ESR_BOFF) != 0U))
      {
      /* Set CAN error code to Bus-Off */
      errorcode |= HAL_CAN_ERROR_BOF;

      /* No need for clear of Error Bus-Off as read-only */
      }

      /* Check Last Error Code Flag */
      if (((interrupts & CAN_IT_LAST_ERROR_CODE) != 0U) &&
          ((esrflags & CAN_ESR_LEC) != 0U))
      {
      switch (esrflags & CAN_ESR_LEC)
      {
          case (CAN_ESR_LEC_0):
            /* Set CAN error code to Stuff error */
            errorcode |= HAL_CAN_ERROR_STF;
            break;
          case (CAN_ESR_LEC_1):
            /* Set CAN error code to Form error */
            errorcode |= HAL_CAN_ERROR_FOR;
            break;
          case (CAN_ESR_LEC_1 | CAN_ESR_LEC_0):
            /* Set CAN error code to Acknowledgement error */
            errorcode |= HAL_CAN_ERROR_ACK;
            break;
          case (CAN_ESR_LEC_2):
            /* Set CAN error code to Bit recessive error */
            errorcode |= HAL_CAN_ERROR_BR;
            break;
          case (CAN_ESR_LEC_2 | CAN_ESR_LEC_0):
            /* Set CAN error code to Bit Dominant error */
            errorcode |= HAL_CAN_ERROR_BD;
            break;
          case (CAN_ESR_LEC_2 | CAN_ESR_LEC_1):
            /* Set CAN error code to CRC error */
            errorcode |= HAL_CAN_ERROR_CRC;
            break;
          default:
            break;
      }

      /* Clear Last error code Flag */
      CLEAR_BIT(hcan->Instance->ESR, CAN_ESR_LEC);
      }
    }

    /* Clear ERRI Flag */
    __HAL_CAN_CLEAR_FLAG(hcan, CAN_FLAG_ERRI);
}

/* Call the Error call Back in case of Errors */
if (errorcode != HAL_CAN_ERROR_NONE)
{
    /* Update error code in handle */
    hcan->ErrorCode |= errorcode;

    /* Call Error callback function */
#if USE_HAL_CAN_REGISTER_CALLBACKS == 1
    /* Call registered callback*/
    hcan->ErrorCallback(hcan);
#else
    /* Call weak (surcharged) callback */
    HAL_CAN_ErrorCallback(hcan);
#endif /* USE_HAL_CAN_REGISTER_CALLBACKS */
}
}

/***************************** 内部函数 End********************************************/

/************************ (C) COPYRIGHT DTWT *****END OF FILE****/



页: [1]
查看完整版本: STM32+CubeMax+CAN通信配置详解