• 初出茅庐的小李博客之STM32F103实现CAN通信


    CAN通信基础知识

    参考上一篇博客
    https://editor.csdn.net/md/?articleId=131026450

    原理图

    在这里插入图片描述
    转换芯片是 TJA1050

    代码实现思路

    发送思路:定时发送 按键测试发送
    接收思路:中断接收

    CAN代码实现

    • 第一步

    定义了两个全局变量TxMessage和RxMessage,
    分别用于发送和接收CAN消息的邮箱结构体。

    
    CanTxMsg TxMessage = {0};//发送邮箱结构体
    CanRxMsg RxMessage = {0};//接收邮箱结构体
    
    
    • 1
    • 2
    • 3
    • 4
    • 第二步

    配置对应的引脚模式
    通过GPIO_InitTypeDef结构体初始化了GPIOA的引脚12,配置为复用推挽输出。接着,初始化了引脚11,配置为浮空输入。

    CAN_RX的模式

    对于CAN接收引脚,可以选择使用浮空输入模式或上拉输入模式,具体取决我们硬件设计和应用要求。

    浮空输入模式(Floating Input)是指将引脚置为高阻态,不连接到任何电平源,允许外部信号自由驱动引脚电平。在CAN总线中,通常使用终端电阻(通常为120欧姆)来驱动CAN引脚电平。这样可以确保在总线空闲状态时,引脚电平为高阻态。

    上拉输入模式(Pull-up Input)是指在引脚和VDD之间连接一个上拉电阻,将引脚电平拉高到VDD(逻辑高电平)或外部信号源。在CAN总线中,上拉输入模式可用于引脚电平的恢复和传输。

    选择浮空输入模式还是上拉输入模式取决于具体应用的要求。通常,如果CAN总线上的其他设备已经提供了终端电阻,可以选择使用浮空输入模式。如果的硬件设计中没有终端电阻,您可以选择使用上拉输入模式,并使用外部上拉电阻将引脚电平拉高到逻辑高电平。

    CAN_TX的模式

    在CAN通信中,CAN_TX模式是用于配置CAN发送引脚的模式。选择适当的CAN_TX模式取决于我们的硬件设计和应用需求。

    常见的CAN_TX模式有以下两种:

    1. 推挽输出模式(Push-Pull Output):在该模式下,CAN发送引脚被配置为推挽输出,可以提供较高的驱动能力,能够输出高电平和低电平两种电平状态。这种模式适用于直接驱动总线上的终端电阻,提供较强的信号驱动能力。

    2. 开漏输出模式(Open-Drain Output):在该模式下,CAN发送引脚被配置为开漏输出,只能提供低电平输出,而高电平状态是通过外部上拉电阻拉高到逻辑高电平。这种模式适用于与其他设备共享总线的情况,需要使用外部上拉电阻来拉高总线电平。

    选择CAN_TX模式取决于我们的硬件设计和连接配置。如果我们的硬件设计中包含终端电阻,并且CAN总线上没有其他设备需要共享总线,您可以选择推挽输出模式。如果我们的硬件设计中没有终端电阻或需要与其他设备共享总线,则可以选择开漏输出模式。

        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能PORTA时钟
    	RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1,  ENABLE);//使能CAN1时钟
    
    
    	GPIO_InitTypeDef GPIO_InitStructure;
    
    	GPIO_InitStructure.GPIO_Pin    = GPIO_Pin_12;
    	GPIO_InitStructure.GPIO_Speed  = GPIO_Speed_50MHz;
    	GPIO_InitStructure.GPIO_Mode   = GPIO_Mode_AF_PP;	//复用推挽
    	GPIO_Init(GPIOA, &GPIO_InitStructure);		//初始化IO
    
    	GPIO_InitStructure.GPIO_Pin    = GPIO_Pin_11;
    	GPIO_InitStructure.GPIO_Mode   = GPIO_Mode_IN_FLOATING;//上拉输入
    	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化IO
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 第三步
      配置CAN相关参数
      定义了一个CAN_InitTypeDef结构体CAN_InitStructure,用于配置CAN的工作模式和速率。在这里,设置了CAN的模式为正常模式,速率为1Mbps。同时,设置了同步跳转宽度(SJW)为1个时间单位,位时间1(BS1)为2个时间单位,位时间2(BS2)为3个时间单位,预分频器(Prescaler)为6。

    通过CAN_Init函数对CAN进行初始化,传入了上述配置结构体。

    然后,定义了一个CAN_FilterInitTypeDef结构体CAN_FilterInitStructure,用于配置CAN的过滤器。在这里,设置了过滤器0,使用标识符屏蔽模式,并且过滤掉所有的标识符和掩码。将过滤器与FIFO0相关联,并激活过滤器。
    通过CAN_FilterInit函数对CAN过滤器进行初始化,传入了上述配置结构体。

    CAN_InitTypeDef   CAN_InitStructure;
    	
    	CAN_InitStructure.CAN_TTCM=DISABLE;						
    	CAN_InitStructure.CAN_ABOM=DISABLE;						
    	CAN_InitStructure.CAN_AWUM=ENABLE;						 
    	CAN_InitStructure.CAN_NART=DISABLE;						 
    	CAN_InitStructure.CAN_RFLM=DISABLE;						
    	CAN_InitStructure.CAN_TXFP=DISABLE;						 
    
    	CAN_InitStructure.CAN_Mode= CAN_Mode_Normal;	         //模式设置
    
      //1Mbits  500K  
    	CAN_InitStructure.CAN_SJW       = CAN_SJW_1tq;
    	CAN_InitStructure.CAN_BS1       = CAN_BS1_2tq;
    	CAN_InitStructure.CAN_BS2       = CAN_BS2_3tq;      
    	CAN_InitStructure.CAN_Prescaler = 6; //6分频
    
    	CAN_Init(CAN1, &CAN_InitStructure);   
    
    	CAN_FilterInitTypeDef    CAN_FilterInitStructure;
    
    	CAN_FilterInitStructure.CAN_FilterNumber              = 0;	  //过滤器0
    	CAN_FilterInitStructure.CAN_FilterMode                = CAN_FilterMode_IdMask; 
    	CAN_FilterInitStructure.CAN_FilterScale               = CAN_FilterScale_32bit; //32位 
    	CAN_FilterInitStructure.CAN_FilterIdHigh              = 0x0000;32位ID
    	CAN_FilterInitStructure.CAN_FilterIdLow               = 0x0000;
    	CAN_FilterInitStructure.CAN_FilterMaskIdHigh          = 0x0000;//32位MASK
    	CAN_FilterInitStructure.CAN_FilterMaskIdLow           = 0x0000;
    	CAN_FilterInitStructure.CAN_FilterFIFOAssignment      = CAN_Filter_FIFO0;//过滤器0关联到FIFO0
    	CAN_FilterInitStructure.CAN_FilterActivation          = ENABLE; 
    
    	CAN_FilterInit(&CAN_FilterInitStructure);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 第四步

    通过CAN_ITConfig函数使能CAN1的FIFO0接收中断。

    然后,定义了一个NVIC_InitTypeDef结构体NVIC_InitStructure,用于配置CAN接收中断的中断优先级。在这里,设置了中断通道为USB_LP_CAN1_RX0_IRQn,主优先级为1,次优先级为0,并使能中断。

    最后,在CAN_Config函数中进行了CAN总线的配置和初始化,并完成了中断的设置。

    CAN接收逻辑实现

    中断方式

     //CAN接收数据的函数
    void USB_LP_CAN1_RX0_IRQHandler(void)
    {
    	if(CAN_GetITStatus(CAN1, CAN_IT_FMP0) != RESET)//查询标志位 
    	{
    		CAN_Receive(CAN1, CAN_FIFO0, &RxMessage); //接收信息
    		
    		// 打印接收到的数据
    		printf("Rece CAN message: ID:0x%02X, DLC:%d, Data:", RxMessage.StdId, RxMessage.DLC);
    		for(uint8_t i = 0; i < RxMessage.DLC; i++)
    		{
    			printf("%02X ", RxMessage.Data[i]);
    		}
    		printf("\n");
    		//CAN_BAOWENJIANCE()
    		//打印信息 串口1 串口2 printf都可以 
    	
    		CAN_ClearITPendingBit(CAN1, CAN_IT_FMP0); // 清除中断标志位
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    CAN发送逻辑的实现

    
    //CAN发送数据的接口函数
    uint8_t CAN_SendMessage(uint32_t StdId, uint8_t* pData, uint8_t Length)
    {
    	uint8_t MailBox   = 0;
    	TxMessage.StdId   = StdId;
    	TxMessage.ExtId   = 0x00;
    	TxMessage.IDE     = CAN_ID_STD;
    	TxMessage.RTR     = CAN_RTR_DATA;
    	TxMessage.DLC     = Length;
    	for (uint8_t i = 0; i < Length; i++)
    	{
    		TxMessage.Data[i] = pData[i];//发送的数据赋值
    	}
    	
      // 打印接发送的数据
    	printf("Send CAN message: ID:0x%02X, DLC:%d, Data:", TxMessage.StdId, TxMessage.DLC);
    	for(uint8_t i = 0; i < TxMessage.DLC; i++)
    	{
    		printf("%02X ", TxMessage.Data[i]);
    	}
    	printf("\n");
    	
    	MailBox = CAN_Transmit(CAN1, &TxMessage);
    	while((CAN_TransmitStatus(CAN1, MailBox)==CAN_TxStatus_Failed));
    	return TxMessage.DLC;		//返回值是发送了几个字节
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    发送信息

    这是一条标准数据帧 由MCU发出

     //发送信息在这里填写
    uint32_t StdId     =  0x55;//数据帧ID
    uint8_t  TxData[8] =  {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF};//8字节数据内容
    uint8_t  Length    =  8;//发送字节个数
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    接收信息

    这是一条标准数据帧 由在这里插入图片描述
    上位机CAN调试助手发出

    测试效果

    在这里插入图片描述

    代码演示

    STM32实现CAN收发通信

  • 相关阅读:
    渗透测试-Windows密码凭证获取
    mybatis分页实现
    ASW3410数据手册|ASW3410设计参数|USB3.0高速数据开关切换IC说明书
    欧洲巨头强力支持Higg项目,事关2000个品牌
    细节控有福了!显微镜级别的检测手法
    SpringBoot+Vue+Element-UI实现协同过滤算法商品推荐系统
    多版本并发控制 MVCC
    Java中的反射是什么?
    求二叉树的最大密度(可运行)
    Vant Weapp的Slider组件自定义button
  • 原文地址:https://blog.csdn.net/weixin_43176183/article/details/131145086