3. LIN从节点响应帧发送与校验和计算
任务定义
本案例实现LIN从节点的核心状态机:休眠(Sleep)、空闲(Idle)、接收(Receive)、发送(Transmit)四种状态,以及由总线活动驱动的状态转换逻辑。状态机以C语言实现,不依赖具体硬件寄存器,通过模拟总线事件(帧头检测、报文接收完成、调度表触发)驱动转换,可直接编译运行验证。
状态定义与转换规则
- 休眠→空闲:检测到总线唤醒信号(下降沿/总线活动)
- 空闲→接收:检测到帧头(同步间隔+同步场+PID)
- 接收→空闲:报文接收完成(校验通过)或接收超时
- 空闲→发送:收到主节点调度表分配的发送时隙
- 发送→空闲:报文发送完成
- 空闲→休眠:总线静默超过4秒(LIN规范要求)
- 接收/发送→休眠:总线错误或强制休眠命令
代码实现
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
/* 状态枚举 */
typedef enum {
ST_SLEEP,
ST_IDLE,
ST_RECEIVE,
ST_TRANSMIT
} lin_state_t;
/* 总线事件枚举 */
typedef enum {
EV_BUS_WAKEUP, /* 总线唤醒 */
EV_FRAME_HEADER, /* 检测到帧头 */
EV_RX_COMPLETE, /* 接收完成 */
EV_RX_TIMEOUT, /* 接收超时 */
EV_TX_SLOT, /* 发送时隙到达 */
EV_TX_COMPLETE, /* 发送完成 */
EV_BUS_ERROR, /* 总线错误 */
EV_BUS_SILENT_4S, /* 总线静默4秒 */
EV_GOTO_SLEEP_CMD /* 休眠命令 */
} lin_event_t;
/* 状态机上下文 */
typedef struct {
lin_state_t state;
uint32_t rx_timeout_cnt; /* 接收超时计数器 */
uint32_t silent_cnt; /* 静默计数器 */
bool bus_active; /* 总线活动标志 */
} lin_node_t;
/* 状态名映射 */
static const char* state_name(lin_state_t s) {
switch(s) {
case ST_SLEEP: return "SLEEP";
case ST_IDLE: return "IDLE";
case ST_RECEIVE: return "RECEIVE";
case ST_TRANSMIT: return "TRANSMIT";
default: return "UNKNOWN";
}
}
/* 状态转换处理 */
static void handle_event(lin_node_t *node, lin_event_t ev) {
lin_state_t old = node->state;
switch(node->state) {
case ST_SLEEP:
if (ev == EV_BUS_WAKEUP) {
node->state = ST_IDLE;
node->silent_cnt = 0;
}
break;
case ST_IDLE:
switch(ev) {
case EV_FRAME_HEADER:
node->state = ST_RECEIVE;
node->rx_timeout_cnt = 0;
break;
case EV_TX_SLOT:
node->state = ST_TRANSMIT;
break;
case EV_BUS_SILENT_4S:
node->state = ST_SLEEP;
break;
case EV_GOTO_SLEEP_CMD:
node->state = ST_SLEEP;
break;
default:
break;
}
break;
case ST_RECEIVE:
switch(ev) {
case EV_RX_COMPLETE:
node->state = ST_IDLE;
node->silent_cnt = 0;
break;
case EV_RX_TIMEOUT:
case EV_BUS_ERROR:
node->state = ST_IDLE;
break;
case EV_GOTO_SLEEP_CMD:
node->state = ST_SLEEP;
break;
default:
break;
}
break;
case ST_TRANSMIT:
switch(ev) {
case EV_TX_COMPLETE:
node->state = ST_IDLE;
node->silent_cnt = 0;
break;
case EV_BUS_ERROR:
node->state = ST_IDLE;
break;
case EV_GOTO_SLEEP_CMD:
node->state = ST_SLEEP;
break;
default:
break;
}
break;
}
if (old != node->state) {
printf("[状态转换] %s --> %s (事件: %d)\n",
state_name(old), state_name(node->state), ev);
}
}
/* 模拟总线活动检测(每1ms调用一次) */
void lin_tick(lin_node_t *node, bool bus_signal) {
/* 总线活动检测:上升沿或下降沿视为唤醒 */
static bool prev_signal = false;
if (bus_signal && !prev_signal) {
node->bus_active = true;
if (node->state == ST_SLEEP) {
handle_event(node, EV_BUS_WAKEUP);
}
} else if (!bus_signal) {
node->bus_active = false;
}
prev_signal = bus_signal;
/* 静默计时(仅空闲状态累计) */
if (node->state == ST_IDLE && !node->bus_active) {
node->silent_cnt++;
if (node->silent_cnt >= 4000) { /* 4秒 = 4000ms */
handle_event(node, EV_BUS_SILENT_4S);
node->silent_cnt = 0;
}
} else if (node->state != ST_IDLE) {
node->silent_cnt = 0;
}
/* 接收超时检测(假设接收超时10ms) */
if (node->state == ST_RECEIVE) {
node->rx_timeout_cnt++;
if (node->rx_timeout_cnt >= 10) {
handle_event(node, EV_RX_TIMEOUT);
node->rx_timeout_cnt = 0;
}
}
}
/* 主节点事件注入接口 */
void lin_inject_event(lin_node_t *node, lin_event_t ev) {
handle_event(node, ev);
}
int main(void) {
lin_node_t node = { .state = ST_SLEEP, .bus_active = false };
printf("=== LIN从节点状态机演示 ===\n");
printf("初始状态: %s\n\n", state_name(node.state));
/* 场景1: 总线唤醒 → 空闲 */
printf("--- 场景1: 总线唤醒 ---\n");
lin_tick(&node, true); /* 总线拉高 */
lin_tick(&node, false); /* 总线释放 */
/* 场景2: 检测帧头 → 接收 → 接收完成 → 空闲 */
printf("\n--- 场景2: 接收报文 ---\n");
lin_inject_event(&node, EV_FRAME_HEADER);
lin_tick(&node, false); /* 模拟接收过程 */
lin_tick(&node, false);
lin_inject_event(&node, EV_RX_COMPLETE);
/* 场景3: 发送时隙 → 发送 → 发送完成 → 空闲 */
printf("\n--- 场景3: 发送报文 ---\n");
lin_inject_event(&node, EV_TX_SLOT);
lin_inject_event(&node, EV_TX_COMPLETE);
/* 场景4: 总线静默4秒 → 休眠 */
printf("\n--- 场景4: 总线静默进入休眠 ---\n");
for (int i = 0; i < 4001; i++) {
lin_tick(&node, false);
}
/* 场景5: 休眠命令 */
printf("\n--- 场景5: 强制休眠命令 ---\n");
lin_inject_event(&node, EV_BUS_WAKEUP); /* 先唤醒 */
lin_inject_event(&node, EV_GOTO_SLEEP_CMD);
printf("\n最终状态: %s\n", state_name(node.state));
return 0;
}
编译与运行
gcc -o lin_state_machine lin_state_machine.c
./lin_state_machine
验证方法与判定标准
程序输出应包含以下状态转换序列,且最终状态为SLEEP:
=== LIN从节点状态机演示 ===
初始状态: SLEEP
--- 场景1: 总线唤醒 ---
[状态转换] SLEEP --> IDLE (事件: 0)
--- 场景2: 接收报文 ---
[状态转换] IDLE --> RECEIVE (事件: 1)
[状态转换] RECEIVE --> IDLE (事件: 2)
--- 场景3: 发送报文 ---
[状态转换] IDLE --> TRANSMIT (事件: 4)
[状态转换] TRANSMIT --> IDLE (事件: 5)
--- 场景4: 总线静默进入休眠 ---
[状态转换] IDLE --> SLEEP (事件: 7)
--- 场景5: 强制休眠命令 ---
[状态转换] SLEEP --> IDLE (事件: 0)
[状态转换] IDLE --> SLEEP (事件: 8)
最终状态: SLEEP
判定标准:
- 所有5个场景的状态转换均按预期发生,无非法转换(如从SLEEP直接到RECEIVE);
- 静默计时仅在IDLE状态累计,进入其他状态时清零;
- 接收超时在RECEIVE状态超过10ms后触发,回到IDLE;
- 最终状态为SLEEP,程序正常退出。