久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

FPGA串口實現(xiàn)(帶FIFO)

 識文廣博 2016-01-23

看了CrazyBingo的書的前幾章,開始寫代碼實踐了,。剛好自己手上有一個xilinx的開發(fā)板,上面有串口的資源,。索性,就來實現(xiàn)這個串口的功能,。實現(xiàn)串口的發(fā)送和接收,。

串口的協(xié)議很簡單,從網(wǎng)上找了個圖說明下:

clip_image002

串口所用的協(xié)議時UART協(xié)議,。UART協(xié)議是異步的通信協(xié)議,。只用一根線完成數(shù)據(jù)的傳輸。協(xié)議的時序如上圖所示:

在空閑狀態(tài),,即沒有數(shù)據(jù)傳輸時,線上電平保持為高電平,。當(dāng)開始有數(shù)據(jù)傳輸時,,線上電平會拉低,表示開始傳輸數(shù)據(jù),。第一個數(shù)據(jù)位開始位,,然后是數(shù)據(jù)位,數(shù)據(jù)位可以是8位,,也可以使8位到4位,,但是順序是低位在前,高位在后,。最后是一個高電平的停止位,。這樣就完成了一次數(shù)據(jù)傳輸。

以發(fā)送8位數(shù)據(jù)8’h28(二進(jìn)制00101000)為例,。

則線上的電平依次為 1->0->0->0->0->1->0->1->0->0->1,。

由于是異步,沒有時鐘同步,,所以通信的雙方要約定好,,傳輸每一位的時間,這個時間就是由波特率來決定的,。

波特率是指傳輸每一位所用的時間,,如果采用波特率256000.那么每一位傳輸?shù)臅r間為:

1/256000 = 3.9025us。

通信是有發(fā)送和接收的,,所以串口就有兩根線,,一根線發(fā)送數(shù)據(jù),一根線接收數(shù)據(jù)。

以上就是串口的一些介紹,,詳細(xì)的介紹可以自行百度,,可以了解到更多串口的細(xì)節(jié)。

以下實現(xiàn)一個全雙工的串口接收和發(fā)送,,采用FIFO對接受到的數(shù)據(jù)進(jìn)行儲存,,然后接收外部發(fā)送信號,就將FIFO中的接收到的數(shù)據(jù),,通過串口發(fā)送,。波特率為256000,數(shù)據(jù)為8位,。

clip_image004

以上是結(jié)構(gòu)圖,,畫的有點挫。接收模塊接收外部發(fā)送的串口數(shù)據(jù),,將數(shù)據(jù)儲存到FIFO中,,F(xiàn)IFO有外部使能信號(這里是按鍵按下),就將FIFO中的數(shù)據(jù)傳給發(fā)送模塊,發(fā)送模塊在將數(shù)據(jù)發(fā)送出去,。

因為是全雙工模式,,所以對于發(fā)送和接收各有一個波特率產(chǎn)生模塊。用來定時數(shù)據(jù)每一位的時間,。以滿足串口協(xié)議的要求,。

對于發(fā)送模塊:

波特率程序:


