STM32大多数型号均使用的是bxCAN这个IP核,该IP核工作非常稳定,以至于从STM32F1系列到STM32F7系列均使用此IP核。下列两张图分别为STM32F1C8Tx系列和STM32F767IITx的CAN配置界面。可以看到配置界面的选项完全相同。
bxCAN的配置例程非常多,应用广泛。FDCAN是2011年博世发布的改进版CAN,FDCAN的性能固然强悍,但是在实际项目中若是多个板之间通信的情况,难免需要让FDCAN兼容CAN通信。本文皆在介绍如何通过CubeMX配置FDCAN使之兼容CAN使用,并讨论和实践FIFO深度参数的实际作用。
首先,设置帧格式为传统模式,模式设置为正常模式;然后,设置“2”处的4个参数,这4个参数与bxCAN中4个决定波特率和采样位置的参数意义相同,具体计算下文介绍;最后设置接收FIFO和发送FIFO,该部分介绍同样在下文展开介绍。
此图是CAN的位时序图,其中SyncSeg部分的时间长度即Nominal Sync Jump Width,Bit segment 1即Nominal Time Seg1,Bit segment 2 即Nominal Time Seg2,而这三个参数的单位是时钟个数,故实际的时间长度即为时钟数乘以每个时钟的时间长度。
每个时钟的时间长度由进入CAN外设的时钟和Nominal Prescaler分频器共同决定。
例如本例中,四个参数分别为10,1,15,8,怎么来的呢?首先已知条件是输入CAN外设的时钟是120MHZ,想要的波特率是500KHZ即0.5MHZ。首先分频 120MHZ/10=12MHZ,然后每个周期的时钟数为1+15+8=24,最后算出波特率为12MHZ/12=0.5MHZ=500KHZ。
到这里想必屏幕前的你也有个疑惑,为啥是1,15,8三个值,别的行不?当然可以,不过要注意这三个值之间的关系决定着采样点的位置。由上图显而易见,BS1+SyncSeg大BS2小时,采样点后移,BS1+SyncSeg小BS2大时,采样点前移。采样点不宜太前或太后,一碗水端平即可。
首先从整个CAN宏观来看FIFO在消息RAM中,该消息RAM总共有10KB,此RAM为CAN的专用RAM,和系统SRAM没有关系。此RAM已经划分好了每个区域的用途和大小,如下图。
由此图可见接收FIFO的每个单元的深度均为64,无论帧长为多少,深度都是64,只不过在用作普通CAN时只用了前8个单元。每个FIFO总共有这样的单元64个,接收部分总共有这样的两个FIFO。即FIFO全压满时可以暂存128条信息,这非常利于批量处理信息。发送部分的FIFO与发送同理,只是空间小一些。当然,消息RAM是FDCAN1和FDCAN2公用的,在实际使用中如果两个FDCAN都是用了,分配FIFO时要注意。
在初期学习时发现大多数参考资料和历程均只用了1个FIFO单元,之后仔细看了数据手册才发现这些资源是不用白不用哦。
基本参数的初始化CubeMX已经自动生成了,我们需要在基本初始化后紧随其后添加准备工作,即接收FIFO设置和过滤器设置,代码如下。这部分直接插入在void MX_FDCAN1_Init(void)函数的用户代码段2即可。
这段代码中的过滤器设置尤为重要,此处使用的是掩码模式,掩码模式和IP地址中的子网掩码原理一样,掩码为1的位必须与指定值相同才可通过,此代码中掩码为全0意味着将接收所有主机发来的信息。
/* USER CODE BEGIN FDCAN1_Init 2 */
//配置RX滤波器
FDCAN1_RXFilter.IdType=FDCAN_STANDARD_ID; //标准ID
FDCAN1_RXFilter.FilterIndex=0; //滤波器索引
FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK; //滤波器类型
FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0; //过滤器0关联到FIFO0
FDCAN1_RXFilter.FilterID1=0x0000; //32位ID
FDCAN1_RXFilter.FilterID2=0x0000; //如果FDCAN配置为传统模式的话,这里是32位掩码
if(HAL_FDCAN_ConfigFilter(&hfdcan1,&FDCAN1_RXFilter)!=HAL_OK) //滤波器初始化
{
Error_Handler();
}
HAL_FDCAN_Start(&hfdcan1); //开启FDCAN
HAL_FDCAN_ActivateNotification(&hfdcan1,FDCAN_IT_RX_FIFO0_NEW_MESSAGE,0);
/* USER CODE END FDCAN1_Init 2 */
发送功能首先设置ID号,该ID号将被CAN总线上的其他主机的过滤器过滤。其次,设置帧类型和帧长度。帧长度设置时尤其要注意,不能直接敲数字上去,要用宏定义,比如8字节长度帧用宏定义FDCAN_DLC_BYTES_8 。
其次该发送函数的套路不同UART\SPI\I2C等,没有区分软件轮询方式、中断方式和DMA方式,而是一个添加信息到FIFO的函数。该函数的功能是,添加当前信息到发送FIFO中,并给FDCAN发送一个发送数据请求。该函数是非阻塞的,不会等着数据发送完毕,所以在批量发送数据时需要注意发送FIFO单元使用数目和每次批量传输数量以及传输时间间隔之间的关系,避免FIFO溢出丢失数据。
uint8_t FDCAN1_Send_Msg(uint8_t* msg,uint32_t len)
{
fdcan1_TxHeader.Identifier=0x12; //32位ID
fdcan1_TxHeader.IdType=FDCAN_STANDARD_ID; //标准ID
fdcan1_TxHeader.TxFrameType=FDCAN_DATA_FRAME; //数据帧
fdcan1_TxHeader.DataLength=len; //数据长度
fdcan1_TxHeader.ErrorStateIndicator=FDCAN_ESI_ACTIVE;
fdcan1_TxHeader.BitRateSwitch=FDCAN_BRS_OFF; //关闭速率切换
fdcan1_TxHeader.FDFormat=FDCAN_CLASSIC_CAN; //传统的CAN模式
fdcan1_TxHeader.TxEventFifoControl=FDCAN_NO_TX_EVENTS; //无发送事件
fdcan1_TxHeader.MessageMarker=0;
if(HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1,&fdcan1_TxHeader,msg)!=HAL_OK) return 1;//发送
return 0;
}
接收也是同理,不是等着数据来,而是去FIFO里面看一圈,有数据就带回来,没有就不管了,所以此函数的返回值在实际使用时是非常重要的,如果FIFO 空了则会返回0,如果没有空返回会大于0,此时应当继续读取。同时,此函数还需要带回来每条信息对应的主机号(ID号)。详细代码如下。
uint8_t FDCAN1_Receive_Msg(uint8_t *buf, uint16_t *Identifier)
{
if(HAL_FDCAN_GetRxMessage(&hfdcan1,FDCAN_RX_FIFO0,&fdcan1_RxHeader,buf)!=HAL_OK)return 0;//接收数据
*Identifier = fdcan1_RxHeader.Identifier;
return fdcan1_RxHeader.DataLength>>16;
}
CAN总线协议属于链路层协议,在使用时必须有完好可以使用的物理层做支持,但是在调试时,不确定物理层是否正常工作时,可以先调试链路层的设置是否正确。此时需要用到回环模式。STM32H7的FDCAN有内部回环和外部回环两种,常用的是内部回环模式。内部回环模式将完全断开与CAN相连接的IO,并置Tx引脚为1。而外部回环模式可以用于物理层测试,此时需要有另一台可以在正常模式下工作的主机,使用外部回环模式会将数据在内部直接接收的同时从CAN的Tx引脚发送出去,若另一台主机此时正确接收了消息,则证明测试主机的物理层的发送功能完好。
上述代码少写入左边的小板子,右边是原子哥的阿波罗开发板(核心板为STM32F429IGT6),使用原子哥的板子发送信息,左边的小板子接收到后返回相同的信息。对比原子哥开发板屏幕上的发送和接收信息即可验证功能。
其次,进一步验证FIFO的作用,这里使用限制轮询和回复消息间隔的方法来验证,验证左边的小板子必须隔500ms才能接受一条消息,并回复一条消息。这样,用原子哥的板子连续发送多条,若在此后每隔500ms,回复一条,且没有遗漏,则说明FIFO起到了作用。
第7章测试中,右侧的大板子是正点原子的阿波罗开发板,运行的是CAN的实验例程,此处下载的是左侧小板子跑的工程,右侧大板子的工程可以直接在正点原子下载站下载。
STM32H7,FDCAN与CAN兼容完整工程-单片机文档类资源-CSDN下载