怎么写状态机
July 8, 2026
先画出你要做的事
传感器检测到变化 │ 等总线空闲 ──→ 发第一次 ──→ 等 25ms ──→ 等总线空闲 ──→ 发第二次 ──→ 结束
找出哪些步骤需要"等"
这个流程里 3 个步骤是瞬间完成的,3 个步骤是需要等的:
传感器检测 瞬间 ✓ 等总线空闲 要等... ⏳ 发第一次 瞬间 ✓ 等 25ms 要等... ⏳ 等总线空闲 要等... ⏳ 发第二次 瞬间 ✓
需要等 = 不能死等 = 必须用状态
单片机里不能写 delay(25) 死等,因为死等期间什么都干不了。所以用状态机:每次进 AppRun() 检查条件,满足了就走下一步。
状态机 = 把"等待"拆成状态,"动作"放在状态切换时执行
│ 现实步骤 │ → 状态 │
│ 空闲 │ IDLE │
│ 等总线通信中 │ WAIT_RX_START │
│ 等总线安静 │ WAIT_RX_END │
│ 发第一次 │ SEND_1(动作态,不等待) │
│ 等 25ms │ WAIT_RETRY │
│ 等总线安静 │ WAIT_BUS_2 │
│ 发第二次 │ SEND_2(动作态,不等待) │
通用方法
以后遇到任何多步骤的异步流程,这样拆:
- 写出步骤清单(按时间顺序)
- 标出哪些步骤需要"等条件"
- 每个"等条件" → 一个 case
- 每个"做动作" → 一个 case(动作做完立刻跳到下一个状态)
- 条件满足 → 跳下一个 case
就这样,没什么神秘的。
void AppRun(void) { // 事件捕获 if (g_flag_trigger_stop == 1) { if (g_tx_state == TX_STATE_IDLE) { g_flag_trigger_stop = 0; g_tx_timeout_ms = 0; g_tx_state = TX_STATE_WAIT_RX_START; } } // 状态机 switch (g_tx_state) { case TX_STATE_IDLE: break; case TX_STATE_WAIT_RX_START: if (g_uart_idle_ms <= 1) { g_tx_state = TX_STATE_WAIT_RX_END; } else if (g_tx_timeout_ms >= 200) { g_tx_state = TX_STATE_WAIT_RX_END; } break; case TX_STATE_WAIT_RX_END: if (g_uart_idle_ms >= 3) { g_tx_state = TX_STATE_SEND_1; } break; case TX_STATE_SEND_1: comm_dataSend((uint8_t*)stop_command, 8); g_tx_delay_ms = 0; g_tx_state = TX_STATE_WAIT_RETRY; break; case TX_STATE_WAIT_RETRY: if (g_tx_delay_ms >= 25) { g_tx_state = TX_STATE_WAIT_BUS_2; } break; case TX_STATE_WAIT_BUS_2: if (g_uart_idle_ms >= 3) { g_tx_state = TX_STATE_SEND_2; } break; case TX_STATE_SEND_2: comm_dataSend((uint8_t*)stop_command, 8); g_tx_state = TX_STATE_IDLE; break; default: g_tx_state = TX_STATE_IDLE; break; } }