状态机设计第一步:空闲状态(IDLE)的设计与实现,如何检测起始位
各位同学,咱们今天聊点实在的。
串口接收,说白了就是一根线上串着传数据。没有时钟线,全靠约定好的波特率来采样。那问题来了——接收端怎么知道什么时候开始收?
答案就是:起始位。
而检测起始位,正是状态机里空闲状态(IDLE)的核心任务。
1. 空闲状态在干什么?
先想想看,串口总线空闲时是什么电平?
对,高电平。这是RS-232标准定的,UART协议也沿用了这个习惯。
所以空闲状态(IDLE)要做的就两件事:
- 盯着RX线,看它什么时候从高电平跳变到低电平
- 确认这个低电平是真正的起始位,而不是毛刺干扰
嗯,这里要注意——起始位是一个位宽的低电平。在约定的波特率下,它持续整整一个位时间。
核心逻辑:检测到下降沿 → 等待半个位时间 → 再次采样确认低电平 → 进入接收状态
2. 为什么不能直接检测下降沿就收?
我刚开始做串口的时候,也犯过这个错。
直接写个边沿检测,下降沿一到就启动接收。结果呢?
- 线上有毛刺,误触发
- 起始位中间有抖动,采样点偏移
- 波特率稍微不准,整个数据全错
所以,必须做“确认”这一步。
我个人习惯的做法是:
- 用系统时钟(比如50MHz)对RX线做同步打拍,消除亚稳态
- 检测同步后的下降沿
- 启动一个半位定时器,等待半个位时间
- 在半个位时间点再次采样RX线
- 如果还是低电平 → 确认是起始位 → 状态机跳转
- 如果是高电平 → 认为是毛刺 → 回到IDLE继续等待
小技巧:半位采样点正好落在起始位的正中间,能最大程度避开边沿抖动。这个位置最稳。
3. 代码怎么写?我给你们一个模板
下面是我在实际项目中用过的代码框架,你们可以直接参考。
// 同步打拍,消除亚稳态
reg rx_sync1, rx_sync2;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
rx_sync1 <= 1'b1;
rx_sync2 <= 1'b1;
end else begin
rx_sync1 <= rx;
rx_sync2 <= rx_sync1;
end
end
// 下降沿检测
wire rx_negedge = rx_sync2 & ~rx_sync1;
// 状态机:IDLE部分
localparam IDLE = 3'd0;
localparam HALF_WAIT = 3'd1;
localparam RECV = 3'd2;
reg [2:0] state, next_state;
reg [7:0] half_timer; // 半位定时器
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
state <= IDLE;
else
state <= next_state;
end
always @(*) begin
next_state = state;
case (state)
IDLE: begin
if (rx_negedge)
next_state = HALF_WAIT;
end
HALF_WAIT: begin
if (half_timer == HALF_BIT_CNT - 1) begin
if (!rx_sync2) // 再次确认是低电平
next_state = RECV;
else
next_state = IDLE; // 毛刺,回到空闲
end
end
// ... 其他状态
endcase
end
注意:HALF_BIT_CNT 要根据你的系统时钟和波特率计算。比如50MHz时钟,9600波特率,一个位时间约5208个时钟周期,半位就是2604。
千万别算错,算错了整个串口都收不到数据。
4. 我曾经踩过的坑
有一次做项目,板子串口死活收不到数据。示波器一看,RX线上波形完全正确。
查了两天,最后发现——同步打拍只打了一级。
一级同步根本消除不了亚稳态,尤其是在时钟频率高、线缆长的情况下。从那以后,我所有跨时钟域信号都至少打两拍,有些关键信号打三拍。
还有一个坑:起始位检测的定时器启动时机。
我建议在检测到下降沿的下一个时钟周期启动定时器,而不是立即启动。为什么?因为边沿检测信号只持续一个周期,你立即启动的话,定时器可能少计一个周期,导致半位采样点偏移。
经验总结:
- 同步打拍至少两级
- 半位采样点确认起始位
- 定时器启动要滞后一个周期
- 毛刺检测不能省
5. 知识体系:一张图看懂
下面我用SVG画了一张流程图,把IDLE状态的核心逻辑串起来。你们可以保存下来,写代码时对照着看。
6. 总结一下
空闲状态(IDLE)的设计,说白了就一句话:用半位采样点确认起始位的真实性。
别小看这一步。我见过太多新手直接检测下降沿就跳转,结果被毛刺坑得死去活来。
记住:FPGA设计里,确认比检测更重要。
最后送大家一句话:状态机设计,IDLE是地基。地基没打好,后面全白搭。
可可精品资料库