串口通信也是一個基礎(chǔ)實驗,是FPGA與電腦、單片機、DSP通信的一種最簡單的方案,對通信速率要求不高時可以選擇UART通信。您可能已經(jīng)知道UART時序的控制、波特率的配置等方面的內(nèi)容,但在實際使用時還是會遇到一些問題,比如如何才能恰當?shù)暮推渌K進行銜接?為什么時序明明沒問題,卻無法和其它控制單元成功通信?本文致力于全面解析在設(shè)計UART通信時的思路方法。
UART通信協(xié)議
UART通信的一幀一般由11到12位數(shù)據(jù)組成。1bit的起始位,檢測為低電平表示數(shù)據(jù)開始傳輸;緊接著8bits的數(shù)據(jù);然后是1bit的奇偶校驗位,可以是奇校驗或者偶校驗;最后是1bit或2bits的停止位,必須為高電平,表示一個字符數(shù)據(jù)的傳輸結(jié)束。
其中校驗位是可選的,用來檢驗數(shù)據(jù)是否傳輸正確。如果有校驗位,則需要保證收發(fā)雙方選擇同樣的一種檢驗方式。奇校驗就是保證數(shù)據(jù)中的1是奇數(shù),比如如果8bit數(shù)據(jù)中有3bits的1,校驗位置0;如果有4bits的1,校驗位置1。偶校驗就是保證數(shù)據(jù)中的1是偶數(shù)。
?
波特率的配置
波特率表示數(shù)據(jù)傳輸?shù)乃俾剩瑔挝籦ps,表示位每秒。比如9600bps就表示1s可以傳輸9600bits的數(shù)據(jù)。異步收發(fā)沒有時鐘打拍來控制數(shù)據(jù)的傳輸,就需要保證收發(fā)雙方在波特率設(shè)置上的一致。確保接收數(shù)據(jù)的完整性。
程序中通常使用16倍速率對UART通信時序進行采樣,則UART通信所需的時鐘就是16*bps,如9600bps通信所需的驅(qū)動時鐘大小就是16*9600=153.6kHz。程序中可以使用一個計數(shù)器對系統(tǒng)時鐘分頻產(chǎn)生UART通信時鐘。
// 分頻生成UART通信時鐘
always @(posedge clk50 or negedge rst_n)
if (!rst_n) begin
clkout 《=1‘b0;
cnt《=0;
end
else if(cnt == 16’d162) begin //近似50%占空比
clkout 《= 1‘b1;
cnt 《= cnt + 16’d1;
end
else if(cnt == 16‘d325) begin //50M/(16*9600)
clkout 《= 1’b0;
cnt 《= 16‘d0;
end
else cnt 《= cnt + 16’d1;
UART發(fā)送數(shù)據(jù)時序設(shè)計
通常我們程序中都會設(shè)計一個UART發(fā)送數(shù)據(jù)的開始信號,對這個開始信號的處理方法和“FPGA基礎(chǔ)設(shè)計(二):PS2鍵盤控制及短按、長按”這篇文章對按鍵有效信號處理的方法相同,采用一級寄存,然后進行邏輯判斷,從而產(chǎn)生一個時鐘寬度的有效信號。那么當檢測到有效信號時便可以啟動UART發(fā)送數(shù)據(jù)的過程。
// 檢測發(fā)送命令wrsig的上升沿
always @(posedge clk)
begin
wrsigbuf 《= wrsig;
wrsigrise 《= (~wrsigbuf) & wrsig;
end
// 啟動串口發(fā)送程序
always @(posedge clk)
if (wrsigrise && (~idle)) //當發(fā)送命令有效且線路為空閑時,啟動新的數(shù)據(jù)發(fā)送進程
send 《= 1‘b1;
else if(cnt == 8’d168) //一幀數(shù)據(jù)發(fā)送結(jié)束
send 《= 1‘b0;
UART是按單bit發(fā)送的,因此在控制時序時可以使用一個計數(shù)器控制,在對應(yīng)的計數(shù)位送出對應(yīng)的數(shù)據(jù)。由于我們使用的是16倍時鐘采樣,因此每個數(shù)據(jù)位之間的計數(shù)間隔便是16,一次完整的發(fā)送過程如下所示:
// 串口發(fā)送程序, 16個時鐘發(fā)送一個bit
always @(posedge clk or negedge rst_n)
begin
if (!rst_n) begin
tx 《= 1’b0;
idle 《= 1‘b0;
cnt《=8’d0;
presult《=1‘b0;
end
else if(send == 1’b1) begin
case(cnt) //產(chǎn)生起始位
8‘d0: begin
tx 《= 1’b0;
idle 《= 1‘b1;
cnt 《= cnt + 8’d1;
end
8‘d16: begin
tx 《= datain[0]; //發(fā)送數(shù)據(jù)0位
presult 《= datain[0]^paritymode;
idle 《= 1’b1;
cnt 《= cnt + 8‘d1;
end
8’d32: begin
tx 《= datain[1]; //發(fā)送數(shù)據(jù)1位
presult 《= datain[1]^presult;
idle 《= 1‘b1;
cnt 《= cnt + 8’d1;
end
8‘d48: begin
tx 《= datain[2]; //發(fā)送數(shù)據(jù)2位
presult 《= datain[2]^presult;
idle 《= 1’b1;
cnt 《= cnt + 8‘d1;
end
8’d64: begin
tx 《= datain[3]; //發(fā)送數(shù)據(jù)3位
presult 《= datain[3]^presult;
idle 《= 1‘b1;
cnt 《= cnt + 8’d1;
end
8‘d80: begin
tx 《= datain[4]; //發(fā)送數(shù)據(jù)4位
presult 《= datain[4]^presult;
idle 《= 1’b1;
cnt 《= cnt + 8‘d1;
end
8’d96: begin
tx 《= datain[5]; //發(fā)送數(shù)據(jù)5位
presult 《= datain[5]^presult;
idle 《= 1‘b1;
cnt 《= cnt + 8’d1;
end
8‘d112: begin
tx 《= datain[6]; //發(fā)送數(shù)據(jù)6位
presult 《= datain[6]^presult;
idle 《= 1’b1;
cnt 《= cnt + 8‘d1;
end
8’d128: begin
tx 《= datain[7]; //發(fā)送數(shù)據(jù)7位
presult 《= datain[7]^presult;
idle 《= 1‘b1;
cnt 《= cnt + 8’d1;
end
8‘d144: begin
tx 《= presult; //發(fā)送奇偶校驗位
presult 《= datain[0]^paritymode;
idle 《= 1’b1;
cnt 《= cnt + 8‘d1;
end
8’d160: begin
tx 《= 1‘b1; //發(fā)送停止位
idle 《= 1’b1;
cnt 《= cnt + 8‘d1;
end
8’d168: begin
tx 《= 1‘b1;
idle 《= 1’b0; //一幀數(shù)據(jù)發(fā)送結(jié)束
cnt 《= cnt + 8‘d1;
end
default: begin
cnt 《= cnt + 8’d1;
end
endcase
end
else begin
tx 《= 1‘b1;
cnt 《= 8’d0;
idle 《= 1‘b0;
end
end
UART接收數(shù)據(jù)時序設(shè)計
UART接收數(shù)據(jù)的過程和發(fā)送數(shù)據(jù)的過程是恰好相反的。區(qū)別只在于UART發(fā)送的開始信號和發(fā)送數(shù)據(jù)端tx是從FPGA內(nèi)部的其它模塊產(chǎn)生的;而UART接收的開始信號和接收數(shù)據(jù)段rx送來的數(shù)據(jù)來自其它設(shè)備,因此需要對外部送來的信號進行監(jiān)測。
一幀數(shù)據(jù)的開始位是低電平有效,因此當檢測到rx線上的下降沿時就表示數(shù)據(jù)通信的開始:
always @(posedge clk) //檢測線路的下降沿
begin
rxbuf 《= rx;
rxfall 《= rxbuf & (~rx);
end
// 啟動串口接收程序
always @(posedge clk)
if (rxfall && (~idle)) //檢測到線路的下降沿并且原先線路為空閑,啟動接收數(shù)據(jù)進程
receive 《= 1‘b1;
else if(cnt == 8’d168) //接收數(shù)據(jù)完成
receive 《= 1‘b0;
接收數(shù)據(jù)的過程和發(fā)送一樣,用一個計數(shù)器來控制,這里不再贅述。
發(fā)送與接收數(shù)據(jù)的打包
UART一次通信只能完成一幀,即傳輸一個8bits的數(shù)據(jù),然而我們通常需要很多幀來組成一次完整的通信。如一種簡單常用的氣象采集站通信格式為:“FF(開始幀)+雨量+溫度+濕度+氣壓+風速+風向+CRC校驗+FE(結(jié)束幀)”。這種情況下可以建立一組寄存器專門存儲發(fā)送或接收的數(shù)據(jù)。比如發(fā)送時可以做如下處理:
/********************************************/
//存儲待發(fā)送的串口信息
/********************************************/
reg [7:0] uart_ad [7:0]; //存儲發(fā)送字符
always @(clk)
begin //定義發(fā)送的字符
if(uart_stat==3‘b000) begin
uart_ad[0]《=8’hFF;
uart_ad[1]《=rain;
uart_ad[2]《=temp;
uart_ad[3]《=humi;
uart_ad[4]《=winddir;
uart_ad[5]《=windspeed;
uart_ad[6]《=CRC16;
uart_ad[7]《=8‘hFE;
end
end
接收的原理類似,只不過接收是在通信過程中接收。這樣我們使用一個計數(shù)器控制,并檢測串口通信的狀態(tài),依次將這組寄存器中的值按發(fā)送順序傳入UART發(fā)送數(shù)據(jù)模塊即可。
另外需要做好的一件事就是串口模塊與其它模塊的銜接。我們在建立好上述的寄存器組之后,只要把數(shù)據(jù)來源填充到寄存器組中對應(yīng)的位置即可。但如果數(shù)據(jù)是從RAM、FIFO等模塊中來的,在串口通信時就應(yīng)該對使能信號、FIFO空滿信號等做出合理的判斷和控制,就像“FPGA數(shù)據(jù)采集-傳輸-顯示系統(tǒng)(一):1.2/50μs沖擊電壓測量與顯示”和“FPGA數(shù)據(jù)采集-傳輸-顯示系統(tǒng)(二):基于FPGA的溫度采集和以太網(wǎng)傳輸”兩篇文章中做的一樣。
評論
查看更多