module band_generate_tx
#(
parameter baud = 115200 //baud 9600-256000
)
(
input clk,
input rst_n,
input start, //start send data mode
input finish, //send data mode finish
output reg time_arr_tx //baud overflow
);
localparam cnt_16 = 15;
/*********************baud counter value calculate*********************/
reg [7:0] cnt_baud;
//calculate baud counter value
//counter value = 50_000_000 / baud /16 -1;
initial begin
case(baud)
9600 : cnt_baud = 328 -1;
14400 : cnt_baud = 217 -1;
19200 : cnt_baud = 163 -1;
28800 : cnt_baud = 109 -1;
38400 : cnt_baud = 81 -1;
56000 : cnt_baud = 56 -1;
57600 : cnt_baud = 54 -1;
115200: cnt_baud = 27 - 1;
128000: cnt_baud = 24 -1;
256000: cnt_baud = 12 -1;
default :cnt_baud = 12 -1;
endcase
end
/*********************baud counter value calculate*********************/
reg [3:0] count_16;
reg start_flag;
always@(posedge clk or negedge rst_n) begin
if(!rst_n)
start_flag <= 'd0;
else
begin
if(start)
start_flag <= 1;
else if(finish)
start_flag <= 0;
else
start_flag <= start_flag;
end
end
always@(posedge clk or negedge rst_n) begin
if(!rst_n)
begin
count_16 <= 0;
end
else
begin
if(start_flag)
begin
if(count_16 >= cnt_16)
begin
count_16 <= 0;
end
else
begin
count_16 <= count_16 + 1'b1;
end
end
end
end
reg [4:0] count_baud;
always@(posedge clk or negedge rst_n) begin
if(!rst_n)
begin
count_baud <= 0;
time_arr_tx <= 0;
end
else
begin
if(start_flag)
begin
if(count_16 >= cnt_16)
begin
if(count_baud>=cnt_baud)
begin
count_baud <= 'd0;
end
else
count_baud <= count_baud + 1'b1;
if(count_baud == cnt_baud - 1'b1)
time_arr_tx <= 1;
else
time_arr_tx <= 0;
end
else
begin
time_arr_tx <= 0;
end
end
else
begin
count_baud <= 0;
time_arr_tx <= 0;
end
end
end
endmodule


該模塊可以實現(xiàn)波特率從9600到256000.只需要例化模塊的時候,改變波特率參數(shù)值即可,。這里采用initial語句來計算產(chǎn)生不同的波特率需要的計數(shù)值,。因為要產(chǎn)生串口協(xié)議波特率規(guī)定的定時時間,所以要有一個計數(shù)器,,來產(chǎn)生這樣的一個定時時間,。而計數(shù)器計數(shù)是需要一個計數(shù)值的。這里采用initial來計算該計數(shù)值,。

我們知道initial語句是不可綜合的,,但是在有些情況下,它又是可以綜合的,。在計算初始值的時候,,是可以綜合的。比如這里計算計數(shù)器的初始值,?;蛘邔拇嫫鞯某跏蓟?





分析一下上面這段程序,。這段程序是實現(xiàn)波特率控制的,,波特率產(chǎn)生不是什么時候都產(chǎn)生,,是需要的時候才產(chǎn)生。在這里,,就是要有發(fā)送數(shù)據(jù)的時候,,才進(jìn)行波特率產(chǎn)生。這里的start信號是外部給的串口發(fā)送數(shù)據(jù)信號,,但是這樣信號只會持續(xù)一個時鐘周期,,而后面的波特率產(chǎn)生是判斷這個信號一直使能在進(jìn)行波特率產(chǎn)生,那這里就有一個問題,,怎么把這一個只持續(xù)一個時鐘周期的信號給擴展為一直持續(xù)使能了,。這個就是上面代碼的功能,將一個只持續(xù)一個時鐘周期的信號給擴展為一直持續(xù)使能,。

當(dāng)start信號有效的時候,,start_flag為高,即使能,,一個時鐘周期后,,start信號無效,但是start_flag保持使能,。一旦發(fā)送數(shù)據(jù)完成后,,finish信號有效到來,將start_flag無效,,是關(guān)閉波特率產(chǎn)生,。

那么問題來了,,上述代碼會對應(yīng)怎樣的一個數(shù)字電路,,大家可以去想想。我想的結(jié)果是一個除法器加3個門,。


波特率產(chǎn)生模塊,,主要是給發(fā)送模塊提供一個波特率溢出信號,提示發(fā)送模塊,,該位數(shù)據(jù)發(fā)送完成,,準(zhǔn)備發(fā)下一位數(shù)據(jù)。

接下來是發(fā)送模塊,。采用狀態(tài)機設(shè)計:


module uart_txd(
//global signal
input clk, //clk 50M
input rst_n, //reset signal, active-low
//input signal
input start, //start uart send mode
input time_arr, //baud overflow signal
input [7:0] data_in, //8-bits data to be send
//output signal
output reg finish, //send mode finish
output reg uart_txd //serial send data
);
//state
localparam idle_state = 4'd0;
localparam start_state = 4'd1;
localparam send_0_state = 4'd2;
localparam send_1_state = 4'd3;
localparam send_2_state = 4'd4;
localparam send_3_state = 4'd5;
localparam send_4_state = 4'd6;
localparam send_5_state = 4'd7;
localparam send_6_state = 4'd8;
localparam send_7_state = 4'd9;
localparam stop_state = 4'd10;
reg [3:0] state;
reg [3:0] state_next;
always@(posedge clk or negedge rst_n) begin
if(!rst_n)
state <= idle_state;
else
state <= state_next;
end
always@(*) begin
state_next = state;
finish = 0;
uart_txd = 0;
case(state)
idle_state: begin //idle state , uart_txd value is hign level.
uart_txd = 1;
if(start)
state_next = start_state;
end
start_state :
begin //send start bit. uart_txd value is 0
uart_txd = 0;
if(time_arr)
begin
state_next = send_0_state;
end
end
send_0_state :
begin
uart_txd = data_in[0];
if(time_arr)
begin
state_next = send_1_state;
end
end
send_1_state :
begin
uart_txd = data_in[1];
if(time_arr)
begin
state_next = send_2_state;
end
end
send_2_state :
begin
uart_txd = data_in[2];
if(time_arr)
begin
state_next = send_3_state;
end
end
send_3_state :
begin
uart_txd = data_in[3];
if(time_arr)
begin
state_next = send_4_state;
end
end
send_4_state :
begin
uart_txd = data_in[4];
if(time_arr)
begin
state_next = send_5_state;
end
end
send_5_state :
begin
uart_txd = data_in[5];
if(time_arr)
begin
state_next = send_6_state;
end
end
send_6_state :
begin
uart_txd = data_in[6];
if(time_arr)
begin
state_next = send_7_state;
end
end
send_7_state :
begin
uart_txd = data_in[7];
if(time_arr)
begin
state_next = stop_state;
end
end
stop_state :
begin
uart_txd = 1;
if(time_arr)
begin
state_next = idle_state;
finish = 1;
end
end
default: state_next = idle_state;
endcase
end
endmodule


代碼也很簡單,,只要有發(fā)送使能信號,,就啟動狀態(tài)機,,發(fā)送數(shù)據(jù),在每一個波特率溢出信號作用下,,進(jìn)行狀態(tài)轉(zhuǎn)移,,以完成每一位數(shù)據(jù)的發(fā)送。這里,要注意,,停止位的數(shù)據(jù)位一定要為1.這里為什么要為1,,后面再解釋。

接著,,就用一個發(fā)送頂層模塊將上述兩個模塊進(jìn)行封裝,。


module uart_tx
#(
parameter baud = 256000
)
(
input clk,
input rst_n,
input start,
input [7:0] tx_data,
output uart_txd,
output finish
);
wire time_arr_tx;
band_generate_tx #(.baud(baud))
band_generate_tx_1
(
.clk(clk),
.rst_n(rst_n),
.start(start),
.finish(finish),
.time_arr_tx(time_arr_tx)
);
uart_txd uart_txd_1(
.clk(clk),
.rst_n(rst_n),
.start(start),
.time_arr(time_arr_tx),
.data_in(tx_data),
.finish(finish),
.uart_txd(uart_txd)
);
endmodule


