串口通信的速率用波特率表示,它表示每秒传输二进制数据的位数,单位是 bps(位/秒),常用的波特率有 9600、19200、38400、57600 以及 115200 等。
- `timescale 1ns / 1ps
-
- module uart_loopback_top (
- input sys_clk_p, //系统差分输入时钟
- input sys_clk_n, //系统差分输入时钟
- input sys_rst_n, //外部复位信号,低有效
-
- input uart_rxd, //UART接收端口
- output uart_txd //UART发送端口
- );
-
- //parameter define
- parameter CLK_FREQ = 100000000; //定义系统时钟频率
- parameter UART_BPS = 115200; //定义串口波特率
-
- //wire define
- wire uart_recv_done; //UART接收完成
- wire [7:0] uart_recv_data; //UART接收数据
- wire uart_send_en; //UART发送使能
- wire [7:0] uart_send_data; //UART发送数据
- wire uart_tx_busy; //UART发送忙状态标志
-
- //*****************************************************
- //** main code
- //*****************************************************
-
- //转换差分信号
- IBUFDS diff_clock (
- .I (sys_clk_p), //系统差分输入时钟
- .IB(sys_clk_n), //系统差分输入时钟
- .O (sys_clk) //输出系统时钟
- );
-
- //串口接收模块
- uart_recv #(
- .CLK_FREQ(CLK_FREQ), //设置系统时钟频率
- .UART_BPS(UART_BPS)
- )
- //设置串口接收波特率
- u_uart_recv (
- .sys_clk (sys_clk),
- .sys_rst_n(sys_rst_n),
-
- .uart_rxd (uart_rxd),
- .uart_done(uart_recv_done),
- .uart_data(uart_recv_data)
- );
-
- //串口发送模块
- uart_send #(
- .CLK_FREQ(CLK_FREQ), //设置系统时钟频率
- .UART_BPS(UART_BPS)
- )
- //设置串口发送波特率
- u_uart_send (
- .sys_clk (sys_clk),
- .sys_rst_n(sys_rst_n),
-
- .uart_en (uart_send_en),
- .uart_din (uart_send_data),
- .uart_tx_busy(uart_tx_busy),
- .uart_txd (uart_txd)
- );
-
- //串口环回模块
- uart_loop u_uart_loop (
- .sys_clk (sys_clk),
- .sys_rst_n(sys_rst_n),
-
- .recv_done(uart_recv_done), //接收一帧数据完成标志信号
- .recv_data(uart_recv_data), //接收的数据
-
- .tx_busy (uart_tx_busy), //发送忙状态标志
- .send_en (uart_send_en), //发送使能信号
- .send_data(uart_send_data) //待发送数据
- );
-
- endmodule
uart_recv.v
uart_recv 为串口接收模块,从串口接收端口 uart_rxd 来接收上位机发送的串行数据,将其转化为并行数据,并在一帧数据接收结束后给出通知信号 uart_done。
- `timescale 1ns / 1ps
-
-
- module uart_recv (
- input sys_clk, //系统时钟
- input sys_rst_n, //系统复位,低电平有效
-
- input uart_rxd, //UART接收端口
- output reg uart_done, //接收一帧数据完成标志
- output reg [7:0] uart_data //接收的数据
- );
-
- //parameter define
- parameter CLK_FREQ = 100000000; //定义系统时钟频率
- parameter UART_BPS = 115200; //定义串口波特率
- localparam BPS_CNT = CLK_FREQ / UART_BPS; //为得到指定波特率,
- //需要对系统时钟计数BPS_CNT次
- //reg define
- reg uart_rxd_d0;
- reg uart_rxd_d1;
- reg [15:0] clk_cnt; //系统时钟计数器
- reg [ 3:0] rx_cnt; //接收数据计数器
- reg rx_flag; //接收过程标志信号
- reg [ 7:0] rxdata; //接收数据寄存器
-
- //wire define
- wire start_flag;
-
- //*****************************************************
- //** main code
- //*****************************************************
- //捕获接收端口下降沿(起始位),得到一个时钟周期的脉冲信号
- assign start_flag = uart_rxd_d1 & (~uart_rxd_d0);
-
- //对UART接收端口的数据延迟两个时钟周期
- always @(posedge sys_clk or negedge sys_rst_n) begin
- if (!sys_rst_n) begin
- uart_rxd_d0 <= 1'b0;
- uart_rxd_d1 <= 1'b0;
- end else begin
- uart_rxd_d0 <= uart_rxd;
- uart_rxd_d1 <= uart_rxd_d0;
- end
- end
-
- //当脉冲信号start_flag到达时,进入接收过程
- always @(posedge sys_clk or negedge sys_rst_n) begin
- if (!sys_rst_n) rx_flag <= 1'b0;
- else begin
- if (start_flag) //检测到起始位
- rx_flag <= 1'b1; //进入接收过程,标志位rx_flag拉高
- //计数到停止位中间时,停止接收过程
- else if ((rx_cnt == 4'd9) && (clk_cnt == BPS_CNT / 2))
- rx_flag <= 1'b0; //接收过程结束,标志位rx_flag拉低
- else rx_flag <= rx_flag;
- end
- end
-
- //进入接收过程后,启动系统时钟计数器
- always @(posedge sys_clk or negedge sys_rst_n) begin
- if (!sys_rst_n) clk_cnt <= 16'd0;
- else if (rx_flag) begin //处于接收过程
- if (clk_cnt < BPS_CNT - 1) clk_cnt <= clk_cnt + 1'b1;
- else clk_cnt <= 16'd0; //对系统时钟计数达一个波特率周期后清零
- end else clk_cnt <= 16'd0; //接收过程结束,计数器清零
- end
-
- //进入接收过程后,启动接收数据计数器
- always @(posedge sys_clk or negedge sys_rst_n) begin
- if (!sys_rst_n) rx_cnt <= 4'd0;
- else if (rx_flag) begin //处于接收过程
- if (clk_cnt == BPS_CNT - 1) //对系统时钟计数达一个波特率周期
- rx_cnt <= rx_cnt + 1'b1; //此时接收数据计数器加1
- else rx_cnt <= rx_cnt;
- end else rx_cnt <= 4'd0; //接收过程结束,计数器清零
- end
- //根据接收数据计数器来寄存uart接收端口数据
- always @(posedge sys_clk or negedge sys_rst_n) begin
- if (!sys_rst_n) rxdata <= 8'd0;
- else if (rx_flag) //系统处于接收过程
- if (clk_cnt == BPS_CNT / 2) begin //判断系统时钟计数器计数到数据位中间
- case (rx_cnt)
- 4'd1: rxdata[0] <= uart_rxd_d1; //寄存数据位最低位
- 4'd2: rxdata[1] <= uart_rxd_d1;
- 4'd3: rxdata[2] <= uart_rxd_d1;
- 4'd4: rxdata[3] <= uart_rxd_d1;
- 4'd5: rxdata[4] <= uart_rxd_d1;
- 4'd6: rxdata[5] <= uart_rxd_d1;
- 4'd7: rxdata[6] <= uart_rxd_d1;
- 4'd8: rxdata[7] <= uart_rxd_d1; //寄存数据位最高位
- default: ;
- endcase
- end else rxdata <= rxdata;
- else rxdata <= 8'd0;
- end
- //数据接收完毕后给出标志信号并寄存输出接收到的数据
- always @(posedge sys_clk or negedge sys_rst_n) begin
- if (!sys_rst_n) begin
- uart_data <= 8'd0;
- uart_done <= 1'b0;
- end else if (rx_cnt == 4'd9) begin //接收数据计数器计数到停止位时
- uart_data <= rxdata; //寄存输出接收到的数据
- uart_done <= 1'b1; //并将接收完成标志位拉高
- end else begin
- uart_data <= 8'd0;
- uart_done <= 1'b0;
- end
- end
- endmodule
uart_send.v
- `timescale 1ns / 1ps
-
-
- module uart_send (
- input sys_clk, //系统时钟
- input sys_rst_n, //系统复位,低电平有效
-
- input uart_en, //发送使能信号
- input [7:0] uart_din, //待发送数据
- output uart_tx_busy, //发送忙状态标志
- output reg uart_txd //UART发送端口
- );
-
- //parameter define
- parameter CLK_FREQ = 100000000; //定义系统时钟频率
- parameter UART_BPS = 115200; //定义串口波特率
- localparam BPS_CNT = CLK_FREQ/UART_BPS; //为得到指定波特率,对系统时钟计数BPS_CNT次
-
- //reg define
- reg uart_en_d0;
- reg uart_en_d1;
- reg [15:0] clk_cnt; //系统时钟计数器
- reg [ 3:0] tx_cnt; //发送数据计数器
- reg tx_flag; //发送过程标志信号
- reg [ 7:0] tx_data; //寄存发送数据
-
- //wire define
- wire en_flag;
-
- //*****************************************************
- //** main code
- //*****************************************************
- //在串口发送过程中给出忙状态标志
- assign uart_tx_busy = tx_flag;
-
- //捕获uart_en上升沿,得到一个时钟周期的脉冲信号
- assign en_flag = (~uart_en_d1) & uart_en_d0;
-
- //对发送使能信号uart_en延迟两个时钟周期
- always @(posedge sys_clk or negedge sys_rst_n) begin
- if (!sys_rst_n) begin
- uart_en_d0 <= 1'b0;
- uart_en_d1 <= 1'b0;
- end else begin
- uart_en_d0 <= uart_en;
- uart_en_d1 <= uart_en_d0;
- end
- end
-
- //当脉冲信号en_flag到达时,寄存待发送的数据,并进入发送过程
- always @(posedge sys_clk or negedge sys_rst_n) begin
- if (!sys_rst_n) begin
- tx_flag <= 1'b0;
- tx_data <= 8'd0;
- end else if (en_flag) begin //检测到发送使能上升沿
- tx_flag <= 1'b1; //进入发送过程,标志位tx_flag拉高
- tx_data <= uart_din; //寄存待发送的数据
- end //计数到停止位结束时,停止发送过程
- else if ((tx_cnt == 4'd9) && (clk_cnt == BPS_CNT - (BPS_CNT / 16))) begin
- tx_flag <= 1'b0; //发送过程结束,标志位tx_flag拉低
- tx_data <= 8'd0;
- end else begin
- tx_flag <= tx_flag;
- tx_data <= tx_data;
- end
- end
-
- //进入发送过程后,启动系统时钟计数器
- always @(posedge sys_clk or negedge sys_rst_n) begin
- if (!sys_rst_n) clk_cnt <= 16'd0;
- else if (tx_flag) begin //处于发送过程
- if (clk_cnt < BPS_CNT - 1) clk_cnt <= clk_cnt + 1'b1;
- else clk_cnt <= 16'd0; //对系统时钟计数达一个波特率周期后清零
- end else clk_cnt <= 16'd0; //发送过程结束
- end
-
- //进入发送过程后,启动发送数据计数器
- always @(posedge sys_clk or negedge sys_rst_n) begin
- if (!sys_rst_n) tx_cnt <= 4'd0;
- else if (tx_flag) begin //处于发送过程
- if (clk_cnt == BPS_CNT - 1) //对系统时钟计数达一个波特率周期
- tx_cnt <= tx_cnt + 1'b1; //此时发送数据计数器加1
- else tx_cnt <= tx_cnt;
- end else tx_cnt <= 4'd0; //发送过程结束
- end
- //根据发送数据计数器来给uart发送端口赋值
- always @(posedge sys_clk or negedge sys_rst_n) begin
- if (!sys_rst_n) uart_txd <= 1'b1;
- else if (tx_flag)
- case (tx_cnt)
- 4'd0: uart_txd <= 1'b0; //起始位
- 4'd1: uart_txd <= tx_data[0]; //数据位最低位
- 4'd2: uart_txd <= tx_data[1];
- 4'd3: uart_txd <= tx_data[2];
- 4'd4: uart_txd <= tx_data[3];
- 4'd5: uart_txd <= tx_data[4];
- 4'd6: uart_txd <= tx_data[5];
- 4'd7: uart_txd <= tx_data[6];
- 4'd8: uart_txd <= tx_data[7]; //数据位最高位
- 4'd9: uart_txd <= 1'b1; //停止位
- default: ;
- endcase
- else uart_txd <= 1'b1; //空闲时发送端口为高电平
- end
- endmodule
uart_loop.v
uart_loop 模块负责完成串口数据的环回功能。它在 uart_recv 模块接收完成后,将接收到的串口数据发送到 uart_send 模块,并通过 send_en 接口给出一个上升沿,以启动发送过程。
- `timescale 1ns / 1ps
-
-
- module uart_loop (
- input sys_clk, //系统时钟
- input sys_rst_n, //系统复位,低电平有效
-
- input recv_done, //接收一帧数据完成标志
- input [7:0] recv_data, //接收的数据
-
- input tx_busy, //发送忙状态标志
- output reg send_en, //发送使能信号
- output reg [7:0] send_data //待发送数据
- );
-
- //reg define
- reg recv_done_d0;
- reg recv_done_d1;
- reg tx_ready;
-
- //wire define
- wire recv_done_flag;
-
- //*****************************************************
- //** main code
- //*****************************************************
-
- //捕获recv_done上升沿,得到一个时钟周期的脉冲信号
- assign recv_done_flag = (~recv_done_d1) & recv_done_d0;
-
- //对发送使能信号recv_done延迟两个时钟周期
- always @(posedge sys_clk or negedge sys_rst_n) begin
- if (!sys_rst_n) begin
- recv_done_d0 <= 1'b0;
- recv_done_d1 <= 1'b0;
- end else begin
- recv_done_d0 <= recv_done;
- recv_done_d1 <= recv_done_d0;
- end
- end
-
- //判断接收完成信号,并在串口发送模块空闲时给出发送使能信号
- always @(posedge sys_clk or negedge sys_rst_n) begin
- if (!sys_rst_n) begin
- tx_ready <= 1'b0;
- send_en <= 1'b0;
- send_data <= 8'd0;
- end else begin
- if (recv_done_flag) begin //检测串口接收到数据
- tx_ready <= 1'b1; //准备启动发送过程
- send_en <= 1'b0;
- send_data <= recv_data; //寄存串口接收的数据
- end else if (tx_ready && (~tx_busy)) begin //检测串口发送模块空闲
- tx_ready <= 1'b0; //准备过程结束
- send_en <= 1'b1; //拉高发送使能信号
- end else begin
- tx_ready <= tx_ready;
- send_en <= send_en;
- send_data <= send_data;
- end
- end
- end
- endmodule
- #IO管脚约束
- #时钟周期约束
- create_clock -period 10.000 -name sys_clk_p [get_ports sys_clk_p]
- #时钟
- set_property IOSTANDARD DIFF_HSTL_I_12 [get_ports sys_clk_p]
- set_property IOSTANDARD DIFF_HSTL_I_12 [get_ports sys_clk_n]
- set_property PACKAGE_PIN AE5 [get_ports sys_clk_p]
- set_property PACKAGE_PIN AF5 [get_ports sys_clk_n]
- #复位
- set_property -dict {PACKAGE_PIN AH11 IOSTANDARD LVCMOS33} [get_ports sys_rst_n]
- #接收发送
- set_property -dict {PACKAGE_PIN AB9 IOSTANDARD LVCMOS33} [get_ports uart_rxd]
- set_property -dict {PACKAGE_PIN AB10 IOSTANDARD LVCMOS33} [get_ports uart_txd]