第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
三个模块对应信号连接文章来源:https://www.toymoban.com/news/detail-788534.html
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 开发板的,并没有跑起来文章来源地址https://www.toymoban.com/news/detail-788534.html
到了这里,关于UART 串口通信的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!