封裝的好處,是方面頂層的調(diào)用,。

接著就是接受數(shù)據(jù)的模塊:

首先是波特率產(chǎn)生:


module band_generate_rx
#(
parameter baud = 115200 //baud 9600-256000
)
(
input clk,
input rst_n,
input start,
input finish,
output reg time_arr_rx
);
localparam cnt_16 = 15;
/*********************baud counter value calculate*********************/
reg [7:0] cnt_baud;
//calculate baud counter value
//counter value = 50_000_000 / baud /16 -1;
initial begin
case(baud)
9600 : cnt_baud = 328 -1;
14400 : cnt_baud = 217 -1;
19200 : cnt_baud = 163 -1;
28800 : cnt_baud = 109 -1;
38400 : cnt_baud = 81 -1;
56000 : cnt_baud = 56 -1;
57600 : cnt_baud = 54 -1;
115200: cnt_baud = 27 - 1;
128000: cnt_baud = 24 -1;
256000: cnt_baud = 12 -1;
default :cnt_baud = 12 -1;
endcase
end
/*********************baud counter value calculate*********************/
reg [3:0] count_16;
reg start_flag;
always@(posedge clk or negedge rst_n) begin
if(!rst_n)
start_flag <= 'd0;
else
begin
if(start)
start_flag <= 1;
else if(finish)
start_flag <= 0;
else
start_flag <= start_flag;
end
end
always@(posedge clk or negedge rst_n) begin
if(!rst_n)
begin
count_16 <= 0;
end
else
begin
if(start_flag)
begin
if(count_16 >= cnt_16)
begin
count_16 <= 0;
end
else
begin
count_16 <= count_16 + 1'b1;
end
end
end
end
reg [4:0] count_baud;
always@(posedge clk or negedge rst_n) begin
if(!rst_n)
begin
count_baud <= 0;
time_arr_rx <= 0;
end
else
begin
if(start_flag)
begin
if(count_16 >= cnt_16)
begin
if(count_baud>=cnt_baud)
begin
count_baud <= 'd0;
end
else
count_baud <= count_baud + 1'b1;
if(count_baud == cnt_baud/2 )
time_arr_rx <= 1;
else
time_arr_rx <= 0;
end
else
begin
time_arr_rx <= 0;
end
end
else
begin
count_baud <= 0;
time_arr_rx <= 0;
end
end
end
endmodule


