2021年5月3日 星期一

HBLbits_Verilog Basic_Fsm serialdata

 HBLbits_Verilog Basic_Fsm serialdata

See also: Serial receiver

Now that you have a finite state machine that can identify when bytes are correctly received in a serial bitstream, add a datapath that will output the correctly-received data byte. out_byte needs to be valid when done is 1, and is don't-care otherwise.
Note that the serial protocol sends the least significant bit first.
Some timing diagrams

源自於 https://makerpro.cc/2019/08/the-difference-between-rs232-and-uart/

UART 序列埠

UART(Universal Asynchronous Receiver/Transmitter)是 IC 吐資料的對外管道,電壓最高就是 chip 自身的邏輯電壓,通常是 5 V 或 3.3 V。由於電壓低、速度慢,通常用在裝置內部電路板的對外溝通,也就是 debug;如果你想讓程式印出變數值,通常會呼叫 printf( ) 函式對吧,這時字串就會從  UART 的 TXD 跑出來。

UART 只是晶片內序列傳輸模組的通稱,Universal 表示是泛用型,你可以 config 它成為各種形式的序列埠,例如要不要 parity check、bit rate 要多少之類的。換言之,UART 只是稱呼,不是標準,因此沒有定義接頭形狀,接頭形式完全看個人方便而定。

另外有人會問,UART 裡的「非同步 Asynchronous」是什麼意思?不知您有無發現 RS232/UART 只用一條線來傳資料,所以它沒有額外的 clock,沒有 clock 輔助表示「接收端即便知道發射端的 bit rate,自身的資料取樣速度也無法完全與發射端相同」,因此稱為非同步,若是像 I2C/SPI 等傳輸介面是包含 clock 訊號的,就稱為「同步傳輸」。

Protocol 通訊協定

簡單來說,我們可以將 RS232 視為 UART 的高電壓版本,下圖的 MAX232 就是做轉電壓。用有學問一點的說法來講,RS232 定義了實體層,方便各種裝置彼此交換資料,而每個裝置中處理器裡面的 UART 模組,則負責資料收送的功能。

UART 與 RS232 的接線方塊圖(圖片來源:實作派提供)

要提醒各位的是,當兩個裝置相連時,電壓準位要相同,千萬別將 RS232 與 UART 相接,這可能會把 UART 打壞,也就是 chip 會壞掉。UART 與 RS232 最大的差別有兩個:

  • RS232 的 Vpp 電壓較高,有 6 V~30 V;UART 則是較低的 3.3 V 或 5 V
  • RS232 為負邏輯, UART 為正邏輯,因此兩者波形是反相的

兩者的通訊協定如下圖所示(以 PC 為例),事實上它本來還有定義 parity check bit,只不過通常在 debug 連線時,大家只是看看變數,而且我目前在 debug 上也還沒看過有人用過 parity,但還是建議大家在長距離大量的連續資料傳送時,用一下 parity 比較保險。

UART 與 RS232 的資料格式(圖片來源:實作派提供)

  • Idle

表示 Serial port 資料送完了,目前沒事做,正在等待下一筆資料中,UART level 固定在 H。

  • Start

將狀態反相,是送資料前的準備動作,這樣接收端才知道後面有資料要送,UART level 固定為 L。

  • D0~D7

送資料的順序是 D0 先送,D7 最後送,所以在示波器顯示的波形是 D0:D7,在判讀資料前記得先在腦中反序變成 D7:D0 再來判讀,也別忘了 RS232 是負邏輯喔!

  • Stop

資料送完後,要變成 Stop 狀態。Stop bit 很妙,我遇過 Stop bit 太短導致接收端誤判的情況(最短一個 bit,最長其實可以很長),由你決定。

實際上 Stop bit 本身可視為下個 bit 的 idle 狀態,因為兩個狀態其實一樣。邏輯上來說,你可以把 Stop state 經過 2 bit 後才視為 Idle,但實際上 D7 送完,接收器馬上就會準備接收下個 byte,除非你跟我一樣遇到兩光接收器,非得每個 byte 都間格一段時間,不然 stop bit 設定為 1 bit 應該就可以了。


