# 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機制

## Master and slave

## Lin bus Protocol

* Header : 包含Break、Sync、ID
* 其中一個不符合Lin bus格式封包都不會傳送
* Break : 13 bit 為LOW
* Sync : 固定0X55
* ID : 0 ~ 64
* Pesponse : 包含Data、Checksum
* 每個byte傳輸方式 起始Start bit 結束Stop bit;

### LIN bus 每個個封包解析
* Break field


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

* ID: 0 ~ 63
* 60 和 61 用於攜帶診斷數據
* 62 保留用於用戶定義的擴展
* 63 保留用於未來的協議增強。

* Data :
* ID 0-64: 通常是 1 到 8 bytes

### 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

### 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 規範之節點。


## LIN Transceiver: Master and Slave 架構圖

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

## 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

## 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);
}
```