1. 程式人生 > >【轉載】串列埠波特率的生成

【轉載】串列埠波特率的生成

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);