串口:
串口在嵌入式设备里经常会用到,串口主要包括RS232C串口,RS485串口等,他们只是电平不一样。
RS232也称标准串口,是一种比较常用的串口,采用标准的DB9接口,RS232采用的是负逻辑电平,即-12~-5V表示逻辑1,+5~+12V表示逻辑0。
一般的MCU或者FPGA的管脚只能输出TTL电平,所以一般需要采用想MAX232等电平转换芯片将UART的TTL电平转换成RS232电平。
UART:
UART是一种通用串行数据总线,用于异步通信。该总线双向通信,可以实现全双工传输和接收。在嵌入式设计中,UART用于主机与辅助设备通信。
UART传输协议:
UART一般具有11个bit位,包括一个起始位,8个数据位,一个校验位,一个停止位,校验位可以省掉。
verilog代码实现:
现以将FPGA接收到上位机的数据然后再将这些数据发送回上位机显示为例,整个串口部分可以分别分为接收和发送模块。
首先接收模块:
接收模块主要包含时钟复位输入,以及8个串行数据输入,以及将接收到的串行数据转换成并行数据输出,并输出一个接收完成标志位。
同时在接收模块内产生串口接收所需要的波特率。
接收模块代码如下:
`timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // Company: // Engineer: // // Create Date: 21:57:52 02/21/2020 // Design Name: // Module Name: uart_rx // Project Name: // Target Devices: // Tool versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // ////////////////////////////////////////////////////////////////////////////////// module uart_rx # ( parameter clk_ref = 50, //参考时钟50M parameter baud_bps = 115200 ) ( input clk_50, input Rst_n, input rx_data, output reg [7:0] uart_rx, output reg rx_done ); localparam bps_cnt = clk_ref*1000000/baud_bps; reg data_temp0; reg data_temp1; wire start_flag; reg rx_flag; reg [15:0] baud_cnt; reg [3:0] bit_cnt; reg [7:0] rx_reg; //====================================================== // //检测RX接收信号的起始位信号,RX信号由高电平变化为低电平 // //====================================================== always @(posedge clk_50 or negedge Rst_n) begin if(!Rst_n) begin data_temp0 <= 1'b0; data_temp1 <= 1'b0; end else begin data_temp0 <= rx_data; data_temp1 <= data_temp0; end end assign start_flag = data_temp1 & (~data_temp0); //======================================================= // //数据接收标志位信号___________ // | | //======================================================= always @(posedge clk_50 or negedge Rst_n) begin if(!Rst_n) rx_flag<=0; else begin if(start_flag) rx_flag<=1; else if((bit_cnt==4'd9)&(baud_cnt==bps_cnt/2)) rx_flag<=0; else rx_flag<=rx_flag; end end //===================================================== // //波特率产生 // //===================================================== always @(posedge clk_50 or negedge Rst_n) begin if(!Rst_n) baud_cnt<=16'd0; else if(rx_flag) begin if(baud_cnt==(bps_cnt-1)) baud_cnt<=16'd0; else baud_cnt<=baud_cnt+1'b1; end else baud_cnt<=16'd0; end //======================================================= // //数据接收位数计数 // //======================================================= always @(posedge clk_50 or negedge Rst_n) begin if(!Rst_n) bit_cnt<=4'd0; else if(rx_flag) begin if(baud_cnt==(bps_cnt-1)) bit_cnt<=bit_cnt+1'b1; else bit_cnt<=bit_cnt; end else bit_cnt<=4'd0; end //======================================================= // //数据接收块,将RX信号线上的信号接收并存在一个8位接收寄存器里 // //======================================================= always @(posedge clk_50 or negedge Rst_n) begin if(!Rst_n) rx_reg<=8'd0; else if(rx_flag) begin if(baud_cnt==bps_cnt/2) begin case(bit_cnt) 4'd1:rx_reg[0]<=rx_data; 4'd2:rx_reg[1]<=rx_data; 4'd3:rx_reg[2]<=rx_data; 4'd4:rx_reg[3]<=rx_data; 4'd5:rx_reg[4]<=rx_data; 4'd6:rx_reg[5]<=rx_data; 4'd7:rx_reg[6]<=rx_data; 4'd8:rx_reg[7]<=rx_data; default:; endcase end else rx_reg<=rx_reg; end else rx_reg<=8'd0; end //======================================================= // //将数据接收寄存器里的数据发送到输出寄存器 // //======================================================= always @(posedge clk_50 or negedge Rst_n) begin if(!Rst_n) begin rx_done<=1'b0; uart_rx<=8'd0; end else if(bit_cnt==9) begin uart_rx<=rx_reg; rx_done<=1'b1; end else begin uart_rx<=8'd0; rx_done<=1'b0; end end endmodule
发送模块:
发送模块主要包括系统时钟和复位输入,准备需要发送的并行数据输入,发送使能输入,以及串行数据输出。同时发送模块产生数据发送的波特率。
发送模块检测到发送使能信号以后开始发送,也就是检测到TX_EN的上升沿开始启动发送。
串口发送部分Verilog代码如下:
`timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // Company: // Engineer: // // Create Date: 14:16:03 02/22/2020 // Design Name: // Module Name: uart_tx // Project Name: // Target Devices: // Tool versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // ////////////////////////////////////////////////////////////////////////////////// module uart_tx #( parameter clk_ref = 50, //参考时钟50M parameter baud_bps = 115200//波特率 ) ( input clk_50, input Rst_n, input [7:0] uart_tx, input tx_en, output reg tx_data ); localparam bps_cnt = clk_ref*1000000/baud_bps; reg en_temp0; reg en_temp1; wire start_flag; reg tx_flag; reg [15:0] baud_cnt; reg [3:0] bit_cnt; //====================================================== // //数据发送使能信号检测,检测到使能信号后开始发送标志位置1 //这里的使能信号使用接收模块里的接收完成标志信号 // //====================================================== always @(posedge clk_50 or negedge Rst_n) begin if(!Rst_n) begin en_temp0 <= 1'b0; en_temp1 <= 1'b0; end else begin en_temp0 <= tx_en; en_temp1 <= en_temp0; end end assign start_flag = (~en_temp1) & en_temp0; //======================================================= // //数据发送标志位信号___________ // | | //======================================================= always @(posedge clk_50 or negedge Rst_n) begin if(!Rst_n) tx_flag<=0; else begin if(start_flag) tx_flag<=1; else if((bit_cnt==4'd9)&(baud_cnt==bps_cnt/2)) tx_flag<=0; else tx_flag<=tx_flag; end end //===================================================== // //波特率产生 // //===================================================== always @(posedge clk_50 or negedge Rst_n) begin if(!Rst_n) baud_cnt<=16'd0; else if(tx_flag) begin if(baud_cnt==(bps_cnt-1)) baud_cnt<=16'd0; else baud_cnt<=baud_cnt+1'b1; end else baud_cnt<=16'd0; end //======================================================= // //数据发送位数计数 // //======================================================= always @(posedge clk_50 or negedge Rst_n) begin if(!Rst_n) bit_cnt<=4'd0; else if(tx_flag) begin if(baud_cnt==(bps_cnt-1)) bit_cnt<=bit_cnt+1'b1; else bit_cnt<=bit_cnt; end else bit_cnt<=4'd0; end //======================================================= // //数据发送块,将需要发送的8位并行数据一位一位的发送到输出 // //======================================================= always @(posedge clk_50 or negedge Rst_n) begin if(!Rst_n) tx_data<=1'b1; else if(tx_flag) begin case(bit_cnt) 4'd0:tx_data<=1'b0; 4'd1:tx_data<=uart_tx[0]; 4'd2:tx_data<=uart_tx[1]; 4'd3:tx_data<=uart_tx[2]; 4'd4:tx_data<=uart_tx[3]; 4'd5:tx_data<=uart_tx[4]; 4'd6:tx_data<=uart_tx[5]; 4'd7:tx_data<=uart_tx[6]; 4'd8:tx_data<=uart_tx[7]; 4'd9:tx_data<=1'b1; default:; endcase end else tx_data<=1'b1; end endmodule
如果需要实现将接收的数据发送,则在顶层模块里进行连接,将接收模块的输出信号连接到发送模块的输入,同时将接收模块的接收完成标志信号作为发送模块的使能信号。
代码如下:
`timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // Company: // Engineer: // // Create Date: 21:54:01 02/21/2020 // Design Name: // Module Name: uart_top // Project Name: // Target Devices: // Tool versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // ////////////////////////////////////////////////////////////////////////////////// module uart_top ( input clk_50, input Rst_n, input rx_data, output tx_data ); wire [7:0] uart_rx; wire rx_done; wire tx_en; uart_rx #( 50, 115200 ) ( .clk_50(clk_50), .Rst_n(Rst_n), .rx_data(rx_data), .uart_rx(uart_rx), .rx_done(rx_done) ); uart_tx #( 50, 115200 ) ( .clk_50(clk_50), .Rst_n(Rst_n), .uart_tx(uart_tx), .tx_en(rx_done), .tx_data(tx_data) ); endmodule
发送模块的仿真文件:
`timescale 1ns / 1ps //////////////////////////////////////////////////////////////////////////////// // Company: // Engineer: // // Create Date: 15:18:38 02/22/2020 // Design Name: uart_tx // Module Name: C:/mydesign/uart/uart_txtb.v // Project Name: uart // Target Device: // Tool versions: // Description: // // Verilog Test Fixture created by ISE for module: uart_tx // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // //////////////////////////////////////////////////////////////////////////////// module uart_txtb; // Inputs reg clk_50; reg Rst_n; reg [7:0] uart_tx; reg tx_en; // Outputs wire tx_data; // Instantiate the Unit Under Test (UUT) uart_tx uut ( .clk_50(clk_50), .Rst_n(Rst_n), .uart_tx(uart_tx), .tx_en(tx_en), .tx_data(tx_data) ); initial begin // Initialize Inputs clk_50 = 0; Rst_n = 0; uart_tx = 0; tx_en = 0; // Wait 100 ns for global reset to finish #10; Rst_n = 1; #10; tx_en = 1; #10; end always #10 clk_50=~clk_50; always @(posedge clk_50) begin uart_tx<=8'b11001011; end // Add stimulus here endmodule
仿真波形:
来源:https://www.cnblogs.com/xiaozhu5208/p/12371687.html