ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
` `UART是常用模块,无论是调试还是用于使用过程中的数据传输,都很重要。该模块使用FPGA实现时候,一般接收数据比较容易实现,但是发送数据容易出错,原因在于时钟不精确,导致数据接收错误。 ` `对于50MHz时钟的晶振,一般使用9600的波特率,因为我们需要得到9600*16Hz的时钟,使用PLL可以得到精确的这个值。 ## UART接收模块 ``` `timescale 1ns/1ns module uart_rd ( input i_rst_n, input i_clk, output [7 : 0] o_rx_data, //UART数据接收接收到的数据 output o_uart_rx_busy, //UART数据接收模块接收数据忙 output o_uart_rx_error, //UART数据接收帧出错 input i_uart_rx //UART串行数据输入 ); reg r_uart_rx_buf; reg r_uart_rx_falling; reg [3 : 0] r_sample_cnt; //采样计数器 reg [1 : 0] cstate, nstate; parameter [1 : 0] idle = 2'b00; parameter [1 : 0] receive_data = 2'b01; parameter [1 : 0] receive_done = 2'b10; reg [3 : 0] r_shift_cnt; reg [9 : 0] r_shift; reg [7 : 0] r_rx_data; reg r_uart_rx_error; assign o_rx_data = r_rx_data; assign o_uart_rx_busy = (cstate == idle)? 1'b0 : 1'b1; assign o_uart_rx_error = r_uart_rx_error; //*********************************PROCESS************************************** // FUNCTION :捕获UART数据的下降沿 //****************************************************************************** always @(posedge i_clk, negedge i_rst_n) begin if(1'b0 == i_rst_n) begin r_uart_rx_buf <= 1'b0; r_uart_rx_falling <= 1'b0; end else begin r_uart_rx_buf <= i_uart_rx; r_uart_rx_falling <= ~i_uart_rx & (r_uart_rx_buf); end end //*********************************PROCESS************************************** // FUNCTION :UART下个状态到现状态的转化 //****************************************************************************** always @(posedge i_clk, negedge i_rst_n) begin if(1'b0 == i_rst_n) cstate <= idle; else cstate <= nstate; end //*********************************PROCESS************************************** // FUNCTION :UART下个状态的转化 //****************************************************************************** always @(*) begin case(cstate) idle : if(1'b1 == r_uart_rx_falling) nstate = receive_data; else nstate = idle; receive_data : if(4'd10 == r_shift_cnt) nstate = receive_done; else nstate = receive_data; receive_done : nstate = idle; default : nstate = idle; endcase end //*********************************PROCESS************************************** // FUNCTION :UART在现状态的操作 //****************************************************************************** always @(posedge i_clk, negedge i_rst_n) begin if(1'b0 == i_rst_n) begin r_shift <= 10'b1111111111; r_shift_cnt <= 4'd0; r_sample_cnt <= 4'd0; r_rx_data <= 8'd0; r_uart_rx_error <= 1'b0; end else begin if(receive_data == cstate) if(4'd5 == r_sample_cnt) begin r_shift_cnt <= r_shift_cnt + 4'd1; r_shift <= {i_uart_rx, r_shift[9 : 1]}; r_sample_cnt <= r_sample_cnt + 4'd1; end else r_sample_cnt <= r_sample_cnt + 4'd1; else if(receive_done == cstate) begin r_shift_cnt <= 4'd0; r_shift <= 10'b1111111111; r_rx_data <= r_shift[8 : 1]; if(1'b0 == r_shift[9]) r_uart_rx_error <= 1'b1; else r_uart_rx_error <= 1'b0; r_shift_cnt <= 4'd0; r_sample_cnt <= 4'd0; end else begin r_shift <= 10'b1111111111; r_shift_cnt <= 4'd0; r_sample_cnt <= 4'd0; end end end endmodule ``` ## UART发送模块 ``` `timescale 1ns/1ns module uart_tx ( input i_rst_n, input i_clk, input i_tx_order, //UART数据发送指令 input [7 : 0] i_tx_data, //UART发送的数据 output o_uart_tx_busy, //UART发送模块忙 output o_uart_tx //UART数据输出 ); reg r_tx_order_buf; reg r_tx_order_rising; // reg [4 : 0] r_div_cnt; //时钟分频计数器 // reg r_div_clk; //分频时钟信号 reg [1 : 0] cstate, nstate; parameter [1 : 0] idle = 2'b00; parameter [1 : 0] load_data = 2'b01; parameter [1 : 0] shift_data = 2'b10; reg [3 : 0] r_shift_cnt; reg [9 : 0] r_shift; reg [3 : 0] r_hold_cnt; assign o_uart_tx = r_shift[0]; assign o_uart_tx_busy = (cstate == idle)? 1'b0 : 1'b1; ////*********************************PROCESS************************************** //// FUNCTION :产生分频时钟 ////****************************************************************************** // // always @(posedge i_clk, negedge i_rst_n) // begin // if(1'b0 == i_rst_n) // begin // r_div_cnt <= 4'd0; // r_div_clk <= 1'b0; // end // else // begin // r_div_cnt <= r_div_cnt + 4'd1; // r_div_clk <= r_div_cnt[3]; // end // end //*********************************PROCESS************************************** // FUNCTION :捕获UART发送指令的上升沿 //****************************************************************************** always @(posedge i_clk, negedge i_rst_n) begin if(1'b0 == i_rst_n) begin r_tx_order_buf <= 1'b0; r_tx_order_rising <= 1'b0; end else begin r_tx_order_buf <= i_tx_order; r_tx_order_rising <= i_tx_order & (~r_tx_order_buf); end end //*********************************PROCESS************************************** // FUNCTION :UART下个状态转化到现状态 //****************************************************************************** always @(posedge i_clk, negedge i_rst_n) begin if(1'b0 == i_rst_n) cstate <= idle; else cstate <= nstate; end //*********************************PROCESS************************************** // FUNCTION :UART下个状态转化进程 //****************************************************************************** always @(*) begin case(cstate) idle : if(1'b1 == r_tx_order_rising) nstate = shift_data; else nstate = idle; shift_data : if(4'd9 == r_shift_cnt) nstate = idle; else nstate = shift_data; default : nstate = idle; endcase end //*********************************PROCESS************************************** // FUNCTION :UART在各个现状态下的操作 //****************************************************************************** always @(posedge i_clk, negedge i_rst_n) begin if(1'b0 == i_rst_n) begin r_shift <= 10'b1111111111; r_shift_cnt <= 4'd0; r_hold_cnt <= 4'd0; end else begin if(idle == cstate && 1'b1 == r_tx_order_rising) begin r_shift_cnt <= 4'd0; r_shift <= {1'b1, i_tx_data, 1'b0}; r_hold_cnt <= r_hold_cnt + 4'd1; end else if(shift_data == cstate) begin r_hold_cnt <= r_hold_cnt + 4'd1; if(4'd15 == r_hold_cnt) begin r_shift_cnt <= r_shift_cnt + 4'd1; r_shift <= {1'b1, r_shift[9 : 1]}; end end else begin r_shift <= 10'b1111111111; r_shift_cnt <= 4'd0; end end end endmodule ``` ## 时钟 ` `时钟使用IP核实现。 ## 测试模块 ``` module top( input wire clk50, //rst,// output reg led, //用于指示 input wire rxd, output wire txd ); wire rx_busy;//16倍波特率 //reset //*********************************PROCESS************************************** // 复位模块 //****************************************************************************** reg rst_n ; reg [9:0]delay_cnt; always@(posedge clk50) begin if(delay_cnt>=10'd1000)begin delay_cnt <= delay_cnt; rst_n <= 1'b1; end else begin rst_n <= 1'b0; delay_cnt <= delay_cnt + 1'b1; end end /********************************************************** //串口接口 **********************************************************/ wire [7:0]data_temp; //wire rx_busy; uart_interface uart_inst_int ( .clk50 (clk50) , .rst_n (rst_n) , .rxd (rxd) , .txd (txd) , .tx_data(data_temp) , .rx_data(data_temp) , .rx_busy(rx_busy) , .tx_start(!rx_busy), .tx_busy() ); //指示灯 //assign txd = led; reg [31:0]cnt; //always@(posedge clk50) //begin // if(cnt >= 32'd25000000 - 1) // begin // cnt <= 0; // led <=~led; // end // else begin // cnt <= cnt + 1'b1 ; // end //end always@(posedge rx_busy) led<=~led; endmodule ```