UART串口通信协议的FPGA实现

it2023-10-06  71

引言

      UART串口通信协议,全称叫做通用异步收发器(Universal Asynchronous Receiver/Transmitter),通常称作UART。UART是异步通信,它只需要一根线就可以进行数据的通信。

1、基本概念

波特率:指每秒传输的bit位数(bit)。一般波特率配置为4800,9600,115200等;起始位:先发出一个逻辑”0”信号,表示传输字符的开始;数据位:可以是5~8位逻辑”0”或”1”。一般情况下都选择8位而不选择7,因为这样能尽可能避免数据的丢失或者混乱。从数据低位开始传输(即从LSB端开始发送数据,而且是串行发送);校验位:当数据位加上校验位后,当1”的位数为偶数时,叫做偶校验,为奇数时,叫做奇校验,主要用于差错控制;停止位:它是一个字符数据的结束标志。可以是1位、1.5位、2位的高电平;空闲位:处于逻辑“1”状态,表示当前线路上没有数据传送。

   具体的时序图如下图所示:

2、Verilog实现代码

uart_tx.v module uart_tx #( parameter BAUD_DIV = 14'd10416 //波特率时钟,9600bps,100Mhz/9600=10416,波特率可调,意为一位数据占用多少时钟周期 ) ( input clk, //100M时钟 input [7:0] uart_tx_data_i, //待发送数据 input uart_tx_en_i, //发送发送使能信号 output uart_tx_o, //串口输出 output uart_tx_done // 串口发送结束信号 1:正在发送 0:发送完成 ); parameter BAUD_DIV_CAP = BAUD_DIV / 2; //波特率时钟中间采样点,100Mhz/9600/2=5208,波特率可调 //-------------------------串口计数,中值采样---------------------- reg [13:0] baud_div=0; //波特率设置计数器 reg baud_bps=0; //数据发送点信号,高有效 reg uart_send_flag=0; //数据发送标志位 always@(posedge clk) begin if(baud_div==BAUD_DIV_CAP) //当波特率计数器计数到数据发送中点时,产生采样信号baud_bps,用来发送数据 begin baud_bps<=1'b1; baud_div<=baud_div+1'b1; end else if(baud_div<BAUD_DIV && uart_send_flag)//数据发送标志位有效期间,波特率计数器累加,以产生波特率时钟 begin baud_div<=baud_div+1'b1; baud_bps<=0; end else begin baud_bps<=0; baud_div<=0; end end //------------------------生成串口发送标志位,串口数据加载与卸载-------------------------- reg [9:0] send_data=10'b1111111111;//待发送数据寄存器,1bit起始信号+8bit有效信号+1bit结束信号 reg [3:0] bit_num=0; //发送数据个数计数器 reg uart_tx_o_r=1; //发送数据寄存器,初始状态位高 always@(posedge clk) begin if(uart_tx_en_i) //接收数据发送使能信号时,产生数据发送标志信号 begin uart_send_flag<=1'b1; send_data<={1'b1,uart_tx_data_i,1'b0};//待发送数据寄存器装填,1bit起始信号0+8bit有效信号+1bit结束信号 end else if(bit_num==4'd10) //发送结束时候,清楚发送标志信号,并清除待发送数据寄存器内部信号 begin uart_send_flag<=1'b0; send_data<=10'b1111_1111_11; end end //-------------------------串口数据发送---------------------------- always@(posedge clk) begin if(uart_send_flag) //发送有效时候 begin if(baud_bps)//检测发送点信号 begin if(bit_num<=4'd9) begin uart_tx_o_r<=send_data[bit_num]; //发送待发送寄存器内数据,从低位到高位 bit_num<=bit_num+1'b1; end end else if(bit_num==4'd10) bit_num<=4'd0; end else begin uart_tx_o_r<=1'b1; //空闲状态时,保持发送端位高电平,以备发送时候产生低电平信号 bit_num<=0; end end assign uart_tx_o=uart_tx_o_r; assign uart_tx_done = uart_send_flag; endmodule uart_rx.v  module uart_rx #( parameter [13:0] BAUD_DIV = 14'd10416 //波特率时钟,9600bps,100Mhz/9600=10416 ) ( input clk, input uart_rx_i, output [7:0] uart_rx_data_o, output uart_rx_done ); parameter BAUD_DIV_CAP = BAUD_DIV /2;//波特率时钟中间采样点,100Mhz/9600/2=5208 //------------------------计数控制------------------------------- reg [13:0] baud_div=0; //波特率设置计数器 reg baud_bps=0; //数据采样点信号 reg bps_start=0; //波特率启动标志 always@(posedge clk) begin if(baud_div==BAUD_DIV_CAP) //当波特率计数器计数到采样点时,产生采样信号baud_bps begin baud_bps<=1'b1; baud_div<=baud_div+1'b1; end else if(baud_div<BAUD_DIV && bps_start) //当波特率计数器启动时,计数器累加 begin baud_div<=baud_div+1'b1; baud_bps<=0; end else begin baud_bps<=0; baud_div<=0; end end //--------------------通过低电平判断是否有数据输入--------------------- reg [4:0] uart_rx_i_r=5'b11111; //数据接收缓存器 always@(posedge clk) begin uart_rx_i_r<={uart_rx_i_r[3:0],uart_rx_i}; end //数据接收缓存器,当连续接收到五个低电平时,即uart_rx_int=0时,作为接收到起始信号 wire uart_rx_int=uart_rx_i_r[4] | uart_rx_i_r[3] | uart_rx_i_r[2] | uart_rx_i_r[1] | uart_rx_i_r[0]; //------------------------数据接收---------------------------------------- reg [3:0] bit_num=0; //接收数据个数计数器 reg uart_rx_done_r=0; //数据接收完成寄存器 reg state=1'b0; reg [7:0] uart_rx_data_o_r0=0;//数据接收过程中,数据缓存器 reg [7:0] uart_rx_data_o_r1=0;//数据接收完成,数据寄存器 always@(posedge clk) begin uart_rx_done_r<=1'b0; case(state) 1'b0 : if(!uart_rx_int)//当连续接收到五个低电平时,即uart_rx_int=0时,作为接收到起始信号,启动波特率时钟 begin bps_start<=1'b1; state<=1'b1; end 1'b1 : if(baud_bps) //每次等待波特率采样中心时,接收数据,放入数据缓存器中 begin bit_num<=bit_num+1'b1; if(bit_num<4'd9) //接收1bit起始信号,8bit有效信号,1bit结束信号 uart_rx_data_o_r0[bit_num-1]<=uart_rx_i; end else if(bit_num==4'd10) //接收完成时候,接收数据个数计数器清零,产生接收完成标志位,并将数据写入数据寄存器,关闭波特率时候 begin bit_num<=0; uart_rx_done_r<=1'b1; uart_rx_data_o_r1<=uart_rx_data_o_r0; state<=1'b0;//进入状态0,再次循环检测 bps_start<=0; end default:; endcase end assign uart_rx_data_o=uart_rx_data_o_r1; assign uart_rx_done=uart_rx_done_r; endmodule  uart_tb.v `timescale 1ns/1ps module uart_tb (); reg clk; //100M时钟 reg [7:0] uart_tx_data_i; //待发送数据 reg uart_tx_en_i; //发送发送使能信号 wire uart_tx_o; //串口输出 wire uart_tx_done; // 串口发送结束信号 1:正在发送 0:发送结束 wire [7:0] uart_rx_data_o;//接受并行数据 wire uart_rx_done;//接收结束信号 reg [8:0] tx_data_r[0:39];//待发送数据寄存数组 reg [5:0] tx_icount;//发送计数 initial begin clk = 1'b1; //寄存器组赋初值 $readmemh("F:/PID/uart/tx_data.txt",tx_data_r); tx_icount = 6'd0; end always #5 clk = ~clk; //输入数据 always @ (posedge clk) begin if(!(uart_tx_done) && (!uart_tx_en_i))begin//控制上一数据传输完成然后输入下一数据 uart_tx_en_i <= 1'b1; uart_tx_data_i <= tx_data_r[tx_icount]; tx_icount <= tx_icount + 1'd1; if(tx_icount == 6'd40) tx_icount <= 6'd0; end else uart_tx_en_i <= 1'b0; end uart_tx tx_demo( .clk(clk), .uart_tx_data_i(uart_tx_data_i), .uart_tx_en_i(uart_tx_en_i), .uart_tx_o(uart_tx_o), .uart_tx_done(uart_tx_done) ); uart_rx rx_demo( .clk(clk), .uart_rx_i(uart_tx_o), .uart_rx_data_o(uart_rx_data_o), .uart_rx_done(uart_rx_done) ); endmodule 仿真波形

最新回复(0)