采用和發(fā)送模塊的波特率產(chǎn)生模塊一樣的結(jié)構(gòu),只是波特率的溢出信號時間不一樣,。發(fā)送的波特率溢出是在波特率時間的末尾,而接受的波特率溢出是在波特率時間的中間,。因為在中間采集的數(shù)據(jù)才是正確有效的,。

這里要明白一個東西,,因為數(shù)據(jù)是一位一位傳輸?shù)?,所以?shù)據(jù)改變的時間也是要注意的,,不能在數(shù)據(jù)改變的時候接收數(shù)據(jù),這樣接受的數(shù)據(jù)就有可能不正確,。數(shù)據(jù)變化是在波特率時間的末尾,,所以接收數(shù)據(jù)要在波特率時間的中間。

下面是接收模塊:


module uart_rxd(
//global signal
input clk, //clk signal .50M
input rst_n, //reset signal , active-low
//input signal
input time_arr, //baud overflow signal
input rxd, //rxd input data
output reg [7:0] rxd_data, //receive rxd 8-bits data
output start, //start receive mode
output reg finish //receive mode finish
);
/********************judge rxd falling edge*********************/
reg rxd_r ;
reg rxd_r_r ;
wire rxd_falling ;
always@( posedge clk ) begin
if( !rst_n )
begin
rxd_r <= 'b1 ;
rxd_r_r <= 'b1 ;
end
else
begin
rxd_r <= rxd ;
rxd_r_r <= rxd_r ;
end
end
assign rxd_falling = rxd_r_r & ( !rxd_r ) ;
/********************judge rxd falling edge*********************/
reg [3:0] i;
reg start_flag;
//receive 9-bits data. but the high 8-bits is real data. the last bit is start data
reg [8:0] rxd_data_reg;
always@(posedge clk or negedge rst_n) begin
if(!rst_n)
begin
rxd_data_reg <= 'd0;
i <= 'd0;
end
else
begin
if(start_flag) //receive data
begin
if(time_arr ==1 )
begin
rxd_data_reg <= {rxd,rxd_data_reg[8:1]};
i <= i +4'd1;
end
end
else
begin
rxd_data_reg <= 'd0;
i <='d0;
end
end
end
always@(posedge clk or negedge rst_n) begin
if(!rst_n)
begin
start_flag <= 'b0;
rxd_data <= 'd0;
finish <= 'b0;
end
else
if(rxd_falling)
start_flag <= 1'b1;
else if(i >= 9 && start_flag == 1)
begin
start_flag <= 1'b0;
rxd_data <= rxd_data_reg[8:1];
finish <= 1'b1;
end
else
finish <= 1'b0;
end
//if rxd generate a falling edge,that indicate receive mode start
assign start = rxd_falling;
endmodule


