STM32Cube学习笔记-CAN通讯调试

    今天要讨论的是使用STM32Cube MX配置CAN通讯。既然能见到这篇文章,说明您对CAN通讯已经有了很多的了解,而且目前正在使用这个东西,并且遇到了一些问题,就是不能通讯的问题。在使用STM32Cube调试CAN的时候我也遇到了很多的问题,这些问题都被解决了,今天就来聊一聊如何使用STM32Cube配置CAN通讯。CAN的基本原理自不必说,有不懂得地方可以直接百度。

    硬件平台:野火野火STM32F429挑战者开发板,周立功USBCAN调试盒;软件:STM32Cube MX、CAN Test和Keil 5。

    本篇文章要完成的工作:

    1.开发板可以通过CAN循环发送数据,配置波特率为250kbits,引脚为PB8和PB9,使用周立功的CAN卡接收,在CAN Test上显示数据,

    发送的数据ID = 0x18ff3333,数据为{0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08};

    2.配置CAN的接收函数,在循环函数中接收CAN数据,当接收到CAN ID为0x18332201的扩展帧时,当接收的Data[0]=0x01的时候熄灭LED灯,否则LED灯点亮。

    3.配置CAN的接收中断,当接收到0x18442201的扩展帧时,当接收到的Data[1]=0x02时熄灭LED灯,否则LED灯点亮。

    设置以上三个任务的理由:配置CAN发送较为简单,可以直接使用CAN卡读取数据,没有读到就很可能是配置错误,或者函数使用错误,当数据能发送,就代表硬件是没有问题的。第二步在while中接收是由于配置中断复杂一点,现在while中看一下CAN的接收函数是否使用正确。第三步使用CAN中断,CAN中断使用较多,配置CAN中断有一定的意义。

打开STM32Cube MX开始进行配置。先配置下载调试口、时钟、LED(PH10见《STM32Cube学习笔记-点灯和打印》)。

然后配置CAN的引脚和参数。开发板的CAN使用的是PB8和PB9,在STM32Cube上配置如下。

左侧先选择CAN1,然后在Configuration的Parameter Settings中配置波特率和基本参数。

波特率计算法方法。

CAN时钟 = CAN所在的外设时钟/Prescaler,

等于分频后CAN外设得到的真正的时钟

波特率 = CAN时钟/(Segment1+Segment2+1). 45MHz/45 = 1MHz;1MHz/(2+1+1) = 250kbits,

那么问题来了,假如我不知道CAN在APB1还是APB2怎么办?看下时钟树,

看这张图哈,有挂接在APB1的外设时钟,有APB1的定时器时钟,有挂接在APB2的外设时钟,有APB2的定时器时钟。一般认为,APB1上挂接的是低速外设,APB2上挂接的是高速外设。

当把CAN的分频系数设置为45,Time Quantum 变为了1000ns,说明就是挂接在了APB1的外设时钟上,按照这个方法可以类推其他的外设时钟配置方式。

CAN的其他配置都很好理解了,CAN的配置就完成了。然后继续配置后面的工程相关的选项,然后生成代码。该工程是在上一个博客《STM32Cube学习笔记-点灯和打印》上配置的。

看代码。

初始化里增加了CAN1的初始化内容,while循环中保留着上一个博客里编写的内容。去找CAN的库函数。

这是在stm32f4xx_hal_can.h中找到了库函数。根据名称基本可以知道,包含了CAN开启、CAN停止、CAN发送、CAN接收等函数。

再往下看,有CAN节点激活、CAN中断等函数。

那我们直接把

HAL_StatusTypeDef HAL_CAN_AddTxMessage(CAN_HandleTypeDef *hcan, CAN_TxHeaderTypeDef *pHeader, uint8_t aData[], uint32_t *pTxMailbox);

放到while中,希望做到的是程序不断的通过CAN发送数据。然后编译下载。

定义发送数据的全局变量。

定义要发送的数据。

定义发送数据全局变量的配置。

调用发送函数。

得到的结果是没有数据发出来,失败!检查了一下从初始化到数据发送,好像都没有出错。然后看库函数的时候,尝试使用在初始化完CAN后,调用CAN启动函数。

