Lin总线通信在STM32作为主机代码以及从机程序

距离上次做资料准备已经过去六天了。最近在学车,上周末就没有开电脑。这周开始进行了Lin通信的代码整理,目前是可以正常通信的了,采用的是增强型校验方式。后期再进一步跟进研究。。。更新一博,留作纪念

在想一个问题。。。为什么树可以长这么高?狗可以单身这么久?

我不明白。。。


先将代码更改前的注释贴出来

/******************************************************************
Lin总线帧格式:帧头+应答

帧头:同步间隔段(至少13个显性电平)+同步间隔段间隔符(至少1位隐形电平)+同步段(0x55)+字节间间隔+PID(ID+校验位)
注:PID=ID(6位)+校验(2位)
ID 取值范围为: 0x00~0x3f
ID的取值分类:  
		信号携带帧 :	   0x00~0x3b
		诊断帧(主机请求):0x3c
		诊断帧(从机应答):0x3d
		保留帧 :          0x3e,0x3f
P0 = ID0⊕ID1⊕ID2⊕ID4            异或运算
P1 = ┐(ID3⊕ID4⊕ID5⊕ID1)	   异或后取非

应答:应答间隔+数据段+校验和段
注:数据段   低字节的低位先发
标准型校验和:只校验数据段
增强型校验和:校验数据段以及PID
诊断帧只能用标准型校验和

******************************************************************/
/******************************************************************
lin中断接收函数功能:
1、回环效果:即主机发送帧头或者主机发送帧头+应答,主机的中断服务程序都会接收数据。
	可以检测出:主机串口Tx、Rx、Lin脚,三个引脚上的信号是相同的(除了电平不同)。
2、当串口检测到连续至少11位显性电平即进入中断开始接收。
3、中断服务函数接收数据时按进程推进
	①接收同步段是否OK?
	②接收ID校验后解析是数据执行还是反馈
	若是执行:					若是反馈:
	③分步接收数据				③准备数据在帧头结束后发送数据
	④匹配校验数据是否正确
	⑤解析数据并执行


******************************************************************/

STM32作为主机部分代码:

// 主机帧头部分
void Lin_SendBreak(void)
{
	USART_SendBreak(USART1);
}

起先是同步间隔段,因为作为主机要连续发送至少13位显性电平,这里用的是STM32自带的库函数,直接调用就行。

oid Lin_SendSyncSegment(void)
{
	USART_SendData(USART1,0x55);
	while(USART_GetFlagStatus(USART1,USART_FLAG_TC) != SET );
}

接着就是同步段,发送0x55

u8 Lin_CheckPID(u8 id)
{
	u8 returnpid ;
	u8 P0 ;
	u8 P1 ;
	
	P0 = (((id)^(id>>1)^(id>>2)^(id>>4))&0x01)<<6 ;
	P1 = ((~((id>>1)^(id>>3)^(id>>4)^(id>>5)))&0x01)<<7 ;
	
	returnpid = id|P0|P1 ;
	
	return returnpid ;
	
}

然后就是发送PID(protect ID),这里的前六位为ID,后两位为校验位,函数功能为:输入ID,返回PID。

void Lin_SendHead(u8 id)
{
	Lin_SendBreak();
	Lin_SendSyncSegment();
	USART_SendData(USART1,Lin_CheckPID(id));
	while(USART_GetFlagStatus(USART1,USART_FLAG_TC) != SET );
}

该函数体就是单片机作为主机发送的帧头,可以指定ID发送帧头,接收从机返回的数据;也可以发送帧头+数据,让从机接收。

// 是经典校验还是增强校验,另:诊断帧只能经典校验
u8 Lin_Checksum(u8 id , u8 data[])
{
	u8 t ;
	u16 sum ;

	sum = data[0];
	if(id == 0x3c)			// 如果是诊断帧,用经典校验
	{
		
		for(t=1;t<8;t++)
		{
			sum += data[t];
			if(sum&0xff00)
			{
				sum&=0x00ff;
				sum+=1;
			}
		}
		sum = ~sum;	
		
		return (u8)sum ;
	}
	
	for(t=1;t<8;t++)
	{
		sum += data[t];
		if(sum&0xff00)
		{
			sum&=0x00ff;
			sum+=1;
		}
	}
	sum+=Lin_CheckPID(id);
	if(sum&0xff00)
	{
		sum&=0x00ff;
		sum+=1;
	}
	sum = ~sum;	
	return (u8)sum ;
}

此段函数功能:输入ID+数据,返回校验和段,里面有调用返回PID函数。诊断帧只能用标准校验这里还没有验证过,因为校验还没有测试。

void Lin_SentData(u8 data[])
{
	u8 t ;
	for(t=0;t<8;t++)
	{
		USART_SendData(USART1,data[t]);
		while(USART_GetFlagStatus(USART1,USART_FLAG_TC) != SET );
	}
}

数据发送函数,仅仅是调用STM32的库函数进行for循环发送。

上面三个函数是单片机无论作为主机还是从机都需要用到的部分,所以在后面进行预编译选择的时候,放到外面。

void Lin_SendAnswer(u8 id ,u8 data[])
{
	Lin_SentData(data);
	USART_SendData(USART1,Lin_Checksum(id,data));
	while(USART_GetFlagStatus(USART1,USART_FLAG_TC) != SET );
}

这里是主机的响应函数调用。

STM32作为从机部分代码:

由于从机部分不需要发送帧头,所以相对来说,任务轻松不少。
除却上面三个共用的函数体外。仅有一个函数体来对接收到的报文进行解析执行。然后就是在中断函数体内,当接收到需要本从机反馈数据时,进行数据反馈。

