---
# System prepended metadata

title: LIN bus（Local Interconnect Network）
tags: ['protocols ']

---

# LIN （Local Interconnect Network）

* LIN bus 是單線半雙工，工作電壓約 0V（dominant）～12V（recessive），屬於車載 12V 系統。
    * 單主多從（single-master, multi-slave）。
    - 跟CAN一樣有，發送header（標頭），包括同步訊號與識別碼（ID）。
    - 由主機以排程方式輪詢從機，通訊有較高的確定性（predictability）。

* Master Node（主節點）：

    * 主節點通常會週期性地輪詢各個 slave。

* Slave Node（從節點）：

    * 接收主節點的命令並做出回應。
    * 有些從節點會回傳資料（response），有些只會接收資料。
    * 一個 LIN bus 最多可以有 15 個 slave nodes。
    
* 在USART被硬體設定為LIN模式時，提供13bit break產生器以及10/11bit break偵測器。
* 傳輸速度 
    * Master端最大 UART baud
        * Lin bus: 20 kbps。
    * Slave端 UART baud
        * UART 設定的 baud rate 就要設成 19200 或 20000 bps（看 MCU 是否支援非標準 baud rate）

* ECU角度去看,主機走LIN BUS,周邊設備多達15個就是Slave機制
 
    ![image](https://hackmd.io/_uploads/ByfE97SAkx.png)
    
## Master and slave    

![image](https://hackmd.io/_uploads/BJdAuNkggg.png)

## Lin bus Protocol

![image](https://hackmd.io/_uploads/HJn-klICye.png)

* Header : 包含Break、Sync、ID
    * 其中一個不符合Lin bus格式封包都不會傳送
    * Break : 13 bit 為LOW
    * Sync : 固定0X55
    * ID : 0 ~ 64
* Pesponse : 包含Data、Checksum


* 每個byte傳輸方式 起始Start bit 結束Stop bit; 
![image](https://hackmd.io/_uploads/HyzoglUAJg.png)

### LIN bus 每個個封包解析

*  Break field
![image](https://hackmd.io/_uploads/Hk-B1uuJgg.png)


![image](https://hackmd.io/_uploads/Hk5ouDuyge.png)


* Sync : 同步是一個位元組字段，資料值為 0x55

![image](https://hackmd.io/_uploads/ryQPkuOJxx.png)


* ID: 0 ~ 63
    *  60 和 61 用於攜帶診斷數據
    *  62 保留用於用戶定義的擴展
    *  63 保留用於未來的協議增強。
    
    ![image](https://hackmd.io/_uploads/ByioXY3AJe.png)

* Data : 
    * ID 0-64: 通常是 1 到 8 bytes
    ![image](https://hackmd.io/_uploads/rJmQLeI0Jx.png)


### LIN Bus Timing

* Because the LIN bus is a polled bus, the processing of each frame is allocated a nominal time slot as follows:
* 
    * THeader_Nominal = 34 * TBit
    * TResponse_Nominal = 10 * (NData + 1) * TBit
    * TFrame_Nominal = THeader_Nominal + TResponse_Nominal
    * Processing of each frame is allocated a maximum time slot as follows:
    * THeader_Maximum = 14 * THeader_Nominal
    * TResponse_Maximum = 1.4 * TResponse_Nominal
    * TFrame_Maximum = THeader_Maximum + TResponse_Maximum


* LIN在Master發送Data時LIN Protocol Spec需要等待5-10ms但實際應用到10-20ms

![image](https://hackmd.io/_uploads/ByZbTObExg.png)



### Checksum

* 在實作 LIN 匯流排通訊時，需特別注意 Classic Checksum 與 Enhanced Checksum 的算法差異。雖然 LIN 2.x 版本主要採用增強型校驗，但為了向下相容，軟體開發時仍需具備自動辨別與切換的能力。
#### 即便專案採用 LIN 2.x 版本，軟體層仍需根據 ID 類型進行動態判斷：

* 邏輯判斷： 當 ID 為 0x3C 或 0x3D 時，務必強制切換回 Classic Checksum；其餘一般傳輸則使用 Enhanced Checksum。
* 相容性調整： 若系統中混有 LIN 1.3 的舊設備，即使 ID 在 0x00 ~ 0x3B 範圍內，也可能需要配置為 Classic Checksum。
* 小建議： 如下程式碼，在撰寫驅動程式時，建議建立一個 Checksum_Type 的判斷函式，傳入 PID 後回傳對應的運算邏輯，以確保通訊的穩定性。

* Classic Checksum (標準校驗)

    * 計算範圍： 僅針對 數據段 (Data Bytes) 進行運算。
    * 適用對象： 診斷標頭檔 (Master Request ID: 0x3C / Slave Response ID: 0x3D)，主要用於 LIN 1.x 規範的舊型節點。

* Enhanced Checksum (增強校驗)

    * 計算範圍： 包含 PID (Protected Identifier) 與 數據段 (Data Bytes)。
    * 適用對象： 一般數據傳輸標頭檔 (ID 範圍：0x00 ~ 0x3B)，主要用於 LIN 2.x 規範之節點。
    
![image](https://hackmd.io/_uploads/rkUE2OZNxe.png)

![image](https://hackmd.io/_uploads/HyBi3dZExx.png)


## LIN Transceiver: Master and Slave 架構圖

![image](https://hackmd.io/_uploads/HyKeiuZExe.png)


### LIN Transceiver 差異

* 市售IC有向下支援到2.2A ~ 1.3 SPEC，但要注意LIN Transceiver 有沒有支援到2.2A.

![image](https://hackmd.io/_uploads/Bkqtou-Neg.png)


## Master : Slave請求順序

* Master : 向Slave1請求 0x01 data
* Master : 向Slave2請求 0x02 data
* Master : 向Slave1、Slave2同時請求 0x10 data(不會成立，因為是半雙工所以只能一問一答)
* Master : 向Slave1、Slave2同時請求 0x20 data(不會成立，因為是半雙工所以只能一問一答)
* Master : 向Slave1請求 0x030 data
* Master : 向Slave2請求 0x035 data
 
![image](https://hackmd.io/_uploads/SkZyouZExl.png)


## STM32上實現LIN BUS



```
unsigned char linbus_01[1] = {0x01};
unsigned char linbus_02[2] = {0x52,0x20};
unsigned char linbus_03[3] = {0xE5,0x20,0x52};
unsigned char linbus_04[4] = {0x10, 0x20, 0x30, 0x40};
unsigned char linbus_05[5] = {0x10, 0x20, 0x30, 0x40, 0x50};
unsigned char linbus_06[6] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60};
unsigned char linbus_07[7] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70};
unsigned char linbus_08[8] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80};
unsigned char linbus_18[4] = {0x10, 0x20, 0x30, 0x40};
unsigned char linbus_1F[5] = {0x10, 0x20, 0x30, 0x40, 0x50};
unsigned char linbus_24[5] = {0x10, 0x20, 0x30, 0x40, 0x50};
unsigned char linbus_28[6] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60};
unsigned char linbus_2C[6] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60};
unsigned char linbus_30[7] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60};
unsigned char linbus_3B[7] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60};
unsigned char linbus_3C[8] = {0x16, 0x26, 0x36, 0x46, 0x56, 0x66, 0x76, 0x86};
unsigned char linbus_3D[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
unsigned char rxdata[1];
int main(void)
{

	HAL_Init();

	SystemClock_Config();
	Init_PeripheralClock();
	Init_GPIO();
	Init_LIN_UART();
	Init_Interrupt();
    while(1)
    {
		Lin_Transmit(0x01, linbus_01, 1, true);
		Lin_Transmit(0x02, linbus_02, 2, true);
		Lin_Transmit(0x03, linbus_03, 3, true);
		Lin_Transmit(0x04, linbus_04, 4, true);
		Lin_Transmit(0x05, linbus_05, 5, true);
		Lin_Transmit(0x06, linbus_06, 6, true);
		Lin_Transmit(0x07, linbus_07, 7, true);
		Lin_Transmit(0x08, linbus_08, 8, true);
		Lin_Transmit(0x3C, linbus_3C, 8, false);
        
		Lin_Receive(0x0C, rxdata, 1, true);
		Lin_Receive(0x0D, rxdata, 2, true);
		Lin_Receive(0x0E, rxdata, 3, true);
		Lin_Receive(0x0F, rxdata, 4, true);
		Lin_Receive(0x10, rxdata, 5, true);
		Lin_Receive(0x11, rxdata, 6, true);
		Lin_Receive(0x12, rxdata, 7, true);
		Lin_Receive(0x13, rxdata, 8, true);
		Lin_Receive(0x3D, rxdata, 8, false);        
    }    
}
```

```
/**************************************************************************************************
Function Name:
    unsigned char LIN_Clock(EnumLinClock chose)
Input:
    chose: LIN_BAUDRATE_9600 or LIN_BAUDRATE_19200.
Output:
    None.
Comment:
    Setting LIN Clock.
**************************************************************************************************/
unsigned long LIN_Clock(EnumLinClock chose)
{
	switch(chose)
	{
		case LIN_BAUDRATE_9600:
			return  LIN_BUS_9600;
		break;
		case LIN_BAUDRATE_19200:
			return  LIN_BUS_19200;
		break;
		default:
        return 0;
	}
}
```

```
/**************************************************************************************************
Function Name:
    void Init_LIN_UART(void)
Input:
    None.
Output:
    None.
Comment:
    Setting UART for LIN BUS
**************************************************************************************************/
void Init_LIN_UART(void)
{
	huart3.Instance = USART3;
	huart3.Init.BaudRate = LIN_Clock(LIN_BAUDRATE_19200);
	huart3.Init.WordLength = UART_WORDLENGTH_8B;
	huart3.Init.StopBits = UART_STOPBITS_1;
	huart3.Init.Parity = UART_PARITY_EVEN;
	huart3.Init.Mode = UART_MODE_TX_RX;
	huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;
	huart3.Init.OverSampling = UART_OVERSAMPLING_16;
	huart3.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
	huart3.Init.ClockPrescaler = UART_PRESCALER_DIV1;
	huart3.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
    HAL_UART_Init(&huart3);
}
```

```
/**************************************************************************************************
Function Name:
    unsigned char Protected_identifier_field(unsigned char linId)
Input:
    linId: Object of LIN peripheral and parameters of state machine for LIN driver.
Output:
    None.
Comment:
    LIN Protected ID  function.
**************************************************************************************************/
unsigned char Protected_identifier_field(unsigned char linId)
{
	unsigned char LIN_ID = linId & 0x3Fu;
	LIN_ID |= (  (linId >> 0u & 0x1u) ^ (linId >> 1u & 0x1u)
             ^ (linId >> 2u & 0x1u) ^ (linId >> 4u & 0x1u)) << 6u;
	LIN_ID |= (  (linId >> 1u & 0x1u) ^ (linId >> 3u & 0x1u)
             ^ (linId >> 4u & 0x1u) ^ (linId >> 5u & 0x1u) ^ 0x1u) << 7u;
	return LIN_ID;
}
```

```
/***********************************************************************************************************************
Function Name:
    unsigned char Lin_Transmit(unsigned char Id, unsigned char *data, unsigned char size, unsigned long enhanced_mode)
Input:
    pid : ID Number.
    data : Transmit data.
    size : Size of data part, the specified number of bytes for data part.
    enhanced_mode : ID 0 - 59 Use enhanced 1, ID 60 -61 Use classic 0.
Output:
    None.
Comment:
   This CANBUS IP provides four modes for application and testing.
***********************************************************************************************************************/
unsigned char Lin_Transmit(unsigned char Id, unsigned char *data, unsigned char size, unsigned long enhanced_mode)
{
	unsigned char sync = 0x55;
	unsigned char pid = Protected_identifier_field(Id);
	unsigned char checksum = LIN_Checksum(pid, data, size, 1);

//	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET);  // enable transmitter
	HAL_LIN_SendBreak(&huart3);
	HAL_UART_Transmit(&huart3, &sync, 1, 300);
	HAL_UART_Transmit(&huart3, &pid, 1, 300);
	HAL_UART_Transmit(&huart3, data, size, 1000);
	HAL_UART_Transmit(&huart3, &checksum, 1, 1000);
	HAL_Delay(30);
//	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);  // disable transmitter
	return 0;
}
```

```
void Lin_Receive(unsigned char id, unsigned char *data, unsigned char size, unsigned long enhanced_mode)
{
    unsigned char pid = Protected_identifier_field(id);
    unsigned char checksum_received;
    unsigned char checksum_calculated;
    if (size > 8) return;
    unsigned char rx_buffer[9] = {0};

    if (HAL_UART_Receive(&huart3, rx_buffer, size + 1, 300) == HAL_OK)
    {
        memcpy(data, rx_buffer, size);

        checksum_received = rx_buffer[size];
        checksum_calculated = LIN_Checksum(pid, data, size, enhanced_mode);

       if (checksum_received != checksum_calculated)
       {
       	Error_Handler();
       }
    }
	HAL_Delay(40);
}
```

```
/***************************************************************************************************
Function Name:
    void Init_GPIO(void)
Input:
    NULL
Output:
    NULL
Comment:
    Initialize GPIO.
***************************************************************************************************/
void Init_GPIO(void)
{
	GPIO_InitTypeDef CAN_MUX = {0};
    GPIO_InitTypeDef Lin_MUX = {0};
	RCC_PeriphCLKInitTypeDef PeriphClkCANInit = {0};
	RCC_PeriphCLKInitTypeDef PeriphClkLINInit = {0};

	// UART clock setting PCLK2（170MHz）
	PeriphClkLINInit.PeriphClockSelection = RCC_PERIPHCLK_USART1;
	PeriphClkLINInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2;
	HAL_RCCEx_PeriphCLKConfig(&PeriphClkLINInit);
    /**USART3 GPIO Configuration
    PB10     ------> USART3_TX
    PB11     ------> USART3_RX
    */
    Lin_MUX.Pin = GPIO_PIN_10|GPIO_PIN_11;
    Lin_MUX.Mode = GPIO_MODE_AF_PP;
    Lin_MUX.Pull = GPIO_NOPULL;
    Lin_MUX.Speed = GPIO_SPEED_FREQ_LOW;
    Lin_MUX.Alternate = GPIO_AF7_USART3;
    HAL_GPIO_Init(GPIOB, &Lin_MUX);
}
```

```
/**************************************************************************************************
Function Name:
    void Init_Interrupt(void)
Input:
    NULL
Output:
    NULL
Comment:
    Initialize Interrupt Service Routine.
    Enable nested interrupt.
**************************************************************************************************/
void Init_Interrupt(void)
{
    uint32_t encodePriority = NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 1, 0);
    NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
    NVIC_SetPriority(USART3_IRQn, encodePriority);
    NVIC_EnableIRQ(USART3_IRQn);
}
```
