STM32F103 -- LIN从机通讯 -- 程序代码(精简版)

3 篇文章 2 订阅

这是详细版,链接:STM32F103 – LIN从机通讯 – 程序代码 – 详细讲解版(2万字长文)。精简版少了串口环形缓冲区以及错误判断等内容,足以应对一般的应用场景,代码也更通俗易懂。
但建议先看详细版,详细版主要代码分为3个文件,usart文件是对GPIO端口和串口Lin模式的初始化,lin_buffer文件是对串口环形缓冲区的编写,lin_driver文件是Lin从机模式驱动代码,详细版讲解了我们如何进行收发,如何编写代码。精简版不多讲解,只挑一些讲解,因为原理是一样的。

一.usart文件

usart文件没有更改,不多讲解。

usart.c 文件代码

#include "main.h"
 
 
/**
  * @brief  串口1初始化
  * @param  baud:波特率
  * @retval None
  */
uint32_t USART1_Init(uint32_t baud)   
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1,ENABLE);  //开启串口1与GPIOA时钟
 
	GPIO_InitTypeDef GPIO_InitStruct;	
	GPIO_InitStruct.GPIO_Pin = USART1_TX_PIN;   	   //串口发送引脚TX  GPIO_Pin_9
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; 	   //复用推挽输出
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;     
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;          //上拉输入
	GPIO_InitStruct.GPIO_Pin = USART1_RX_PIN;		   //串口接收引脚RX GPIO_Pin_10
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;	   //可没有	
	GPIO_Init(GPIOA,&GPIO_InitStruct);	
	
	
	USART_InitTypeDef USART_InitStruct;
	
	USART_InitStruct.USART_BaudRate = baud;   								 	 //串口波特率
	USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;				 //收发模式
	USART_InitStruct.USART_Parity = USART_Parity_No;    						 //不使用奇偶校验
	USART_InitStruct.USART_StopBits = USART_StopBits_1;                          //停止位:1位
	USART_InitStruct.USART_WordLength = USART_WordLength_8b;                     //8位数据长度
	USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //不使用硬件控制流		
	USART_Init(USART1,&USART_InitStruct); 									     //初始化串口配置
	USART_LINBreakDetectLengthConfig(USART1,USART_LINBreakDetectLength_11b);	 //设置USART1_LIN断开符检测长度:11位
	
 
	USART_Cmd(USART1,ENABLE);	  //使能串口时钟
	USART_LINCmd(USART1,ENABLE);  //使能串口LIN模式
	
	NVIC_InitTypeDef  NVIC_InitStruct;        		//定义NVIC结构体	
	NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;//配置抢占优先级为最高优先级
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;//配置响应优先级为最高优先级	
	NVIC_Init(&NVIC_InitStruct);
 
	USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);		//使能串口接收中断
	USART_ITConfig(USART1, USART_IT_LBD,ENABLE);        //打开LIN断开符检测中断	
	//USART_ITConfig(USART1, USART_IT_IDLE,ENABLE);       //开启空闲中断
	//LIN_BUF_Init(&LINRxBuffer,(LIN_MSG*)LINRxDataBuf,LIN_BUFFER_SIZE);
	return baud; //返回波特率
}

usart.h 文件代码

#ifndef __USART_H
#define __USART_H
 
#include "main.h" 
 
/* USART1 引脚定义 */
#define   USART1_TX_PIN   				 GPIO_Pin_9  		
#define   USART1_RX_PIN  		   		 GPIO_Pin_10
 
 
typedef struct __FILE FILE;
uint32_t USART1_Init(uint32_t baud);
 
 
#endif

二. lin_driver 文件代码

lin_driver.c 文件代码

对lin_buffer文件进行了大改,不再使用缓冲区,减少了结构体的使用,加入了超时判断。
接收到的数据格式为:0–同步段 1–ID 2~9–数据段 10–校验和段。
Lin_Rx_Data[10] = 0xAA; //超时标志 校验和段如果是AA,即超时。
其他没什么了,只要你用心分析代码,就一定能看懂。

#include "main.h"  

