原创 : FPGA-UART串口 历史版本:
上次修改时间:
; **波特率**又称码元率,是指每秒传输码元的数目,单位波特(Band) \n\n码元:在数字通信中常常用时间间隔相同的符号来表示一个二进制数字[二进制数字](https://baike.baidu.com/item/%E4%BA%8C%E8%BF%9B%E5%88%B6%E6%95%B0%E5%AD%97/5920908?fromModule=lemma_inlink),这样的时间间隔内的信号称为(二进制)码元。 而这个间隔被称为码元长度\n\n### **2.1UART串口发送逻辑分析**\n\n实现设计的模块端口\n\n**输入:**\n\nData[7:0] : 8位输入 对应8个拨码开关 ;\n\n时序电路,所以有CLK时钟,以及Reset_n复位;\n\n**输出:**\n\nuart_tx : 需要发送的数据;\n\nLED : 发送完LED闪烁;\n\n**细节梳理 :**\n\n9600波特率:计数器实现\n\n起始位 bit1,bit2....bit7 停止位(一共10位)----->;定义一个4位计数器 在发送数据时在0-9计数,\n\n然后根据计数器的值 来决定发起始位还是8位数据还是停止位。\n\n其中,每1秒发送一次数据 (每秒都能发送)\n\n当发送到bit3时 我飞快的改变bit4,bit5,bit6,bit7,使其为1,**那我们究竟发0 还是发1呢?**\n\n**答案是肯定发送0 因为在启动发送的时候是0,即使之后变为了1,我们也只能发送启动发送的值 那就得想办法在启动发送的时候把拨码开关的值,存储下来,只发生存储的值。而不管拨码开关当前的值。**\n\n那怎么存呢 ----->;**D触发器 存储特性**\n\n其实就是找到发送完成的时刻。\n\n思路:通过线性序列机,当串口完成发送时,翻转LED状态\n\n### 2.2编写代码分析\n\n输入 : data;Clk;Reset;\n\n输出:uart_tx; LED;\n\n首先 波特率 --->; 波特率计数器 \n\n然后 每一位发送 ----->; 位计数器\n\n之后 位发送逻辑\n\n最后 控制LED翻转\n\n**开发流程可参考([FPGA_Vivado开发流程](https://blog.csdn.net/weixin_46897065/article/details/135588455?spm=1001.2014.3001.5501))**\n\n代码如下\n\n```\nmodule uart_tx0(\n data,\n Clk,\n Reset,\n uart_tx,\n LED\n );\n input [7:0]data;\n input Clk;\n input Reset;\n output reg uart_tx;\n output LED;\n reg LED;\n \n parameter DELAY = 50000000-1; //1000000000/20;\n parameter MCNT_DIV = 5208 - 1; //1000000000/9600/20; \n parameter MCNT_BIT = 10-1;\n \n reg [3:0]bit_count;\n reg en_bud;\n reg [25:0]delay_count;\n //reg [11:0]delay_cnt;\n \n //波特率计数\n reg [12:0]div_count;\n always@(posedge Clk or negedge Reset)\n if(!Reset)\n div_count >;= 0;\n else if(en_bud)begin\n if(div_count >;= MCNT_DIV)\n div_count >;= 0;\n else\n div_count >;= div_count + 1;\n end\n else\n div_count >;= 0;\n \n //使能 \n always@(posedge Clk or negedge Reset)\n if(!Reset)\n en_bud >;= 0;\n else if(delay_count == DELAY)\n en_bud >;= 1;\n else if(div_count ==MCNT_DIV && bit_count == MCNT_BIT)\n en_bud >;= 0;\n \n //位计数\n always@(posedge Clk or negedge Reset)\n if(!Reset)\n bit_count >;= 0;\n else if(div_count == MCNT_DIV)begin\n if(bit_count == MCNT_BIT)\n bit_count >;= 0;\n else\n bit_count >;= bit_count + 1\'d1;\n end\n \n // 1S 延时计数器\n always@(posedge Clk or negedge Reset)\n if(!Reset)\n delay_count >;= 0;\n else if (delay_count == DELAY)\n delay_count >;= 0;\n else\n delay_count >;= delay_count + 1\'d1;\n \n reg[7:0]temp_data;\n always@(posedge Clk or negedge Reset)\n if(!Reset)\n temp_data >;= 0;\n else if(delay_count == DELAY)\n temp_data >;= data;\n else\n temp_data >;= temp_data;\n\n always@(posedge Clk or negedge Reset)\n if(!Reset)\n uart_tx >;= 0;\n else if(en_bud==0)\n uart_tx >;= 1;\n else begin\n case (bit_count)\n 0 : uart_tx >;= 0 ;\n 1 : uart_tx >;= temp_data[0];\n 2 : uart_tx >;= temp_data[1];\n 3 : uart_tx >;= temp_data[2];\n 4 : uart_tx >;= temp_data[3];\n 5 : uart_tx >;= temp_data[4];\n 6 : uart_tx >;= temp_data[5];\n 7 : uart_tx >;= temp_data[6];\n 8 : uart_tx >;= temp_data[7];\n 9 : uart_tx >;= 1; \n default: uart_tx >;= 0; \n endcase\n end\n \n //控制LED翻转\n always@(posedge Clk or negedge Reset)\n if(!Reset)\n LED >;= 0;\n else if(div_count ==MCNT_DIV && bit_count == MCNT_BIT)\n LED >;= ~LED;\n else\n LED >;= 0;\n\nendmodule\n\n```\n\n然后编写testbench测试文件 \n\n代码如下(在测试中我们将时间参数减小便于调试仿真)\n\n```\n`timescale 1ns / 1ps\nmodule uart_tx0_tb();\n reg [7:0]data;\n reg Clk;\n reg Reset;\n wire uart_tx;\n wire LED;\n uart_tx0 uart_tx00(\n .data(data),\n .Clk(Clk),\n .Reset(Reset),\n .uart_tx(uart_tx),\n .LED(LED)\n );\n\n //时间计数参数调小 便于测试仿真\n defparam uart_tx0.DELAY = 500000 -1;\n \n initial Clk = 0;\n always #10 Clk = ~Clk;\n \n initial begin\n Reset = 0;\n data = 0;\n #201\n Reset = 1;\n #100\n data = 8\'h57;\n #20000000;\n data = 8\'h75;\n #20000000;\n $stop;\n end\n \nendmodule\n\n```\n\n仿真波形如下:\n\n对uart发送进行优化 加入Send_GO信号 Baud_Set信号波特率设置,支持 6种,baud set为 3 位\n\n以及发送完成信号\n\n代码如下\n\n```\nmodule uart_tx_3(\n Clk,\n Reset_n,\n Data,\n Send_G0,\n Baud_Set,\n uart_tx,\n Tx_Done\n );\n input Clk; //模块全局时钟 50MHz \n input Reset_n; //模块全局复位信号\n input [7:0]Data; //待传输 8bit 数\n input Send_G0; //发送使能信号\n input [2:0]Baud_Set; //baud set 波特率设置,支持 6种,baud set为 3 位\n output reg uart_tx; //串口发送信号输出\n output reg Tx_Done; //发送结束信号,一个时钟周期高电平\n \n parameter MCNT_BIT = 10-1;\n\n //波特率\n reg [15:0]bps_DR;\n reg [15:0]div_count; //波特率计数器\n reg [3:0]bit_count;\n reg [7:0]temp_data;\n \n always@(*)\n if(!Reset_n)\n bps_DR >;= 1000000000/9600/20;\n else begin\n case(Baud_Set)\n 0:bps_DR = 1000000000/4800/20;\n 1:bps_DR = 1000000000/9600/20;\n 2:bps_DR = 1000000000/19200/20;\n 3:bps_DR = 1000000000/38400/20;\n 4:bps_DR = 1000000000/57600/20;\n 5:bps_DR = 1000000000/115200/20;\n default:bps_DR >;= 1000000000/9600/20; \n endcase\n end\n \n always@(posedge Clk or negedge Reset_n)\n if(!Reset_n)\n div_count >;= 0;\n else if (Send_G0) begin\n if(div_count == bps_DR -1)\n div_count >;= 0;\n else\n div_count >;= div_count + 1\'d1;\n end\n else\n div_count >;= 0;\n \n always@(posedge Clk or negedge Reset_n)\n if(!Reset_n)\n bit_count >;= 0;\n else if(div_count == bps_DR -1)begin\n if(bit_count == MCNT_BIT)\n bit_count >;= 0;\n else\n bit_count >;= bit_count + 1\'d1; \n end\n \n always@(posedge Clk )\n if(Send_G0)\n temp_data >;= Data;\n else\n temp_data >;= temp_data;\n \n always@(posedge Clk or negedge Reset_n)\n if(!Reset_n)\n uart_tx >;= 0;\n else begin\n case (bit_count)\n 0 : uart_tx >;= 0 ;\n 1 : uart_tx >;= temp_data[0];\n 2 : uart_tx >;= temp_data[1];\n 3 : uart_tx >;= temp_data[2];\n 4 : uart_tx >;= temp_data[3];\n 5 : uart_tx >;= temp_data[4];\n 6 : uart_tx >;= temp_data[5];\n 7 : uart_tx >;= temp_data[6];\n 8 : uart_tx >;= temp_data[7];\n 9 : uart_tx >;= 1; \n default: uart_tx >;= uart_tx; \n endcase\n end\n \n \n always@(posedge Clk or negedge Reset_n)\n if(!Reset_n)\n Tx_Done >;= 0;\n else if ((bit_count == MCNT_BIT)&& (div_count == bps_DR -1))\n Tx_Done >;= 1;\n else \n Tx_Done >;= 0;\n \nendmodule\n\n```\n\ntestbench代码:\n\n```\n`timescale 1ns / 1ps\nmodule uart_tx_3_tb();\n reg [7:0]Data;\n reg Clk;\n reg Reset_n;\n reg Send_G0;\n reg [3:0]Baud_Set;\n wire uart_tx;\n wire Tx_Done;\n uart_tx_3 uart_tx_31(\n .Clk(Clk),\n .Reset_n(Reset_n),\n .Data(Data),\n .Send_G0(Send_G0),\n .Baud_Set(Baud_Set),\n .uart_tx(uart_tx),\n .Tx_Done(Tx_Done)\n );\n \n \n initial Clk =1;\n always#10 Clk = ~Clk;\n initial begin\n Reset_n = 0;\n Data =0;\n Send_G0 = 0;\n Baud_Set = 5;\n #201\n Reset_n = 1;\n #100\n \n Data = 8\'h57;\n Send_G0 = 1;\n #20;\n @(posedge Tx_Done);\n Send_G0 = 0;\n #20000;\n \n Data = 8\'h75;\n Send_G0 = 1;\n #20;\n @(posedge Tx_Done);\n Send_G0 = 0;\n #20000;\n $stop;\n end \nendmodule\n\n```\n\n其中在testbench中 新语法 @(posedge TX_done),死循环,一直等待TX_done的到来,再执行下一语句\n\n仿真结果\n\n## 3,实现UART-接收控制器\n\n### 3.1UART串口接收逻辑分析\n\n把接收到的8位串行数据还原出8位并行数据\n\n\nuart串口来说 u有可能不是一个系统 ,两者之间仅通过一根信号线进行通信,那接收方如何恰好接收到数据呢?uart作为异步通信协议,没有同步信号来实现这些功能的。所以在通信之前,收发双方必须提前约定好各种参数(波特率,数据位个数,校验位,停止位位宽等等)\n\n\n要点1:对于串口接收来说,我们应该怎么样从串行数据中准确的获取到每一位数据?要点1:对于串口接收来说,我们应该怎么样从串行数据中准确的获取到每一位数据?\n\n\n每一位数据的中点\n\n\n 当对于数据线上的每一位进行采样时,一般情 况下认为每一位数据的中间点是最稳定的。因此一般应用中,采集中间时刻时的电平即认为 是此位数据的电平,如下图所示。\n\n但是在实际工业应用中,现场往往有非常强的电磁干扰,只采样一次就作为该数据的电 平状态是不可靠的。很有可能恰好采集到被干扰的信号而导致结果出错,因此这里提出以下 改进型的单bit数据接收方式示意图,使用多次采样求概率的方式进行状态判定,如下图所 示。\n\n 在上图中,将每一位数据再平均分成了**16**小段。对于Bit_x这一位数据,考虑到数据 在刚刚发生变化和即将发生变化的这一时期,数据极有可能不稳定的(用深灰色标出的两 段),在这两个时间段采集数据,很有可能得到错误的结果,因此判定这两段时间的电平无 效,采集时直接忽略。而中间这一时间段(用浅灰色标出),数据本身是比较稳定的,一般都代表了正确的结果。也就是前面提到的中间测量方式,但是也不排除该段数据受强电磁干 扰而出现错误的电平脉冲。因此对这一段电平,进行多次采样,并求高低电平发生的概率, 6 次采集结果中,取出现次数多的电平作为采样结果。例如,采样 6 次的结果分别为 1/1/1/1/0/1/,则取电平结果为 1,若为0/0/1/0/0/0,,则取电平结果为0,当 6次采样结果中1 和0各占一半(各3次),则可判断当前通信线路环境非常恶劣,数据不具有可靠性,不进行处理。\n\nFPGA中有专门的边沿检测电路:\n\n检测到uart信号下降沿开始 \n\n在什么时候停止呢?\n\n使用位计数器\n\n### 3.2编写代码分析** **\n\n//波特率计数器逻辑\n\n//UART信号边沿检测逻辑\n\n//波特率计数器使能逻辑\n\n//位计数器逻辑\n\n//位接收逻辑\n\n//接收完成标志信号\n\n**单 bit 异步信号同步设计**\n\n这里串口接收的信号uart_rx相对于FPGA内部信号来说是一个异步信号,如不进行处理直接将其输入使用,容易出现时序违例导致亚稳态。uart_rx信号由外界输入得到,在什么时候得到,我们无法预知和控制, 而其他信号都是由时钟信号驱动,所以uart_rx信号有可能在Clk信号上升沿很近的位置发生变化,甚至同步发生变化,就会导致D触发器无法正确存储D端口数据,无法正确有效的判断存储这个时刻的uart状态,就会引起D触发器输出震荡---->;从而产生亚稳态。\n\n因此这里就需要先将信号同步到 FPGA的时钟域内才可以供后续模块使用,常见的同步方法即使用两级触发器,也就是使 用触发器对信号打两拍的方式进行与系统时钟进行同步,参考电路即如下图所示。其中 uart_rx 为异步串口输入信号,uart_rx_sync2为同步后的信号。\n\n设计代码如下:\n\n```\nmodule uart_rx_0(\n Clk,\n Reset_n,\n uart_rx,\n Baud_Set,\n Data,\n Rx_done\n );\n input Clk;\n input Reset_n;\n input uart_rx;\n input [2:0]Baud_Set;\n output reg [7:0]Data;\n output reg Rx_done;\n \n reg [1:0]uart_rx_r;\n wire posedge_uart_rx;\n wire negedge_uart_rx; \n reg [9:0]Bps_DR; \n reg [9:0]div_count;\n reg [7:0]bit_count; //每位分成16次采样\n reg RX_EN; //检测到下降沿----发送使能 \n reg uart_rx_sync1;\n reg uart_rx_sync2;\n reg [2:0]r_data[7:0];\n reg [2:0]start_bit;\n reg [2:0]stop_bit;\n \n //异步信号转同步\n always@(posedge Clk or negedge Reset_n)\n if(!Reset_n)begin\n uart_rx_sync1 >;= 0;\n uart_rx_sync2 >;= 0;\n end\n else begin\n uart_rx_sync1 >;= uart_rx;\n uart_rx_sync2 >;= uart_rx_sync1;\n end\n \n \n //边沿检测 \n always@(posedge Clk)begin\n uart_rx_r[0] >;= uart_rx_sync2;\n uart_rx_r[1] >;= uart_rx_r[0];\n end\n assign posedge_uart_rx = (uart_rx_r == 2\'b01);\n assign negedge_uart_rx = (uart_rx_r == 2\'b10);\n //波特率设置\n always@(*)\n case(Baud_Set)\n 0: Bps_DR = 1000000000/4800/20/16 - 1;\n 1: Bps_DR = 1000000000/9600/20/16 - 1;\n 2: Bps_DR = 1000000000/19200/20/16 - 1;\n 3: Bps_DR = 1000000000/38400/20/16 - 1;\n 4: Bps_DR = 1000000000/57600/20/16 - 1;\n 5: Bps_DR = 1000000000/115200/20/16 - 1;\n default: Bps_DR = 1000000000/9600/20/16 - 1;\n endcase\n \n always@(posedge Clk or negedge Reset_n)\n if(!Reset_n)\n RX_EN >;= 0;\n else if(negedge_uart_rx)\n RX_EN >;= 1;\n// else if(Rx_done || (stop_bit >;= 4))\n else if((div_count == 1)&&(bit_count ==0)&&(uart_rx_sync2 == 1))\n RX_EN >;= 0;\n else if(Rx_done)\n RX_EN >;= 0; \n \n \n //分频计数器\n always@(posedge Clk or negedge Reset_n)\n if(!Reset_n)\n div_count >;= 0;\n else if(RX_EN) begin\n if(div_count == Bps_DR)\n div_count >;= 0;\n else\n div_count >;= div_count+ 1\'b1;\n end\n else\n div_count >;= 0;\n //位计数器 \n always@(posedge Clk or negedge Reset_n)\n if(!Reset_n) \n bit_count >;= 0;\n else if(RX_EN) begin\n if(div_count == 1)begin\n //if(bit_count == 160 -1 | bit_count == 12 && (start_bit >;2))\n if(bit_count == 159 )\n bit_count >;= 0;\n else\n bit_count >;= bit_count + 1\'b1; \n end \n end\n else\n bit_count >;= bit_count;\n \n always@(posedge Clk or negedge Reset_n)\n if(!Reset_n)begin\n start_bit >;= 0;\n r_data[0] >;= 0;\n r_data[1]>;= 0;\n r_data[2]>;= 0;\n r_data[3]>;= 0;\n r_data[4]>;= 0;\n r_data[5]>;= 0;\n r_data[6]>;= 0;\n r_data[7]>;= 0;\n stop_bit >;= 0;\n end\n else if(div_count == 1)begin\n case(bit_count)\n 0:begin\n start_bit >;= 0;\n r_data[0] >;= 0;\n r_data[1]>;= 0;\n r_data[2]>;= 0;\n r_data[3]>;= 0;\n r_data[4]>;= 0;\n r_data[5]>;= 0;\n r_data[6]>;= 0;\n r_data[7]>;= 0;\n stop_bit >;= 0;\n end\n 6,7,8,9,10,11 :start_bit >;= start_bit + uart_rx_sync2;\n 22,23,24,25,26,27:r_data[0] >;= r_data[0] + uart_rx_sync2;\n 38,39,40,41,42,43:r_data[1] >;= r_data[1] + uart_rx_sync2;\n 54,55,56,57,58,59:r_data[2] >;= r_data[2] + uart_rx_sync2;\n 70,71,72,73,74,75:r_data[3] >;= r_data[3] + uart_rx_sync2;\n 86,87,88,89,90,91:r_data[4] >;= r_data[4] + uart_rx_sync2;\n 102,103,104,105,106,107:r_data[5] >;= r_data[5] + uart_rx_sync2;\n 118,119,120,121,122,123:r_data[6] >;= r_data[6] + uart_rx_sync2;\n 134,135,136,137,138,139:r_data[7] >;= r_data[7] + uart_rx_sync2;\n 150,151,152,153,154,155:stop_bit >;= stop_bit + uart_rx_sync2;\n default:begin\n start_bit >;= start_bit;\n r_data[0] >;= r_data[0];\n r_data[1] >;= r_data[1];\n r_data[2] >;= r_data[2];\n r_data[3] >;= r_data[3];\n r_data[4] >;= r_data[4];\n r_data[5] >;= r_data[5];\n r_data[6] >;= r_data[6];\n r_data[7] >;= r_data[7];\n stop_bit >;= stop_bit ;\n end\n endcase \n end\n always@(posedge Clk or negedge Reset_n)\n if(!Reset_n)\n Data >;= 0;\n else if((div_count == 1) && (bit_count == 159)) begin\n Data[0] >;= (r_data[0] >;= 4) ? 1 : 0;\n Data[1] >;= (r_data[1] >;= 4) ? 1 : 0;\n Data[2] >;= (r_data[2] >;= 4) ? 1 : 0;\n Data[3] >;= (r_data[3] >;= 4) ? 1 : 0;\n Data[4] >;= (r_data[4] >;= 4) ? 1 : 0;\n Data[5] >;= (r_data[5] >;= 4) ? 1 : 0;\n Data[6] >;= (r_data[6] >;= 4) ? 1 : 0;\n Data[7] >;= (r_data[7] >;= 4) ? 1 : 0;\n end \n \n always@(posedge Clk or negedge Reset_n)\n if(!Reset_n) \n Rx_done >;= 0;\n else if((div_count == 1) && (bit_count == 159)) \n Rx_done >;= 1;\n else\n Rx_done >;= 0;\n \n \nendmodule\n\n```\n\n编写测试代码:\n\n```\n`timescale 1ns / 1ps\nmodule uart_rx_0_tb();\n reg Clk;\n reg Reset_n;\n reg uart_rx;\n reg [2:0]Baud_Set;\n wire [7:0]Data;\n wire Rx_done;\n\n uart_rx_0 uart_rx_0(\n .Clk(Clk),\n .Reset_n(Reset_n),\n .uart_rx(uart_rx),\n .Baud_Set(Baud_Set),\n .Data(Data),\n .Rx_done(Rx_done)\n );\n \n initial Clk = 1;\n always #10 Clk = ~Clk;\n \n initial begin\n Reset_n = 0;\n uart_rx = 1;\n #201;\n Reset_n = 1;\n Baud_Set = 5;\n #200;\n uart_tx(8\'b01110101);\n #90000;\n uart_tx(8\'b10111010);\n #90000;\n uart_tx(8\'b11100110);\n #90000;\n #2000;\n $stop;\n end\n \n task uart_tx;\n input [7:0]tx_data;\n begin\n uart_rx = 1;\n #20;\n uart_rx = 0;\n #8680;\n uart_rx = tx_data[0];\n #8680;\n uart_rx = tx_data[1];\n #8680;\n uart_rx = tx_data[2];\n #8680;\n uart_rx = tx_data[3];\n #8680;\n uart_rx = tx_data[4];\n #8680;\n uart_rx = tx_data[5];\n #8680;\n uart_rx = tx_data[6];\n #8680;\n uart_rx = tx_data[7];\n #8680;\n uart_rx = 1;\n #8680;\n end\n endtask\n\n \nendmodule\n\n```\n\n仿真波形如下:\n\n**以上实现了Uart接收模块,然后设计顶层模块----->;控制LED**\n\n代码如下:\n\n```\nmodule uart_rx_LED(\n Clk,\n Reset_n,\n uart_rx,\n LED,\n Rx_Data\n );\n \n input Clk;\n input Reset_n;\n input uart_rx;\n output reg LED;\n output Rx_Data;\n wire Rx_Done;\n uart_rx_0 uart_rx_00(\n .Clk(Clk),\n .Reset_n(Reset_n),\n .uart_rx(uart_rx),\n .Baud_Set(5),\n .Data(Rx_Done),\n .Rx_done(Rx_Done)\n );\n always@(posedge Clk or negedge Reset_n)\n if(!Reset_n)\n LED >;= 0;\n else if(Rx_Done)\n LED >;= ~LED;\nendmodule\n\n```\n\n至此,uart串口发送-接收逻辑到此结束!\n -->