(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)beginstate <= IDLE;en_cnt <= 1'd0;endelse begincase(state)IDLE:beginif(key_in == 1'd0)beginstate <= PRESS;en_cnt <= 1'd1;endelse beginstate <= state;en_cnt <= 1'd0;endendPRESS:beginif(key_in == 1'd1)beginstate <= IDLE;en_cnt <= 1'd0;end else if(cnt == MCNT - 20'd1)beginstate <= DOWN;en_cnt <= 1'd0;endelse beginstate <= state;en_cnt <= en_cnt;endendDOWN:beginif(key_in == 1'd1)beginstate <= REPRESS;en_cnt <= 1'd1;endelse beginstate <= state;en_cnt <= 1'd0;endendREPRESS:beginif(key_in == 1'd0)beginstate <= DOWN;en_cnt <= 1'd0;endelse if(cnt == MCNT - 20'd1)beginstate <= IDLE;en_cnt <= 1'd0;endelse beginstate <= state;en_cnt <= en_cnt;endenddefault:beginstate <= IDLE;en_cnt <= 1'd0;endendcaseendassign 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)begincase(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; //其余波特率一律视为9600endcase
endalways@(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)begincase(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;endcaseendelsetx <= 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 begincase(key_out)4'b0001 : tx_data <= 8'h48; //H4'b0010 : tx_data <= 8'h45; //E4'b0100 : tx_data <= 8'h4C; //L4'b1000 : tx_data <= 8'h4F; //Odefault : tx_data <= 8'h00;endcaseendalways@(posedge clk or negedge reset_n)if(!reset_n)beginstate <= IDLE;tx_start <= 1'd0;endelse begincase(state)IDLE:begintx_start <= 1'b0;if(|key_out)state <= SEND;elsestate <= state;endSEND:begintx_start <= 1'b1;state <= WAIT;endWAIT:begintx_start <= 1'b0;if(tx_done && key_out == 4'b0000)state <= IDLE;else if(tx_done)state <= REPRESS;else state <= state;endREPRESS:beginif(key_out == 4'b0000)state <=IDLE;else state <= state;enddefault: state <= IDLE;endcaseendassign 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)实验现象: