串口发送、接收字符串
任务内容:
串口的接收与发送,PC 端发送特定指令后,开发板可发送相应指令到 PC 端显示。
指令一:PC 端发送 I like FPGA,开发板回应 ILke FPGA, too 在 PC 端显示。
指令二:PC 端发送 I like verilog,开发板回应 ILke verilog, too 在 PC 端显示。
顶层代码:顶层代码主要负责各个模块的例化,本次任务由3个模块组成,分别是rx模块(字符串的读取)、tx模块(字符串的发送)和ctrl模块(根据接收到的字符串,控制需要发送的字符串)。
module top(
input wire clk,
input wire rst_n,
input wire rx,
output wire tx
);
wire [7:0]po_data;
wire po_flag;
wire [111:0]str;
wire [7:0]tx_str;
wire tx_flag;
wire tx_start;
wire [4:0]tx_cnt;
uart_rx inst_uart_rx (
.sclk (clk),
.rst_n (rst_n),
.rx (rx),
.po_data (po_data),
.po_flag (po_flag),
.str(str)
);
uart_tx inst_uart_tx (
.sclk (clk),
.rst_n (rst_n),
.tx (tx),
.pi_data (tx_str),
.tx_start(tx_start),
.tx_down(tx_flag),
.tx_cnt(tx_cnt),
.str(str)
);
ctrl inst_uart_trcl (
.clk (clk),
.rst_n (rst_n),
.str (str),
.tx_start (tx_start),
.tx_str (tx_str),
.po_data(po_data),
.tx_flag(tx_flag),
.tx_cnt(tx_cnt)
);
endmodule
仿真图:
rx部分代码:将上位机发送的串行数据转换为并行数据,供fpga进一步处理。
注:上位机发送过来的数据是串行的、且由高到低发送
module uart_rx(
input wire sclk,
input wire rst_n,
input wire rx, //上位机发送的数据,1bit
output reg [7:0] po_data, //接收到的字符
output reg po_flag, //接收完1个字符标志位
output reg [111:0]str//14*8,拼接成的字符串
);
parameter CNT_BAUD_MAX = 5207; //9600波特率,时钟周期20ns
parameter CNT_HALF_BAUD_MAX = 2603; //5208的一半
reg rx1,rx2,rx2_reg;
reg rx_flag;
reg [12:0] cnt_baud;
reg bit_flag;
reg [3:0] bit_cnt;
//打拍
always @(posedge sclk) begin
{rx2_reg,rx2,rx1}<={rx2,rx1,rx};
end
//控制开始读取上位机发来的数据
always @(posedge sclk) begin
if (rst_n == 1'b0) begin
rx_flag <= 1'b0;
end
else if (bit_cnt == 4'd8 && bit_flag == 1'b1) begin //接收完一个字符(1bit起始位+8bit数据位)
rx_flag <= 1'b0;
end
else if (rx2_reg == 1'b1 && rx2 == 1'b0) begin //检测到起始位(下降沿)
rx_flag <= 1'b1;
end
end
//当rx_flag == 1'b1时,cnt_baud循环计数
always @(posedge sclk) begin
if (rst_n == 1'b0) begin
cnt_baud <= 'd0;
end
else if (rx_flag == 1'b0) begin
cnt_baud <= 'd0;
end
else if (rx_flag == 1'b1 && cnt_baud == CNT_BAUD_MAX) begin //1bit数据接收完成
cnt_baud <= 'd0;
end
else if (rx_flag == 1'b1) begin
cnt_baud <= cnt_baud + 1'b1;
end
end
//1bit数据的中间时刻标志位(中间时刻数据最稳定)
always @(posedge sclk) begin
if (rst_n == 1'b0) begin
bit_flag <= 1'b0;
end
else if (rx_flag == 1'b1 && cnt_baud == CNT_HALF_BAUD_MAX) begin //计数到1bit数据的中间时刻
bit_flag <= 1'b1;
end
else begin
bit_flag <= 1'b0;
end
end
//对bit_flag计数,0-8循环
always @(posedge sclk) begin
if (rst_n == 1'b0) begin
bit_cnt <= 'd0;
end
else if (bit_flag == 1'b1 && bit_cnt == 'd8) begin //1bit发送位和8bi数据位结束完毕
bit_cnt <= 'd0;
end
else if (bit_flag == 1'b1) begin
bit_cnt <= bit_cnt + 1'b1;
end
end
//拼接rx2_reg,串行数据转并行数据
always @(posedge sclk ) begin
if (rst_n == 1'b0) begin
po_data <='d0;
end
else if (bit_cnt >= 4'd1 && bit_flag == 1'b1) begin
po_data <= {rx2_reg,po_data[7:1]};
end
end
//拼接完8bit数据时,拉高一个clk的高电平
always @(posedge sclk) begin
if (rst_n == 1'b0) begin
po_flag <= 1'b0;
end
else if (bit_cnt == 4'd8 && bit_flag == 1'b1) begin
po_flag <= 1'b1;
end
else begin
po_flag <= 1'b0;
end
end
//将接受到的字符拼接成字符串
always @(posedge sclk) begin
if (rst_n == 1'b0) begin
str <= 0;
end
else if (po_flag==1) begin //一个字符接收完毕
str <= {str[103:0],po_data};
end
end
endmodule
仿真图:细节放大:
tx部分:将fpga的并行数据转成串行数据发送给上位机。
注:由高位到低位发送。
module uart_tx(
input wire sclk,
input wire rst_n,
input wire tx_start, //触发fpga开始发送,与ctrl模块相连
input wire [7:0] pi_data, //需要发送的字符,与ctrl模块相连
input wire [4:0]tx_cnt, //控制发送第几个字符,与ctrl模块相连
input wire[111:0] str, //rx接收到的字符串
output reg tx, //发送的字符
output reg tx_down //发送完一个字符的标志位
);
parameter CNT_BAUD_MAX = 5207;
reg [7:0] data_reg;
reg tx_flag;
reg [12:0] cnt_baud;
reg bit_flag;
reg [3:0] bit_cnt;
//缓存数据,避免上一个模块发生变化而影响需要发送的数据
always @(posedge sclk) begin
if (rst_n == 1'b0) begin
data_reg <='d0;
end
else
data_reg <= pi_data;
end
//tx_flag=1时开始发送
always @(posedge sclk) begin
if (rst_n == 1'b0) begin
tx_flag <= 1'b0;
end
//首先,每发送完一个字符tx_flag <= 1'b0;其次,需要发送的字符串发送完tx_flag <= 1'b0
else if((bit_flag == 1'b1 && bit_cnt == 4'd9)||(str[87:0]=="I Like FPGA"&&tx_cnt==12&&tx_down==1)||(str[111:0]=="I Like verilog"&&tx_cnt==15&&tx_down==1)) begin
tx_flag <= 1'b0;
end
//首先,接收到‘I Like FPGA’或‘I Like verilog’,tx_flag <= 1'b1;其次,每发送完一个字符,tx_flag <= 1'b1
else if (tx_start== 1'b1 || tx_down==1) begin
tx_flag <= 1'b1;
end
end
//当tx_flag == 1'b1时,cnt_baud循环计数(0-5207)
always @(posedge sclk) begin
if (rst_n == 1'b0) begin
cnt_baud <= 'd0;
end
else if(tx_flag == 1'b0)begin
cnt_baud <='d0;
end
else if (tx_flag == 1'b1 && cnt_baud == CNT_BAUD_MAX) begin
cnt_baud <= 'd0;
end
else if (tx_flag == 1'b1) begin
cnt_baud <= cnt_baud + 1'b1;
end
end
//接收到1bit数据标志位
always @(posedge sclk) begin
if (rst_n == 1'b0) begin
bit_flag <= 1'b0;
end
else if (cnt_baud == CNT_BAUD_MAX -1 && tx_flag == 1'b1) begin
bit_flag <= 1'b1;
end
else begin
bit_flag <= 1'b0;
end
end
//对bit_flag计数,0-9循环
always @(posedge sclk) begin
if (rst_n == 1'b0) begin
bit_cnt <= 'd0;
end
else if(bit_flag == 1'b1 && bit_cnt == 4'd9) begin //1bit发送位+8bit数据位+1bit停止位发送完
bit_cnt <= 'd0;
end
else if (bit_flag == 1'b1) begin
bit_cnt <= bit_cnt + 1'b1;
end
end
//往上位机发送的串行数据
always @(posedge sclk ) begin
if (rst_n == 1'b0)
tx <= 1'b1;
//需要发送的字符串发送完, tx <= 1'b1
else if ((str[87:0]=="I Like FPGA"&&tx_cnt==12&&tx_down==1)||(str[111:0]=="I Like verilog"&&tx_cnt==15&&tx_down==1))
tx <= 1'b1;
// 1bit起始位。首先,接收到正确指令,tx <= 1'b0;其次,每发送完一个字符,tx <= 1'b0
else if (tx_start == 1'b1 || tx_down==1) begin
tx <= 1'b0;
end
else if (bit_cnt<=7 && bit_flag == 1'b1) begin //8位数据位,并转串
tx <= data_reg[bit_cnt];
end
else if (bit_flag == 1'b1 && bit_cnt == 4'd8) //1bit起始位+8位数据位发送完毕,产生1bit停止位
tx <= 1'b1;
end
//一个字符发送完毕,tx_down <= 1'b1
always @(posedge sclk)
if (rst_n == 1'b0)
tx_down <= 1'b0;
else if (bit_flag == 1'b1 && bit_cnt == 4'd9)
tx_down <= 1'b1;
else
tx_down <= 1'b0;
endmodule
仿真图:
ctrl部分:根据接收到的字符串,控制发送出去的字符串
module ctrl(
input wire clk,
input wire rst_n,
input wire [111:0]str, //接收到的字符串
input wire tx_flag, //一个字符发送完毕
input wire[7:0]po_data, //接收到的字符
output reg [7:0]tx_str, //需要发送的字符
output reg tx_start, //当接受完正确的字符串时,控制开始发送字符串
output reg [4:0]tx_cnt //发送第几个字符
);
reg [2:0]flag_send;
//判断是什么字符串
always @(posedge clk)
if (rst_n==0)
begin
flag_send<=3'b000;
end
else if(str[87:0]=="I Like FPGA")
begin
flag_send<=3'b001;
end
else if(str[111:0]=="I Like verilog")
begin
flag_send<=3'b010;
end
else begin
flag_send<=3'b000;
end
//tx_start<=1时开始发送字符串
always @(posedge clk)
if (rst_n==0)
tx_start<=0;
else if((str[79:0]=="I Like FPG"&&po_data=="A")||(str[103:0]=="I Like verilo"&&po_data=="g")) //接收到正确的字符串
tx_start<=1;
else tx_start<=0;
//判断发送第几个字符
always @(posedge clk)
if (rst_n==0)
tx_cnt<=0;
else if(tx_cnt==12&&str[87:0]=="I Like FPGA"&&tx_flag==1)
tx_cnt<=0;
else if(tx_cnt==15&&str[111:0]=="I Like verilog"&&tx_flag==1)
tx_cnt<=0;
else if(tx_flag==1)
tx_cnt<=tx_cnt+1;
//确定发送的字符
always @(posedge clk)
if (rst_n==0)
tx_str<=1;
else
case(tx_cnt)
'd0:tx_str<="I";
'd1:tx_str<="L";
'd2:tx_str<="k";
'd3:tx_str<="e";
'd4:tx_str<=" ";
'd5:if(flag_send==3'b001)
tx_str<="F";
else if(flag_send==3'b010)
tx_str<="v";
'd6:if(flag_send==3'b001)
tx_str<="P";
else if(flag_send==3'b010)
tx_str<="e";
'd7:if(flag_send==3'b001)
tx_str<="G";
else if(flag_send==3'b010)
tx_str<="r";
'd8:if(flag_send==3'b001)
tx_str<="A";
else if(flag_send==3'b010)
tx_str<="i";
'd9:if(flag_send==3'b001)
tx_str<=",";
else if(flag_send==3'b010)
tx_str<="l";
'd10:if(flag_send==3'b001)
tx_str<="t";
else if(flag_send==3'b010)
tx_str<="o";
'd11:if(flag_send==3'b001)
tx_str<="o";
else if(flag_send==3'b010)
tx_str<="g";
'd12:if(flag_send==3'b001)
tx_str<="o";
else if(flag_send==3'b010)
tx_str<=",";
'd13:if(flag_send==3'b010)
tx_str<="t";
'd14:if(flag_send==3'b010)
tx_str<="o";
'd15:if(flag_send==3'b010)
tx_str<="o";
default:tx_str<=0;
endcase
endmodule
仿真图: