一、引言

Lin是一种一主多从的单总线协议。
背景历史这些都可以在文末参考链接中查阅。
Lin和Can都是车上常用的总线,所以做个对比,这个网上对比文章很多。
Lin和Uart其实从硬件时序图来看,很多相似处,甚至可以用串口来模拟Lin,所以这部分也做了一个对比。
英文解释如下:
LIN:Local Interconnect Network
Can: 控制器局域网 - Controller Area Network
Uart:通用异步收发传输器 - Universal Asynchronous Receiver/Transmitter
Usart:通用同步/异步串行接收/发送器 - Universal Synchronous/Asynchronous Receiver/Transmitter
SCI:串行通信接口 - Serial Communication Interface,Lin、Uart都属于SCI

二、Lin总线

(一)时序

Lin时序图
Lin总线 (与Can总线/Uart对比)  uart模拟lin - 图2
时序的完整说明看这里
从上面链接转了一些十分直观的图过来,看完这几张图基本就看完了Lin时序,十分详细,精确搭配到每一位。
一帧数据 = 报头 + 响应
报头 = 间隔场 + 同步场 + 标志符场
响应 = 数据场 + 校验和场

  1. 间隔场
    Lin总线 (与Can总线/Uart对比)  uart模拟lin - 图3
  2. 同步场
    注意这里发送0x55实际是10101010,和串口发送顺序一致。
    Lin总线 (与Can总线/Uart对比)  uart模拟lin - 图4
  3. 标识符场
    Lin总线 (与Can总线/Uart对比)  uart模拟lin - 图5
  4. 数据场
    Lin总线 (与Can总线/Uart对比)  uart模拟lin - 图6
  5. 字节场
    Lin总线 (与Can总线/Uart对比)  uart模拟lin - 图7
  6. 校验和场Lin总线 (与Can总线/Uart对比)  uart模拟lin - 图8
    1. /******************************************************************
    2. Lin总线帧格式:帧头+应答
    3. 帧头:
    4. 同步间隔段(至少13个显性电平)+同步间隔段间隔符(至少1位隐形电平)+同步段(0x55)+字节间间隔+PID(ID+校验位)
    5. 注:PID=ID(6位)+校验(2位)
    6. ID 取值范围为: 0x00~0x3f
    7. ID的取值分类:
    8. 信号携带帧 : 0x00~0x3b
    9. 诊断帧(主机请求):0x3c
    10. 诊断帧(从机应答):0x3d
    11. 保留帧 : 0x3e,0x3f
    12. P0 = ID0⊕ID1⊕ID2⊕ID4 异或运算
    13. P1 = ID1⊕ID3⊕ID4⊕ID5 异或后取非
    14. 应答:
    15. 应答间隔+数据段+校验和段
    16. 数据段 低字节的低位先发
    17. 标准型校验和:只校验数据段
    18. 增强型校验和:校验数据段以及PID
    19. 诊断帧只能用标准型校验和
    20. ******************************************************************/

    (二)总线

    Lin总线 (与Can总线/Uart对比)  uart模拟lin - 图9
    Lin总线是一主多从的结构吗,LIN网络的节点数量不应超过16个。

    三、与Can总线对比

    Lin与Can的对比网上文章很多,这里就简单写一下。
  • Can可以是多主机,都可以主动发消息,can控制器会进行仲裁。
    Lin是一主多从,查询回复
  • 二者物理层电平协议不同,Can是2.5V上下1V,Lin是12V。
    所以LIN收发器用的是TJA1020
    CAN收发器用的是TJA1050
  • Can速度高,Lin是低速总线。
    Can通常为500kb/s,最低的也达到100kb/s。
    Lin传输速率最高可达20Kbps,通常使用19200b/s或9600b/s的速率。
  • 车载应用场景不同,Lin主要用于车顶、信号灯控制、汽车顶篷、车门、车窗玻璃、方向盘
    方向控制开关、空调、座椅等等这些低速,校验不强的场景。

Lin总线 (与Can总线/Uart对比)  uart模拟lin - 图10

四、与Uart对比

Lin和Uart在协议上不同,在硬件是很像的,或者说一样的,都是串行通用异步收发器。
因此有些IC没有Lin的情况下都是使用Uart来模拟Lin。
一般情况下,二者是不能直接模拟的,因为,因为Lin的字节场 和 Uart一样都是8N1的格式,但是Lin的头部场不符合这块。帧间隔场是连续13个显性电平,与串口的8位不符,所以帧间隔这部分就比较难搞。

(一)波形对比

1、Lin波形

具体时序图可以看2.1节
帧头:同步间隔段(至少13个显性电平)+同步间隔段间隔符(至少1位隐形电平)+同步段(0x55)+字节间间隔+PID(ID+校验位)
应答:应答间隔+数据段+校验和段
Lin总线 (与Can总线/Uart对比)  uart模拟lin - 图11

2、串口波形

串口波形如下:
以1字节(8位)为一帧,
1bit 起始位 + 8位 数据位 + 1位校验位(或者没有) + 1位 停止位

Lin总线 (与Can总线/Uart对比)  uart模拟lin - 图12(二)Uart模拟Lin

看了一下stm32的cubeMx,uart确实可以选做Lin功能,说明在没有专用的Lin控制器的情况下,利用一些特别的做法,是可以通过Uart来实现lin的接收发送的。
Lin总线 (与Can总线/Uart对比)  uart模拟lin - 图13
借用一份网上看到的说法:

用普通串口是怎么实现LIN Break的低电平长度的?发一个字节的0x00总共才8bit不够长,两个字节0x00又太长而且中间断开了不连续;就算切换低波特率来发送0x00,但是切换波特率比较耗时,高电平长度明显超过协议要求,不符合LIN协议要求。 因此普通串口作为LIN从模式还行,作为主模式完全不符合LIN的标准。作为主模式就必须使用增强型串口才能解决 LIN Break问题。 然后是软件问题,由于LIN总线是半双工通信的,通信效率低而且没有硬件过滤器,必然会对MCU的性能产生极大的影响。这种情况下采用RTOS必然是可选项,否则容易导致事件响应实时性问题。

从网上下到了一份stm32的uart模拟lin的代码,比较有参考意义,简单介绍如下:

这个是我的一个车机项目中正在使用的LIN代码.MCU是STM32F103使用的是UCOS-II系统.这段代码包括LIN的从模式的初始化、接收、回应等。利用的是串口的LIN模式中断。带有很多中文注释。希望对你有用。 (This is the use of a car engine project my code in LIN.MCU is the use of the STM32F103 is UCOS-II system. This code includes LIN from model initialization, receiving, response. Use of the interrupt serial port LIN mode. With a lot of Chinese notes. I hope useful to you.)