void Lin_DataProcess(void)
{
	u8 ReceiveID ;
	u8 PIDChecksum ;
	u8 SumCheck ;

	
	if(DataReceiveflag == 1)
	{
		
		ReceiveID = ReceivePID&0x3f ;
		PIDChecksum = Lin_CheckPID(ReceiveID);
		if (PIDChecksum != ReceivePID)
		{
			
			return ;
		}
		else
		{}
		if(FrameReceiveOverFlag == 1)							// 从机需要执行信号
		{
			SumCheck = Lin_Checksum(ReceiveID,LinReceiveData);
			if(ReceiveCheckSum != SumCheck)
			{
				return ;
			}
			else
			{}
					
			if(ReceiveID == 0x23)
			{
				if(LinReceiveData[3] == 0x01)
				{
					LED0 = 0 ;
				}
				else if(LinReceiveData[3] == 0x02)
				{
					LED0 = 1 ;
				}
				else
				{}

			}
				
		}
		
		FrameReceiveOverFlag = 0 ;
		DataReceiveflag = 0 ;
	}
	
	
}

当接收到ID为0x23的报文时,进行LED灯的点亮功能。

void USART1_IRQHandler(void)                	//串口1中断服务程序
{
	u8 ReceiveData;
	u8 ReceiveID;

	 if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断
	 {
		ReceiveData =USART_ReceiveData(USART1);	//读取接收到的数据
		if(DataProcess == 0)
		{
			if(ReceiveData != 0x55)
			{
				return ;
			}
			if(ReceiveData == 0x55)
			{
				DataProcess = 1 ;
				return ;
			}
		}
		if(DataProcess == 1)
		{
			ReceivePID = ReceiveData ;
			ReceiveID = ReceivePID&0x3f ;
			if(ReceiveID == 0x33)							// 从机需要反馈信号
			{
				Lin_SentData(testdata);
				USART_SendData(USART1,Lin_Checksum(ReceiveID,testdata));
				while(USART_GetFlagStatus(USART1,USART_FLAG_TC) != SET );
				
				DataProcess = 0 ;
				return ;
			}
			DataReceiveflag = 1 ;
			DataProcess = 2 ;
			return ;
		}
		if(DataProcess == 2)
		{
			if(DtRProcess<8)
			{
				LinReceiveData[DtRProcess] = ReceiveData ;
				DtRProcess += 1 ;
				if(DtRProcess == 8)
				{
					DtRProcess = 0 ;
					DataProcess = 3 ;
					return ;
				}
			}
			
			
		}
		if(DataProcess == 3)
		{
			ReceiveCheckSum = ReceiveData ;
			FrameReceiveOverFlag = 1 ;
			DataProcess = 0 ;
		}

     } 

} 

标注下今天出现的问题:
①调用串口发送函数时,忘记检测发送完成标志,因为数据发送比较快,导致数据严重丢失。
②当单片机作为从机进行数据反馈时,反馈数据发送是在主机发送帧头之后紧接着的。起初是设置一个标志位,在中断外面进行数据发送的,这就导致帧头与应答部分时间间隔不明确,极大可能会超过响应间隔的时间。所以后期是在中断里面发送应答部分。当检测到ID为需要应答时,在中断里应答,后面数据不再接收。
2020.05.23增加:
③当单片机作为从机需要反馈数据时(主机发送帧头,从机需补充数据时),响应不在中断里面做。中断里设置标志位,在中断外填充数据,每1ms检测一次。在用RTOS时,将优先级设置最高?(只要响应与帧头的间隔时间在Lin的规定最长时间内就行)。。另外,由于环回效果(即自身发送的同时也会进入中断接收自身发送到总线的数据)可能会将从机的反馈与帧头的时间加大,可逐步屏蔽进入中断条件,待需要接收时再重新开启。

工程包我会上传到资料以做备份使用。

测试截图:
单片机作为主机进行主机帧头+主机应答
单片机作为主机进行主机帧头+主机应答
单片机作为主机进行主机帧头+从机(上位机)应答
单片机作为主机进行主机帧头+从机(上位机)应答
主机(上位机)帧头+主机(上位机)数据控制单片机灯亮灭
主机(上位机)帧头+主机(上位机)数据控制单片机灯亮灭(单片机读取并解析数据执行)
主机(上位机)帧头+从机(单片机)应答
主机(上位机)帧头+从机(单片机)应答

版权声明:本文为CSDN博主「春乏之秋困」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Hgrjtz/article/details/106243026

生成海报
点赞 0

春乏之秋困

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

趣聊51之串口通信(概念篇)

对于刚刚接触单片机的同学们来说,串口通信似乎是一个神秘感十足的东西,笔者在刚刚开始学习51单片机时,读的是郭天祥先生的那本著名的《新概念51单片机教程》,贼厚的一本书,但是等

串口通信之————IIC(软件驱动)

B站账号:小光学嵌入式 ⏩ 大家好哇!我是小光,嵌入式爱好者,一个想要成为系统架构师的大二学生。⏩最近开始系统性补习STM32基础知识,规划有:串口通信&#xf

STM32——串口通信及实验

目录 1、按照数据传送的方向,分为: 2、按照通信方式,分为: STM32串口通信基础 串口通信过程 UART(USART)框图 串口通信实验 编

openmv和stm32串口通信完成二维码识别

openmv和stm32串口通信完成二维码识别 前言 注:我只是个大一的小白,本文只完成基本功能,希望能帮助有困惑的人(我也是刚刚走出谜团,大佬勿喷。) 工