关于stm32 can通讯的一些总结

关于stm32 can通讯的一些总结

1.相对而言,我使用只是一主一从,两个终端的通信。所以两边都是加上终端电阻120欧,另外stm32的can通讯相对工业和汽车上使用的can通讯比较简单。stm32的库函数可以解决大多数问题。
2.首先stm32的can外设具有4个模式,其中的回环模式,就可以测试配置是否正常,可以用来检测一些基本的配置,回环模式正确的情况下再检测normal模式。
#define CAN_Mode_Normal ((uint8_t)0x00) /*!< normal mode /
#define CAN_Mode_LoopBack ((uint8_t)0x01) /
!< loopback mode /
#define CAN_Mode_Silent ((uint8_t)0x02) /
!< silent mode /
#define CAN_Mode_Silent_LoopBack ((uint8_t)0x03) /
!< loopback combined with silent mode */
3.can的传输速率很快,波特率可以通过结构体来配置波特率,有个很简单的表格。在这里插入图片描述
在这里插入图片描述
其实从这里就看出为什么硬件的工资低于软件的工资,是因为硬件只是在使用工具很多内容已成熟,只需要装配整合资源。编程内容也不复杂,一些单片机都有基本的寄存器配置和库函数使用,stm32的cube更加``方便。

	CAN_InitStructure.CAN_SJW=CAN_SJW_1tq;		   //BTR-SJW 重新同步跳跃宽度 2个时间单元
	 
	/* ss=1 bs1=5 bs2=3 位时间宽度为(1+5+3) 波特率即为时钟周期tq*(1+3+5)  */
	CAN_InitStructure.CAN_BS1=CAN_BS1_3tq;		   //BTR-TS1 时间段1 占用了5个时间单元
	CAN_InitStructure.CAN_BS2=CAN_BS2_2tq;		   //BTR-TS1 时间段2 占用了3个时间单元	
	
	/* CAN Baudrate = 1 MBps (1MBps已为stm32的CAN最高速率) (CAN 时钟频率为 APB1 = 36 MHz) */
	CAN_InitStructure.CAN_Prescaler =12;		   ///
	CAN_Init(CANx, &CAN_InitStructure);

后面的备注错误啊,我这里对照表格配置到了500k。
4.接下来就是can通讯中最难的筛选器部分。对于can我是很喜欢的,因为它的缓存FIFO邮箱用起来非常方便也不占栈区资源。数据处理起来也很方便。我之前看过一篇对于筛选器讲的非常好的文章,非常形象。https://blog.csdn.net/bonson2004/article/details/68942442,文章中用身份证做比喻,带着各种ID号的信息就是用着不同身份证的人。每个人需要被安排到不同的城池去(邮箱),筛选方法主要是四种。
在这里插入图片描述
首先我们要搞清楚掩码模式和列表模式,列表模式比较简单,当然筛选的人也比较狭窄。比如我列了标准帧的0x01和0x02,那就只有这两个ID的小伙伴可以进我的城池,像16位的列表模式最多只有4个小伙伴。这就是列表模式的优缺点。下面是我写的16位列表模式的代码。

static void CAN_Filter_Config(void)
{
	CAN_FilterInitTypeDef  CAN_FilterInitStructure;

	/*CAN筛选器初始化*/
	CAN_FilterInitStructure.CAN_FilterNumber=0;						//筛选器组0,F103只有0至13组
	CAN_FilterInitStructure.CAN_FilterMode= CAN_FilterMode_IdList ;	//工作在列表模式
	CAN_FilterInitStructure.CAN_FilterScale= CAN_FilterScale_16bit;	//筛选器位宽为单个16位。
	/* 使能筛选器,按照标志的内容进行比对筛选,扩展ID不是如下的就抛弃掉,是的话,会存入FIFO0。 */

CAN_FilterInitStructure.CAN_FilterIdHigh= ((((u32)0x002<<21)|CAN_ID_STD|CAN_RTR_DATA)&0xFFFF0000)>>16;		//要筛选的ID高位 
	CAN_FilterInitStructure.CAN_FilterIdLow= ((((u32)0x003<<21)|CAN_ID_STD|CAN_RTR_DATA)&0xFFFF0000)>>16; //要筛选的ID低位 
	CAN_FilterInitStructure.CAN_FilterMaskIdHigh=((((u32)0x001<<21)|CAN_ID_STD|CAN_RTR_DATA)&0xFFFF0000)>>16;	//筛选器高16位每位必须匹配
	CAN_FilterInitStructure.CAN_FilterMaskIdLow=((((u32)0x004<<21)|CAN_ID_STD|CAN_RTR_DATA)&0xFFFF0000)>>16;	
	
	
	CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0 ;				//筛选器被关联到FIFO0存储在缓冲0
	CAN_FilterInitStructure.CAN_FilterActivation=ENABLE;			//使能筛选器
	CAN_FilterInit(&CAN_FilterInitStructure);
	/*CAN通信中断使能*/
	CAN_ITConfig(CANx, CAN_IT_FMP0, ENABLE);
}

