使用FPGA做串口收发实验
一、实验目的
使用全双工串口通过时钟分频,产生串口波特率时钟,然后通过起始、结束比特位控制一个字节数据的收发。
二、Vivado工程新建
三、串口时序简介
通信原理:通过一根总线发送到接收端
通信过程:空闲,信号初始为高,然后开始拉低一定时间,产生起始信号,然后依次送出地位到高位的BIT数据。
波特率:波特率决定了通信的快慢。比如115200波特率,表示每秒钟产生115200个bit的速率进行传输。
四、代码讲解
顶层模块
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer: yu meng ya
//
// Create Date: 2022/05/02 13:52:28
// Design Name: 串口收发实验
// Module Name: uart_eco
// Project Name:fpga_uart_eco
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module uart_eco(
input wire rst,
input wire clk,
input wire rxd,
output wire txd
);
wire [7:0]uart_data_wire_tx;
wire rx_flag;
wire [19:0]ila_probe0;
assign ila_probe0[0]=rxd;
assign ila_probe0[1]=txd;
assign ila_probe0[2]=rx_flag;
assign ila_probe0[10:3]=uart_data_wire_tx[7:0];
assign ila_probe0[19:11]='b0;
ila_0 ila_0_inst
(
.clk(clk),
.probe0(ila_probe0)
);
usart_rxusart_rx_inst(
.clock(clk),
.rxd(rxd),
.rst_n(rst),
.rx_data_byte(uart_data_wire_tx),
.rx_valid_wire(rx_flag)
);
usart_tx usart_tx_inst(
.clock(clk),
.txd(txd),
.rst_n(rst),
.tx_data_byte(uart_data_wire_tx),
.tx_triger_flag(rx_flag)
);
endmodule
串口接收模块代码,代码包含模块输入时钟,接收信号线,复位信号,以及接收到的字节数据,以及接收有效标志。当接收完一个字节,接收字节数据就有效。并且,接受有效信号会拉高至少一个时钟周期。
首先,串口平时空闲,rx信号一直为高。当有数据来临时,信号拉低,持续一个波特率的周期为低,这个比特是起始位。我们的一个字节接收rx_start_flag标志在这个时候就置为1,表示正在接收这个字节了。然后,这个标志位1的时候,我们bit接收计数器就开始计数,从0计数到一个波特率周期最大值,然后清零,再次计数。计数到最大值的一半,即为采样时刻,这个时候我们就把rx值读过来,放到一个8bit移位寄存器。接收8bit数据需要9次计数周期,为什么是9次呢?因为起始位也算一个bit。起始bit不会把数据读来放到接收移位寄存器,所以数据开始接收存储到移位寄存器是从第二位开始的。
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer: yu meng ya
//
// Create Date: 2022/05/02 14:08:25
// Design Name: 串口接收模块
// Module Name: uart_rx
// Project Name: fpga_uart_ec
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module uart_rx(
input wire clock,
input wire rxd,
input wire rst_n,
output wire [7:0]rx_data_byte,
output wire rx_valid_wire
);
parameter CLOCK_FRQ=50_000_000;
parameter BADRATE =115_200;
parameter UART_COUTER_MAX=CLOCK_FRQ/BADRATE;
parameter UART_BIT =8;
reg [7:0]rx_data_byte_reg;
reg rx_start_flag;
reg [19:0]rx_counter;
reg [19:0]baud_counter;
reg baud_rat_clk;
reg rxd_reg1;
reg rxd_reg2;
reg rxd_reg3;
wire rxd_negedge;
reg [7:0]rx_bit_cnt;
reg [7:0]rx_data;
reg rx_valid_flag_pre1;
reg rx_valid_flag_pre2;
wire rx_valid_flag;
assign rx_valid_wire=rx_valid_flag_pre2;
assign rx_valid_flag=(rx_bit_cnt==9)?1:0;
assign rxd_negedge=~rxd_reg2&rxd_reg3;
assign rx_data_byte=rx_data_byte_reg;
assign clk_badrate=baud_rat_clk;
always @(posedge clock)
begin
rxd_reg1<=rxd;
rxd_reg2<=rxd_reg1;
rxd_reg3<=rxd_reg2;
end
always @(posedge clock or negedge rst_n)
begin
if(rst_n==0)
begin
baud_counter<=0;
baud_rat_clk=0;
end
else begin
rx_valid_flag_pre2<=rx_valid_flag_pre1;
rx_valid_flag_pre1=rx_valid_flag;
baud_counter<=baud_counter+1;
if(baud_counter==UART_COUTER_MAX)
begin
baud_counter<=0;
baud_rat_clk<=~baud_rat_clk;
end
end
end
always @(posedge clock or negedge rst_n)
begin
if(rst_n==0)rx_counter<=0;
else begin
if(rx_start_flag==1)
begin
rx_counter<=rx_counter+1;
if(rx_counter==UART_COUTER_MAX)rx_counter<=0;
end
end
end
always @(posedge clock or negedge rst_n)
begin
if(rst_n==0)rx_bit_cnt<=0;
else
begin
if(rx_counter==UART_COUTER_MAX)
begin
rx_bit_cnt<=rx_bit_cnt+1;
end
if(rx_bit_cnt==UART_BIT+1)rx_bit_cnt<=0;
if(rx_bit_cnt==UART_BIT+1)rx_data_byte_reg<=rx_data;
end
end
always @(posedge clock or negedge rst_n)
begin
if(rst_n==0)
begin
rx_data<=0;
end
else begin
if(rx_counter==UART_COUTER_MAX/2)
begin
if((rx_bit_cnt>=1)&&(rx_bit_cnt<UART_BIT+1))
begin
rx_data<={rxd,rx_data[7:1]};
end
end
end
end
always @(posedge clock or negedge rst_n)
begin
if(rst_n==0)
begin
rx_start_flag<=0;
end
else
begin
if(rxd_negedge==1)rx_start_flag<=1;
else if(rx_bit_cnt==UART_BIT+1)rx_start_flag<=0;
end
end
endmodule
发送部分代码
发送代码比接收简单。就是需要发送的数据,当发送触发为1,则触发发送。波特率计数器开始计数,先产生一个bit起始拉低信号,然后依次将8bit数据移位发送出去,最后回到空闲状态。
module usart_tx(
input wire clock,
output wire txd,
input wire rst_n,
input wire [7:0]tx_data_byte,
input wire tx_triger_flag
);
parameter CLOCK_FRQ=50_000_000;
parameter BADRATE =115_200;
parameter UART_COUTER_MAX=CLOCK_FRQ/BADRATE;
parameter UART_BIT =8;
reg [7:0]tx_bit_cnt_reg;
reg [19:0]tx_counter;
reg [19:0]baud_counter;
reg txd_reg;
reg tx_start_flag;
assign txd=txd_reg;
always @(posedge clock)
begin
if(tx_start_flag==1)
begin
case (tx_bit_cnt_reg)
0:txd_reg<=0;
1:txd_reg<=tx_data_byte[0];
2:txd_reg<=tx_data_byte[1];
3:txd_reg<=tx_data_byte[2];
4:txd_reg<=tx_data_byte[3];
5:txd_reg<=tx_data_byte[4];
6:txd_reg<=tx_data_byte[5];
7:txd_reg<=tx_data_byte[6];
8:txd_reg<=tx_data_byte[7];
9:txd_reg<=1;
default:txd_reg<=1;
endcase
end
else txd_reg<=1;
end
always @(posedge tx_triger_flag or posedge clock)
begin
if(tx_triger_flag==1)tx_start_flag<=1;
else begin
if(tx_bit_cnt_reg==UART_BIT+1)tx_start_flag<=0;
end
end
always @(posedge clock or negedge rst_n)
begin
if(rst_n==0)
begin
tx_counter<=0;
end
else begin
if(tx_start_flag==1)begin
if(tx_counter<UART_COUTER_MAX)tx_counter<=tx_counter+1;
else begin
tx_counter<=0;
end
end
end
end
always @(posedge clock or negedge rst_n)
begin
if(rst_n==0)
begin
tx_bit_cnt_reg<=0;
end
else begin
if(tx_counter==UART_COUTER_MAX)
begin
if(tx_bit_cnt_reg<UART_BIT+1)tx_bit_cnt_reg<=tx_bit_cnt_reg+1;
end
else begin
if(tx_bit_cnt_reg==UART_BIT+1)tx_bit_cnt_reg<=0;
end
end
end
endmodule
本文 zblog模板 原创,转载保留链接!网址:http://www.xn--zqqs03dbu6a.cn/?id=47
1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。