2. LIN报文帧结构解析与字节序验证
任务定义
本案例要求校准LIN主节点的串口波特率,使其精确工作在20kbps。校准方法为:主节点发送一个包含已知字节序列的LIN帧,通过示波器或逻辑分析仪测量帧起始位(Break)之后的显性位时间,根据实测位时间与理论值50µs的偏差,调整主节点UART的波特率寄存器值,直至误差在允许范围内。
实现方案
采用STM32F103系列MCU作为LIN主节点,使用其USART外设的波特率寄存器BRR进行校准。核心思路:
- 发送一个固定格式的LIN帧(包含0x55同步字节),该字节的位序列为01010101b,便于测量单个位时间;
- 通过逻辑分析仪捕获同步字节的下降沿间隔,计算实际位时间;
- 根据实测位时间与目标50µs的偏差,按比例修正BRR值;
- 重复发送与测量,直至位时间误差≤2%(即49µs~51µs)。
代码实现
// lin_baud_calib.c - LIN主节点波特率校准(STM32F103, 8MHz HSE)
#include "stm32f10x.h"
#include <stdio.h>
// 目标:20kbps,位时间50µs
#define TARGET_BIT_TIME_US 50
#define TOLERANCE_US 1 // ±1µs(±2%)
// 系统时钟72MHz,APB2=72MHz,USART1挂载在APB2
// BRR = fck / (16 * baud)
// 初始波特率设为19200,后续根据测量修正
volatile uint32_t current_brr = 0x4B0; // 19200bps对应BRR值(72MHz/16/19200=234.375→234)
// 发送一字节(阻塞)
void uart_send_byte(USART_TypeDef* USARTx, uint8_t data) {
while (!(USARTx->SR & USART_FLAG_TXE));
USARTx->DR = data;
}
// 发送LIN帧头:Break(13位显性) + 同步间隔 + 0x55同步字节
void lin_send_frame_header(void) {
// 发送Break:拉低TX引脚13位时间(650µs @20kbps)
// 此处用GPIO模拟Break,然后切换回USART
GPIO_InitTypeDef GPIO_InitStructure;
// 配置TX引脚(PB6)为推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// 拉低13位时间(650µs @20kbps)
GPIO_ResetBits(GPIOB, GPIO_Pin_6);
delay_us(650);
GPIO_SetBits(GPIOB, GPIO_Pin_6);
delay_us(50); // 间隔
// 切换回USART复用功能
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// 发送同步字节0x55
uart_send_byte(USART1, 0x55);
}
// 根据实测位时间更新BRR
// measured_us: 实测的单个位时间(微秒)
void update_baud_from_measurement(float measured_us) {
float error_ratio = measured_us / TARGET_BIT_TIME_US;
uint32_t new_brr = (uint32_t)(current_brr * error_ratio);
// 限制范围:15kbps~25kbps
if (new_brr < 180) new_brr = 180; // 25kbps
if (new_brr > 300) new_brr = 300; // 15kbps
current_brr = new_brr;
USART1->BRR = current_brr;
}
// 简单延时(非精确,仅用于Break)
void delay_us(uint32_t us) {
for (volatile uint32_t i = 0; i < us * 8; i++);
}
// 主校准流程
void baud_calibration_loop(void) {
// 假设外部测量工具(逻辑分析仪)通过串口/GPIO反馈实测位时间
// 实际应用中,可通过ADC读取电位器或通过上位机命令输入
float measured_bit_time;
printf("LIN波特率校准开始,目标20kbps\r\n");
for (int iter = 0; iter < 5; iter++) {
// 发送测试帧
lin_send_frame_header();
// 此处模拟从测量工具获取位时间(实际项目中通过通信接口读取)
// 真实场景:逻辑分析仪测量0x55字节的位宽,通过UART/USB反馈
printf("迭代%d:请测量同步字节位时间并输入(µs): ", iter+1);
scanf("%f", &measured_bit_time);
if (measured_bit_time >= (TARGET_BIT_TIME_US - TOLERANCE_US) &&
measured_bit_time <= (TARGET_BIT_TIME_US + TOLERANCE_US)) {
printf("校准完成!位时间=%.1fµs,在±1µs容差内\r\n", measured_bit_time);
return;
}
update_baud_from_measurement(measured_bit_time);
printf("已更新BRR=0x%X,当前理论波特率=%.1f kbps\r\n",
current_brr, 72000000.0f / (16 * current_brr) / 1000);
}
printf("达到最大迭代次数,校准结束\r\n");
}
// USART1初始化(PB6=TX, PB7=RX)
void usart1_init(uint32_t baud) {
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOB, ENABLE);
// TX=PB6, RX=PB7
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOB, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = baud;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_TX | USART_Mode_RX;
USART_Init(USART1, &USART_InitStructure);
USART_Cmd(USART1, ENABLE);
}
int main(void) {
// 系统时钟已配置为72MHz(外部8MHz晶振)
usart1_init(19200);
// 重定向printf到USART1(需实现fputc)
// ...
baud_calibration_loop();
while (1);
}
位时间测量与校准原理
测量点说明:逻辑分析仪捕获TX引脚(PB6)波形,测量0x55同步字节中连续两个下降沿之间的时间间隔。由于0x55的位模式为01010101b,每个数据位均发生跳变,因此相邻下降沿间隔恰好等于1个位时间。
校准流程与验证
校准步骤:
- 将逻辑分析仪探头连接至主节点TX引脚(PB6),采样率建议≥1MHz;
- 运行程序,发送包含0x55的LIN帧头;
- 在逻辑分析仪波形上测量同步字节中相邻下降沿间隔(应测量第2~3个下降沿之间的时间,避开起始位边缘效应);
- 将实测值(µs)输入程序,程序自动计算误差并更新BRR寄存器;
- 重复步骤2~4,直至实测位时间落在49µs~51µs区间。
判定标准:当连续两次测量的位时间均在50µs±1µs范围内,且BRR值不再变化,即认为校准完成。此时主节点波特率误差≤2%,满足LIN协议对主节点±0.5%的精度要求(实际校准后通常可达±0.2%以内)。
验证方法:校准完成后,发送完整的LIN帧(包含同步、标识符和数据),使用从节点或总线分析仪验证通信正常。若从节点能正确响应,则证明波特率校准成功。
关键参数说明
| 参数 | 值 | 说明 |
|---|---|---|
| 目标波特率 | 20kbps | LIN标准速率 |
| 位时间理论值 | 50µs | 1/20000 |
| 容差范围 | ±1µs | ±2%误差 |
| BRR初值 | 0x4B0 (1200) | 对应19200bps |
| BRR计算公式 | BRR = 72MHz / (16 × baud) | USART1挂载APB2 |
注意事项:Break信号的发送使用GPIO模拟,其时长精度不影响位时间测量;测量时应以同步字节的位间隔为准,而非Break长度。若系统时钟本身存在偏差,可通过调整BRR值一并修正。