1►
LIN总线简介
LIN(Local Interconnect Network)总线是基于UART/SCI(通用异步收发器/串行接口)的低成本串行通讯协议,其目标定位于车身网络模块节点间的低端通信,主要用于智能传感器和执行器的串行通信。LIN总线采用单主多从的组网方式,没有CAN总线那样的仲裁机制,辅以简单驱动程序便可实现LIN协议。LIN节点由控制芯片和LIN收发器构成,一般通过芯片搭载的UART模块来实现,主节点控制传输时刻,控制整个网络的通信,从节点按照主节点的调度进行通信。
2►
LIN报文结构
LIN总线上有“显性”和“隐性”两种互补的逻辑电平。显性电平是逻辑 0,隐性电平是逻辑1,总线上实行“线与”。
一帧LIN报文由帧头(Header)和应答(Response)两部分组成。主机任务负责发送帧头,从机任务接收帧头并对帧头所包含信息进行解析,然后决定是发送应答,还是接收应答,还是不作任何反应。帧在总线上的传输如下图所示:
帧头包括同步间隔段、同步段以及受保护ID段(PID)。应答包括数据段和校验和段。LIN报文帧整体结构如下图所示。
同步间隔段
同步间隔段标志一帧的开始,由同步间隔(Break)和间隔符(Break Delimiter)构成。同步间隔段至少有13个显性位,间隔符至少有一个隐形位。
同步段
同步段固定一个字节,值固定为0x55。
在LIN帧中,除了同步间隔段,后面各段都是通过字节域的格式传输的。LIN的字节域就是指标准的UART数据传输格式,字节域包括1位起始位(显性)+8位数据位+1位停止位(隐性)。数据传输都是先发送LSB,最后发送 MSB。LIN总线将下降沿作为判断标志,通过字节0x55(01010101b)进行同步,从机节点上可以采用非高精度时钟,如果存在偏差,可以通过同步场来调整,使从机节点数据的波特率与主机节点一致。
受保护ID段
受保护ID段由6位帧ID和2位奇偶校验位组成,帧ID范围为0x00~0x3F共64个。
帧ID标识了帧的类别,从机任务根据帧头ID作出反应(接收/发送/忽略应答),其中P0与P1效验如下:
P0 = ID0⊕ID1⊕ID2⊕ID4
P1 = ¬(ID1⊕ID3⊕ID4⊕ID5)
其中“⊕”代表“异或”运算,“¬”代表“取非”运算。
由公式可以看出,PID 不会出现全 0 或全 1 的情况,如果从机节点收到了“0xFF”或“0x00”,可判断传输错误。LIN总线根据帧ID的不同,将报文分为信号携带帧、诊断帧、保留帧。
应注意从机应答帧是一个完整的帧,与帧结构中的“应答”不同。
数据段
数据段包含1~8个字节,可以分为两种数据类型:信号和诊断消息。信号由信号携带帧传递,诊断消息由诊断帧传递。LIN协议规定可传输的LIN字节数为2、4、8,并不是1~8内任意一个数字。一般应用方面会统一字节数,通常是每帧传输8个字节。
校验和段
校验和段是为了对帧传输内容进行效验。效验分为标准型校验与增强型校验。
将校验对象的各字节作带进位二进制加法(当结果大于等于256 时就减去255),并将所得最终的和逐位取反,以该结果作为要发送的校验和。接收方根据校验和类型,对接收数据作相同的带进位二进制加法,最终的和不取反,并将该和与接收到的校验和作加法,如果结果为0xFF,则校验和无误。这在一定程度上保证了数据传输的正确性。
采用标准型还是增强型是由主机节点管理,发布节点和收听节点根据帧ID来判断采用哪种校验和。
3►
LIN通信实验
MM32F5270的UART支持LIN协议下收发断开符号,通过配置UART,根据总线特征编写LIN驱动程序,实现LIN总线通信。相关代码参考灵动官网的LibSamples或在此基础上修改。
3.1 LIN驱动程序
同步间隔段
配置UART支持LIN协议下收发断开符号:
voidLIN_MASTER_Break(void) { LIN_MASTER_TXBRK_InterruptFlag=0; UART_LINCmd(UART1,ENABLE); UART_SendBreak(UART1); while(0==LIN_MASTER_TXBRK_InterruptFlag) { } }
同步段
主机发送0x55:
voidLIN_MASTER_SyncByte(void) { LIN_MASTER_SendData(0x55); }
受保护ID段
uint8_tLIN_FrameIDToPID(uint8_tFrameID) { uint8_ti=0; uint8_tP0=0,P1=0,PID=0xFF; uint8_tID_BIT[6]= { 0,0,0,0,0,0 }; if(FrameID< 0x40) { PID = FrameID; for (i = 0; i < 6; i++) { if (FrameID & (0x01 << i)) { ID_BIT[i] = 1; } else { ID_BIT[i] = 0; } } P0 = (ID_BIT[0] ^ ID_BIT[1] ^ ID_BIT[2] ^ ID_BIT[4]) & 0x01; P1 = ~(ID_BIT[1] ^ ID_BIT[3] ^ ID_BIT[4] ^ ID_BIT[5]) & 0x01; if (P0) { PID |= 0x40; } if (P1) { PID |= 0x80; } } return (PID); }
数据段
主机发送数据:
voidLIN_MASTER_SendData(uint8_tData) { UART_SendData(UART1,Data); while(RESET==UART_GetFlagStatus(UART1,UART_FLAG_TXC)) { } }
从机发送数据:
voidLIN_SLAVE_SendData(uint8_tData) { UART_SendData(UART1,Data); while(RESET==UART_GetFlagStatus(UART1,UART_FLAG_TXC)) { } }
校验和段
标准型校验:
uint8_tLIN_ClassicChecksum(uint8_t*Buffer,uint8_tLength) { uint8_ti=0; uint16_tChecksum=0; for(i=0;i< Length; i++) { Checksum += Buffer[i]; if (Checksum >0xFF) { Checksum%=0xFF; } } return(~(uint8_t)(Checksum&0x00FF)); }
增强型校验:
uint8_tLIN_EnhancedChecksum(uint8_tPID,uint8_t*Buffer,uint8_tLength) { uint8_ti=0; uint16_tChecksum=PID; for(i=0;i< Length; i++) { Checksum += Buffer[i]; if (Checksum >0xFF) { Checksum%=0xFF; } } return(~(uint8_t)(Checksum&0x00FF)); }
主机发送帧头
voidLIN_MASTER_SendHeader(uint8_tPID) { LIN_MASTER_Break(); LIN_MASTER_SyncByte(); LIN_MASTER_SendData(PID); }
主机发送报文
诊断帧ID包括主机请求帧0x3C、从机应答帧0x3D,诊断帧用标准型校验和,其他帧使用增强型校验和。
voidLIN_Master_SendFrame(uint8_tFrameID,uint8_t*Buffer,uint8_tLength) { uint8_ti=0; uint8_tChecksum=0; uint8_tPID=LIN_FrameIDToPID(FrameID); if((0x3C==FrameID)||(0x3D==FrameID)) { Checksum=LIN_ClassicChecksum(Buffer,Length); } else { Checksum=LIN_EnhancedChecksum(PID,Buffer,Length); } LIN_MASTER_SendHeader(PID); for(i=0;i< Length; i++) { LIN_MASTER_SendData(Buffer[i]); } LIN_MASTER_SendData(Checksum); }
从机发布数据
从机解析帧头信息,将主机发送的PID得到帧ID,根据帧ID选择校验类型,发送数据段和校验和段。
voidLIN_SLAVE_Response(uint8_t*Buffer,uint8_tLength) { uint8_ti=0; uint8_tChecksum=0,FrameID=0; FrameID=LIN_PIDToFrameID(LIN_SLAVE_RxBuffer[1]); Checksum=0; if((0x3C==FrameID)||(0x3D==FrameID)) { Checksum=LIN_ClassicChecksum(Buffer,Length); } else { Checksum=LIN_EnhancedChecksum(LIN_SLAVE_RxBuffer[1],Buffer,Length); } for(i=0;i< Length; i++) { LIN_SLAVE_SendData(Buffer[i]); } LIN_SLAVE_SendData(Checksum); }
3.2 主机程序
主机UART配置
voidUART_Configure(uint32_tBaudrate) { GPIO_InitTypeDefGPIO_InitStruct; NVIC_InitTypeDefNVIC_InitStruct; UART_InitTypeDefUART_InitStruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_UART1,ENABLE); UART_StructInit(&UART_InitStruct); UART_InitStruct.BaudRate=Baudrate; UART_InitStruct.WordLength=UART_WordLength_8b; UART_InitStruct.StopBits=UART_StopBits_1; UART_InitStruct.Parity=UART_Parity_No; UART_InitStruct.HWFlowControl=UART_HWFlowControl_None; UART_InitStruct.Mode=UART_Mode_Rx|UART_Mode_Tx; UART_Init(UART1,&UART_InitStruct); UART_IDLRConfig(UART1,100);/*LINMasterOnly!!!*/ UART_ITConfig(UART1,UART_IT_RX,ENABLE); UART_ITConfig(UART1,UART_IT_TXBRK,ENABLE); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA,ENABLE); GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_7); GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_7); GPIO_StructInit(&GPIO_InitStruct); GPIO_InitStruct.GPIO_Pin=GPIO_Pin_9; GPIO_InitStruct.GPIO_Speed=GPIO_Speed_High; GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP; GPIO_Init(GPIOA,&GPIO_InitStruct); GPIO_StructInit(&GPIO_InitStruct); GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10; GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU; GPIO_Init(GPIOA,&GPIO_InitStruct); NVIC_InitStruct.NVIC_IRQChannel=UART1_IRQn; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=0; NVIC_InitStruct.NVIC_IRQChannelSubPriority=1; NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE; NVIC_Init(&NVIC_InitStruct); UART_Cmd(UART1,ENABLE); }
主机中断服务子程序
voidUART1_IRQHandler(void) { uint8_ti=0; if(SET==UART_GetITStatus(UART1,UART_IT_TXBRK)) { UART1_RxLength=0; UART_ClearITPendingBit(UART1,UART_IT_TXBRK); UART_ITConfig(UART1,UART_IT_RXIDLE,ENABLE); LIN_MASTER_TXBRK_InterruptFlag=1; } if(SET==UART_GetITStatus(UART1,UART_IT_RX)) { UART1_RxBuffer[UART1_RxLength]=UART1->RDR&0x00FF; UART1_RxLength=(UART1_RxLength+1)%100; UART_ClearITPendingBit(UART1,UART_IT_RX); } if(SET==UART_GetITStatus(UART1,UART_IT_RXIDLE)) { for(i=0;i< UART1_RxLength; i++) { LIN_MASTER_RxBuffer[i] = UART1_RxBuffer[i]; } LIN_MASTER_RxLength = UART1_RxLength; LIN_MASTER_RxFinish = 1; UART_ClearITPendingBit(UART1, UART_IT_RXIDLE); UART_ITConfig(UART1, UART_IT_RXIDLE, DISABLE); } }
主机例程
主机间隔500ms发布和接收数据,发送帧ID和数据依次累加:
voidUART_LIN_Master_Sample(void) { uint8_ti=0; uint8_tFrameID=0,Mode=0; uint8_tBuffer[2]={0,0}; printf(" Test%s",__FUNCTION__); LIN_MASTER_RxLength=0; LIN_MASTER_RxFinish=0; for(i=0;i< 100; i++) { LIN_MASTER_RxBuffer[i] = 0; } UART_Configure(19200); while (1) { if (Mode == 0) { printf(" LIN Master Write..."); LIN_Master_SendFrame(FrameID, Buffer, sizeof(Buffer)); } else { printf(" LIN Master Read...."); LIN_MASTER_SendHeader(LIN_FrameIDToPID(FrameID)); while (0 == LIN_MASTER_RxFinish) { } LIN_MASTER_RxFinish = 0; printf(" LIN Master Rx Length : %d, Rx Buffer : ", LIN_MASTER_RxLength); for (i = 0; i < LIN_MASTER_RxLength; i++) { printf("0x%02x ", LIN_MASTER_RxBuffer[i]); } printf(" "); for (i = 0; i < sizeof(Buffer); i++) { Buffer[i]++; } FrameID = (FrameID + 1) % 0x40; } Mode = (0 == Mode) ? 1 : 0; PLATFORM_DelayMS(500); } }
3.3 从机程序
从机UART配置
使能UART LIN总线模式、使能UART接收断开帧中断、使能接收单字节中断。
voidUART_Configure(uint32_tBaudrate) { GPIO_InitTypeDefGPIO_InitStruct; NVIC_InitTypeDefNVIC_InitStruct; UART_InitTypeDefUART_InitStruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_UART1,ENABLE); UART_StructInit(&UART_InitStruct); UART_InitStruct.BaudRate=Baudrate; UART_InitStruct.WordLength=UART_WordLength_8b; UART_InitStruct.StopBits=UART_StopBits_1; UART_InitStruct.Parity=UART_Parity_No; UART_InitStruct.HWFlowControl=UART_HWFlowControl_None; UART_InitStruct.Mode=UART_Mode_Rx|UART_Mode_Tx; UART_Init(UART1,&UART_InitStruct); UART_LINCmd(UART1,ENABLE); UART_ITConfig(UART1,UART_IT_RX,ENABLE); UART_ITConfig(UART1,UART_IT_RXBRK,ENABLE); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA,ENABLE); GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_7); GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_7); GPIO_StructInit(&GPIO_InitStruct); GPIO_InitStruct.GPIO_Pin=GPIO_Pin_9; GPIO_InitStruct.GPIO_Speed=GPIO_Speed_High; GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP; GPIO_Init(GPIOA,&GPIO_InitStruct); GPIO_StructInit(&GPIO_InitStruct); GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10; GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU; GPIO_Init(GPIOA,&GPIO_InitStruct); NVIC_InitStruct.NVIC_IRQChannel=UART1_IRQn; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=0; NVIC_InitStruct.NVIC_IRQChannelSubPriority=1; NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE; NVIC_Init(&NVIC_InitStruct); UART_Cmd(UART1,ENABLE); }
从机中断服务子程序
voidUART1_IRQHandler(void) { uint8_ti=0; if(SET==UART_GetITStatus(UART1,UART_IT_RXBRK)) { UART1_RxLength=0; UART_ClearITPendingBit(UART1,UART_IT_RXBRK); UART_ITConfig(UART1,UART_IT_RXIDLE,ENABLE); } if(SET==UART_GetITStatus(UART1,UART_IT_RX)) { UART1_RxBuffer[UART1_RxLength]=UART_ReceiveData(UART1); UART1_RxLength=(UART1_RxLength+1)%100; UART_ClearITPendingBit(UART1,UART_IT_RX); } if(SET==UART_GetITStatus(UART1,UART_IT_RXIDLE)) { for(i=0;i< UART1_RxLength; i++) { LIN_SLAVE_RxBuffer[i] = UART1_RxBuffer[i]; } LIN_SLAVE_RxLength = UART1_RxLength; LIN_SLAVE_RxFinish = 1; UART_ClearITPendingBit(UART1, UART_IT_RXIDLE); UART_ITConfig(UART1, UART_IT_RXIDLE, DISABLE); } }
从机例程
从机对帧头包含信息解析,确定是发送应答,还是接收应答。
voidUART_LIN_Slave_Sample(void) { uint8_ti=0; uint8_tChecksum=0,FrameID=0; uint8_tLength=0,Buffer[100]; printf(" Test%s",__FUNCTION__); Length=0; LIN_SLAVE_RxLength=0; LIN_SLAVE_RxFinish=0; for(i=0;i< 100; i++) { Buffer[i] = 0; LIN_SLAVE_RxBuffer[i] = 0; } UART_Configure(19200); while (1) { if (1 == LIN_SLAVE_RxFinish) { LIN_SLAVE_RxFinish = 0; if (0x55 == LIN_SLAVE_RxBuffer[0]) { if (2 == LIN_SLAVE_RxLength) { LIN_SLAVE_Response(Buffer, Length); } else { for (i = 2; i < LIN_SLAVE_RxLength - 1; i++) { Buffer[i - 2] = LIN_SLAVE_RxBuffer[i]; } Length = LIN_SLAVE_RxLength - 3; } } } } }
3.4 验证
通过UART接口连接两块MM32F5270 MiniBoard,观察串口调试助手:
先由主机发布数据,从机接收数据,接着由从机发布数据,主机接收数据,依次循环进行。根据截图信息,主从机收发数据一致,与程序逻辑相符,两块板LIN通信成功。
审核编辑:刘清
-
uart
+关注
关注
22文章
1245浏览量
102136 -
异步收发器
+关注
关注
0文章
36浏览量
10895 -
串行通讯
+关注
关注
2文章
77浏览量
16472 -
LIN通信
+关注
关注
2文章
8浏览量
3853 -
MM32
+关注
关注
1文章
106浏览量
862
原文标题:灵动微课堂 (第280讲)|MM32F5270 UART实现LIN通信
文章出处:【微信号:MindMotion-MMCU,微信公众号:灵动MM32MCU】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
基于MM32F5270开发板获取雨滴传感器的数据相关资料介绍
一文解析MM32F5270开发板+PWM测试与调制
基于MM32F5270控制器的I2S音频播放
【MM32F5270】Keil开发环境搭建

评论