3. LIN从节点响应帧发送与校验和计算

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

判定标准:

  1. 所有5个场景的状态转换均按预期发生,无非法转换(如从SLEEP直接到RECEIVE);
  2. 静默计时仅在IDLE状态累计,进入其他状态时清零;
  3. 接收超时在RECEIVE状态超过10ms后触发,回到IDLE;
  4. 最终状态为SLEEP,程序正常退出。

状态机总览

SLEEP IDLE RECEIVE TRANSMIT 总线唤醒 静默4s / 休眠命令 帧头 接收完成/超时 发送时隙 发送完成 总线错误/休眠命令 总线错误/休眠命令 图例 正常转换 异常/命令转换