FPGA学习之串口发送程序设计(来自小梅哥的教程学习者)
1. UART说明
可参考维基百科的文档:https://zh.wikipedia.org/wiki/UART
2. 实现逻辑图示
3. 程序实现
module UART_TX(rst_n,clk,send_en,baud_set,data_in,data_tx,flag,state);
input rst_n;
input clk;
input send_en;
input [1:0] baud_set;
input [7:0] data_in;
output reg data_tx;
output reg flag; //发送完成标志
output reg state; //当前状态
localparam START = 1'b0;
localparam STOP = 1'b1;
wire neg,pos;
TRI_edge U1(
.Clk(clk),
.Rst_n(rst_n),
.data_in(send_en),
.neg_out(neg),
.pos_out(pos)
);
//查表选择波特率
//波特率 计数值
//9600 50000000/9600 ≈ 5208
//19200 50000000/19200 ≈ 2604
//38400 50000000/38400 ≈ 1302
//115200 50000000/115200≈ 434
reg [15:0] bps_cnt;
always @(posedge clk or negedge rst_n)
if(!rst_n)
bps_cnt <= 16'd433;
else
case (baud_set)
0 : bps_cnt <= 16'd5207;
1 : bps_cnt <= 16'd2603;
2 : bps_cnt <= 16'd1301;
3 : bps_cnt <= 16'd433;
endcase
reg [15:0] cnt;
always @(posedge clk or negedge rst_n)
if(!rst_n)
cnt <= 16'd0;
else if(state) begin
if(cnt == bps_cnt)
cnt <= 16'd0;
else
cnt <= cnt + 1'b1;
end
else
cnt <= 16'd0;
//得到波特率相应时钟
reg baud_clk;
always @(posedge clk or negedge rst_n)
if(!rst_n)
baud_clk <= 1'b0;
else if(cnt == 16'd1)
baud_clk <= 1'b1;
else
baud_clk <= 1'b0;
//数波特率时钟上升沿,每加一次,传输一个数据位
reg [3:0] data_sel;
always @(posedge clk or negedge rst_n)
if(!rst_n)
data_sel <= 4'd0;
else if(data_sel == 4'd11)
data_sel <= 4'd0;
else if(baud_clk)
data_sel <= data_sel + 1'b1;
else
data_sel <= data_sel;
always @(posedge clk or negedge rst_n)
if(!rst_n)
flag <= 1'b0;
else if(data_sel == 4'd11)
flag <= 1'b1;
else
flag <= 1'b0;
reg [7:0] r_data_in;
always @(posedge clk or negedge rst_n)
if(!rst_n)
r_data_in <= 8'd0;
else if(neg)
r_data_in <= data_in;
else
r_data_in <= r_data_in;
//reg r_data_tx;
always @(posedge clk or negedge rst_n)
if(!rst_n)
data_tx <= 1'b1;
else
case (data_sel)
0 : data_tx <= 1'b1;
1 : data_tx <= START;
2 : data_tx <= r_data_in[0];
3 : data_tx <= r_data_in[1];
4 : data_tx <= r_data_in[2];
5 : data_tx <= r_data_in[3];
6 : data_tx <= r_data_in[4];
7 : data_tx <= r_data_in[5];
8 : data_tx <= r_data_in[6];
9 : data_tx <= r_data_in[7];
10 : data_tx <= STOP;
default:data_tx <= 1'b1;
endcase
/*
always @(*)
case (data_sel)
0 : r_data_tx = 1'b1;
1 : r_data_tx = START;
2 : r_data_tx = r_data_in[0];
3 : r_data_tx = r_data_in[1];
4 : r_data_tx = r_data_in[2];
5 : r_data_tx = r_data_in[3];
6 : r_data_tx = r_data_in[4];
7 : r_data_tx = r_data_in[5];
8 : r_data_tx = r_data_in[6];
9 : r_data_tx = r_data_in[7];
10 : r_data_tx = STOP;
endcase
*/
always @(posedge clk or negedge rst_n)
if(!rst_n)
state <= 1'b0;
else if(neg)
state <= 1'b1;
else if(data_sel == 4'd11)
state <= 1'b0;
else
state <= state;
endmodule
4. 程序优化说明
在小梅哥实现发送使能检测,为高电平将会把数据发送出去,这样将导致数据会无数次发送,只要使能信号为高电平。这里优化为使用边沿检测模块:
wire neg,pos;
TRI_edge U1(
.Clk(clk),
.Rst_n(rst_n),
.data_in(send_en),
.neg_out(neg),
.pos_out(pos)
);
将使能检检测改为上边沿触发或者下边沿触发,问题解决。
reg [7:0] r_data_in;
always @(posedge clk or negedge rst_n)
if(!rst_n)
r_data_in <= 8'd0;
else if(neg)
r_data_in <= data_in;
else
r_data_in <= r_data_in;


浙公网安备 33010602011771号