头文件

  1. /*
  2. *******************************************************************************************
  3. * DVG001项目LIN总线分析头文件
  4. * 文件名称: LIN.h
  5. * 版本号 : V0.9
  6. * 编写人 : Rock.Wu
  7. * 编写日期:2012-03-30
  8. *******************************************************************************************
  9. */
  10. #ifndef __LIN_H
  11. #define __LIN_H
  12. /*******************************包含的文件******************************/
  13. #include <data_dispose.h>
  14. /*******************************宏定义************************************/
  15. #ifdef LIN_MODULE
  16. #define LIN_EXT
  17. #else
  18. #define LIN_EXT extern
  19. #endif
  20. #define LIN_MODE 1/*当为1时工作在从模式,当为0时工作在主模式*/
  21. #define LIN_CHECKSUM_MODE 1/*0为普通模式只效验数据,1为增强行模式连
  22. ID一起效验*/
  23. #define LIN_ID_NUM 2/*从模式中需要处理的ID个数*/
  24. #define LIN_LEN_MODE 1/*0是直接从ID的Bit5、bIT4位获得,1是按约定从表格中
  25. 获得*/
  26. /*接收ID*/
  27. #define ID_LIFE_BELT 0xf0
  28. #define ID_ACK 0x50
  29. /*******************************数据类型定义***************************/
  30. enum SlaveWorkMode{
  31. SLAVE_IDLE,
  32. SLAVE_RX_MODE,
  33. SLAVE_TX_MODE
  34. };
  35. typedef struct
  36. {
  37. INT8U CMD_ID; /*命令ID*/
  38. void (*pFunc)(void); /*对应的命令处理函数*/
  39. } CMD_LINType;
  40. /*******************************全局变量和事件定义*****************/
  41. LIN_EXT OS_EVENT *LINMutex;
  42. LIN_EXT OS_EVENT *LINRxSem;
  43. LIN_EXT LINDataType LINTempData;
  44. #ifdef LIN_MODULE
  45. /*这里是标注要处理的ID是接收数据还是要回应数据,还有数据长度*/
  46. #if (LIN_LEN_MODE == 0)
  47. const INT8U LIN_ID_List[LIN_ID_NUM][2]={
  48. {ID_LIFE_BELT, SLAVE_RX_MODE},
  49. {ID_ACK, SLAVE_TX_MODE}
  50. };
  51. #else
  52. const INT8U LIN_ID_List[LIN_ID_NUM][3]={
  53. {ID_LIFE_BELT, SLAVE_RX_MODE, 8},
  54. {ID_ACK, SLAVE_TX_MODE, 4}
  55. };
  56. #endif
  57. #endif
  58. /*******************************函数声明定义***************************/
  59. LIN_EXT void BSP_LIN_Init(void);
  60. LIN_EXT void LinSend(INT8U *ComputeData);
  61. LIN_EXT void LIN_DataAnalyse(void);
  62. #endif
  63. /*************************************************************************************************
  64. ** End Of File
  65. *************************************************************************************************/

