FPGA开发--串口接收模块
文章摘要:
本文主要描述了串口接收模块的实现;
设计思路:
1.通过检测数据线上的下降沿来确定起始位;
2.检测每一位的中间位置的电平值,来确定该位的数据;
知识要点: 边沿检测,有限状态机;
硬件平台: EP4CE6F17C8
开发环境: Quartus II 13.1
/*
* 功能描述:串口接收模块
*/
module uart_recv(
input clk, // 模块时钟
input rst_n, // 模块复位,低电平有效
input rxd, // 串口接收引脚
output reg[7:0] rdata, // 接收到的数据
output reg rxdone // 接收完成标志,高电平有效
);
parameter BAUD_CNT = 5208; // 9600的波特率
parameter BAUD_CNT_HALF = 2604; // 中间值
parameter S_IDLE = 4'd0;
parameter S_START = 4'd1;
parameter S_BIT0 = 4'd2;
parameter S_BIT1 = 4'd3;
parameter S_BIT2 = 4'd4;
parameter S_BIT3 = 4'd5;
parameter S_BIT4 = 4'd6;
parameter S_BIT5 = 4'd7;
parameter S_BIT6 = 4'd8;
parameter S_BIT7 = 4'd9;
parameter S_STOP = 4'd10;
reg [3:0] status; // 状态机当前状态
reg rxd_last; // 接收引脚前一次电平
reg rxd_curr; // 接收引脚当前电平
wire rxd_start; // 接收起始位标志(高电平有效)
reg [31:0] timer; // 波特率计时器
//--------------------------------------------------
// 接收起始位处理
always @(posedge clk)
begin
rxd_last <= rxd_curr;
rxd_curr <= rxd;
end
// 前一次为高,当前值为低,视做下降沿
assign rxd_start = rxd_last & ~rxd_curr;
//--------------------------------------------------
always @(posedge clk or negedge rst_n)
begin
if(~rst_n)
begin
status = S_IDLE;
end
else
begin
timer = timer + 1'b1; // 波特率计算
case(status)
S_IDLE:
begin
rxdone <= 1'b0;
if(rxd_start) // 空闲状态检测到下降沿
begin
timer <= 0;
status <= S_START; // 接收起始位
end
end
S_START:
begin
// 中间时刻仍为低电平,视做有效起始位
if(timer == BAUD_CNT_HALF)
begin
if(~rxd)
begin
timer <= 0;
status <= S_BIT0;
end
else
status <= S_IDLE; // 起始位无效,重新接收
end
end
S_BIT0:
begin
// 因为上一次是在中间位置重置timer,所以这里也是中间位置
if(timer == BAUD_CNT)
begin
timer <= 0; // 重置定时器
rdata[0]<= rxd; // 当前值即为有效数据
status <= S_BIT1; // 准备接收下一bit
end
else
status <= S_BIT0; // 这一句纯粹是编程习惯
end
S_BIT1:
begin
if(timer == BAUD_CNT)
begin
timer <= 0;
rdata[1] <= rxd;
status <= S_BIT2;
end
else
status <= S_BIT1;
end
S_BIT2:
begin
if(timer == BAUD_CNT)
begin
timer <= 0;
rdata[2] <= rxd;
status <= S_BIT3;
end
else
status <= S_BIT2;
end
S_BIT3:
begin
if(timer == BAUD_CNT)
begin
timer <= 0;
rdata[3] <= rxd;
status <= S_BIT4;
end
else
status <= S_BIT3;
end
S_BIT4:
begin
if(timer == BAUD_CNT)
begin
timer <= 0;
rdata[4] <= rxd;
status <= S_BIT5;
end
else
status <= S_BIT4;
end
S_BIT5:
begin
if(timer == BAUD_CNT)
begin
timer <= 0;
rdata[5] <= rxd;
status <= S_BIT6;
end
else
status <= S_BIT5;
end
S_BIT6:
begin
if(timer == BAUD_CNT)
begin
timer <= 0;
rdata[6] <= rxd;
status <= S_BIT7;
end
else
status <= S_BIT6;
end
S_BIT7:
begin
if(timer == BAUD_CNT)
begin
timer <= 0;
rdata[7] <= rxd;
status <= S_STOP; // 处理停止位
end
else
status <= S_BIT7;
end
S_STOP:
// 因为上一次是在中间置0的,所以这里也是中点
// 但是不等到接收完再跳转了,因为如果连续发送的话,可能会错过起始位
if(timer == BAUD_CNT)
begin
if(rxd)
begin
rxdone <= 1'b1; // 设置接收完成标置
end
status <= S_IDLE; // 无论如何,均进入空闲状态
timer <= 0;
end
endcase
end
end
endmodule