第18.1讲 UART串口通信原理讲解_哔哩哔哩_bilibili
并行通信
一个周期同时发送8bit的数据,占用引脚资源多

串行通信

串行通信的通信方式:
- 同步通信
 
同一时钟下进行数据传输

- 异步通信
 
发送设备和接收设备的时钟不同
但是需要约束波特率(1s内传输的bit数)

串行通信的传输方向:

常见串行通信接口:

UART
UART(universal asynchronous receiver-transmitter):通用异步收发传输器
异步串行通信
功能:
- 发送数据时将并行数据转换为串行数据进行传输
 - 接收数据时将串行数据转换为并行行数据进行传输
 
协议层
数据格式:

校验位:奇偶校验
UART使用两根信号线实现,一根用于串口发送,另一根负责串口接收
传输速率 波特率
串口通信的速率用波特率表示,它表示每秒传输的二进制数据的位数,单位为bps(位/秒)
9600 19200 38400…
1s=109ns1 s = 10^9 ns 1s=109ns
假设波特率是115200 bit/s
那么发送一个bit需要 10910^9109/ 115200 ns
当频率为50Hz的时候,一个周期为 20 ns
发送一个bit需要的周期数为: 109/115200/2010^9/115200/20109/115200/20= 434
拉低的起始位,拉高的数据为,校验位,停止位都需要434个周期
物理层:接口标准

负逻辑电平:
1对应负电压,0对应正电压
3线:TX RX GND
差分传输:

RS232

DB9接口定义

USB接口

Data -/+ 差分信号
实验
实验任务
开发板与上位机通过串口通信,完成数据环回实验

程序设计

串口接收、发送:

uart_recv
串行转并行
module uart_recv (
    input               clk,
    input               rst,
    input               uart_rxd,
    output reg [7:0]    uart_data,
    output reg          uart_done
);
// 抓取接收信号下降沿(获取数据接收的标志)
reg uart_rxd_cur, uart_rxd_pre;
wire start_flag;
assign start_flag = ~uart_rxd_cur & uart_rxd_pre;
always @(posedge clk or posedge rst) begin
    if(rst) begin
        uart_rxd_cur <= 1'b0;
        uart_rxd_pre <= 1'b0;
    end
    else begin
        uart_rxd_cur <= uart_rxd;
        uart_rxd_pre <= uart_rxd_cur;
    end