這里沒有采用狀態(tài)機的方式設(shè)計,,這里可以看出,,采用狀態(tài)機設(shè)計,代碼便于理解和編寫,。

封裝上述兩個模塊,,成一個接收模塊:


module uart_rx
#(
parameter baud = 256000
)
(
input clk,
input rst_n,
input uart_rxd,
output [7:0] receive_data,
output finish
);
wire time_arr_rx;
wire start;
band_generate_rx #(.baud(baud))
band_generate_rx_1 (
.clk(clk),
.rst_n(rst_n),
.start(start),
.finish(finish),
.time_arr_rx(time_arr_rx)
);
uart_rxd uart_rxd_1 (
.clk(clk),
.rst_n(rst_n),
.time_arr(time_arr_rx),
.rxd(uart_rxd),
.rxd_data(receive_data),
.start(start),
.finish(finish)
);
endmodule


然后在用一個串口頂層模塊對上面發(fā)送和接受模塊封裝,。


module uart_top
#(
parameter baud_tx = 256000,
parameter baud_rx = 256000
)
(
input clk,
input rst_n,
input uart_rxd, //input serial rxd data
input tx_start, //input start send data module signal
input [7:0] tx_data, //input 8-bits send data
output tx_finish, //output send mode finish
output rx_finish, //output receive mode finish
output uart_txd, //output serial txd data
output [7:0] receive_data //output receive 8-bits data
);
//receive module
uart_rx #(.baud(baud_rx))
uart_rx_1 (
.clk(clk),
.rst_n(rst_n),
.uart_rxd(uart_rxd),
.receive_data(receive_data),
.finish(rx_finish)
);
//send module
//when tx_start is 1, send module will send 8-bit input tx_data to the serial txd
uart_tx #(.baud(baud_tx))
uart_tx_1 (
.clk(clk),
.rst_n(rst_n),
.start(tx_start),
.tx_data(tx_data),
.uart_txd(uart_txd),
.finish(tx_finish)
);
endmodule


這樣,,就完成了整個的串口設(shè)計模塊了。

剩下的就是FIFO模塊了,,這里是調(diào)用xilinx的FIFO IP核,。直接拿來使用,位寬8位,,深度4096.意思可以存儲4096個外部發(fā)送的字節(jié)數(shù)據(jù),。


module test_uart_fifo(
input clk,
input rst_n,
input key,
input rx_finish,
input [7:0] rx_data,
input tx_finish,
//input start,
output reg tx_start,
output [7:0] tx_data
);
//wire start;
//wire full;
wire empty;
reg rd_en;
key_button key_button_1 (
.clk(clk),
.rst_n(rst_n),
.key(key),
.key_down(start)
);
fifo_uart fifo_uart_1 (
.clk(clk), // input clk
.rst(rst_n), // input rst
.din(rx_data), // input [7 : 0] din
.wr_en(rx_finish), // input wr_en
.rd_en(rd_en), // input rd_en
.dout(tx_data), // output [7 : 0] dout
.full(), // output full
.empty(empty) // output empty
);
localparam idle_state = 0;
localparam send_state = 1;
reg start_flag;
reg state;
always@(posedge clk or negedge rst_n) begin
if(!rst_n)
begin
state <= idle_state;
tx_start <= 0;
rd_en <= 0;
end
else
begin
case(state)
idle_state:
begin
if(start_flag)
begin
tx_start <= 1;
state <= send_state;
rd_en <= 1;
end
else
begin
tx_start <= 0;
state <= idle_state;
rd_en <= 0;
end
end
send_state:
begin
tx_start <= 0;
rd_en <= 0;
if(tx_finish)
state <= idle_state;
end
endcase
end
end
always@(posedge clk or negedge rst_n) begin
if(!rst_n)
start_flag <= 0;
else
begin
if(start)
start_flag <= 1;
else if(empty)
start_flag <= 0;
end
end
endmodule


其中key_button模塊,,是檢測按鍵下降沿的。我這里是設(shè)定我按下按鍵,,就將FIFO的所有數(shù)據(jù)發(fā)送出去,。而fifo_uart模塊,是例化FIFO的IP,。