//发送数据
u8 Lin_Tx_Data[8] = {0x01,0x00,0x3C,0x00,0x00,0x00,0x00,0x00};
//接收数据  0--同步段  1--ID  2~9--数据段 10--校验和段
u8 Lin_Rx_Data[11] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
//接收数据 方便校验
u8 Checksum_Data[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
u8 LIN_RxStateGet = 0;
extern u8 Tim_time;

/**
  * @brief  LIN从机中断函数
  * @param  None
  * @retval None
	*/
void LIN_MASTER_IRQHandler(void)
{
	uint8_t ReceiveData = 0;
	
	//LIN断开帧中断
	if ((USART_GetITStatus(USART1,USART_IT_LBD) == SET)){		//LIN断路检测中断  1 -- 有同步间隔段	
		USART1->SR;     
    USART1->DR;	
  	LIN_RxStateGet = 1;
		Tim_time = 0;       //进行超时判断
		TIM_Cmd(TIM3,ENABLE); 
		USART_ClearITPendingBit(USART1,USART_IT_LBD);         //清除LIN断路检测中断
		USART_ClearITPendingBit(USART1,USART_IT_RXNE);
		return;		
	}

	if (USART_GetITStatus(USART1,USART_IT_RXNE) == SET){    			  //LIN接收中断			
		ReceiveData = USART_ReceiveData(USART1);	                  	//返回USARTx外设最近接收到的数据。
		if (USART_GetFlagStatus(USART1,USART_FLAG_FE) == RESET){ 		  //帧错误标志  0 -- 没有检测到帧错误	
			if ((ReceiveData == 0x55)&&(LIN_RxStateGet == 0)){ 		  //处理无同步间隔信号的LIN数据 
				USART1->SR;     
				USART1->DR;	
				return;   
			}
			Tim_time = 0;  
			LIN_MasterRxMsg(ReceiveData);   //消息处理
		}
		USART_ClearITPendingBit(USART1,USART_IT_RXNE);	
	}
}

/**
  * @brief  LIN从机接收处理函数
	* @param  Data: 串口消息
  * @retval None
  */
u8 i = 0,j = 0,Checksum = 0;
uint8_t ID,PID;
void LIN_MasterRxMsg(uint8_t ReceiveData){
	switch(LIN_RxStateGet){
		case 1:
			for(i = 0;i < 12 ;i++){
				Lin_Rx_Data[i] = 0x00;
			}
			i = 0;
			j = 0;
			if(ReceiveData == 0x55){  //判断同步段
				Lin_Rx_Data[i] = ReceiveData;
				i = 1;
				LIN_RxStateGet = 2;				
			}else{
				LIN_RxStateGet = 0;
			}			
		break;
		case 2:
			ID = ReceiveData&0x3F;
			PID = LIN_GetPID(ID);
			if(PID == ReceiveData){   //判断PID
				if(ID == 0x3A){
					LIN_Tx_data(PID,Lin_Tx_Data,8); 
					if(Lin_Tx_Data[0] == 0x02){
						Lin_Tx_Data[0] = 0x01;
					}else{
						Lin_Tx_Data[0] = 0x02;
				}
				LIN_RxStateGet = 0;
				}else if(ID == 0x39){
					LIN_RxStateGet = 3;
				}   
				Lin_Rx_Data[i] = ID;
				i = 2;
			}		
		break;
		case 3:
			Lin_Rx_Data[i] = ReceiveData;
			Checksum_Data[j] = ReceiveData;
			j++;
			i++;
			LIN_RxStateGet = (i>=10)?4:3;  //判断接收数据长度
		break;
		case 4: 
			Lin_Rx_Data[i] = 0x00;
			Checksum = 0x00;
			Checksum = ~LIN_GetChecksum(PID,Checksum_Data,8);
			Tim_time = 0;  
			if((Checksum + ReceiveData) == 0xFF){
				Lin_Rx_Data[i] = 0xFF;   //校验成功
			}else{
				Lin_Rx_Data[i] = 0x00;
			}			
			Tim_time = 0;
			TIM_Cmd(TIM3,DISABLE);   //定时器失能
			LIN_RxStateGet = 0;
		break;
	}  
}


//发送数据,包括校验和
void LIN_Tx_data(uint8_t PID,uint8_t* pData,uint8_t DataLen){
  uint8_t Linbuffer[9]; 	                               //定义发送数组(数据+校验和)
  uint8_t Checksum = LIN_GetChecksum(PID,pData,DataLen);     //获取校验和段
  for (uint8_t i = 0; i < DataLen; i++)                        //存DataLen个字节数据段
  {     
    Linbuffer[i] = *(pData + i);		
  }
  Linbuffer[DataLen] = Checksum;           //校验和
  LIN_SendBytes(USART1,Linbuffer ,DataLen+1);      //发送从机数据
}



//定时器3中断服务程序
void TIM3_IRQHandler(void)   //TIM3中断
{
	if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)  //检查TIM3更新中断发生与否
	{
		TIM_ClearITPendingBit(TIM3, TIM_IT_Update);  //清除TIMx更新中断标志 
		Tim_time++;
		if(LIN_RxStateGet == 0){
			Tim_time = 0;
			TIM_Cmd(TIM3,DISABLE);
		}
		if(Tim_time >= 5){
			LIN_RxStateGet = 0;
			Lin_Rx_Data[10] = 0xAA;   //超时标志
			Tim_time = 0;
			TIM_Cmd(TIM3,DISABLE);
		}
	}
}


/**
  * @brief  LIN发送字节	
  * @param  USARTx:串口号,pData:数据指针,Length:数据长度
  * @retval 无
  */
void LIN_SendBytes(USART_TypeDef* USARTx,uint8_t* pData,uint8_t DataLen)
{
	for (uint8_t i = 0; i < DataLen; i++){
		USART_SendData(USARTx,*pData++);
		while (USART_GetFlagStatus(USARTx,USART_FLAG_TXE) == RESET);	//传输数据寄存器空标志
	}
	while (USART_GetFlagStatus(USARTx,USART_FLAG_TC) == RESET);		//传输完成标志
}



/**
  * @brief  LIN协议规定校验和长度为1个字节,获取校验和	
  * @param  PID:校验ID,pData:数据指针,DataLen:数据长度
  * @retval 累加校验和
  */
