基于verilog的很基礎(chǔ)的RS232串口收發(fā)代碼
寫串口的Verilog代碼關(guān)鍵是要搞明白R(shí)S232串口的通信協(xié)議,它并不像單片機(jī),直接讀寫SBUF就可實(shí)現(xiàn)串口的收發(fā)功能,收發(fā)整個(gè)字節(jié)。而FPGA要一位一位的收發(fā),因此必須了解RS232的數(shù)據(jù)格式。
起始位:RS232約定一位起始位“0”。
停止位:約定停止位為“1”。可選一位或兩位停止位。
奇偶校驗(yàn)位:可選。
通過串口發(fā)送數(shù)據(jù)時(shí),要嚴(yán)格遵守RS232的數(shù)據(jù)格式,先發(fā)送起始位,然后是數(shù)據(jù),最后是停止位(無(wú)奇偶校驗(yàn)的情況)。
通過串口接收數(shù)據(jù)時(shí),若接收端無(wú)數(shù)據(jù)輸入,會(huì)一直處于高電平,若開始接收數(shù)據(jù),會(huì)首先收到來(lái)自串口的起始位“0”,然后是要接收的數(shù)據(jù),最后為停止位(無(wú)奇偶校驗(yàn)的情況)。所以對(duì)于接收模塊,可如此設(shè)計(jì),F(xiàn)PGA一直檢測(cè)接收端是否有下降沿到來(lái),直到檢測(cè)到下降沿,才開始接收數(shù)據(jù)。
波特率設(shè)置的重要性不言而喻,毋庸贅述。
此設(shè)計(jì)為最基礎(chǔ)的串口收發(fā)代碼,控制邏輯簡(jiǎn)單,適合編寫第一次編寫串口代碼的朋友。
此設(shè)計(jì)收發(fā)的數(shù)據(jù)格式為1位起始位,1位停止位,無(wú)奇偶校驗(yàn)位,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 : 一個(gè)極為簡(jiǎn)單的串口收發(fā)工程,適于串口收發(fā)的入門。只能收發(fā)單個(gè)字節(jié),沒有
// 奇偶校驗(yàn)位。
// 工作流程為:串口發(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)輸入時(shí)鐘
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輸出時(shí)鐘
wire clk_baud; //波特率時(shí)鐘
PLL u1(.inclk0(clk),.c0(clk2)); //PLL輸出低頻時(shí)鐘
clk_baud_gen u2(.clk(clk2),.rst_n(rst_n),.clk_baud(clk_baud)); //產(chǎn)生波特率時(shí)鐘
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ā)出時(shí),先發(fā)送起始位“0”,即如果
//接收端出現(xiàn)由高到低的跳變,說明串口有數(shù)據(jù)發(fā)出,應(yīng)開始接收
module serial_rxd(rst_n,clk,rxd,data);
input rst_n; //全局復(fù)位
input clk; //接收時(shí)鐘
input rxd; //FPGA接收串口數(shù)據(jù)的接收端
output [7:0] data; //FPGA接收的來(lái)自串口的數(shù)據(jù),輸出至LED顯示
reg [3:0] cnt; //接收數(shù)據(jù)計(jì)數(shù)器
reg rec_reg1; //起始位檢測(cè)寄存器1
reg rec_reg2; //起始位檢測(cè)寄存器2
reg [7:0] data; //FPGA接收的數(shù)據(jù)
always @(posedge clk or negedge rst_n)
if(!rst_n)
begin
rec_reg1<=1'b1; //起始位檢測(cè)寄存器置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) //檢測(cè)rxd下降沿,也即是否有低電平到來(lái)
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ù)接收完畢,起始位檢測(cè)寄存器復(fù)位,
rec_reg2<=1'b1;//以準(zhǔn)備下次接收
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; //檢測(cè)到起始位后,接收數(shù)據(jù)計(jì)數(shù)器啟動(dòng)
endmodule
發(fā)送模塊
//此模塊的作用是FPGA控制模塊向串口發(fā)送數(shù)據(jù),起始位為“0”,停止位為“1”
//延時(shí)電路的設(shè)計(jì)思想為按鍵按下彈起之后開始計(jì)時(shí),時(shí)長(zhǎng)為1010/11920秒
//延時(shí)去抖結(jié)束后給出發(fā)送標(biāo)志位,直至FPGA向串口發(fā)送完畢
module serial_txd(rst_n,clk,ena,data,txd);
input rst_n; //全局復(fù)位
input clk; //串口發(fā)送時(shí)鐘
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ù)計(jì)數(shù)器
reg [9:0] cnt_delay; //延時(shí)去抖計(jì)數(shù)器,延時(shí)時(shí)間為1010/11920秒
reg ena_reg1; //按鍵狀態(tài)寄存器1
reg ena_reg2; //按鍵狀態(tài)寄存器2
wire tx_flag; //發(fā)送標(biāo)志位,高電平表示正在發(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) //檢測(cè)按鍵按下后彈起,即ena的上升沿(因?yàn)闊o(wú)動(dòng)作時(shí)連接按鍵的pin處于高電平)
case (cnt_delay)
10'd1011:begin
cnt_delay<=10'd0; //延時(shí)去抖結(jié)束,計(jì)數(shù)器清零
ena_reg1<=1'b1; //按鍵狀態(tài)寄存器置1,等待下次ena上升沿的到來(lái)
ena_reg2<=1'b1;
end
default:cnt_delay<=cnt_delay+10'd1; //檢測(cè)到上升沿,延時(shí)去抖計(jì)數(shù)器啟動(dòng)
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)&& //延時(shí)去抖結(jié)束后給出發(fā)送忙標(biāo)志,持續(xù)10
(cnt_delay<=10'd1010)); //個(gè)周期,以等待FPGA向串口發(fā)送完畢
always @(posedge clk or negedge rst_n)
if(!rst_n)
cnt<=4'd0; //串口發(fā)送計(jì)數(shù)器復(fù)位
else if(!tx_flag)
cnt<=4'd0; //若沒有檢測(cè)到串口發(fā)送標(biāo)志位,則計(jì)數(shù)器等待
else
cnt<=(cnt>=4'd10)?4'd11:cnt+4'd1; //檢測(cè)到串口發(fā)送標(biāo)志位,啟動(dòng)計(jì)數(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)生模塊(此模塊的輸入時(shí)鐘來(lái)自PLL,頻率為12MHz,PLL模塊為宏功能函數(shù))
//此模塊為波特率生成模塊,修改BAUDRATE的值可改變波特率
//串口波特率時(shí)鐘的高電平僅僅持續(xù)一個(gè)clk周期
module clk_baud_gen(clk,rst_n,clk_baud);
input clk; //波特率基準(zhǔn)時(shí)鐘,此時(shí)鐘來(lái)自PLL
input rst_n; //全局復(fù)位
output clk_baud; //串口波特率時(shí)鐘
wire clk_baud;
reg [9:0] cnt; //波特率時(shí)鐘計(jì)數(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í)鐘計(jì)數(shù)器啟動(dòng)
assign clk_baud=(cnt==BAUDRATE-10'd2);
endmodule