串口收发之ram存取
项目名称
串口收发之ram存取
具体要求
串口发送6个数据到FPGA,通过双端口ram将数据缓存,每按下一个按键,上位机接收一个数据,按下按键6次接收5位数据完毕
设计说明
下图为设计框架,除了ram_ctrl模块,剩下的模块在前面都介绍过,将前设计的模块进行调用总体比较简单。
这里将前面设计的串口发送模块重新设计一下,根据以下的时序图可以很轻松设计出串口发送模块。
需要注意的是笔者在进行ram控制模块设计的时候遇到了一个问题,在进行读地址的时候,如果按以下的方式进行设计,也是没有问题的,问题是当发送使能为1的时候,下一个时钟上升沿才执行if下的语句,而在这期间就开始读出0地址的数据,在下一个时钟上升沿读出1地址的数据,但是0地址的数据并没有被读出,应该是在时钟上升沿的时候才开始被执行吧。
aaa@qq.com(posedge clk or negedge rst_n)
if(!rst_n)
rdaddr<=0;
else if(send_en)begin
if(rdaddr==5)
rdaddr<=0;
else
rdaddr<=rdaddr+1;
end
else
rdaddr<=rdaddr;
笔者将上面的代码进行修改用状态机来实现可以正常读取出存入的5位数据。
reg state;
aaa@qq.com(posedge clk or negedge rst_n)
if(!rst_n) begin
rdaddr<=0;
state<=0;
end
else if(send_en)begin
case(state)
0: begin
rdaddr<=0;
state<=1;
end
1:begin
if(rdaddr==5)begin
rdaddr<=0;
state<=1;
end
else
rdaddr<=rdaddr+1;
end
endcase
end
else
rdaddr<=rdaddr;
代码设计
顶层模块
module uart_top(
input clk,
input rst_n,
input rs232_data,
input key_in,
output rs232_tx
);
wire [7:0] data;
wire flag;
wire key_flag;
wire key_state;
uart_rx uart_rx(
.clk(clk),
.rst_n(rst_n),
.rs232_data(rs232_data),
.data(data),
.flag(flag)
);
key_filter key_filter(
. clk(clk),
. rst_n(rst_n),
. key_in(key_in),
. key_flag(key_flag),
. key_state(key_state)
);
wire send_en;
wire wren;
wire [7:0] wraddr;
wire [7:0] rdaddr;
wire [7:0] q;
wire tx_done;
ram_ctrl ram_ctrl(
.clk(clk),
.rst_n(rst_n),
.flag(flag),
.key_flag(key_flag),
.key_state(key_state),
.send_en(send_en),
.tx_done(tx_done),
.wren(wren),
.wraddr(wraddr),
.rdaddr(rdaddr)
);
my_ram my_ram(
.clock(clk),
.data(data),
.rdaddress(rdaddr),
.wraddress(wraddr),
.wren(wren),
.q(q)
);
uart_tx uart_tx(
.clk(clk),
.rst_n(rst_n),
.send_en(send_en),
.data_byte(q),
.rs232_tx(rs232_tx),
.tx_done()
);
endmodule
串口接收模块
module uart_rx(
input clk,
input rst_n,
input rs232_data,
output reg [7:0]data,
output reg flag
);
localparam bps_cnt_end=5207;
//串口数据同步处理
reg rx_data1;
reg rx_data2;
reg rx_data3;
aaa@qq.com(posedge clk or negedge rst_n)
if(!rst_n)begin
rx_data1<=0;
rx_data2<=0;
rx_data3<=0;
end
else begin
rx_data1<=rs232_data;
rx_data2<=rx_data1;
rx_data3<=rx_data2;
end
//下降沿起始位检测
wire nedge= !rx_data2 & rx_data3;
reg data_flag;
reg [12:0]bps_cnt;
aaa@qq.com(posedge clk or negedge rst_n)
if(!rst_n)
bps_cnt<=0;
else if(data_flag)begin
if(bps_cnt<5207)
bps_cnt<=bps_cnt+1;
else
bps_cnt<=0;
end
else
bps_cnt<=0;
reg [3:0] data_cnt;
aaa@qq.com(posedge clk or negedge rst_n)
if(!rst_n)
data_flag<=0;
else if(nedge)
data_flag<=1;
else if(bps_cnt==5205 && data_cnt==10)
data_flag<=0;
reg data_sample;
reg [12:0] bps_cnt_mid=bps_cnt_end/2+1;
aaa@qq.com(posedge clk or negedge rst_n)
if(!rst_n)
data_sample<=0;
else if(data_flag && bps_cnt==bps_cnt_mid)
data_sample<=1;
else
data_sample<=0;
aaa@qq.com(posedge clk or negedge rst_n)
if(!rst_n)
data_cnt<=0;
else if(data_sample)begin
if(data_cnt<10)
data_cnt<=data_cnt+1;
else
data_cnt<=1;
end
else
data_cnt<=data_cnt;
aaa@qq.com(posedge clk or negedge rst_n)
if(!rst_n)
flag<=0;
else if(data_cnt==9 && bps_cnt==bps_cnt_mid+3)
flag<=1;
else
flag<=0;
//接收数据
aaa@qq.com(posedge clk or negedge rst_n)
if(!rst_n)
data<=0;
else if(data_sample && data_cnt>=1 && data_cnt<=8)
data<={rx_data3,data[7:1]};
else
data<=data;
endmodule
按键消抖模块
module key_filter(
input clk,
input rst_n,
input key_in,
output reg key_flag,
output reg key_state
);
//异步信号处理
reg key_s0,key_s1;
aaa@qq.com(posedge clk or negedge rst_n)
if(!rst_n)begin
key_s0<=0;
key_s1<=0;
end
else begin
key_s0<=key_in;
key_s1<=key_s0;
end
//上升沿与下降沿检测
reg key_in0;
reg key_in1;
aaa@qq.com(posedge clk or negedge rst_n)
if(!rst_n)begin
key_in0<=0;
key_in1<=0;
end
else begin
key_in0<=key_s1;
key_in1<=key_in0;
end
wire nedge=key_in1 && (!key_in0);//下降沿检测
wire podge=(!key_in1) && key_in0;//上升沿检测
//20ms的计数
reg [24:0] cnt;
reg en_cnt;//使能计数
aaa@qq.com(posedge clk or negedge rst_n)
if(!rst_n)begin
cnt<=0;
end
else if(en_cnt)begin
if(cnt<25'd1_000_000-1)
cnt<=cnt+1'b1;
else
cnt<=0;
end
else
cnt<=0;
wire delay_20ms_done=(cnt==25'd1_000_000-1) && (en_cnt);//计满标志信号
//状态机的设计
reg [2:0] state;
localparam IDLE =3'd0;
localparam FILTER1=3'd1;
localparam DOWM =3'd2;
localparam FILTER2=3'd3;
aaa@qq.com(posedge clk or negedge rst_n)
if(!rst_n)begin
key_flag<=0;
key_state<=1;
state<=IDLE;
en_cnt<=0;
end
else begin
case(state)
IDLE : begin
if(nedge)begin
state<=FILTER1;
en_cnt<=1;
end
else
state<=IDLE;
end
FILTER1: begin
if(delay_20ms_done)begin
if(podge)begin
state<=IDLE;
en_cnt<=0;
end
else begin
state<=DOWM;
en_cnt<=0;
key_flag<=1;
key_state<=0;
end
end
else
state<=FILTER1;
end
DOWM : begin
key_flag<=0;
if(podge)begin
state<=FILTER2;
en_cnt<=1;
end
else begin
state<=DOWM;
end
end
FILTER2: begin
if(delay_20ms_done)begin
if(nedge)begin
state<=DOWM;
en_cnt<=0;
end
else begin
state<=IDLE;
en_cnt<=0;
key_state<=1;
end
end
else begin
state<=FILTER2;
end
end
default: begin
key_flag<=0;
key_state<=1;
state<=IDLE;
en_cnt<=0;
end
endcase
end
endmodule
ram控制模块
module ram_ctrl(
input clk,
input rst_n,
input flag,
input key_flag,
input key_state,
input tx_done,
output reg send_en,
output wren,
output reg[7:0] wraddr,
output reg[7:0] rdaddr
);
assign wren=flag;
//写地址,写使能一旦打开就开始写数据,从0地址开始写数据
aaa@qq.com(posedge clk or negedge rst_n)
if(!rst_n)
wraddr<=0;
else if(flag)begin
if(wraddr==5)
wraddr<=0;
else
wraddr<=wraddr+1;
end
else
wraddr<=wraddr;
//读数据,检测到按键信号就开始发送数据(未检测到按键信号ram中q一直是0地址中的数据)
reg state;
aaa@qq.com(posedge clk or negedge rst_n)
if(!rst_n) begin
rdaddr<=0;
state<=0;
end
else if(send_en)begin
case(state)
0: begin
rdaddr<=0;
state<=1;
end
1:begin
if(rdaddr==5)begin
rdaddr<=0;
state<=1;
end
else
rdaddr<=rdaddr+1;
end
endcase
end
else
rdaddr<=rdaddr;
//aaa@qq.com(posedge clk or negedge rst_n)
// if(!rst_n)
// rdaddr<=0;
// else if(send_en)begin
// if(rdaddr==5)
// rdaddr<=0;
// else
// rdaddr<=rdaddr+1;
// end
// else
// rdaddr<=rdaddr;
//发送使能
aaa@qq.com(posedge clk or negedge rst_n)
if(!rst_n)
send_en<=0;
else if(key_flag && (!key_state))
send_en<=1;
else
send_en<=0;
endmodule
串口发送模块
module uart_tx(
input clk,
input rst_n,
input send_en,
input [7:0] data_byte,
output reg rs232_tx,
output reg tx_done
);
//数据同步,消除亚稳态
reg [7:0] data_byte_r;
aaa@qq.com(posedge clk or negedge rst_n)
if(!rst_n)
data_byte_r<=0;
else
data_byte_r<=data_byte;
//波特率计数
reg [15:0] bps_cnt;
reg bps_cnt_en;//波特率使能计数
reg [3:0] bps_clk_cnt;//波特率时钟计数
aaa@qq.com(posedge clk or negedge rst_n)
if(!rst_n)
bps_cnt_en<=0;
else if(send_en)
bps_cnt_en<=1;
else if(bps_clk_cnt==11)
bps_cnt_en<=0;
else
bps_cnt_en<=bps_cnt_en;
aaa@qq.com(posedge clk or negedge rst_n)
if(!rst_n)
bps_cnt<=0;
else if(bps_cnt_en)begin
if(bps_cnt==5207)
bps_cnt<=0;
else
bps_cnt<=bps_cnt+1;
end
else
bps_cnt<=0;
//生成波特率时钟
reg bps_clk;
aaa@qq.com(posedge clk or negedge rst_n)
if(!rst_n)
bps_clk<=0;
else if(bps_cnt_en && bps_cnt==1)
bps_clk<=1;
else
bps_clk<=0;
//波特率时钟计数
aaa@qq.com(posedge clk or negedge rst_n)
if(!rst_n)
bps_clk_cnt<=0;
else if(bps_cnt_en)begin
if(bps_clk)
bps_clk_cnt<=bps_clk_cnt+1;
else
bps_clk_cnt<=bps_clk_cnt;
end
else
bps_clk_cnt<=0;
//数据发送完成标志
aaa@qq.com(posedge clk or negedge rst_n)
if(!rst_n)
tx_done<=0;
else if(bps_clk_cnt==11)
tx_done<=1;
else
tx_done<=0;
//发送数据
aaa@qq.com(posedge clk or negedge rst_n)
if(!rst_n)
rs232_tx<=0;
else begin
case(bps_clk_cnt)
1: rs232_tx<=0;
2: rs232_tx<=data_byte_r[0];
3: rs232_tx<=data_byte_r[1];
4: rs232_tx<=data_byte_r[2];
5: rs232_tx<=data_byte_r[3];
6: rs232_tx<=data_byte_r[4];
7: rs232_tx<=data_byte_r[5];
8: rs232_tx<=data_byte_r[6];
9: rs232_tx<=data_byte_r[7];
10:rs232_tx<=1;
11:rs232_tx<=1;
default:rs232_tx<=1;
endcase
end
endmodule
实验结果
通过串口发送11,22,33,44,55,66到fpga,按下按键发现数据可以正常被读出。
下一篇: 我的FPGA学习笔记--串口接收模块