end
// 定义常量
parameter CLK_FREQ = 50000000;
parameter UART_BPS = 115200;
// 不可更改的常量
localparam BPS_CNT = CLK_FREQ / UART_BPS;
reg rx_flag;
reg [3:0] rx_cnt;
reg [8:0] clk_cnt;
always @(posedge clk or posedge rst) begin
    if(rst)
        rx_flag <= 1'b0;
    else begin
        if(start_flag)
            rx_flag <= 1'b1;
        else if(rx_cnt == 4'd9 && (clk_cnt == BPS_CNT/2))
            // 8个bit数据传输完成,且经过半个波特的停止位
            rx_flag <= 1'b0;
        else
            rx_flag <= rx_flag;
    end
end
// clk_cnt 计数
always @(posedge clk or posedge rst) begin
    if(rst)
        clk_cnt <= 1'b0;
    else if(rx_flag) begin
        if(clk_cnt < BPS_CNT - 1)
            clk_cnt <= clk_cnt + 1'b1;
        else
            clk_cnt <= 9'd0;
    end
    else
        clk_cnt <= 9'b0;
end
// rx_cnt 根据 clk_cnt 计数
always @(posedge clk or posedge rst) begin
    if(rst)
        rx_cnt <= 4'd0;
    else if(rx_flag) begin
        if(clk_cnt == BPS_CNT - 1)
            rx_cnt <= rx_cnt + 1'b1;
        else
            rx_cnt <= rx_cnt;
    end
    else
        rx_cnt <= 4'b0;
end
// 在中间值的时候赋值
reg [7:0] rx_data; // 临时寄存器(寄存数据)
always @(posedge clk or posedge rst) begin
    if(rst)
        rx_data <= 8'd0;
    else if(rx_flag) begin
        if(clk_cnt == BPS_CNT / 2) begin
            rx_data[rx_cnt - 4'b1] <= uart_rxd_pre;
        end
        else
            rx_data <= rx_data;
    end
    else
        rx_data <= 8'd0;
end
// 设置输出数据uart_data和输出完成信号uart_done
always @(posedge clk or posedge rst) begin
    if(rst) begin
        uart_data <= 8'd0;
        uart_done <= 1'b0;
    end
    else if(rx_cnt == 4'd9) begin
        uart_data <= rx_data;
        uart_done <= 1'b1;
    end
    else begin
        uart_data <= 8'd0;
        uart_done <= 1'b0;
    end
end
endmodule
 
uart_send
并行转串行
module uart_send (
    input   clk,
    input   rst,
    input   uart_en,
    input   [7:0] uart_din,
    output  reg uart_txd,
    output  uart_rx_busy
);
// 抓取uart_en上升沿
reg uart_en_pre, uart_en_cur;
wire en_flag;
assign en_flag = ~uart_en_pre & uart_en_cur;
always @(posedge clk or posedge rst) begin
    if(rst) begin
        uart_en_pre <= 1'b0;
        uart_en_cur <= 1'b0;
    end
    else begin
        uart_en_cur <= uart_en;
        uart_en_pre <= uart_en_cur; 
    end
end
reg [7:0] tx_data;
reg tx_flag;
reg [3:0] tx_cnt;
reg [8:0] clk_cnt;
// 定义常量
parameter CLK_FREQ = 50000000;
parameter UART_BPS = 115200;
// 不可更改的常量
localparam BPS_CNT = CLK_FREQ / UART_BPS;
// 写信号忙
assign uart_rx_busy = tx_flag;
// clk_cnt 计数
always @(posedge clk or posedge rst) begin
    if(rst)
        clk_cnt <= 1'b0;
    else if(tx_flag) begin
        if(clk_cnt < BPS_CNT - 1)
            clk_cnt <= clk_cnt + 1'b1;
        else
            clk_cnt <= 9'd0;
    end
    else
        clk_cnt <= 9'b0;
end
// tx_cnt 根据 clk_cnt 计数
always @(posedge clk or posedge rst) begin
    if(rst)
        tx_cnt <= 4'd0;
    else if(tx_flag) begin
        if(clk_cnt == BPS_CNT - 1)
            tx_cnt <= tx_cnt + 1'b1;
        else
            tx_cnt <= tx_cnt;
    end
    else
        tx_cnt <= 4'b0;
end
always @(posedge clk or posedge rst) begin
    if(rst) begin
        tx_flag <= 1'b0;
        tx_data <= 8'd0;
    end
    else begin
       if(en_flag) begin    // 写使能
            tx_flag <= 1'b1;    // 写标志
            tx_data <= uart_din; // 暂存数据
       end
       else if(tx_cnt == 4'd9 && clk_cnt == (BPS_CNT-BPS_CNT/16)) begin
            // 传输结束
            tx_flag <= 1'b0;
            tx_data <= 8'd0;
       end
       else begin
            tx_flag <= tx_flag;
            tx_data <= tx_data;
       end
    end
end
// uart_txd 传输数据
always @(posedge clk or posedge rst) begin
    if(rst)
        uart_txd <= 1'b1;
    else if(tx_flag) begin
        if(tx_cnt == 4'd0) uart_txd <= 1'b0; // start bit拉低
        else if(tx_cnt == 4'd9) uart_txd <= 1'b1; // stop bit拉低
        else uart_txd <= tx_data[tx_cnt - 4'b1]; // 传输数据(cnt比bit位计数多1)
    end
    else uart_txd <= 1'b1;
end
endmodule
 
uart_loopback_top
三个模块对应信号连接
module uart_loopback_top(
    input  sys_clk,
    input  sys_rst,
    input  uart_rxd,
    output uart_txd
);
wire uart_en;
wire [7:0] uart_din;
wire [7:0] uart_data;
wire uart_done;
wire uart_rx_busy;
uart_recv uart_recv_u(
    .clk        (sys_clk),
    .rst        (sys_rst),
    .uart_rxd   (uart_rxd),
    .uart_data  (uart_data),
    .uart_done  (uart_done) 
);
uart_send uart_send_u(
    .clk            (sys_clk),
    .rst            (sys_rst),
    .uart_en        (uart_en),
    .uart_din       (uart_din),
    .uart_txd       (uart_txd),
    .uart_rx_busy   (uart_rx_busy)
);
uart_loop uart_loop_u(
    .clk         (sys_clk),
    .rst         (sys_rst),
    .recv_done   (uart_done),
    .recv_data   (uart_data),
    .tx_busy     (uart_rx_busy),
    .send_en     (uart_en),
    .send_data   (uart_din)
);
endmodule
 
约束
create_clock -period 20.000 -name clk [get_ports {sys_clk}]
#Clock signal
set_property -dict { PACKAGE_PIN L16   IOSTANDARD LVCMOS33 } [get_ports { sys_clk }];
#Buttons
set_property -dict { PACKAGE_PIN R18   IOSTANDARD LVCMOS33 } [get_ports { rst }];
set_property -dict { PACKAGE_PIN B12   IOSTANDARD LVCMOS18 } [get_ports { uart_rxd }];
set_property -dict { PACKAGE_PIN C12   IOSTANDARD LVCMOS18 } [get_ports { uart_txd }];
 
这里的约束找不到对应的 zybo 开发板的,并没有跑起来


