有数据了,还是有点兴奋的。

然后在while中添加接收函数。

HAL_StatusTypeDef HAL_CAN_GetRxMessage(CAN_HandleTypeDef *hcan, uint32_t RxFifo, CAN_RxHeaderTypeDef *pHeader, uint8_t aData[]);

定义接收的全局变量。定义要接收的数据存储的位置。

程序按照要求,当接到的数据的Data0位0x01,且Id为0x18332201时熄灭LED。

实际进行了测试,失败!这个是为什么呢?一直没有搞明白,肯定还是有东西没有配置。

我看了程序,都是没有问题,CAN也启动了,就是通过CAN Test把数据发送后,LED灯不会熄灭。

我实在没招了,就去看了例程程序,人家比我多了一个滤波器配置。然后我就配置了滤波器。

再次编译下载,LED灯终于熄灭了,大功告成。

最后一个是配置CAN接收中断函数。首先先回到STM32Cube配置接收中断函数。

生成代码后,去stm32f4xx_it.c中找到一个名为 CAN1_RX0_IRQHandler(void)的东西,这个函数调用了HAL_CAN_IRQHandler(&hcan1),那就进入到这个被调用的函数。

我们看到了有很多这样的回调函数,可以基本确认的是逻辑程序应该写到这种函数中。然后找到一个应该是对的回调函数。

这两个函数看着像。然后我先找到了HAL_CAN_RxFifo0MsgPendingCallback()的回调函数;

这是个弱函数,弱函数的意思是如果工程中没有定义这个函数,就调用这个函数,如果有其他的同名函数,函数可以存在的,而且优先调用其他的同名函数。所以我们直接在main中定义一个这个函数;

编译下载,然后按照要求发送数据,LED灯没有熄灭,失败。这是什么原因呢?

额....我记得中断都是需要先激活的。走,去库函数中找找。

找到了一个HAL_StatusTypeDef HAL_CAN_ActivateNotification(CAN_HandleTypeDef *hcan, uint32_t ActiveITs);

意思是激活节点,而且注意第二个参数,ActiveITs,IT是不是很熟悉,中断的意思,不管了,先写到初始化中。

编译,下载,可以使LED熄灭了,成功。

而且在找第二个参数的时候,发现了这个

我似乎发现了什么,然后就把HAL_CAN_RxFifo0MsgPendingCallback()的回调函数换成了HAL_CAN_RxFifo0FullCallback();

把激活点换成了CAN_IT_RX_FIFO0_MSG_PENDING,试一下,然后发现的问题是我发送了三次LED灯才会熄灭。

到此,使用STM32Cube配置CAN试验部分讲解玩了。

然后我在配置CAN中断的时候看到了这个东西,咱们再讨论一下。

CAN1 TX interrupts是CAN1的发送函数,CAN1 RX0 interrupts和CAN1 RX1 interrupt的区别是什么?CAN1 SCE interrupt是CAN的错误中断,使用的不多。

那就讨论一下CAN1 RX0 interrupts和CAN1 RX1 interrupt的区别是什么?

说是CAN1的缓存器,就是FIFO有两个,一个是FIFO0,一个是FIFO1,当配置的是FIFO0就使用CAN1 RX0 interrupts,当配置的是FIFO1就使用CAN1 RX1 interrupts.

这个配置需要注意的地方:

HAL_CAN_ActivateNotification(&hcan1,CAN_IT_RX_FIFO0_FULL);

s_CAN_Filter.FilterFIFOAssignment = CAN_RX_FIFO0;

HAL_CAN_GetRxMessage(&hcan1,CAN_RX_FIFO0,&CAN_RxHeader,Rece_Data);

就这三个地方了。

那么我想探究一下我发的数据放在哪个FIFO了,我配置了两个中断,设置两个滤波器。

根据试验结果,执行了HAL_CAN_RxFifo1MsgPendingCallback();

然后我把两个滤波器的配置的FIFO顺序颠倒一下,发现执行了HAL_CAN_RxFifo0MsgPendingCallback();

这个在配置的时候注意下。到此,CAN的讲解结束,有不对之处还请指正!

 

 

 


版权声明:本文为qq_25207883原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。