寫代碼,記筆記,,防忘記,,須牢記。 寫串口的Verilog代碼關(guān)鍵是要搞明白RS232串口的通信協(xié)議,,它并不像單片機,,直接讀寫SBUF就可實現(xiàn)串口的收發(fā)功能,收發(fā)整個字節(jié),。而FPGA要一位一位的收發(fā),,因此必須了解RS232的數(shù)據(jù)格式。 起始位:RS232約定一位起始位“0”。 停止位:約定停止位為“1”,??蛇x一位或兩位停止位。 奇偶校驗位:可選,。 通過串口發(fā)送數(shù)據(jù)時,,要嚴格遵守RS232的數(shù)據(jù)格式,先發(fā)送起始位,,然后是數(shù)據(jù),,最后是停止位(無奇偶校驗的情況)。 通過串口接收數(shù)據(jù)時,,若接收端無數(shù)據(jù)輸入,會一直處于高電平,,若開始接收數(shù)據(jù),,會首先收到來自串口的起始位“0”,然后是要接收的數(shù)據(jù),,最后為停止位(無奇偶校驗的情況),。所以對于接收模塊,可如此設(shè)計,,F(xiàn)PGA一直檢測接收端是否有下降沿到來,,直到檢測到下降沿,才開始接收數(shù)據(jù),。 波特率設(shè)置的重要性不言而喻,,毋庸贅述。 此設(shè)計為最基礎(chǔ)的串口收發(fā)代碼,,控制邏輯簡單,,適合編寫第一次編寫串口代碼的朋友。 此設(shè)計收發(fā)的數(shù)據(jù)格式為1位起始位,,1位停止位,,無奇偶校驗位,8位數(shù)據(jù)位,。波特率為19200,,代碼中可隨意更改。 具體Verilog代碼如下: 頂層模塊 `timescale 1ns / 1ps //////////////////////////////////////////////////////////////////////////////// // Company : 杭州電子科技大學(xué) // Engineer : 曉曉川 // Create Date : 2012.08.26 // Design Name : serial_test // Module Name : serial_test // Project Name: serial_test // Target Device: CycloneII EP2C5T144C8 // Tool versions: Quartus II 11.0 // Revision : V1.0 // Description : 一個極為簡單的串口收發(fā)工程,,適于串口收發(fā)的入門,。只能收發(fā)單個字節(jié),沒有 // 奇偶校驗位。 // 工作流程為:串口發(fā)送數(shù)據(jù)給FPGA,,以LED燈的亮滅直觀顯示接收到數(shù)據(jù),,按下 // 相應(yīng)按鍵并彈起后,F(xiàn)PGA又將接收到的串口數(shù)據(jù)發(fā)送出去。 // Additional Comments : // //////////////////////////////////////////////////////////////////////////////// module serial_test(clk,rst_n,ena,txd,rxd,data); input clk; //系統(tǒng)輸入時鐘 input rst_n; //異步復(fù)位 input ena; //FPGA發(fā)送使能,,即按鍵輸入端 input rxd; //FPGA接收端 output txd; //FPGA發(fā)送端 output [7:0] data; //至LED顯示的數(shù)據(jù) wire [7:0] data; wire txd; wire clk2; //PLL輸出時鐘 wire clk_baud; //波特率時鐘 PLL u1(.inclk0(clk),.c0(clk2)); //PLL輸出低頻時鐘 clk_baud_gen u2(.clk(clk2),.rst_n(rst_n),.clk_baud(clk_baud)); //產(chǎn)生波特率時鐘 serial_txd u3(.clk(clk_baud),.rst_n(rst_n),.ena(ena),.data(data),.txd(txd)); //FPGA發(fā)送模塊 serial_rxd u4(.clk(clk_baud),.rst_n(rst_n),.rxd(rxd),.data(data)); //FPGA接收模塊 endmodule 接收模塊 //此模塊是FPGA控制模塊從串口接收數(shù)據(jù),,不接收起始位“0”和停止位“1” //在接收端,若串口沒有數(shù)據(jù)發(fā)出,,則一直處于高電平,,有數(shù)據(jù)發(fā)出時,先發(fā)送起始位“0”,,即如果 //接收端出現(xiàn)由高到低的跳變,,說明串口有數(shù)據(jù)發(fā)出,應(yīng)開始接收 module serial_rxd(rst_n,clk,rxd,data); input rst_n; //全局復(fù)位 input clk; //接收時鐘 input rxd; //FPGA接收串口數(shù)據(jù)的接收端 output [7:0] data; //FPGA接收的來自串口的數(shù)據(jù),,輸出至LED顯示 reg [3:0] cnt; //接收數(shù)據(jù)計數(shù)器 reg rec_reg1; //起始位檢測寄存器1 reg rec_reg2; //起始位檢測寄存器2 reg [7:0] data; //FPGA接收的數(shù)據(jù) always @(posedge clk or negedge rst_n) if(!rst_n) begin rec_reg1<=1'b1; //起始位檢測寄存器置1,, rec_reg2<=1'b1; //處于等待接收狀態(tài) data<=8'hzz; //輸出復(fù)位,LED全滅 end else if(rec_reg1&&rec_reg2) begin rec_reg1<=rxd; //rec_reg1寄存rxd當(dāng)前周期的值 rec_reg2<=rec_reg1; //rec_reg2寄存rxd前一周期的值 end else if(!rec_reg1&&rec_reg2) //檢測rxd下降沿,也即是否有低電平到來 case (cnt) 4'd0:data[0]<=rxd; //接收第一位數(shù)據(jù) 4'd1:data[1]<=rxd; //接收第二位數(shù)據(jù) 4'd2:data[2]<=rxd; //接收第三位數(shù)據(jù) 4'd3:data[3]<=rxd; //接收第四位數(shù)據(jù) 4'd4:data[4]<=rxd; //接收第五位數(shù)據(jù) 4'd5:data[5]<=rxd; //接收第六位數(shù)據(jù) 4'd6:data[6]<=rxd; //接收第七位數(shù)據(jù) 4'd7:begin data[7]<=rxd; //接收第八位數(shù)據(jù) rec_reg1<=1'b1;//數(shù)據(jù)接收完畢,,起始位檢測寄存器復(fù)位,, rec_reg2<=1'b1;//以準備下次接收 end default:begin data<=8'hzz; rec_reg1<=1'b1; rec_reg2<=1'b1; end endcase always @(posedge clk or negedge rst_n) if(!rst_n) cnt<=4'd0; //復(fù)位,接收數(shù)據(jù)技術(shù)器清零 else if(!rec_reg1&&rec_reg2) cnt<=(cnt<4'd7)?cnt+4'd1:4'd0; //檢測到起始位后,,接收數(shù)據(jù)計數(shù)器啟動 endmodule 發(fā)送模塊//此模塊的作用是FPGA控制模塊向串口發(fā)送數(shù)據(jù),,起始位為“0”,停止位為“1” //延時電路的設(shè)計思想為按鍵按下彈起之后開始計時,,時長為1010/11920秒 //延時去抖結(jié)束后給出發(fā)送標志位,,直至FPGA向串口發(fā)送完畢 module serial_txd(rst_n,clk,ena,data,txd); input rst_n; //全局復(fù)位 input clk; //串口發(fā)送時鐘 input ena; //串口發(fā)送使能輸入端,即按鍵輸入端 input [7:0] data; //FPGA向串口發(fā)送的數(shù)據(jù) output txd; //FPGA向串口發(fā)送數(shù)據(jù)的發(fā)送端 reg txd; reg [3:0] cnt; //發(fā)送數(shù)據(jù)計數(shù)器 reg [9:0] cnt_delay; //延時去抖計數(shù)器,延時時間為1010/11920秒 reg ena_reg1; //按鍵狀態(tài)寄存器1 reg ena_reg2; //按鍵狀態(tài)寄存器2 wire tx_flag; //發(fā)送標志位,,高電平表示正在發(fā)送串口數(shù)據(jù) always @(posedge clk or negedge rst_n) if(!rst_n) begin ena_reg1<=1'b1; ena_reg2<=1'b1; cnt_delay<=10'd0; end else if(ena_reg1&!ena_reg2) //檢測按鍵按下后彈起,,即ena的上升沿(因為無動作時連接按鍵的pin處于高電平) case (cnt_delay) 10'd1011:begin cnt_delay<=10'd0; //延時去抖結(jié)束,計數(shù)器清零 ena_reg1<=1'b1; //按鍵狀態(tài)寄存器置1,,等待下次ena上升沿的到來 ena_reg2<=1'b1; end default:cnt_delay<=cnt_delay+10'd1; //檢測到上升沿,,延時去抖計數(shù)器啟動 endcase else begin ena_reg1<=ena; //ena_reg1寄存ena當(dāng)前周期的狀態(tài) ena_reg2<=ena_reg1; //ena_reg2寄存ena前一周期的狀態(tài) end assign tx_flag=((cnt_delay>=10'd1000)&& //延時去抖結(jié)束后給出發(fā)送忙標志,持續(xù)10 (cnt_delay<=10'd1010)); //個周期,,以等待FPGA向串口發(fā)送完畢 always @(posedge clk or negedge rst_n) if(!rst_n) cnt<=4'd0; //串口發(fā)送計數(shù)器復(fù)位 else if(!tx_flag) cnt<=4'd0; //若沒有檢測到串口發(fā)送標志位,,則計數(shù)器等待 else cnt<=(cnt>=4'd10)?4'd11:cnt+4'd1; //檢測到串口發(fā)送標志位,啟動計數(shù)器 always @(posedge clk or negedge rst_n) if(!rst_n) txd<=1'bz; //發(fā)送端復(fù)位,,高阻態(tài) else case (cnt) 4'd0:txd<=1'bz; 4'd1:txd<=1'b0; //發(fā)送起始位 4'd2:txd<=data[0]; //發(fā)送第一位 4'd3:txd<=data[1]; //發(fā)送第二位 4'd4:txd<=data[2]; //發(fā)送第三位 4'd5:txd<=data[3]; //發(fā)送第四位 4'd6:txd<=data[4]; //發(fā)送第五位 4'd7:txd<=data[5]; //發(fā)送第六位 4'd8:txd<=data[6]; //發(fā)送第七位 4'd9:txd<=data[7]; //發(fā)送第八位 4'd10:txd<=1'b1; //發(fā)送停止位 default:txd<=1'bz; endcase endmodule 波特率產(chǎn)生模塊(此模塊的輸入時鐘來自PLL,,頻率為12MHz,PLL模塊為宏功能函數(shù)) //此模塊為波特率生成模塊,,修改BAUDRATE的值可改變波特率 //串口波特率時鐘的高電平僅僅持續(xù)一個clk周期 module clk_baud_gen(clk,rst_n,clk_baud); input clk; //波特率基準時鐘,,此時鐘來自PLL input rst_n; //全局復(fù)位 output clk_baud; //串口波特率時鐘 wire clk_baud; reg [9:0] cnt; //波特率時鐘計數(shù)器 parameter BAUDRATE=10'd625; always @(posedge clk or negedge rst_n) if(!rst_n) cnt<=10'd0; else cnt<=(cnt==BAUDRATE-10'd2)?10'd0:cnt+1'b1; //波特率時鐘計數(shù)器啟動 assign clk_baud=(cnt==BAUDRATE-10'd2); endmodule |
|
來自: 獨孤瑯?gòu)?/a> > 《FPGA》