wordpress 自建cdn,烟台网站排名优化价格,电影网页设计报告,西安百度关键词排名公司UART 串口简介
常用的通信方式可分为为串行通信#xff08;serial communication#xff09;和并行通信#xff08;parallel communication#xff09;两种。并行通信是多比特数据同时通过并行线进行传送#xff08;一般以字或字节为单位并行进行传输#xff09;#x…UART 串口简介
常用的通信方式可分为为串行通信serial communication和并行通信parallel communication两种。并行通信是多比特数据同时通过并行线进行传送一般以字或字节为单位并行进行传输这种传输方式用的线多、成本高、速率高、要考虑时延匹配不宜进行长距离通信因此并行通信一般用于板载外设的通信。串行通信是数据在一条线上一比特接一比特地按顺序进行传送这种传输方式线少、成本低、速率低事宜用于长距离通信因此串行通信常用于不同设备之间的通信。 串行通信可分为同步串行通信synchronized serial communication和异步串行通信asynchronous serial communication。同步串行通信需要通信双方在同一时钟的控制下同步传输数据如SPI、IIC等而异步串行通信只需要通信双方约定好通信速率、起始标志、结束标志然后在各自的时钟的控制下进行数据传输如UART等。 UART 是一种采用异步串行通信方式的通用异步收发传输器universal asynchronous receivertransmitter它在发送数据时将并行数据转换成串行数据来传输在接收数据时将接收到的串行数据转换成并行数据。UART 通信需要两根信号线来实现一根用于串口发送另外一根负责串口接收。
UART传输时序
UART 在发送或接收过程中的一帧数据由 4 部分组成起始位、数据位、奇偶校验位和停止位 起始位不传输数据时 UART 数据传输线通常保持高电压电平。若要开始数据传输发送方会将传输线从高电平拉到低电平并保持 1 个波特率周期。当接收方检测到高电平到低电平的跃迁时便开始以波特率对应的频率读取数据帧中的位。 数据帧数据帧包含所传输的实际数据。如果使用奇偶校验位数据帧长度可以是 5 位到 8 位如果不使用奇偶校验位数据帧长度可以是 9 位。在大多数情况下数据以最低有效位优先方式发送。 奇偶校验接收方可以通过奇偶校验位来判断数据传输期间是否发生改变奇校验就是确保数据位和校验位中 1 的个数为奇数偶校验就是确保数据位和校验位中 1 的个数为偶数。 停止位表示数据包结束发送方将数据传输线从拉到高并保持 1 到 2 位的时间。 在进行UART通信时双方需要约定好数据位数可选择 5、6、7、8 位、奇偶校验可选择奇校验、偶校验、无校验、停止位可选择 1 、1.5 、 2 位、波特率每秒传输二进制数据的位数。
UART 硬件
在设置好数据格式及波特率之后UART 只负责完成数据的串并转换而信号的传输则由外部驱动电路实现。电信号的传输过程有着不同的电平标准和接口规范针对异步串行通信的接口标准有TTL、 RS232、RS422、RS485 等它们定义了接口的电气特性如 RS-232 是单端输入输出而 RS-422/485 为差分输入输出等。其中RS232是最常见的标准之一它一般采用DB9类型的接口如下图所示
硬件设计 原理图中的 CH340 就是转换芯片用于将 TTL 标准的 UART 转换为 USB 其中 UART1_RXD 连接到 FPGA 芯片的的 RX (E14)管脚UART1_TXD 连接到 FPGA 芯片的 TXD17 管脚CH340_P/N 分别接到开发板的 TYPE - C 插座 D/-上。
系统框图
本次实验任务是实现串口数据的回环以此整个设计包括两个子模块分别用于串口的发送和接收其系统框图如下
代码编写
串口接收模块设计
串口接收模块的框图如下 其时序图如下 代码如下
module uart_rx #(parameter CLK_FREQ 50000000, //系统时钟parameter UART_BPS 115200 //波特率
)(input sys_clk, //系统时钟input sys_rst_n, //系统复位input uart_rxd, //串口接收线output reg data_flag, //数据有效标志随数据一起输出只保持一个时钟周期output reg [7:0] rx_data //串口接收到的数据
);localparam BAUD_CNT_MAX CLK_FREQ/UART_BPS; //波特率计数器的计数周期//串口输入延迟一拍用于捕获上升沿
reg uart_rxd_d0;
//启动标志在空闲状态检测到开始信号拉高
wire start_flag;
//接收忙标志接收过程中拉高
reg rx_busy;
//波特率计数器
reg [31:0] baud_count;
//接收计数
reg [3:0] rx_count;
//串口接收移位寄存器
reg [7:0] uart_rx_data;//延迟一拍用于捕获下降沿
always (posedge sys_clk) beginif(!sys_rst_n)uart_rxd_d0 1b1;elseuart_rxd_d0 uart_rxd;
end//捕获下降沿在下降沿时刻且接收空闲则拉高start_flag
assign start_flag ((uart_rxd_d0 ~uart_rxd ~rx_busy)) ? 1 : 0;//接收忙标志控制
//检测到start_flag时设置为忙当接收到8bit数据不包括启动位且波特率计数器溢出时设置为空闲
always (posedge sys_clk) beginif(!sys_rst_n)rx_busy 1b0;else if(start_flag 1b1)rx_busy 1b1;else if((rx_count 4d8) (baud_count (BAUD_CNT_MAX - 1)))rx_busy 1b0;
end//波特率计数器当接收处于忙状态时进行周期计数
always (posedge sys_clk) beginif(!sys_rst_n)baud_count 0;else if(rx_busy 1b1) beginif(baud_count (BAUD_CNT_MAX - 1))baud_count baud_count 1;elsebaud_count 0;endelsebaud_count 0;
end//接收计数器当接收忙且波特率计数器溢出时进行递增计数
always (posedge sys_clk) beginif(!sys_rst_n)rx_count 4b0;else if(rx_busy 1b1) beginif(baud_count (BAUD_CNT_MAX - 1))rx_count rx_count 4b1;endelserx_count 4b0;
end//将数据采集到移位寄存器中
always (posedge sys_clk) beginif(!sys_rst_n)uart_rx_data 8b0;else if(rx_busy 1b1) beginif(baud_count ((BAUD_CNT_MAX - 1) / 2)) begincase(rx_count)4d1: uart_rx_data[0] uart_rxd_d0; //bit04d2: uart_rx_data[1] uart_rxd_d0; //bit14d3: uart_rx_data[2] uart_rxd_d0; //bit24d4: uart_rx_data[3] uart_rxd_d0; //bit34d5: uart_rx_data[4] uart_rxd_d0; //bit44d6: uart_rx_data[5] uart_rxd_d0; //bit54d7: uart_rx_data[6] uart_rxd_d0; //bit64d8: uart_rx_data[7] uart_rxd_d0; //bit7default: ;endcaseendendelseuart_rx_data 8b0;
end//数据有效标志输出
always (posedge sys_clk) beginif(!sys_rst_n)data_flag 1b0;else if((rx_count 8) (baud_count (BAUD_CNT_MAX - 1)))data_flag 1b1;elsedata_flag 1b0;
end//数据输出
always (posedge sys_clk) beginif(!sys_rst_n)rx_data 1b0;else if((rx_count 8) (baud_count (BAUD_CNT_MAX - 1)))rx_data uart_rx_data;
endendmodule串口发送模块设计
串口发送模块框图如下 对应的时序图如下 程序代码如下
module uart_tx #(parameter CLK_FREQ 50000000, //系统时钟parameter UART_BPS 115200 //波特率
)(input sys_clk, //系统时钟input sys_rst_n, //系统复位output reg uart_txd, //串口发送线input data_flag, //数据有效标志随数据一起输入只保持一个时钟周期input [7:0] tx_data, //串口需要发送的数据output reg tx_busy //串口发送忙标志
);localparam BAUD_CNT_MAX CLK_FREQ/UART_BPS; //波特率计数器的计数周期//波特率计数器
reg [31:0] baud_count;
//接收计数
reg [3:0] tx_count;
//串口接收移位寄存器
reg [7:0] uart_tx_data;//切换到忙状态控制
//当数据有效且处于空闲状态则转换到忙状态
//当完成发送后又转换到空闲状态发送空闲周期时提前BAUD_CNT_MAX/32周期结束确保回环测试时发送速度大于接收速度
always (posedge sys_clk) beginif(!sys_rst_n)tx_busy 1b0;else if((data_flag 1b1) (tx_busy 1b0))tx_busy 1b1;else if((tx_count 10d9) (baud_count (BAUD_CNT_MAX - BAUD_CNT_MAX / 32 - 1)))tx_busy 1b0;
end//锁定需要发送的数据到移位寄存器
always (posedge sys_clk) beginif(!sys_rst_n)uart_tx_data 8b0;else if((data_flag 1b1) (tx_busy 1b0))uart_tx_data tx_data;else if((tx_count 4d9) (baud_count (BAUD_CNT_MAX - BAUD_CNT_MAX / 32 - 1)))uart_tx_data 8b0;
end//波特率计数器当发送处于忙状态时进行周期计数
always (posedge sys_clk) beginif(!sys_rst_n)baud_count 0;else if(tx_busy 1b1) beginif(baud_count (BAUD_CNT_MAX - 1))baud_count baud_count 1;elsebaud_count 0;endelsebaud_count 0;
end//发送计数器当发送忙且波特率计数器溢出时进行递增计数
always (posedge sys_clk) beginif(!sys_rst_n)tx_count 4b0;else if(tx_busy 1b1) beginif(baud_count (BAUD_CNT_MAX - 1))tx_count tx_count 4b1;endelsetx_count 4b0;
end//将移位寄存器中的数据通过txd发送出去
always (posedge sys_clk) beginif(!sys_rst_n)uart_txd 8b1;else if(tx_busy 1b1) begincase(tx_count)4d0: uart_txd 1b0; //起始位4d1: uart_txd uart_tx_data[0]; //bit04d2: uart_txd uart_tx_data[1]; //bit14d3: uart_txd uart_tx_data[2]; //bit24d4: uart_txd uart_tx_data[3]; //bit34d5: uart_txd uart_tx_data[4]; //bit44d6: uart_txd uart_tx_data[5]; //bit54d7: uart_txd uart_tx_data[6]; //bit64d8: uart_txd uart_tx_data[7]; //bit74d9: uart_txd 1b1; //停止位default: uart_txd 1b1;endcaseendelseuart_txd 8b1;
endendmodule顶层模块设计
顶层模块主要负责例化串口接收和发送模块并将接收到的数据给发送模块进行发送相应的代码如下
module uart_loopback #(parameter CLK_FREQ 50000000, //系统时钟parameter UART_BPS 115200 //波特率
)(input sys_clk, //系统时钟input sys_rst_n, //系统复位input uart_rxd, //串口接收线output uart_txd //串口发送线
);//串口输入延迟一拍
reg uart_rxd_d0;
//串口输入延迟二拍
reg uart_rxd_d1;//数据有效标志
wire data_flag;
//串口收到的数据
wire [7:0] rx_data;
//串口发送忙标志
wire tx_busy;//延迟两拍用于消除亚稳态
always (posedge sys_clk) beginif(!sys_rst_n) beginuart_rxd_d0 1b1;uart_rxd_d1 1b1;endelse beginuart_rxd_d0 uart_rxd;uart_rxd_d1 uart_rxd_d0;end
end//例化串口接收模块
uart_rx #(.CLK_FREQ(CLK_FREQ), //系统时钟.UART_BPS(UART_BPS) //波特率
)u_uart_rx_inst0(.sys_clk(sys_clk), //系统时钟.sys_rst_n(sys_rst_n), //系统复位.uart_rxd(uart_rxd_d1), //串口接收线.data_flag(data_flag), //数据有效标志随数据一起输出只保持一个时钟周期.rx_data(rx_data) //串口接收到的数据
);//例化串口发送模块
uart_tx #(.CLK_FREQ(CLK_FREQ), //系统时钟.UART_BPS(UART_BPS) //波特率
)u_uart_tx_inst0(.sys_clk(sys_clk), //系统时钟.sys_rst_n(sys_rst_n), //系统复位.uart_txd(uart_txd), //串口发送线.data_flag(data_flag), //数据有效标志随数据一起输入只保持一个时钟周期.tx_data(rx_data), //串口需要发送的数据.tx_busy(tx_busy) //串口发送忙标志
);endmodule仿真激励代码设计
仿真激励代码先对顶层模块进行复位然后通过uart_rxd向顶层模块注入数据同时还要产生50M的时钟其代码如下
timescale 1ns / 1psmodule tb_uart_loopback();//parameter define
parameter CLK_PERIOD 20; //时钟周期为20ns//reg define
reg sys_clk; //时钟信号
reg sys_rst_n; //复位信号
reg uart_rxd; //UART接收端口
wire uart_txd; //UART发送端口//发送8h558b0101_0101
initial beginsys_clk 1b0;sys_rst_n 1b0;uart_rxd 1b1;#200sys_rst_n 1b1;#1000uart_rxd 1b0; //起始位#8680 //延时433个时钟周期uart_rxd 1b1; //D0#8680uart_rxd 1b0; //D1#8680uart_rxd 1b1; //D2#8680uart_rxd 1b0; //D3#8680uart_rxd 1b1; //D4#8680uart_rxd 1b0; //D5#8680uart_rxd 1b1; //D6#8680uart_rxd 1b0; //D7 #8680uart_rxd 1b1; //停止位#8680uart_rxd 1b1; //空闲状态
end//50Mhz的时钟周期则为1/50Mhz20ns,所以每10ns电平取反一次
always #(CLK_PERIOD/2) sys_clk ~sys_clk;//例化顶层模块
uart_loopback u_tb_uart_loopback_inst0(.sys_clk(sys_clk),.sys_rst_n(sys_rst_n),.uart_rxd(uart_rxd),.uart_txd(uart_txd)
);endmodule