uint8_t LIN_GetChecksum(uint8_t PID, uint8_t* pData,uint8_t DataLen) 			 
{  
  uint16_t CheckSum = 0;
  //FrameID为3C 3D的PID为3C 7D
  if((PID!=0x3c) && (PID!=0x7D)){   //诊断帧只能使用标准校验和,标准校验和不包含PID 只校验数据段             
    CheckSum = PID;
  }
  for(uint8_t i = 0;i < DataLen; i++){
    CheckSum += pData[i];
    if(CheckSum > 0xFF){
      CheckSum -= 0xFF;
    }
  }
    return ~(CheckSum & 0xFF);    //发送方
}



/**
  * @brief LIN_PID校验函数   STM8单片机可实现奇偶校验位自动检测		
  * @param ID(FrameID):帧ID(0 ~ 63) 
  * P0(bit6) =   ID0 ^ ID1 ^ ID2 ^ ID4   <==>  (偶校验:操作数中1的个数为偶数,校验位为0,1的个数为奇数校验位为1)
  * P1(bit7) = ~(ID1 ^ ID3 ^ ID4 ^ ID5)  <==>  (奇校验:操作数中1的个数为奇数,校验位为0,1的个数为偶数校验位为1)
  * @retval 返回PID 
  */
uint8_t LIN_GetPID(uint8_t ID)  
{
  uint8_t PID = 0,P0 = 0,P1 = 0;
  P0 = (((ID>>0)^(ID>>1)^(ID>>2)^(ID>>4))&0x01)<<6; //偶校验位           			
  P1 = ((~((ID>>1)^(ID>>3)^(ID>>4)^(ID>>5)))&0x01)<<7; //奇校验位	
  PID = (ID|P0|P1);	
  return PID;
}

lin_driver.h 文件代码

#ifndef __LINDRIVER_H
#define __LINDRIVER_H
#include "stm32f10x.h"          

#define  LIN_MASTER_IRQHandler 		 	 USART1_IRQHandler   //串口1中断定义

void LIN_Init(uint32_t bau);
void LIN_MasterRxMsg(uint8_t ReceiveData);
void LIN_Tx_data(uint8_t PID,uint8_t* pData,uint8_t DataLen);
void LIN_SendBytes(USART_TypeDef* USARTx,uint8_t* pData,uint8_t DataLen);
uint8_t LIN_GetChecksum(uint8_t PID, uint8_t* pData,uint8_t DataLen) ;
uint8_t LIN_GetPID(uint8_t ID);


#endif

三. tim定时文件代码

tim.c 文件代码

#include "main.h"


/**
  * @brief  通用定时器3初始化配置
  * @param  自动重装载值,该参数范围0x0000 到 0xFFFF
  * @param  预分频器,该参数范围0x0000 到 0xFFFF
  * @retval None
  */
void TIM3_Init(void)   
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);	        	//使能定时器3时钟
	TIM_InternalClockConfig(TIM3);							    	//时基单元时钟选择内部时钟(定时器复位后默认为内部时钟)
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	//每隔1ms产生一次更新中断
	TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; 	   //定时器定时模式:向上计数
	TIM_TimeBaseInitStruct.TIM_Period = 10 - 1;  			  		   //自动重装载值
	TIM_TimeBaseInitStruct.TIM_Prescaler = 7200 - 1;          		    //预分频器

	TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;					//重复计数器(只存在于高级定时器中)
	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStruct);      				//初始化定时器
	
	TIM_ClearFlag(TIM3,TIM_FLAG_Update);
	TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);   			  				//使能定时器更新中断

	NVIC_InitTypeDef  NVIC_InitStruct;

	NVIC_InitStruct.NVIC_IRQChannel = TIM3_IRQn;            //开启定时器中断源

	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;  //抢占优先级
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;         //响应优先级
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;            //使能定时器中断	
	NVIC_Init(&NVIC_InitStruct);    								  //初始化定时器
	
//	TIM_Cmd(TIM3,ENABLE);                     		    				//使能定时器
	
}

tim.h 文件代码

#ifndef __TIM_H
#define __TIM_H

#include "main.h"

void TIM3_Init(void);


#endif

四. main文件代码

#include "main.h"

//接收数据  0--同步段  1--ID  2~9--数据段 10--校验和段 0xFF:校验成功 0x00:校验失败 0xAA:超时
extern u8 Lin_Rx_Data[11];
u8 Tim_time = 0;

int main(void)	
{
	//设置中断优先级分组为2 
	SystemInit();
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 
	delay_init();
	LED_Init();							//LED初始化	
	USART1_Init(19200);     //串口1初始化:波特率19200 | 2400字节每秒
	TIM3_Init();            //1ms中断一次
	while (1)
	{	  
		if((Lin_Rx_Data[1] == 0x39) && (Lin_Rx_Data[10] == 0xFF)){
			if(Lin_Rx_Data[2] == 0x01){
				LED_L;
			}else if(Lin_Rx_Data[2] == 0x02){
				LED_M;
			}
		}
	}
} 	
  • 9
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值