module top_module(
    input clk,
    input in,
    input reset,    // Synchronous reset
    output [7:0] out_byte,
    output done
); //
    localparam idle=0,start=1,data=2,stop=3,error=4;
    
    reg[2:0] state, next_state;
    reg[3:0] cnt;
    reg done_r;
    reg[7:0] out;
    
//transition
    always@(*)begin
        case(state)
            idle:next_state=in?idle:start;
            start:next_state=data;
            data:next_state=(cnt==8)?(in?stop:error):data;
            stop:next_state=in?idle:start;
            error:next_state=in?idle:error;
        endcase
    end
    
//state
    always@(posedge clk)begin
        if(reset)
            state <= idle;
        else
            state <= next_state;
    end
    
//out
    always@(posedge clk)begin
        if(reset)
            out<=0;
        else
            case(next_state)
                start:out<=0;
                data:out<={in,out[7:1]}; //移位寄存器
            endcase
    end
    
//cnt
    always@(posedge clk)begin
        if(reset)
            cnt<=0;
        else
            case(next_state)
                start:cnt<=0;
                data:cnt<=cnt+1;
                default:cnt<=cnt;
            endcase
    end
    
//done_r
    always@(posedge clk)
        case(next_state)
            stop:done_r <= 1;
            default:done_r <= 0;
        endcase
    
    assign done = done_r;
    assign out_byte = out;
   
endmodule

//另一方法

module top_module(
    input clk,
    input in,
    input reset,    // Synchronous reset
    output [7:0] out_byte,
    output done
); //
    //定义状态
    parameter [3:0] IDLE  = 4'd0;
    parameter [3:0] START = 4'd1;
    parameter [3:0] BIT1  = 4'd2;
    parameter [3:0] BIT2  = 4'd3;
    parameter [3:0] BIT3  = 4'd4;
    parameter [3:0] BIT4  = 4'd5;
    parameter [3:0] BIT5  = 4'd6;
    parameter [3:0] BIT6  = 4'd7;
    parameter [3:0] BIT7  = 4'd8;
    parameter [3:0] BIT8  = 4'd9;
    parameter [3:0] STOP  = 4'd10;
    parameter [3:0] ERROR  = 4'd11;
    reg [3:0] state,nstate;
    reg [7:0] data_r;
    //状态转移
    always @(posedge clk)begin
        if(reset)begin
            state <= IDLE;
        end
        else begin
           state <= nstate; 
        end
    end
    
    always @(*)begin
       nstate = IDLE;
        case(state)
            IDLE: nstate = in? IDLE:START;
            START:nstate = BIT1;
            BIT1: nstate = BIT2;
            BIT2: nstate = BIT3;
            BIT3: nstate = BIT4;
            BIT4: nstate = BIT5;
            BIT5: nstate = BIT6;
            BIT6: nstate = BIT7;
            BIT7: nstate = BIT8;
            BIT8: nstate = in? STOP:ERROR;
            STOP: nstate = in? IDLE:START;
            ERROR: nstate = in? IDLE:ERROR;
            default: nstate = IDLE;
        endcase
    end
    //寄存输入数据
    always @(posedge clk)begin
        case(nstate)
            BIT1: data_r <= {in,data_r[7:1]};
            BIT2: data_r <= {in,data_r[7:1]};
            BIT3: data_r <= {in,data_r[7:1]};
            BIT4: data_r <= {in,data_r[7:1]};
            BIT5: data_r <= {in,data_r[7:1]};
            BIT6: data_r <= {in,data_r[7:1]};
            BIT7: data_r <= {in,data_r[7:1]};
            BIT8: data_r <= {in,data_r[7:1]}; 
        endcase
    end
    //输出
    assign done = (state == STOP);
    assign out_byte = (state == STOP)? data_r:0;
endmodule
   




沒有留言:

張貼留言

Messaging API作為替代方案

  LINE超好用功能要沒了!LINE Notify明年3月底終止服務,有什麼替代方案? LINE Notify將於2025年3月31日結束服務,官方建議改用Messaging API作為替代方案。 //CHANNEL_ACCESS_TOKEN = 'Messaging ...