三星電子正式成立機器人業務團隊:推進家用機器人商業化
最近學習瞭解了一些常用的通訊協議,整理了一下,分享出來。另外,歡迎各位關注個人公
眾號——FPGAer的自我修養,正在更新一些FPGA、Verilog相關的基礎知識,期待和同樣愛
好FPGA的你相遇。
1.UART
UART是非同步序列通訊口的總稱。它所包含的RS232\RS449\RS423等等是對應各種非同步序列
通訊口的介面標準和匯流排標準。他們規定了通訊口的電氣特性、傳輸速率、連線特性和機械特
性等一系列內容,實際上屬於通訊網路的底層概念,與通訊協議沒有直接關係。
幾個相關名詞的解釋:
·波特率:每秒鐘傳送的bit的個數。
·起始位:先發出一個邏輯0的訊號,表示傳輸資料的開始。
·資料位:
輸。
·奇偶校驗位:UART傳送時,檢查傳送資料中“1”的個數,自動在奇偶校驗位新增1/0,
用於傳送資料的校驗。
·停止位:一個數據的結束標誌,可以為1位、1.5位、2位的高電平。
·空閒位:處於邏輯1狀態,表示當前線路上無資料傳輸。
·時序圖:
·傳送資料過程:空閒狀態,線路處於高電平,當收到傳送資料指令後,拉低電平一個數
據位的時間,接著資料按從低位到高位依次傳送,資料傳送完畢,接著傳送奇偶校驗位和停止
位(停止位為高電平),一幀資料傳送結束。
·接收資料過程:空閒狀態,線路處於高電平,當檢測到線路的下降沿,說明線路有資料
傳輸,按照約定的波特率從低位到高位接收資料,資料接收完畢,接著接收並比較奇偶校驗位
是否正確,如果正確則通知接收端裝置準備接收資料或存入快取。
由於UART是非同步傳輸,沒有同步傳輸時鐘。為保證資料傳輸的正確性,每個資料有16個
時鐘取樣,取中間的取樣值,以保證不會誤碼或滑碼。
·設計例項:
下面是一個UART的迴環例項程式碼設計:
接收模組uart_rx:
module uart_rx( input rxd, input clk, output receive_ack, output reg [7:0] data_i ); parameter IDLE = 0; parameter RECEIVE = 1; parameter RECEIVE_END = 2; reg [3:0] CS,NS; reg [4:0] count; reg [7:0] data_o_tmp; always@(posedge clk) CS <= NS; always@(*) begin NS <= CS; case(CS) IDLE: if(!rxd) NS = RECEIVE; RECEIVE: if(count == 7) NS = RECEIVE_END;else NS = NS; RECEIVE_END:NS = IDLE; default: NS = IDLE; endcase end always@(posedge clk) if(CS == RECEIVE) count <= count + 1; else if(CS == IDLE | CS == RECEIVE_END) count <= 0; always @(posedge clk) if(CS == RECEIVE)begin data_i[6:0] <= data_i[7:1]; data_i[7] <= rxd; end assign receive_ack = (CS == RECEIVE_END) ? 1 : 0; endmodule
傳送模組uart_tx:
module uart_tx(
input [7:0] data_o,
input clk,
input receive_ack,
output reg txd
);
parameter IDLE = 0;
parameter SEND_START = 1;
parameter SEND_DATA = 2;
parameter SEND_END = 3;
reg [3:0] CS,NS;
reg [4:0] count;
reg [7:0] data_o_tmp;
always @ (posedge clk)
CS <= NS;
always @ (*) begin
NS <= CS;
case(CS)
IDLE: begin if(receive_ack) NS = SEND_START; end
SEND_START: begin NS = SEND_DATA; end
SEND_DATA: begin if(count == 7) NS = SEND_END; end
SEND_END: begin if(receive_ack) NS = SEND_START; end
default: NS = IDLE;
endcase
end
always @(posedge clk)
if(CS == SEND_START)
count <= count + 1;
else if(CS == IDLE | CS == SEND_END)
count <= 0;
else
count <= count;
always @(posedge clk)
if(CS == SEND_START)
data_o_tmp <= data_o;
else if(CS == SEND_DATA)
data_o_tmp[6:0] <= data_o_tmp[7:1];
always @(posedge clk)
if(CS == SEND_START)
txd <= 0;
else if(CS == SEND_DATA)
txd <= data_o_tmp;
else if(CS == SEND_END)
txd <= 1;
endmodule
特定波特率產生模組clk_div:
module clk_div(
input clk,
output reg clk_out
);
parameter baud_rata = 9600;
parameter div_num = 'd125_000_000 /baud_rata; //分頻數等於時鐘頻率除以想要得到的波特率
reg [15:0] num;
always @(posedge clk) begin
if(num == div_num) begin
num <= 0;
clk_out <= 1;
end
else begin
num <= num + 1;
clk_out <= 0;
end
end
endmodule
頂層檔案uart_top:
module uart_top(
input clk,
input rxd,
output txd
);
wire clk_9600;
wire receive_ack;
wire [7:0] data;
uart_tx uart_tx
(
.clk (clk_9600),
.txd (txd),
.data_o (data),
.receive_ack(receive_ack)
);
uart_rx uart_rx
(
.clk (clk_9600),
.rxd (rxd),
.data_i (data),
.receive_ack(receive_ack)
);
clk_div clk_div
(
.clk (clk),
.clk_out (clk_9600)
);
endmodule
2.PS/2
PS/2是一種雙向同步序列通訊協議。介面是一種6針的連線口,但只有四個引腳是有意義
的,分別是Clock(時鐘)、Data(資料)、VCC和GND。其中時鐘和資料引腳是雙向的。PS/2常
用於連線某些輸入裝置,例如滑鼠、鍵盤等。通訊的兩端通過時鐘來同步,通過資料引腳來交
換資料。任何一方想要抑制另外一方的通訊,只需要將時鐘引腳拉低即可。
如果是PC和PS/2鍵盤之間通訊,PC必須做主機,即PC可以抑制鍵盤傳送資料,而鍵盤不能
抑制PC傳送資料。
PS/2的每一位資料幀包含11-12位,具體含義如下:
資料位名稱 | 說明 |
1個起始位 | 總是邏輯0 |
8個數據位 | 低位在前 |
1個奇偶校驗位 | 奇校驗 |
1個停止位 | 總是邏輯1 |
1個應答位 | 僅用在主機對裝置的通訊中 |
·PS/2的時序圖:
由裝置產生時鐘和資料,主機根據時鐘來讀取資料。以FPGA和PS/2鍵盤為例,鍵盤產生時
鍾和資料,FPGA只需要讀資料。當時鐘下降沿時,FPGA記錄資料訊號。
·設計例項:
主機為FPGA,根據PS/2的時序,得到鍵盤的按鍵值。雖然在時序圖中,主機是在時鐘下降
沿讀取資料,但實際上要為了排除噪聲干擾,需要在FPGA端對訊號進行濾波。下面給出設計
程式碼。
module ps2_keyboard(
input clk,
input clr,
input PS2C, //ps2 clk in
input PS2D, //ps2 data in
output [15:0] xkey
);
reg PS2CF;
reg PS2DF;
reg [7:0] ps2c_filter;
reg [7:0] ps2d_filter;
reg [10:0] shift1;
reg [10:0] shift2;
assign xkey = { shift2[8:1], shift1[8:1] };
always @(posedge clk or posedge clr) begin
if (clr) begin
ps2c_filter <= 11'b0;
ps2d_filter <= 11'b0;
PS2CF <= 1;
PS2DF <= 1;
end
else begin
ps2c_filter[7] <= PS2C;
ps2c_filter[6:0] <= ps2c_filter[7:1];
ps2d_filter[7] <= PS2D;
ps2d_filter[6:0] <= ps2d_filter[7:1];
if(ps2c_filter == 8'b1111_1111)
PS2CF <= 1; //去時鐘毛刺
else if(ps2c_filter == 8'b0000_0000)
PS2CF <= 0;
if(ps2d_filter == 8'b1111_1111)
PS2DF <= 1; //去資料毛刺
else if(ps2d_filter == 8'b0000_0000)
PS2DF <= 0;
end
end
always @(negedge PS2CF or posedge clr) begin
if (clr) begin
shift1 <= 11'b0;
shift2 <= 11'b0;
end
else begin
shift1 <= {PS2DF, shift1[10:1]};
shift2 <= {shift1[0], shift2[10:1]};
end
end
endmodule
參考文獻:
[1]湯勇明,張聖清,陸佳華.搭建你的數字積木-數位電路與邏輯設計[M].北京:清華大學出版
社,2017.