最終的頂層代碼:


module test_uart(
input clk,
input rst_n,
input uart_rxd,
input key,
//input start,
output uart_txd,
output [7:0] LED
);
wire tx_start;
wire rx_finish;
wire tx_finish;
wire [7:0] tx_data;
wire [7:0] receive_data;
uart_top #(.baud_tx(256000),
.baud_rx(256000))
uart_top_1 (
.clk(clk),
.rst_n(rst_n),
.uart_rxd(uart_rxd),
.tx_start(tx_start),
.tx_data(tx_data),
.tx_finish(tx_finish),
.rx_finish(rx_finish),
.uart_txd(uart_txd),
.receive_data(receive_data)
);
test_uart_fifo test_uart_fifo_1 (
.clk(clk),
.rst_n(rst_n),
.key(key),
//.start(start),
.rx_finish(rx_finish),
.rx_data(receive_data),
.tx_finish(tx_finish),
.tx_start(tx_start),
.tx_data(tx_data)
);
assign LED = ~receive_data;
endmodule


代碼也很簡單,,就將兩個模塊連接起來。

綜合,,分配管腳,,然后布局布線,最后下載,。使用串口獵人,,用來發(fā)送數(shù)據(jù)和接收數(shù)據(jù)。

串口測試成功了,。

最終效果如下,,很酷吧。,。發(fā)送的數(shù)據(jù)是Crazybingo寫的書的自序的一部分,。

clip_image006

發(fā)送數(shù)據(jù)后,按下開發(fā)板按鍵,,就將發(fā)送的數(shù)據(jù)發(fā)回,。使用串口獵人捕獲,即可得到數(shù)據(jù),。

在實現(xiàn)這個功能時候,,有遇到一下問題:

發(fā)送單個數(shù)據(jù),接收單個數(shù)據(jù)正確,,但是發(fā)送一串?dāng)?shù)據(jù),,接收就只能接受到最后一個數(shù)據(jù),,而不是發(fā)送的一串?dāng)?shù)據(jù),。

這個問題,我可折騰了好久,。最開始以為是FIFO沒有正常工作,,寫testbench仿真,發(fā)現(xiàn)還真的是有這個問題,。FIFO的復(fù)位信號弄反了,。這個系統(tǒng)是設(shè)定的低電平復(fù)位,而FIFO設(shè)定的高電平復(fù)位,,所以接收數(shù)據(jù)不對,。將復(fù)位信號更正后,,發(fā)現(xiàn)還是有問題。在仿真FIFO,,發(fā)現(xiàn)FIFO是正常工作的,。那出現(xiàn)的問題,肯定就是我的串口模塊的問題,。

各種寫testbench代碼仿真,,仿真后,發(fā)現(xiàn),,接收模塊是沒有問題的,,那問題就應(yīng)該是發(fā)送模塊的問題。通過仿真,,發(fā)現(xiàn)發(fā)送的時序是對的,,確實將每個數(shù)據(jù)按照規(guī)定的時序發(fā)送出去。但是發(fā)現(xiàn)發(fā)完一個數(shù)據(jù)后,,只隔了一個系統(tǒng)時鐘周期,,就馬上發(fā)送第二個數(shù)據(jù)。這里,,就猜想,,是不是數(shù)據(jù)發(fā)送太快,數(shù)據(jù)發(fā)送完,,要在等一個波特率時間在發(fā)送下一個數(shù)據(jù),。百度下,發(fā)現(xiàn),,原來,,發(fā)送的停止位的數(shù)據(jù)是要為1的。而我寫程序的時候,,以為停止位是0或1都沒有關(guān)系,,就給了個0.將這里改正,發(fā)現(xiàn),,程序?qū)α?。能實現(xiàn)發(fā)一串?dāng)?shù)據(jù),然后FPGA在回發(fā)一串?dāng)?shù)據(jù)了,。

所以要注意,,發(fā)送的時候,停止位的數(shù)據(jù)一定要為高電平?。,。。,。,。,。。,。,。。,。,。。,。,。?/p>

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購買等信息,,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請點擊一鍵舉報,。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多