核心内容摘要
黑土脚法娴熟
串口介绍串口通常指 UART通用异步收发传输器是一种异步串行通信接口也是嵌入式系统中最常用的通信方式之一它通过一根线发送数据、另一根线接收数据实现设备间的全双工通信。
串口组成
物理层
协议串口协议传输1个字节传输多个字节
代码使用的是米联客的开发板串口的实现思路状态机计数器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 32d0; else if(tcnt 32d99_999_
tcnt tcnt 1b1; else tcnt 32d0; end wire led_en (tcnt 32d99_999_
; always (posedge sys_clk or negedge sys_rst_n)begin if(!sys_rst_n) LED_o 2b1; else if(led_en) if(LED_o 2b
LED_o 2b1; else LED_o LED_o 1; end endmoduleuart_loopback.vtimescale 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), // 探头1UART接收信号核心观测 .probe2(uart_rx_data[7:0]), // 探头2UART发送信号核心观测 .probe3(uart_rxd), // 探头3LED输出辅助参考 .probe4(rx_cnt) // 探头4LED翻转使能辅助参考 ); endmoduleuart_rx.vtimescale 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_d
(~rx_flag); //针对异步信号的同步处理 always (posedge clk or negedge rst_n) begin if(!rst_n) begin uart_rxd_d0 1b0; uart_rxd_d1 1b0; uart_rxd_d2 1b0; 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 1b0; else if(start_en) //检测到起始位 rx_flag 1b1; //接收过程中标志信号rx_flag拉高 //在停止位一半的时候即接收过程结束标志信号rx_flag拉低 else if((rx_cnt 4d
(baud_cnt BAUD_CNT_MAX/2 - 1b
) rx_flag 1b0; else rx_flag rx_flag; end //波特率的计数器赋值 always (posedge clk or negedge rst_n) begin if(!rst_n) baud_cnt 16d0; else if(rx_flag) begin //处于接收过程时波特率计数器baud_cnt进行循环计数 if(baud_cnt BAUD_CNT_MAX - 1b
baud_cnt baud_cnt 16b1; else baud_cnt 16d0; //计数达到一个波特率周期后清零 end else baud_cnt 16d0; //接收过程结束时计数器清零 end //对接收数据计数器rx_cnt进行赋值 always (posedge clk or negedge rst_n) begin if(!rst_n) rx_cnt 4d0; else if(rx_flag) begin //处于接收过程时rx_cnt才进行计数 if(baud_cnt BAUD_CNT_MAX - 1b
//当波特率计数器计数到一个波特率周期时 rx_cnt rx_cnt 1b1; //接收数据计数器加1 else rx_cnt rx_cnt; end else rx_cnt 4d0; //接收过程结束时计数器清零 end //根据rx_cnt来寄存rxd端口的数据 always (posedge clk or negedge rst_n) begin if(!rst_n) rx_data_t 8b0; else if(rx_flag) begin //系统处于接收过程时 if(baud_cnt BAUD_CNT_MAX/2 - 1b
begin //判断baud_cnt是否计数到数据位的中间 case(rx_cnt) 4d1 : rx_data_t[0] uart_rxd_d2; //寄存数据的最低位 4d2 : rx_data_t[1] uart_rxd_d2; 4d3 : rx_data_t[2] uart_rxd_d2; 4d4 : rx_data_t[3] uart_rxd_d2; 4d5 : rx_data_t[4] uart_rxd_d2; 4d6 : rx_data_t[5] uart_rxd_d2; 4d7 : rx_data_t[6] uart_rxd_d2; 4d8 : rx_data_t[7] uart_rxd_d2; //寄存数据的高低位 default : ; endcase end else rx_data_t rx_data_t; end else rx_data_t 8b0; end //给接收完成信号和接收到的数据赋值 always (posedge clk or negedge rst_n) begin if(!rst_n) begin uart_rx_done 1b0; uart_rx_data 8b0; end //当接收数据计数器计数到停止位且baud_cnt计数到停止位的中间时 else if(rx_cnt 4d9 baud_cnt BAUD_CNT_MAX/2 - 1b
begin uart_rx_done 1b1 ; //拉高接收完成信号 uart_rx_data rx_data_t; //并对UART接收到的数据进行赋值 end else begin uart_rx_done 1b0; uart_rx_data uart_rx_data; end end endmoduleuart_tx.vtimescale 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 8b0; uart_tx_busy 1b0; end //发送使能时寄存要发送的数据并拉高BUSY信号 else if(uart_tx_en) begin tx_data_t uart_tx_data; uart_tx_busy 1b1; end //当计数到停止位结束时停止发送过程 else if(tx_cnt 4d9 baud_cnt BAUD_CNT_MAX - BAUD_CNT_MAX/
begin tx_data_t 8b0; //清空发送数据寄存器 uart_tx_busy 1b0; //并拉低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 16d0; //当处于发送过程时波特率计数器baud_cnt进行循环计数 else if(uart_tx_busy) begin if(baud_cnt BAUD_CNT_MAX - 1b
baud_cnt baud_cnt 16b1; else baud_cnt 16d0; //计数达到一个波特率周期后清零 end else baud_cnt 16d0; //发送过程结束时计数器清零 end //tx_cnt进行赋值 always (posedge clk or negedge rst_n) begin if(!rst_n) tx_cnt 4d0; else if(uart_tx_busy) begin //处于发送过程时tx_cnt才进行计数 if(baud_cnt BAUD_CNT_MAX - 1b
//当波特率计数器计数到一个波特率周期时 tx_cnt tx_cnt 1b1; //发送数据计数器加1 else tx_cnt tx_cnt; end else tx_cnt 4d0; //发送过程结束时计数器清零 end //根据tx_cnt来给uart发送端口赋值 always (posedge clk or negedge rst_n) begin if(!rst_n) uart_txd 1b1; else if(uart_tx_busy) begin case(tx_cnt) 4d0 : uart_txd 1b0 ; //起始位 4d1 : uart_txd tx_data_t[0]; //数据位最低位 4d2 : uart_txd tx_data_t[1]; 4d3 : uart_txd tx_data_t[2]; 4d4 : uart_txd tx_data_t[3]; 4d5 : uart_txd tx_data_t[4]; 4d6 : uart_txd tx_data_t[5]; 4d7 : uart_txd tx_data_t[6]; 4d8 : uart_txd tx_data_t[7]; //数据位最高位 4d9 : uart_txd 1b1 ; //停止位 default : uart_txd 1b1; endcase end else uart_txd 1b1; //空闲时发送端口为高电平 end endmodule引脚约束文件# # 时钟约束差分时钟P/N对 # #
创建主时钟10ns(100MHz)50%占空比时钟源P端 create_clock -period
1