源文件

  1. /*
  2. **********************************************************************************************
  3. * DVG02项目的LIN总线分析文件
  4. * 文件名称: LIN.c
  5. * 版本号 : V0.9
  6. * 编写人 : Rock.Wu
  7. * 编写日期:2012-08-20
  8. **********************************************************************************************
  9. */
  10. #define LIN_MODULE
  11. /*******************************包含的文件******************************/
  12. #include <includes.h>
  13. #include <cpu.h>
  14. #include <LIN.h>
  15. /*******************************宏定义************************************/
  16. #define BIT(A,B) ((A>>B)&0x01)
  17. #define LIN_RXCMD_BUF_SIZE 5 /*接受缓存大小10个报文*/
  18. /*******************************数据类型定义***************************/
  19. /*******************************变量定义*********************************/
  20. enum LinState{
  21. IDLE,
  22. SYNCH,
  23. ID_LEN,
  24. DATA_GET,
  25. CHECKSUM
  26. };
  27. enum LinErrState{
  28. NO_ERR,
  29. SYNC_ERR,
  30. ID_ERR,
  31. CHKSUM_ERR
  32. };
  33. static INT8U AnalysePlus4 = IDLE;
  34. #if (LIN_LEN_MODE == 0)
  35. static const INT8U LIN_Len[4]={2, 2, 4, 8};
  36. #endif
  37. static LINDataType TempLinData;
  38. LINDataType LINRxMsgBuf[LIN_RXCMD_BUF_SIZE];
  39. COMM_LIN_Q LIN_RxMsg;
  40. extern CMD_LINType const LIN_CMD[LIN_ID_NUM];
  41. static INT8U SendDataLen =0;
  42. static INT8U LinSendBuf[9]={0,0,0,0,0,0,0,0,0};
  43. /*******************************函数声明*********************************/
  44. static void LIN_IRQ_Handler(void);
  45. static void LIN_Rx_Analyse(INT8U LIN_Data);
  46. /******************************************************************************
  47. ** 功能描述: 初始化UART4,用于与LIN通讯
  48. ** 输  入:
  49. ** 全局变量: UART1Sem
  50. ** 调用模块:
  51. ** 编写人 : Rock.Wu
  52. ** 编写日期:2012-09-14
  53. *******************************************************************************/
  54. void BSP_LIN_Init(void)
  55. {
  56. USART_InitTypeDef USART_InitStructure;/*串口设置恢复默认参*/
  57. GPIO_InitTypeDef GPIO_InitStructure;
  58. INT8U i,j;
  59. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
  60. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
  61. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  62. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  63. GPIO_Init(GPIOC, &GPIO_InitStructure);
  64. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
  65. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  66. GPIO_Init(GPIOC, &GPIO_InitStructure);
  67. RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART4, ENABLE);
  68. USART_InitStructure.USART_BaudRate = 19200;
  69. USART_InitStructure.USART_WordLength = USART_WordLength_8b;
  70. USART_InitStructure.USART_StopBits = USART_StopBits_1;
  71. USART_InitStructure.USART_Parity = USART_Parity_No;
  72. USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  73. USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
  74. USART_Init(UART4, &USART_InitStructure);
  75. USART_LINBreakDetectLengthConfig(UART4, USART_LINBreakDetectLength_11b);
  76. USART_LINCmd(UART4, ENABLE);
  77. USART_Cmd(UART4, ENABLE);
  78. USART_ITConfig(UART4, USART_IT_RXNE, ENABLE);/*先使能接收中断*/
  79. USART_ITConfig(UART4, USART_IT_TXE, DISABLE); /*先禁止发送中断*/
  80. USART_ITConfig(UART4, USART_IT_LBD, ENABLE);/*先使能LIN间隔场检测到中断*/
  81. BSP_IntVectSet(BSP_INT_ID_UART4, LIN_IRQ_Handler);
  82. BSP_IntPrioSet(BSP_INT_ID_UART4, 1);
  83. BSP_IntEn(BSP_INT_ID_UART4);
  84. TempLinData.ErrorType = NO_ERR;
  85. for(i=0;i<LIN_RXCMD_BUF_SIZE;i++) /*初始化接收队列数据*/
  86. {
  87. LINRxMsgBuf[i].CMD_ID = 0;
  88. LINRxMsgBuf[i].Len = 0;
  89. for(j=0;j<8;j++)
  90. LINRxMsgBuf[i].Data[j]=0;
  91. }
  92. COMM_LIN_QCreate(&LIN_RxMsg, LINRxMsgBuf, LIN_RXCMD_BUF_SIZE); /*创建队列*/
  93. }
  94. /**************************************************************************************
  95. ** 功能描述: UART4 LIN 通讯接收发送中断服务程序
  96. ** 全局变量:
  97. ** 调用模块:
  98. ** 编写人 : Rock.Wu
  99. ** 编写日期:2012-09-14
  100. **************************************************************************************/
  101. static void LIN_IRQ_Handler(void)
  102. {
  103. if(USART_GetITStatus(UART4, USART_IT_LBD) != RESET)
  104. {
  105. USART_ClearITPendingBit(UART4, USART_IT_LBD);/*清除LIN间隔场检测标志*/
  106. AnalysePlus4 = SYNCH;/*确认检测到间隔场下一个开始接收的是同步场数据*/
  107. TempLinData.ErrorType = NO_ERR;
  108. TempLinData.WorkMode = SLAVE_IDLE;
  109. USART_ITConfig(UART4, USART_IT_TXE, DISABLE); /*先禁止发送中断*/
  110. }
  111. if(USART_GetITStatus(UART4, USART_IT_RXNE) != RESET)
  112. {
  113. USART_ClearITPendingBit(UART4, USART_IT_RXNE);/*清除接收中断标志*/
  114. LIN_Rx_Analyse((INT8U)USART_ReceiveData(UART4));
  115. }
  116. /*溢出-如果发生溢出需要先读SR,再读DR寄存器则可清除不断入*/
  117. if(USART_GetFlagStatus(UART4, USART_FLAG_ORE)==SET)
  118. {
  119. USART_ClearFlag(UART4, USART_FLAG_ORE); /*读SR其实就是清除标志*/
  120. USART_ReceiveData(UART4);/*读DR*/
  121. }
  122. if(USART_GetITStatus(UART4, USART_IT_TXE) != RESET)
  123. {
  124. USART_ClearITPendingBit(UART4, USART_IT_TXE);/*清除接收中断标志*/
  125. if((LINTempData.WorkMode == SLAVE_TX_MODE)&&(SendDataLen != 0))
  126. {
  127. SendDataLen--;/*这个必须防在先面的发送函数前才能得到
  128. 正确的数据,因为之前它加了1*/
  129. USART_SendData(UART4, ((u16) LinSendBuf[SendDataLen]));
  130. }else{
  131. SendDataLen = 0;
  132. LINTempData.WorkMode = SLAVE_IDLE;
  133. USART_ITConfig(UART4, USART_IT_TXE, DISABLE); /*先禁止发送中断*/
  134. }
  135. }
  136. }
  137. /**************************************************************************************
  138. ** 功能描述: UART4 LIN 通讯接收解析服务程序
  139. ** 全局变量:
  140. ** 调用模块:
  141. ** 编写人 : Rock.Wu
  142. ** 编写日期:2012-09-17
  143. **************************************************************************************/
  144. static void LIN_Rx_Analyse(INT8U LIN_Data)
  145. {
  146. INT8U TempData = 0, TempNum = 0;
  147. static INT8U TempLen = 0;
  148. static INT16U TempCheckSum = 0;
  149. switch(AnalysePlus4)
  150. {
  151. case 1:
  152. if(LIN_Data == 0x55)
  153. {
  154. AnalysePlus4 = ID_LEN;/*下一个将接收ID信息*/
  155. }else{
  156. AnalysePlus4 = IDLE;
  157. TempLinData.ErrorType = SYNC_ERR;
  158. }
  159. break;
  160. case 2:
  161. TempData = (~(BIT(LIN_Data,1)^BIT(LIN_Data,3)^BIT(LIN_Data,4)^BIT(LIN_Data,5)))<<7;
  162. TempData |= (BIT(LIN_Data,0)^BIT(LIN_Data,1)^BIT(LIN_Data,2)^BIT(LIN_Data,4))<<6;
  163. if(TempData == (LIN_Data&0xC0))
  164. {
  165. TempLinData.CMD_ID = LIN_Data;
  166. #if (LIN_CHECKSUM_MODE == 0)
  167. TempCheckSum = 0;
  168. #else
  169. TempCheckSum = TempLinData.CMD_ID;
  170. #endif
  171. for(TempNum = 0; TempNum < LIN_ID_NUM; TempNum++)
  172. {
  173. if(TempLinData.CMD_ID == LIN_ID_List[TempNum][0])
  174. {
  175. TempLinData.WorkMode = LIN_ID_List[TempNum][1];
  176. #if (LIN_LEN_MODE == 0)
  177. TempLinData.Len = LIN_Len[((LIN_Data>>4)&0x03)];
  178. #else
  179. TempLinData.Len = LIN_ID_List[TempNum][2];
  180. #endif
  181. if(TempLinData.WorkMode == SLAVE_RX_MODE)
  182. {
  183. TempLen = 1;
  184. AnalysePlus4 = DATA_GET;
  185. }else if(TempLinData.WorkMode == SLAVE_TX_MODE)
  186. {
  187. AnalysePlus4 = IDLE;
  188. COMM_LIN_QPushStr(&LIN_RxMsg, &TempLinData);
  189. OSSemPost(LINRxSem);
  190. }
  191. return;
  192. }
  193. }
  194. AnalysePlus4 = IDLE; TempLinData.CMD_ID = 0; TempLinData.Len = 0;
  195. }else{
  196. AnalysePlus4 = IDLE;
  197. TempLinData.ErrorType = ID_ERR;
  198. }
  199. break;
  200. case DATA_GET:
  201. TempLinData.Data[(TempLen-1)] = LIN_Data;
  202. TempCheckSum += LIN_Data;
  203. if(TempCheckSum&0xFF00)
  204. TempCheckSum = (TempCheckSum&0x00FF)+1;
  205. if(TempLinData.Len > TempLen)
  206. {
  207. TempLen++;
  208. }else{
  209. AnalysePlus4 = CHECKSUM;
  210. TempLinData.CheckSum = (INT8U)((~TempCheckSum)&0x00ff);
  211. TempLen = 0;
  212. }
  213. break;
  214. case CHECKSUM:
  215. AnalysePlus4 = IDLE;
  216. if(TempLinData.CheckSum == LIN_Data)
  217. {
  218. COMM_LIN_QPushStr(&LIN_RxMsg, &TempLinData);
  219. TempLinData.CMD_ID = 0; TempLinData.Len = 0; TempLinData.CheckSum = 0;
  220. OSSemPost(LINRxSem);
  221. }else{
  222. TempLinData.CMD_ID = 0; TempLinData.ErrorType = CHKSUM_ERR;
  223. TempLinData.Len = 0; TempLinData.CheckSum = 0;
  224. }
  225. break;
  226. default:
  227. break;
  228. }
  229. }
  230. /*****************************************************************************
  231. ** 功能描述: LIN 接收数据处理程序
  232. ** 全局变量:
  233. ** 调用模块:
  234. ** 编写人 : Rock.Wu
  235. ** 编写日期:2012-09-17
  236. ******************************************************************************/
  237. void LIN_DataAnalyse(void)
  238. {
  239. COMM_Q_Status Q_Status_Data;
  240. INT8U i=0;
  241. Q_Status_Data = COMM_LIN_QQuery(&LIN_RxMsg);
  242. if((Q_Status_Data == COMM_Q_DATA)||(Q_Status_Data == COMM_Q_FULL))
  243. {
  244. if(COMM_LIN_QPopStr(&LIN_RxMsg, &LINTempData) == COMM_Q_OK)
  245. {
  246. for(i=0;i<LIN_ID_NUM;i++)
  247. {
  248. if(LINTempData.CMD_ID == LIN_CMD[i].CMD_ID)
  249. {
  250. LIN_CMD[i].pFunc();
  251. break;
  252. }
  253. }
  254. }
  255. // Q_Status_Data = COMM_LIN_QQuery(&LIN_RxMsg);
  256. }
  257. }
  258. /*****************************************************************************
  259. ** 功能描述: LIN 从模式发送函数
  260. ** 全局变量:
  261. ** 调用模块:
  262. ** 编写人 : Rock.Wu
  263. ** 编写日期:2012-09-17
  264. ******************************************************************************/
  265. void LinSend(INT8U *ComputeData)
  266. {
  267. INT8U i = 0;
  268. #if (LIN_CHECKSUM_MODE == 0)
  269. INT16U TempData = 0;
  270. #else
  271. INT16U TempData = LINTempData.CMD_ID;
  272. #endif
  273. if((LINTempData.WorkMode != SLAVE_TX_MODE)||(SendDataLen != 0))
  274. return;
  275. SendDataLen = LINTempData.Len+1;/*用SendDataLen做下标加上CHECKSUM的长度*/
  276. for(i = 0; i<LINTempData.Len; i++)
  277. {
  278. LinSendBuf[(LINTempData.Len-i)] = ComputeData[i];
  279. TempData += ComputeData[i];
  280. if(TempData&0xFF00)
  281. TempData = (TempData&0x00FF)+1;
  282. }
  283. LinSendBuf[0] = (INT8U)((~TempData)&0x00ff);
  284. USART_ITConfig(UART4, USART_IT_TXE, ENABLE); /*启动发送中断*/
  285. }
  286. /*************************************************************************************************
  287. ** End Of File
  288. *************************************************************************************************/

五、参考链接