• 串口波特率的生成


    FPGA通常工作在MHz,而串口波特率远远低于该频率(最高标准速率115200),所以我们需要想办法生成一个滴答时钟来尽可能的接近串口波特率。这里我们用串口链路的最高速度来举例说明:

    先来啰嗦一段

    在典型的串口设计中,RS-232芯片通常使用1.8432MHz的时钟,因为这样比较容易生成串口的标准波特。1.8432MHz除以16得到115200Hz。当然,也有直接使整个系统工作在11.0592MHz的时钟方案。

    // 假设FPGA时钟信号是1.8432MHz
    // 设计一个4-bit计数器
    reg [3:0] BaudDivCnt;
    always @(posedge clk) 
        BaudDivCnt <= BaudDivCnt + 1; // count forever from 0 to 15
    
    // 滴答时钟(每16个时钟滴答一次,即115200次每秒)
    wire BaudTick = (BaudDivCnt==15);
    

    是不是很简单?
    但是如果你的时钟不是1.8432MHz,比如是2MHz,怎么办?为了从2MHz时钟得到115200Hz, 我们需要除以17.361111111...,然而这并不是一个整数,FPGA也表示不出来。解决的办法是:有时候我们用17来除,有时候用18来除,确保最后的比例是17.361111111即可,这样实现起来就容易多了:

    先来看C代码:

    while(1) // repeat forever
    {
      acc += 115200;
      if(acc>=2000000) 
        printf("*"); 
      else 
        printf(" ");
    
      acc %= 2000000;
    }
    

    代码的执行结果是以一个特定的比率打印"*",这个比率就是17.361111111...。

    为了在FPGA上得到相同的效果,有一个很重要的前提,那就是:我们的串口是可以接受波特率在一定范围内产生偏差的,即115200Hz ± x%。

    如果我们的时钟频率刚好是2的幂次方就最好了,但是显然2MHz不是。为了在FPGA上好做一点,我们需要用一个近似值来代替2000000/115200,这里以1024/59 = 17.356为例。 这已经很接近我们想要的17.361111...了。

    在FPGA上可以这样实现:设定一个10-bit的累加器,累加步长为59,把它的溢出信号作为滴答信号即可:

    // 假设FPGA工作在2.0000MHz
    // 一个11-bit累加器,最高位用作累加器的溢出信号
    reg [10:0] acc;   // 11 bits
    
    // 每次自加59
    always @(posedge clk)
      acc <= acc[9:0] + 59; 
    
    wire BaudTick = acc[10]; // 溢出信号作为滴答信号
    

    在2MHz时钟频率下,每秒滴答115234次,相比于理想波特率115200,其误差为0.03%,这是可以接受的。

    参数化的FPGA波特率发生器:

    前面的例子我们用到了10 bit的累加器,但是随着时钟频率的增加,显然我们需要使用更多的比特。

    我们再以25MHz时钟为例,设置一个16 bit累加器,并且将其参数化

    parameter ClkFreq = 25000000; // 25MHz
    parameter Baud = 115200;
    parameter BaudGenAccWidth = 16;
    parameter BaudGenInc = (Baud<<BaudGenAccWidth)/ClkFreq;
    
    reg [BaudGenAccWidth:0] BaudGenAcc;
    always @(posedge clk)
      BaudGenAcc <= BaudGenAcc[BaudGenAccWidth-1:0] + BaudGenInc;
    
    wire BaudTick = BaudGenAcc[BaudGenAccWidth];
    

    如果你按照上面的方法去做了,细心的同学会发现这里是有问题的。因为verilog使用了32bit的中间结果,而我们在计算BaudGenInc显然溢出了。解决办法如下:

    parameter BaudGenInc = ((Baud<<(BaudGenAccWidth-4)) + (ClkFreq>>5)) / (ClkFreq>>4);
    
  • 相关阅读:
    Gym 100801D Distribution in Metagonia (数学思维题)
    Gym 100801E Easy Arithmetic (思维题)
    GNOME编辑器--gedit 构建基本脚本
    linux默认编辑器 sublime
    su和su-命令的本质区别
    #ifdef #ifndef使用
    linux 安装软件程序
    linux命令行与shell脚本编程大全---更多bash shell命令
    预处理语句--#define、#error和#warning
    FW开发代码规范---小任性(2)
  • 原文地址:https://www.cnblogs.com/christsong/p/5506342.html
Copyright © 2020-2023  润新知