(1)UART是一种串行、异步、全双工的通讯协议,其中串行指的是发送线和接收线一次都只能接收1bit的数据,异步指的是上位机和FPGA之间一个传输一个接收不需要统一的时钟,两边的工作时钟可以不一致,全双工指的是FPGA可以同时进行发送数据和接收数据。
(2)实验目标:显示设计一个按键消抖模块和串口发送驱动模块,四个按键按下后,分别可以产生“H”、“E”、“L”、“O”,以此实现FPGA向电脑PC端传输字符串HELLO的功能。
(3)代码实现:
- 按键消抖模块:
module key_filter
(
input wire clk ,
input wire key_in ,
input wire reset_n ,
output wire key_out
);
//20ms = 20ns * 1_000_000;
parameter MCNT = 20'd1_000_000;
parameter IDLE = 4'b0001;
parameter PRESS = 4'b0010;
parameter DOWN = 4'b0100;
parameter REPRESS = 4'b1000;
reg [19:0] cnt;
reg [3:0] state;
reg en_cnt;
always@(posedge clk or negedge reset_n)
if(!reset_n)
cnt <= 20'd0;
else if(!en_cnt)
cnt <= 20'd0;
else if(cnt == MCNT - 20'd1)
cnt <= 20'd0;
else
cnt <= cnt + 20'd1;
always@(posedge clk or negedge reset_n)
if(!reset_n)begin
state <= IDLE;
en_cnt <= 1'd0;
end
else begin
case(state)
IDLE:begin
if(key_in == 1'd0)begin
state <= PRESS;
en_cnt <= 1'd1;
end
else begin
state <= state;
en_cnt <= 1'd0;
end
end
PRESS:begin
if(key_in == 1'd1)begin
state <= IDLE;
en_cnt <= 1'd0;
end
else if(cnt == MCNT - 20'd1)begin
state <= DOWN;
en_cnt <= 1'd0;
end
else begin
state <= state;
en_cnt <= en_cnt;
end
end
DOWN:begin
if(key_in == 1'd1)begin
state <= REPRESS;
en_cnt <= 1'd1;
end
else begin
state <= state;
en_cnt <= 1'd0;
end
end
REPRESS:begin
if(key_in == 1'd0)begin
state <= DOWN;
en_cnt <= 1'd0;
end
else if(cnt == MCNT - 20'd1)begin
state <= IDLE;
en_cnt <= 1'd0;
end
else begin
state <= state;
en_cnt <= en_cnt;
end
end
default:begin
state <= IDLE;
en_cnt <= 1'd0;
end
endcase
end
assign key_out = (state == DOWN);
endmodule
- 串口发送驱动模块:
module uart_rs232_tx
(
input wire clk ,
input wire reset_n ,
input wire [16:0] baud_set ,
input wire [7:0] tx_data ,
input wire tx_start ,
output reg tx ,
output reg tx_done
);
reg [15:0] BAUD_MAX;
reg en_baud_cnt;
reg [15:0] baud_cnt;
reg [3:0] bit_cnt;
always@(posedge clk)begin
case(baud_set)
17'd4800 : BAUD_MAX <= 16'd10415;
17'd9600 : BAUD_MAX <= 16'd5207;
17'd14400 : BAUD_MAX <= 16'd3471;
17'd115200 : BAUD_MAX <= 16'd433;
default : BAUD_MAX <= 16'd5207; //其余波特率一律视为9600
endcase
end
always@(posedge clk or negedge reset_n)
if(!reset_n)
en_baud_cnt <= 1'd0;
else if(tx_start)
en_baud_cnt <= 1'd1;
else if((bit_cnt == 4'd9)&&(baud_cnt == 16'd1))
en_baud_cnt <= 1'd0;
else
en_baud_cnt <= en_baud_cnt;
always@(posedge clk or negedge reset_n)
if(!reset_n)
baud_cnt <= 16'd0;
else if(!en_baud_cnt||((bit_cnt == 4'd9)&&(baud_cnt == 16'd1)))
baud_cnt <= 16'd0;
else if(baud_cnt == BAUD_MAX)
baud_cnt <= 16'd0;
else
baud_cnt <= baud_cnt + 16'd1;
always@(posedge clk or negedge reset_n)
if(!reset_n)
bit_cnt <= 4'd0;
else if((bit_cnt == 4'd9)&&(baud_cnt == 16'd1))
bit_cnt <= 4'd0;
else if(baud_cnt == BAUD_MAX)
bit_cnt <= bit_cnt + 4'd1;
else
bit_cnt <= bit_cnt;
always@(posedge clk or negedge reset_n)
if(!reset_n)
tx <= 1'd1;
else if(!en_baud_cnt)
tx <= 1'd1;
else if(baud_cnt == 16'd1)begin
case(bit_cnt)
0: tx <= 1'd0;
1: tx <= tx_data[0];
2: tx <= tx_data[1];
3: tx <= tx_data[2];
4: tx <= tx_data[3];
5: tx <= tx_data[4];
6: tx <= tx_data[5];
7: tx <= tx_data[6];
8: tx <= tx_data[7];
9: tx <= 1'd1;
default :tx <= 1'd1;
endcase
end
else
tx <= tx;
always@(posedge clk or negedge reset_n)
if(!reset_n)
tx_done <= 1'd0;
else if((bit_cnt == 4'd9)&&(baud_cnt == 16'd1))
tx_done <= 1'd1;
else
tx_done <= 1'd0;
endmodule
- 顶层模块:
module uart_key
(
input wire clk ,
input wire reset_n ,
input wire [3:0] key_in ,
output wire tx ,
output wire led
);
localparam IDLE = 4'b0001;
localparam SEND = 4'b0010;
localparam WAIT = 4'b0100;
localparam REPRESS = 4'b1000;
wire [3:0] key_out;
wire tx_done;
reg [7:0] tx_data;
reg [3:0] state;
reg tx_start;
key_filter key_filter_inst0
(
.clk (clk ),
.key_in (key_in[0] ),
.reset_n (reset_n ),
.key_out (key_out[0] )
);
key_filter key_filter_inst1
(
.clk (clk ),
.key_in (key_in[1] ),
.reset_n (reset_n ),
.key_out (key_out[1] )
);
key_filter key_filter_inst2
(
.clk (clk ),
.key_in (key_in[2] ),
.reset_n (reset_n ),
.key_out (key_out[2] )
);
key_filter key_filter_inst3
(
.clk (clk ),
.key_in (key_in[3] ),
.reset_n (reset_n ),
.key_out (key_out[3] )
);
always@(posedge clk or negedge reset_n)
if(!reset_n)
tx_data <= 8'h00;
else begin
case(key_out)
4'b0001 : tx_data <= 8'h48; //H
4'b0010 : tx_data <= 8'h45; //E
4'b0100 : tx_data <= 8'h4C; //L
4'b1000 : tx_data <= 8'h4F; //O
default : tx_data <= 8'h00;
endcase
end
always@(posedge clk or negedge reset_n)
if(!reset_n)begin
state <= IDLE;
tx_start <= 1'd0;
end
else begin
case(state)
IDLE:begin
tx_start <= 1'b0;
if(|key_out)
state <= SEND;
else
state <= state;
end
SEND:begin
tx_start <= 1'b1;
state <= WAIT;
end
WAIT:begin
tx_start <= 1'b0;
if(tx_done && key_out == 4'b0000)
state <= IDLE;
else if(tx_done)
state <= REPRESS;
else
state <= state;
end
REPRESS:begin
if(key_out == 4'b0000)
state <=IDLE;
else
state <= state;
end
default: state <= IDLE;
endcase
end
assign led = ~(state == IDLE);
uart_rs232_tx uart_rs232_tx_inst
(
.clk (clk ),
.reset_n (reset_n ),
.baud_set (17'd115200 ),
.tx_data (tx_data ),
.tx_start (tx_start ),
.tx (tx ),
.tx_done (tx_done )
);
endmodule
(3)实验现象: