参考:https://blog.csdn.net/u012308586/article/details/81001102
https://blog.csdn.net/flydream0/article/details/52317532
https://blog.csdn.net/u012587637/article/details/52032630
1.CAN配置
54/6/(10+7+1)=500k
2.usart.c
/* USER CODE BEGIN 0 */
#include "usart.h"
//2个3级深度的FIFO
#define CAN1FIFO CAN_RX_FIFO0
CAN_TxHeaderTypeDef TxMeg;
CAN_RxHeaderTypeDef RxMeg;
/*****************************************************************************
* 函 数 名 : CAN_User_Init
* 负 责 人 : by
* 创建日期 : 2020年8月26日
* 函数功能 :
* 输入参数 : 无
* 输出参数 : 无
* 返 回 值 :
* 调用关系 :
* 其 它 :
过滤器组:STM32总共提供14个过滤器组来处理CAN接收过滤问题,每个过滤器组包含两个32位寄存器CAN_FxR0和CAN_FxR1组成,在设置为屏蔽位模式下,其中一个作为标识符寄存器,另一个作为屏蔽码寄存器。过滤器组中的每个过滤器,编号(叫做过滤器号)从0开始,到某个最大数值(这时最大值并非13,而是取决于14个过滤器组的模式和位宽的设置,当全部配置为位宽为16,且为标识符列表模式时,最大编号为14*4-1=55)。
过滤器的过滤模式:STM32提供两种过滤模式供用户设置:屏蔽位模式和标识符列表模式。
屏蔽位模式:为了过滤出一组标识符,应该设置过滤器组工作在屏蔽位模式。在屏蔽位模式下,标识符寄存器和屏蔽寄存器一起,指定报文标识符的任何一位,应该按照“必须匹配”或“不用关心”处理。
标识符模式:为了过滤出一个标识符,应该设置过滤器组工作在标识符列表模式。在标识符列表模式下,屏蔽寄存器也被当作标识符寄存器用。因此,不是采用一个标识符加一个屏蔽位的方式,而是使用2个标识符寄存器。接收报文标识符的每一位都必须跟过滤器标识符相同。
列表模式:准备好一张表,把我们需要关注的所有CAN报文ID写上去,开始过滤的时候只要对比这张表,如果接收到的报文ID与表上的相符,则通过,如果表上没有,则不通过,这个就是简单的过滤方案。一个报文ID,则需要往列表中写入这个ID,如果需要关注两个,则需要写入两个报文ID,列表的方式受到列表容量大小的限制,实际上,bxCAN的一个过滤器若工作在列表模式下,scale为32时,每个过滤器的列表只能写入两个报文ID,若scale为16时,每个过滤器的列表最多可写入4个CAN ID
过滤器的位宽:
每个过滤器组的位宽都可以独立配置,以满足应用程序的不同需求。根据位宽的不同,每个过滤器组可提供:
●1个32位过滤器,包括:STDID[10:0]、EXTID[17:0]、IDE和RTR位
●2个16位过滤器,包括:STDID[10:0]、IDE、RTR和EXTID[17:15]位
过滤器组的过滤模式和位宽设置
过滤器组可以通过相应的CAN_FMR寄存器(CAN过滤器主控寄存器)配置。但是不是什么时候都可以直接配置,在配置一个过滤器组前,必须通过清除CAN_FAR寄存器(CAN过滤器激活寄存器)的FACT位,把它设置为禁用状态。然后才能设置或设置过滤器组的配置。
通过设置CAN_FS1R(CAN过滤器位宽寄存器)的相应FSCx位,可以配置一个过滤器组的位宽。
通过CAN_FM1R(CAN过滤器模式寄存器)的FBMx位,可以配置对应的屏蔽/标识符寄存器的标识符列表模式或屏蔽位模式。
*****************************************************************************/
void CAN_User_Init(CAN_HandleTypeDef* hcan ) //用户初始化函数
{
CAN_FilterTypeDef sFilterConfig;
HAL_StatusTypeDef HAL_Status;
TxMeg.IDE=CAN_ID_STD;//标准帧模式
TxMeg.RTR=CAN_RTR_DATA;//数据帧(对应有远程帧)
sFilterConfig.FilterBank = 0; //过滤器0
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; //设为标识符模式,STM32提供两种过滤模式供用户设置:屏蔽位模式和标识符列表模式
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; //过滤器位数32,只接收4个16位标准帧,或者2个32位帧
sFilterConfig.FilterIdHigh = 0x0000<<5; 设置标识符寄存器高字节.CAN_FilterIdHigh包含的是STD[0~10]和EXID[13~17],标准CAN ID本身是不包含扩展ID数据,因此为了要将标准CAN ID放入此寄存器,标准CAN ID首先应左移5位后才能对齐.
sFilterConfig.FilterIdLow = 0x0000<<5;
sFilterConfig.FilterMaskIdHigh =0x0000<<5;
sFilterConfig.FilterMaskIdLow =0x0000<<5;
sFilterConfig.FilterFIFOAssignment = CAN1FIFO; //接收到的报文放入到FIFO0中
sFilterConfig.FilterActivation = ENABLE; //激活过滤器
sFilterConfig.SlaveStartFilterBank = 0;
HAL_Status=HAL_CAN_ConfigFilter(hcan, &sFilterConfig);
HAL_Status=HAL_CAN_Start(hcan); //开启CAN
if(HAL_Status!=HAL_OK){
printf("开启CAN失败\r\n");
}
HAL_Status=HAL_CAN_ActivateNotification(hcan, CAN_IT_RX_FIFO0_MSG_PENDING);
if(HAL_Status!=HAL_OK){
printf("开启挂起中段允许失败\r\n");
}
}
/*****************************************************************************
* 函 数 名 : HAL_CAN_RxFifo0MsgPendingCallback
* 负 责 人 : by
* 创建日期 : 2020年8月26日
* 函数功能 :
* 输入参数 : 无
* 输出参数 : 无
* 返 回 值 :
* 调用关系 :
* 其 它 :
*****************************************************************************/
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) //接收回调函数
{
uint8_t Data[8];
HAL_StatusTypeDef HAL_RetVal;
if(hcan ==&hcan1){
HAL_RetVal=HAL_CAN_GetRxMessage(hcan,CAN1FIFO,&RxMeg,Data);
if ( HAL_OK==HAL_RetVal){
//在这里接收数据
Usart1SendData_DMA(Data,8);
}
}
}
/*****************************************************************************
* 函 数 名 : CANx_SendNormalData
* 负 责 人 : by
* 创建日期 : 2020年8月26日
* 函数功能 :
* 输入参数 : 无
* 输出参数 : 无
* 返 回 值 :
* 调用关系 :
* 其 它 :
*****************************************************************************/
uint8_t CANx_SendNormalData(CAN_HandleTypeDef* hcan,uint16_t ID,uint8_t *pData,uint16_t Len)
{
HAL_StatusTypeDef HAL_RetVal;
uint16_t SendTimes,SendCNT=0;
uint8_t FreeTxNum=0;
TxMeg.StdId=ID;
if(!hcan || ! pData ||!Len) return 1;
SendTimes=Len/8+(Len%8?1:0);
FreeTxNum=HAL_CAN_GetTxMailboxesFreeLevel(hcan);
TxMeg.DLC=8;
while(SendTimes--){
if(0==SendTimes){
if(Len%8)
TxMeg.DLC=Len%8;
}
while(0==FreeTxNum){
FreeTxNum=HAL_CAN_GetTxMailboxesFreeLevel(hcan);
}
HAL_Delay(1); //没有延时很有可能会发送失败
HAL_RetVal=HAL_CAN_AddTxMessage(hcan,&TxMeg,pData+SendCNT,(uint32_t*)CAN_TX_MAILBOX0);
if(HAL_RetVal!=HAL_OK)
{
return 2;
}
SendCNT+=8;
}
return 0;
}
/* USER CODE END 0 */
3程序说明
3.1.can.h头文件添加对应函数
void CAN_User_Init(CAN_HandleTypeDef* hcan );
uint8_t CANx_SendNormalData(CAN_HandleTypeDef* hcan,uint16_t ID,uint8_t *pData,uint16_t Len);
3.2Usart1SendData_DMA(Data,8); 这个函数是dma串口发送函数,如果没有可以用printf代替,重定义一下即可(&huart2,串口2打印)
#ifdef __GNUC__
/* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
PUTCHAR_PROTOTYPE
{
HAL_UART_Transmit(&huart2,(uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
3.3.若想接收特定的ID数据,可使用屏蔽列表模式,如四个标准帧01、02 、03 、 04
sFilterConfig.FilterMode = CAN_FILTERMODE_IDLIST; //设为列表模式
sFilterConfig.FilterScale = CAN_FILTERSCALE_16BIT; //过滤器位数16
sFilterConfig.FilterIdHigh = 0x0001<<5;
sFilterConfig.FilterIdLow = 0x0002<<5;
sFilterConfig.FilterMaskIdHigh =0x0003<<5;
sFilterConfig.FilterMaskIdLow =0x0004<<5;
若对某些位进行区分,查看手册
4.main.c
/* USER CODE BEGIN 2 */ 里添加
CAN_User_Init(&hcan1);
uint8_t datasend[8]={0x11,0x12,0x13,0x14,0x15};
CANx_SendNormalData(&hcan1,0x11,datasend,8);
HAL_Delay(100);
结果,CAN卡