第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) beginif(rst) beginuart_rxd_cur <= 1'b0;uart_rxd_pre <= 1'b0;endelse beginuart_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) beginif(rst)rx_flag <= 1'b0;else beginif(start_flag)rx_flag <= 1'b1;else if(rx_cnt == 4'd9 && (clk_cnt == BPS_CNT/2))// 8个bit数据传输完成,且经过半个波特的停止位rx_flag <= 1'b0;elserx_flag <= rx_flag;end
end// clk_cnt 计数
always @(posedge clk or posedge rst) beginif(rst)clk_cnt <= 1'b0;else if(rx_flag) beginif(clk_cnt < BPS_CNT - 1)clk_cnt <= clk_cnt + 1'b1;elseclk_cnt <= 9'd0;endelseclk_cnt <= 9'b0;
end// rx_cnt 根据 clk_cnt 计数
always @(posedge clk or posedge rst) beginif(rst)rx_cnt <= 4'd0;else if(rx_flag) beginif(clk_cnt == BPS_CNT - 1)rx_cnt <= rx_cnt + 1'b1;elserx_cnt <= rx_cnt;endelserx_cnt <= 4'b0;end// 在中间值的时候赋值
reg [7:0] rx_data; // 临时寄存器(寄存数据)
always @(posedge clk or posedge rst) beginif(rst)rx_data <= 8'd0;else if(rx_flag) beginif(clk_cnt == BPS_CNT / 2) beginrx_data[rx_cnt - 4'b1] <= uart_rxd_pre;endelserx_data <= rx_data;endelserx_data <= 8'd0;
end// 设置输出数据uart_data和输出完成信号uart_done
always @(posedge clk or posedge rst) beginif(rst) beginuart_data <= 8'd0;uart_done <= 1'b0;endelse if(rx_cnt == 4'd9) beginuart_data <= rx_data;uart_done <= 1'b1;endelse beginuart_data <= 8'd0;uart_done <= 1'b0;end
endendmodule
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) beginif(rst) beginuart_en_pre <= 1'b0;uart_en_cur <= 1'b0;endelse beginuart_en_cur <= uart_en;uart_en_pre <= uart_en_cur; end
endreg [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) beginif(rst)clk_cnt <= 1'b0;else if(tx_flag) beginif(clk_cnt < BPS_CNT - 1)clk_cnt <= clk_cnt + 1'b1;elseclk_cnt <= 9'd0;endelseclk_cnt <= 9'b0;
end// tx_cnt 根据 clk_cnt 计数
always @(posedge clk or posedge rst) beginif(rst)tx_cnt <= 4'd0;else if(tx_flag) beginif(clk_cnt == BPS_CNT - 1)tx_cnt <= tx_cnt + 1'b1;elsetx_cnt <= tx_cnt;endelsetx_cnt <= 4'b0;endalways @(posedge clk or posedge rst) beginif(rst) begintx_flag <= 1'b0;tx_data <= 8'd0;endelse beginif(en_flag) begin // 写使能tx_flag <= 1'b1; // 写标志tx_data <= uart_din; // 暂存数据endelse if(tx_cnt == 4'd9 && clk_cnt == (BPS_CNT-BPS_CNT/16)) begin// 传输结束tx_flag <= 1'b0;tx_data <= 8'd0;endelse begintx_flag <= tx_flag;tx_data <= tx_data;endend
end// uart_txd 传输数据
always @(posedge clk or posedge rst) beginif(rst)uart_txd <= 1'b1;else if(tx_flag) beginif(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)endelse uart_txd <= 1'b1;
endendmodule
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 开发板的,并没有跑起来