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(FrameID0x40) ????{ ????????PID?=?FrameID; ????????for?(i?=?0;?i?6;?i++) ????????{ ????????????if?(FrameID?&?(0x01?<
數據段
主機發送數據:
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;i0xFF) { 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;i0xFF) { 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
從機發布數據
從機解析幀頭信息,將主機發送的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
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
主機例程
主機間隔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;i100;?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?
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
從機例程
從機對幀頭包含信息解析,確定是發送應答,還是接收應答。
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;i100;?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?
3.4 驗證
通過UART接口連接兩塊MM32F5270 MiniBoard,觀察串口調試助手:
先由主機發布數據,從機接收數據,接著由從機發布數據,主機接收數據,依次循環進行。根據截圖信息,主從機收發數據一致,與程序邏輯相符,兩塊板LIN通信成功。
審核編輯:劉清
-
uart
+關注
關注
22文章
1162瀏覽量
100195 -
異步收發器
+關注
關注
0文章
36瀏覽量
10804 -
串行通訊
+關注
關注
2文章
77瀏覽量
16280 -
LIN通信
+關注
關注
2文章
8瀏覽量
3728 -
MM32
+關注
關注
1文章
105瀏覽量
607
原文標題:靈動微課堂 (第280講)|MM32F5270 UART實現LIN通信
文章出處:【微信號:MindMotion-MMCU,微信公眾號:靈動MM32MCU】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論