verilog串口通信程序.docx
- 文档编号:17371126
- 上传时间:2023-04-24
- 格式:DOCX
- 页数:37
- 大小:106.27KB
verilog串口通信程序.docx
《verilog串口通信程序.docx》由会员分享,可在线阅读,更多相关《verilog串口通信程序.docx(37页珍藏版)》请在冰豆网上搜索。
verilog串口通信程序
FPGA实现RS-232串口收发的仿真过程(Quartus+Synplify+ModelSim)(2007-09-1112:
17:
37)
网上关于RS-232的异步收发介绍得很多,最近没事学着摸索用ModelSim来做时序仿真,就结合网上的参考资料和自己的琢磨,做了这个东西。
针对我这个小程序结合FPGA的开发流程,主要走了以下几步:
1.文本程序输入(VerilogHDL)
2.功能仿真(ModelSim,查看逻辑功能是否正确,要写一个TestBench)
3.综合(SynplifyPro,程序综合成网表)
4.布局布线(QuartusII,根据我选定的FPGA器件型号,将网表布到器件中,并估算出相应的时延)
5.时序仿真(ModelSim,根据时延做进一步仿真)
这里贴出我的程序和各个详细步骤,能和各位正在学习的新手们一起分享。
0.原理
略
一、文本程序输入(VerilogHDL)
发送端:
moduletrans(clk,
rst,
TxD_start,
TxD_data,
TxD,
TxD_busy
);
input clk,
rst,
TxD_start;
input[7:
0]TxD_data; //待发送的数据
output TxD, //输出端口发送的串口数据
TxD_busy;
reg TxD;
reg[7:
0] TxD_dataReg; //寄存器发送模式,因为在串口发送过程中输入端不可能一直保持有效电平
reg[3:
0] state;
parameter ClkFrequency=25000000; //时钟频率-25MHz
parameter Baud=115200; //串口波特率-115200
//波特率产生
parameterBaudGeneratorAccWidth=16;
reg [BaudGeneratorAccWidth:
0]BaudGeneratorAcc;
wire[BaudGeneratorAccWidth:
0]BaudGeneratorInc=((Baud<<(BaudGeneratorAccWidth-4))+(ClkFrequency>>5))/(ClkFrequency>>4);
wireBaudTick=BaudGeneratorAcc[BaudGeneratorAccWidth];
wireTxD_busy;
always@(posedgeclkornegedgerst)
if(~rst)
BaudGeneratorAcc<=0;
elseif(TxD_busy)
BaudGeneratorAcc<=BaudGeneratorAcc[BaudGeneratorAccWidth-1:
0]+BaudGeneratorInc;
//发送端状态
wire TxD_ready=(state==0); //当state=0时,处于准备空闲状态,TxD_ready=1
assignTxD_busy=~TxD_ready; //空闲状态时TxD_busy=0
//把待发送数据放入缓存寄存器TxD_dataReg
always@(posedgeclkornegedgerst)
if(~rst)
TxD_dataReg<=8'b00000000;
elseif(TxD_ready&TxD_start)
TxD_dataReg<=TxD_data;
//发送状态机
always@(posedgeclkornegedgerst)
if(~rst)
begin
state<=4'b0000; //复位时,状态为0000,发送端一直发1电平
TxD<=1'b1;
end
else
case(state)
4'b0000:
if(TxD_start)begin
state<=4'b0100;//接受到发送信号,进入发送状态
end
4'b0100:
if(BaudTick)begin
state<=4'b1000; //发送开始位-0电平
TxD<=1'b0;
end
4'b1000:
if(BaudTick)begin
state<=4'b1001; //bit0
TxD<=TxD_dataReg[0];
end
4'b1001:
if(BaudTick)begin
state<=4'b1010; //bit1
TxD<=TxD_dataReg[1];
end
4'b1010:
if(BaudTick)begin
state<=4'b1011; //bit2
TxD<=TxD_dataReg[2];
end
4'b1011:
if(BaudTick)begin
state<=4'b1100; //bit3
TxD<=TxD_dataReg[3];
end
4'b1100:
if(BaudTick)begin
state<=4'b1101; //bit4
TxD<=TxD_dataReg[4];
end
4'b1101:
if(BaudTick)begin
state<=4'b1110; //bit5
TxD<=TxD_dataReg[5];
end
4'b1110:
if(BaudTick)begin
state<=4'b1111; //bit6
TxD<=TxD_dataReg[6];
end
4'b1111:
if(BaudTick)begin
state<=4'b0010; //bit7
TxD<=TxD_dataReg[7];
end
4'b0010:
if(BaudTick)begin
state<=4'b0011; //stop1
TxD<=1'b1;
end
4'b0011:
if(BaudTick)begin
state<=4'b0000; //stop2
TxD<=1'b1;
end
default:
if(BaudTick)begin
state<=4'b0000;
TxD<=1'b1;
end
endcase
endmodule
接收端:
modulercv(clk,
rst,
RxD,
RxD_data,
RxD_data_ready,
);
input clk,
rst,
RxD;
output[7:
0]RxD_data; //接收数据寄存器
output RxD_data_ready; //接收完8位数据,RxD_data值有效时,RxD_data_ready输出读信号
parameter ClkFrequency=25000000; //时钟频率-25MHz
parameter Baud=115200; //波特率-115200
reg[2:
0] bit_spacing;
reg RxD_delay;
reg RxD_start;
reg[3:
0] state;
reg[7:
0] RxD_data;
reg RxD_data_ready;
//波特率产生,使用8倍过采样
parameterBaud8=Baud*8;
parameterBaud8GeneratorAccWidth=16;
wire [Baud8GeneratorAccWidth:
0]Baud8GeneratorInc=((Baud8<<(Baud8GeneratorAccWidth-7))+(ClkFrequency>>8))/(ClkFrequency>>7);
reg [Baud8GeneratorAccWidth:
0]Baud8GeneratorAcc;
always@(posedgeclkornegedgerst)
if(~rst)
Baud8GeneratorAcc<=0;
else
Baud8GeneratorAcc<=Baud8GeneratorAcc[Baud8GeneratorAccWidth-1:
0]+Baud8GeneratorInc;
//Baud8Tick为波特率的8倍-115200*8=921600
wire Baud8Tick=Baud8GeneratorAcc[Baud8GeneratorAccWidth];
//next_bit为波特率-115200
always@(posedgeclkornegedgerst)
if(~rst||(state==0))
bit_spacing<=0;
elseif(Baud8Tick)
bit_spacing<=bit_spacing+1;
wirenext_bit=(bit_spacing==7);
//检测到RxD有下跳沿时,RxD_start置1,准备接收数据
always@(posedgeclk)
if(Baud8Tick)
begin
RxD_delay<=RxD;
RxD_start<=(Baud8Tick&RxD_delay&(~RxD));
end
//状态机接收数据
always@(posedgeclkornegedgerst)
if(~rst)
state<=4'b0000;
else if(Baud8Tick)
case(state)
4'b0000:
if(RxD_start)state<=4'b1000; //检测到下跳沿
4'b1000:
if(next_bit) state<=4'b1001; //bit0
4'b1001:
if(next_bit) state<=4'b1010; //bit1
4'b1010:
if(next_bit) state<=4'b1011; //bit2
4'b1011:
if(next_bit) state<=4'b1100; //bit3
4'b1100:
if(next_bit) state<=4'b1101; //bit4
4'b1101:
if(next_bit) state<=4'b1110; //bit5
4'b1110:
if(next_bit) state<=4'b1111; //bit6
4'b1111:
if(next_bit) state<=4'b0001; //bit7
4'b0001:
if(next_bit) state<=4'b0000; //停止位
default:
state<=4'b0000;
endcase
//保存接收数据到RxD_data中
always@(posedgeclkornegedgerst)
if(~rst)
RxD_data<=8'b00000000;
elseif(Baud8Tick&&next_bit&&state[3])
RxD_data<={RxD,RxD_data[7:
1]};
//RxD_data_ready置位信号
always@(posedgeclkornegedgerst)
if(~rst)
RxD_data_ready<=0;
else
RxD_data_ready<=(Baud8Tick&&next_bit&&state==4'b0001);
endmodule
为了测试收发是否正常,写的TestBench
`timescale1ns/1ns
modulers232_test;
reg clk,
rst,
TxD_start;
reg[7:
0] TxD_data;
wire[7:
0] RxD_data;
wire //RxD,
TxD,
TxD_busy,
RxD_data_ready;
transtrans(.clk(clk),
.rst(rst),
.TxD_start(TxD_start),
.TxD_busy(TxD_busy),
.TxD_data(TxD_data),
.TxD(TxD)
);
rcvrcv(.clk(clk),
.rst(rst),
.RxD(TxD), //收发相接时RxD=TxD
.RxD_data(RxD_data),
.RxD_data_ready(RxD_data_ready)
);
initial begin
TxD_start=0;
TxD_data=0;
clk=0;
rst=1;
#54rst=0;
#70rst=1;
#40TxD_start=1'b1;
#10TxD_data=8'b11011001;
#100TxD_start=1'b0;
end
alwaysbegin
#30clk=~clk;
#10clk=~clk;
end
endmodule
二、综合
三、
FPGA与PC串口自收发通信
串口通信其实简单实用,这里我就不多说,只把自己动手写的verilog代码共享下。
实现的功能如题,就是FPGA里实现从PC接收数据,然后把接收到的数据发回去。
使用的是串口UART协议进行收发数据。
上位机用的是老得掉牙的串口调试助手,如下:
发送数据的波特率可选9600bps,19200bps,38400bps,57600bps,115200bps等,是可调的。
发送格式为:
1bit起始位,8bit数据,1bit停止位,无校验位。
以下的代码有比较详细的注释,经过下载验证,存在误码率(<5%),仅供学习!
代码如下:
(顶层模块):
modulemy_uart_top(clk,rst_n,rs232_rx,rs232_tx);
inputclk;//50MHz主时钟
inputrst_n;//低电平复位信号
inputrs232_rx;//RS232接收数据信号
outputrs232_tx;//RS232发送数据信号
wirebps_start;//接收到数据后,波特率时钟启动信号置位
wireclk_bps;//clk_bps的高电平为接收或者发送数据位的中间采样点
wire[7:
0]rx_data;//接收数据寄存器,保存直至下一个数据来到
wirerx_int;//接收数据中断信号,接收到数据期间始终为高电平
//----------------------------------------------------
speed_selectspeed_select(.clk(clk),//波特率选择模块,接收和发送模块复用,不支持全双工通信
.rst_n(rst_n),
.bps_start(bps_start),
.clk_bps(clk_bps)
);
my_uart_rxmy_uart_rx(.clk(clk),//接收数据模块
.rst_n(rst_n),
.rs232_rx(rs232_rx),
.clk_bps(clk_bps),
.bps_start(bps_start),
.rx_data(rx_data),
.rx_int(rx_int)
);
my_uart_txmy_uart_tx(.clk(clk),//发送数据模块
.rst_n(rst_n),
.clk_bps(clk_bps),
.rx_data(rx_data),
.rx_int(rx_int),
.rs232_tx(rs232_tx),
.bps_start(bps_start)
);
endmodule
modulespeed_select(clk,rst_n,bps_start,clk_bps);
inputclk;//50MHz主时钟
inputrst_n;//低电平复位信号
inputbps_start;//接收到数据后,波特率时钟启动信号置位
outputclk_bps;//clk_bps的高电平为接收或者发送数据位的中间采样点
parameterbps9600=5207,//波特率为9600bps
bps19200=2603,//波特率为19200bps
bps38400=1301,//波特率为38400bps
bps57600=867,//波特率为57600bps
bps115200=433;//波特率为115200bps
parameterbps9600_2=2603,
bps19200_2=1301,
bps38400_2=650,
bps57600_2=433,
bps115200_2=216;
reg[12:
0]bps_para;//分频计数最大值
reg[12:
0]bps_para_2;//分频计数的一半
reg[12:
0]cnt;//分频计数
regclk_bps_r;//波特率时钟寄存器
//----------------------------------------------------------
reg[2:
0]uart_ctrl;//uart波特率选择寄存器
//----------------------------------------------------------
always@(posedgeclkornegedgerst_n)begin
if(!
rst_n)begin
uart_ctrl<=3'd0;//默认波特率为9600bps
end
elsebegin
case(uart_ctrl)//波特率设置
3'd0:
begin
bps_para<=bps9600;
bps_para_2<=bps9600_2;
end
3'd1:
begin
bps_para<=bps19200;
bps_para_2<=bps19200_2;
end
3'd2:
begin
bps_para<=bps38400;
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- verilog 串口 通信 程序