掩码模式就是相对复杂一点了,简单点来说,就是我还是列了个表,类似于身份证的地址码,江苏32,那就前几位是32的可以进来,再精确点,江苏镇江的才能进来。这样筛选的条件又多了,进来的人相对而言又少了。掩码就是这样一个功能,比如掩码的第二位为1,也就是要进来的人id第二位都和我手上的id的第二位一样。这样筛选进城的人数就非常的灵活。还有三种的模式的代码,我就不贴了,直接修改id号就可以达到目的。可以参考这篇文章。https://blog.csdn.net/Auto0313/article/details/97904562。
5.接下来就是数据处理阶段了,使用接收中断可以快速处理不同id号数据,直接调用接收结构体就行了,最后记得释放FIFO。


void CAN_RX_IRQHandler(void)
{
	
	/*从邮箱中读出报文*/
	
	CAN_Receive(CANx, CAN_FIFO0, &RxMessage);

	/* 比较ID是否为0x1314 */ 
	if((RxMessage.StdId==0x001) && (RxMessage.IDE==CAN_ID_STD)  )
	{
	flag = 1;		//接收成功 
	
PData.Current48V[0]=(((float)((uint16_t)(RxMessage.Data[4])<<8|RxMessage.Data[5])*55/4096)-10);//1号板电流大小
PData.Voltage48V[0]=(((float)((uint16_t)(RxMessage.Data[6])<<8|RxMessage.Data[7])*3.3/4096)*192/10);//1号电压大小
//	PData.Current48V[0]=	(uint16_t)(RxMessage.Data[4])<<8|RxMessage.Data[5];
//	PData.Voltage48V[0]=(uint16_t)(RxMessage.Data[6])<<8|RxMessage.Data[7]	;
	}
	else if((RxMessage.StdId==0x002) && (RxMessage.IDE==CAN_ID_STD))
	{
	flag = 2; 					   //接收失败
PData.Current48V[1]=(((float)((uint16_t)(RxMessage.Data[0])<<8|RxMessage.Data[1])*55/4096)-10);//1号板电流大小
PData.Voltage48V[1]=(((float)((uint16_t)(RxMessage.Data[2])<<8|RxMessage.Data[3])*3.3/4096)*192/10);//1号电压大小		
PData.Power_wendu=RxMessage.Data[4];
PData.Power_shidu=RxMessage.Data[5];		
	}
	else if((RxMessage.StdId==0x003) && (RxMessage.IDE==CAN_ID_STD))
	{
	flag = 3; 
	PData.Leak_alarm=RxMessage.Data[3];//漏水字节信号,正常为1,故障为0
	if(RxMessage.Data[3]==1)
	  {
	CAN_SetMsg2(&TxMessage);
		CAN_Transmit(CANx, &TxMessage);
	
	  }
	}
	else
	{
	flag = 0;
	}
	CAN_FIFORelease(CAN1,CAN_FIFO0);
}

加了can通讯之后单片机一直卡死,使用debug调试发现卡死在B.处,是因为中断没有处理好,使能某项的中断,但是却没有中断处理函数。我把不用的中断关闭就好了。
本人也是入门选手,写代码也是,写文章也是。但是做项目,一定要总结,思考,分段debug,不要一直盲目试验,浪费了时间,要多查查资料。可能有些实验现象网上搜不到和自己项目有差异,但是分析一下原因再去搜索,肯定大差不差了,因为外设的应用就这么多。
我每天都会写工作笔记,总结今天遇到的问题和提出明天准备的解决方案。
文章只是作为我总结的一方面,可能有点晦涩,但是我们可以多讨论。拜拜!

猜你喜欢

转载自blog.csdn.net/jwgdhuwdhg/article/details/107523886