1.串口介绍
串口(通常指 UART,通用异步收发传输器)是一种异步串行通信接口,也是嵌入式系统中最常用的通信方式之一,它通过一根线发送数据、另一根线接收数据,实现设备间的全双工通信。
2.串口组成
1.物理层
2.协议
串口协议:
传输1个字节:
传输多个字节:
3.代码
使用的是米联客的开发板:
串口的实现思路:
状态机+计数器
top.v:
`timescale 1ns / 1ps module top ( input sys_clk_p, //系统差分输入时钟 input sys_clk_n, //系统差分输入时钟 input sys_rst_n, //系外部复位信号,低有效 //UART端口 input uart_rxd , //UART接收端口 output uart_txd , //UART发送端口 output reg [1:0] LED_o ); reg [31:0]tcnt; //***************************************************** //** main code //***************************************************** //转换差分信号 IBUFDS diff_clock ( .I (sys_clk_p), //系统差分输入时钟 .IB(sys_clk_n), //系统差分输入时钟 .O (sys_clk) //输出系统时钟 ); //例化串口模块 uart_loopback uart_looop( .sys_clk(sys_clk), //时钟信号 .sys_rst_n(sys_rst_n), //系外部复位信号,低有效 .uart_rxd (uart_rxd), //UART接收端口 .uart_txd(uart_txd) //UART发送端口 ); always @(posedge sys_clk or negedge sys_rst_n)begin if(!sys_rst_n) tcnt <= 32'd0; else if(tcnt < 32'd99_999_999) tcnt <= tcnt + 1'b1; else tcnt <= 32'd0; end wire led_en = (tcnt == 32'd99_999_999); always @(posedge sys_clk or negedge sys_rst_n)begin if(!sys_rst_n) LED_o <= 2'b1; else if(led_en) if(LED_o == 2'b10) LED_o <= 2'b1; else LED_o <= LED_o << 1; end endmoduleuart_loopback.v
`timescale 1ns / 1ps module uart_loopback( input sys_clk, //时钟信号 input sys_rst_n, //系外部复位信号,低有效 //UART端口 input uart_rxd , //UART接收端口 output uart_txd //UART发送端口 ); //parameter define parameter CLK_FREQ = 100000000; //定义系统时钟频率 parameter UART_BPS = 115200 ; //定义串口波特率 //wire define wire uart_rx_done; //UART接收完成信号 wire [7:0] uart_rx_data; //UART接收数据 wire [3:0] rx_cnt; //***************************************************** //** main code //***************************************************** //串口接收模块 uart_rx #( .CLK_FREQ (CLK_FREQ), .UART_BPS (UART_BPS) ) u_uart_rx( .clk (sys_clk ), .rst_n (sys_rst_n ), .uart_rxd (uart_rxd ), .uart_rx_done (uart_rx_done), .uart_rx_data (uart_rx_data), .rx_cnt(rx_cnt) ); //串口发送模块 uart_tx #( .CLK_FREQ (CLK_FREQ), .UART_BPS (UART_BPS) ) u_uart_tx( .clk (sys_clk ), .rst_n (sys_rst_n ), .uart_tx_en (uart_rx_done), .uart_tx_data (uart_rx_data), .uart_txd (uart_txd ), .uart_tx_busy ( ) ); ila_1 u_ila_1 ( .clk(sys_clk), // ILA的采样时钟(必须和被观测信号同时钟,此处用系统时钟100MHz) .probe0(sys_rst_n), // 探头0:复位信号(参考用) .probe1(uart_rx_done), // 探头1:UART接收信号(核心观测) .probe2(uart_rx_data[7:0]), // 探头2:UART发送信号(核心观测) .probe3(uart_rxd), // 探头3:LED输出(辅助参考) .probe4(rx_cnt) // 探头4:LED翻转使能(辅助参考) ); endmoduleuart_rx.v
`timescale 1ns / 1ps module uart_rx( input clk , //系统时钟 input rst_n , //系统复位,低有效 input uart_rxd , //UART接收端口 output reg uart_rx_done, //UART接收完成信号 output reg [7:0] uart_rx_data, //UART接收到的数据 output reg [3:0] rx_cnt ); //parameter define parameter CLK_FREQ = 100000000; //系统时钟频率 parameter UART_BPS = 115200 ; //串口波特率 localparam BAUD_CNT_MAX = CLK_FREQ/UART_BPS; //为得到指定波特率,对系统时钟计数BPS_CNT次 //reg define reg uart_rxd_d0; reg uart_rxd_d1; reg uart_rxd_d2; reg rx_flag ; //接收过程标志信号 reg [3:0 ] rx_cnt ; //接收数据计数器 reg [15:0] baud_cnt ; //波特率计数器 reg [7:0 ] rx_data_t ; //接收数据寄存器 //wire define wire start_en; //***************************************************** //** main code //***************************************************** //捕获接收端口下降沿(起始位),得到一个时钟周期的脉冲信号 assign start_en = uart_rxd_d2 & (~uart_rxd_d1) & (~rx_flag); //针对异步信号的同步处理 always @(posedge clk or negedge rst_n) begin if(!rst_n) begin uart_rxd_d0 <= 1'b0; uart_rxd_d1 <= 1'b0; uart_rxd_d2 <= 1'b0; end else begin uart_rxd_d0 <= uart_rxd; uart_rxd_d1 <= uart_rxd_d0; uart_rxd_d2 <= uart_rxd_d1; end end //给接收标志赋值 always @(posedge clk or negedge rst_n) begin if(!rst_n) rx_flag <= 1'b0; else if(start_en) //检测到起始位 rx_flag <= 1'b1; //接收过程中,标志信号rx_flag拉高 //在停止位一半的时候,即接收过程结束,标志信号rx_flag拉低 else if((rx_cnt == 4'd9) && (baud_cnt == BAUD_CNT_MAX/2 - 1'b1)) rx_flag <= 1'b0; else rx_flag <= rx_flag; end //波特率的计数器赋值 always @(posedge clk or negedge rst_n) begin if(!rst_n) baud_cnt <= 16'd0; else if(rx_flag) begin //处于接收过程时,波特率计数器(baud_cnt)进行循环计数 if(baud_cnt < BAUD_CNT_MAX - 1'b1) baud_cnt <= baud_cnt + 16'b1; else baud_cnt <= 16'd0; //计数达到一个波特率周期后清零 end else baud_cnt <= 16'd0; //接收过程结束时计数器清零 end //对接收数据计数器(rx_cnt)进行赋值 always @(posedge clk or negedge rst_n) begin if(!rst_n) rx_cnt <= 4'd0; else if(rx_flag) begin //处于接收过程时rx_cnt才进行计数 if(baud_cnt == BAUD_CNT_MAX - 1'b1) //当波特率计数器计数到一个波特率周期时 rx_cnt <= rx_cnt + 1'b1; //接收数据计数器加1 else rx_cnt <= rx_cnt; end else rx_cnt <= 4'd0; //接收过程结束时计数器清零 end //根据rx_cnt来寄存rxd端口的数据 always @(posedge clk or negedge rst_n) begin if(!rst_n) rx_data_t <= 8'b0; else if(rx_flag) begin //系统处于接收过程时 if(baud_cnt == BAUD_CNT_MAX/2 - 1'b1) begin //判断baud_cnt是否计数到数据位的中间 case(rx_cnt) 4'd1 : rx_data_t[0] <= uart_rxd_d2; //寄存数据的最低位 4'd2 : rx_data_t[1] <= uart_rxd_d2; 4'd3 : rx_data_t[2] <= uart_rxd_d2; 4'd4 : rx_data_t[3] <= uart_rxd_d2; 4'd5 : rx_data_t[4] <= uart_rxd_d2; 4'd6 : rx_data_t[5] <= uart_rxd_d2; 4'd7 : rx_data_t[6] <= uart_rxd_d2; 4'd8 : rx_data_t[7] <= uart_rxd_d2; //寄存数据的高低位 default : ; endcase end else rx_data_t <= rx_data_t; end else rx_data_t <= 8'b0; end //给接收完成信号和接收到的数据赋值 always @(posedge clk or negedge rst_n) begin if(!rst_n) begin uart_rx_done <= 1'b0; uart_rx_data <= 8'b0; end //当接收数据计数器计数到停止位,且baud_cnt计数到停止位的中间时 else if(rx_cnt == 4'd9 && baud_cnt == BAUD_CNT_MAX/2 - 1'b1) begin uart_rx_done <= 1'b1 ; //拉高接收完成信号 uart_rx_data <= rx_data_t; //并对UART接收到的数据进行赋值 end else begin uart_rx_done <= 1'b0; uart_rx_data <= uart_rx_data; end end endmoduleuart_tx.v
`timescale 1ns / 1ps module uart_tx( input clk , //系统时钟 input rst_n , //系统复位,低有效 input uart_tx_en , //UART的发送使能 input [7:0] uart_tx_data, //UART要发送的数据 output reg uart_txd , //UART发送端口 output reg uart_tx_busy //发送忙状态信号 ); //parameter define parameter CLK_FREQ = 100000000; //系统时钟频率 parameter UART_BPS = 115200 ; //串口波特率 localparam BAUD_CNT_MAX = CLK_FREQ/UART_BPS; //为得到指定波特率,对系统时钟计数BPS_CNT次 //reg define reg [7:0] tx_data_t; //发送数据寄存器 reg [3:0] tx_cnt ; //发送数据计数器 reg [15:0] baud_cnt ; //波特率计数器 //***************************************************** //** main code //***************************************************** //当uart_tx_en为高时,寄存输入的并行数据,并拉高BUSY信号 always @(posedge clk or negedge rst_n) begin if(!rst_n) begin tx_data_t <= 8'b0; uart_tx_busy <= 1'b0; end //发送使能时,寄存要发送的数据,并拉高BUSY信号 else if(uart_tx_en) begin tx_data_t <= uart_tx_data; uart_tx_busy <= 1'b1; end //当计数到停止位结束时,停止发送过程 else if(tx_cnt == 4'd9 && baud_cnt == BAUD_CNT_MAX - BAUD_CNT_MAX/16) begin tx_data_t <= 8'b0; //清空发送数据寄存器 uart_tx_busy <= 1'b0; //并拉低BUSY信号 end else begin tx_data_t <= tx_data_t; uart_tx_busy <= uart_tx_busy; end end //波特率的计数器赋值 always @(posedge clk or negedge rst_n) begin if(!rst_n) baud_cnt <= 16'd0; //当处于发送过程时,波特率计数器(baud_cnt)进行循环计数 else if(uart_tx_busy) begin if(baud_cnt < BAUD_CNT_MAX - 1'b1) baud_cnt <= baud_cnt + 16'b1; else baud_cnt <= 16'd0; //计数达到一个波特率周期后清零 end else baud_cnt <= 16'd0; //发送过程结束时计数器清零 end //tx_cnt进行赋值 always @(posedge clk or negedge rst_n) begin if(!rst_n) tx_cnt <= 4'd0; else if(uart_tx_busy) begin //处于发送过程时tx_cnt才进行计数 if(baud_cnt == BAUD_CNT_MAX - 1'b1) //当波特率计数器计数到一个波特率周期时 tx_cnt <= tx_cnt + 1'b1; //发送数据计数器加1 else tx_cnt <= tx_cnt; end else tx_cnt <= 4'd0; //发送过程结束时计数器清零 end //根据tx_cnt来给uart发送端口赋值 always @(posedge clk or negedge rst_n) begin if(!rst_n) uart_txd <= 1'b1; else if(uart_tx_busy) begin case(tx_cnt) 4'd0 : uart_txd <= 1'b0 ; //起始位 4'd1 : uart_txd <= tx_data_t[0]; //数据位最低位 4'd2 : uart_txd <= tx_data_t[1]; 4'd3 : uart_txd <= tx_data_t[2]; 4'd4 : uart_txd <= tx_data_t[3]; 4'd5 : uart_txd <= tx_data_t[4]; 4'd6 : uart_txd <= tx_data_t[5]; 4'd7 : uart_txd <= tx_data_t[6]; 4'd8 : uart_txd <= tx_data_t[7]; //数据位最高位 4'd9 : uart_txd <= 1'b1 ; //停止位 default : uart_txd <= 1'b1; endcase end else uart_txd <= 1'b1; //空闲时发送端口为高电平 end endmodule引脚约束文件:
# ============================================= # 时钟约束:差分时钟(P/N对) # ============================================= # 1. 创建主时钟:10ns(100MHz),50%占空比,时钟源P端 create_clock -period 10.000 -name sysclk -waveform {0 5} [get_ports sys_clk_p] # 2. 差分时钟引脚分配 set_property PACKAGE_PIN AE5 [get_ports sys_clk_p] # 3. 差分时钟电平标准(P/N端同时设置DIFF_SSTL12 set_property IOSTANDARD DIFF_SSTL12 [get_ports sys_clk_p] # ============================================= # 复位信号:引脚+电平约束 # ============================================= set_property PACKAGE_PIN B14 [get_ports sys_rst_n] set_property IOSTANDARD LVCMOS18 [get_ports sys_rst_n] # ============================================= # LED灯:引脚+电平约束 # ============================================= set_property PACKAGE_PIN E14 [get_ports {LED_o[1]}] set_property PACKAGE_PIN E13 [get_ports {LED_o[0]}] set_property IOSTANDARD LVCMOS18 [get_ports {LED_o[*]}] # ============================================= # PL调试串口:引脚+电平约束 # ============================================= set_property PACKAGE_PIN A15 [get_ports uart_txd] set_property PACKAGE_PIN B15 [get_ports uart_rxd] set_property IOSTANDARD LVCMOS18 [get_ports {uart_txd uart_rxd}] # ============================================= # 全局配置:开启比特流压缩 # ============================================= set_property BITSTREAM.GENERAL.COMPRESS true [current_design]