还剩32页未读,继续阅读
本资源只提供10页预览,全部文档请下载后查看!喜欢就下载吧,查找使用更方便
文本内容:
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)发送端moduletransclk 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; //时钟频率-25MHzparameter Baud=115200; //串口波特率-115200 //波特率产生parameterBaudGeneratorAccWidth=16;reg [BaudGeneratorAccWidth:0]BaudGeneratorAcc;wire[BaudGeneratorAccWidth:0]BaudGeneratorInc=BaudBaudGeneratorAccWidth-4+ClkFrequency5/ClkFrequency4;wireBaudTick=BaudGeneratorAcc[BaudGeneratorAccWidth];wireTxD_busy;always@posedgeclkornegedgerst if~rst BaudGeneratorAcc=0; elseifTxD_busy BaudGeneratorAcc=BaudGeneratorAcc[BaudGeneratorAccWidth-1:0]+BaudGeneratorInc;//发送端状态wire TxD_ready=state==0; //当state=0时,处于准备空闲状态,TxD_ready=1assignTxD_busy=~TxD_ready; //空闲状态时TxD_busy=0//把待发送数据放入缓存寄存器TxD_dataRegalways@posedgeclkornegedgerst if~rst TxD_dataReg=8b00000000; elseifTxD_readyTxD_start TxD_dataReg=TxD_data; //发送状态机always@posedgeclkornegedgerst if~rst begin state=4b0000; //复位时,状态为0000,发送端一直发1电平 TxD=1b1; end else casestate 4b0000:ifTxD_startbegin state=4b0100;//接受到发送信号,进入发送状态 end 4b0100:ifBaudTickbegin state=4b1000; //发送开始位-0电平 TxD=1b0; end 4b1000:ifBaudTickbegin state=4b1001; //bit0 TxD=TxD_dataReg
[0]; end 4b1001:ifBaudTickbegin state=4b1010; //bit1 TxD=TxD_dataReg
[1]; end 4b1010:ifBaudTickbegin state=4b1011; //bit2 TxD=TxD_dataReg
[2]; end 4b1011:ifBaudTickbegin state=4b1100; //bit3 TxD=TxD_dataReg
[3]; end 4b1100:ifBaudTickbegin state=4b1101; //bit4 TxD=TxD_dataReg
[4]; end 4b1101:ifBaudTickbegin state=4b1110; //bit5 TxD=TxD_dataReg
[5]; end 4b1110:ifBaudTickbegin state=4b1111; //bit6 TxD=TxD_dataReg
[6]; end 4b1111:ifBaudTickbegin state=4b0010; //bit7 TxD=TxD_dataReg
[7]; end 4b0010:ifBaudTickbegin state=4b0011; //stop1 TxD=1b1; end 4b0011:ifBaudTickbegin state=4b0000; //stop2 TxD=1b1; end default:ifBaudTickbegin state=4b0000; TxD=1b1; end endcaseendmodule接收端modulercvclk 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; //时钟频率-25MHzparameter Baud=115200; //波特率-115200reg[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=Baud8Baud8GeneratorAccWidth-7+ClkFrequency8/ClkFrequency7;reg [Baud8GeneratorAccWidth:0]Baud8GeneratorAcc;always@posedgeclkornegedgerst if~rst Baud8GeneratorAcc=0; else Baud8GeneratorAcc=Baud8GeneratorAcc[Baud8GeneratorAccWidth-1:0]+Baud8GeneratorInc;//Baud8Tick为波特率的8倍-115200*8=921600wire Baud8Tick=Baud8GeneratorAcc[Baud8GeneratorAccWidth]; //next_bit为波特率-115200always@posedgeclkornegedgerst if~rst||state==0 bit_spacing=0; elseifBaud8Tick bit_spacing=bit_spacing+1;wirenext_bit=bit_spacing==7;//检测到RxD有下跳沿时,RxD_start置1,准备接收数据always@posedgeclk ifBaud8Tick begin RxD_delay=RxD; RxD_start=Baud8TickRxD_delay~RxD; end //状态机接收数据always@posedgeclkornegedgerst if~rst state=4b0000; else ifBaud8Tick casestate 4b0000:ifRxD_startstate=4b1000; //检测到下跳沿 4b1000:ifnext_bit state=4b1001; //bit0 4b1001:ifnext_bit state=4b1010; //bit1 4b1010:ifnext_bit state=4b1011; //bit2 4b1011:ifnext_bit state=4b1100; //bit3 4b1100:ifnext_bit state=4b1101; //bit4 4b1101:ifnext_bit state=4b1110; //bit5 4b1110:ifnext_bit state=4b1111; //bit6 4b1111:ifnext_bit state=4b0001; //bit7 4b0001:ifnext_bit state=4b0000; //停止位 default:state=4b0000; endcase//保存接收数据到RxD_data中always@posedgeclkornegedgerst if~rst RxD_data=8b00000000; elseifBaud8Ticknext_bitstate
[3] RxD_data={RxDRxD_data[7:1]};//RxD_data_ready置位信号always@posedgeclkornegedgerst if~rst RxD_data_ready=0; else RxD_data_ready=Baud8Ticknext_bitstate==4b0001;endmodule 为了测试收发是否正常,写的TestBench`timescale1ns/1nsmodulers232_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.clkclk .rstrst .TxD_startTxD_start .TxD_busyTxD_busy .TxD_dataTxD_data .TxDTxD ; rcvrcv.clkclk .rstrst .RxDTxD //收发相接时RxD=TxD .RxD_dataRxD_data .RxD_data_readyRxD_data_ready ; initial begin TxD_start=0; TxD_data=0; clk=0; rst=1; #54rst=0; #70rst=1; #40TxD_start=1b1; #10TxD_data=8b11011001; #100TxD_start=1b0;end alwaysbegin #30clk=~clk; #10clk=~clk; endendmodule
二、综合
三、FPGA与PC串口自收发通信串口通信其实简单实用,这里我就不多说,只把自己动手写的verilog代码共享下实现的功能如题,就是FPGA里实现从PC接收数据,然后把接收到的数据发回去使用的是串口UART协议进行收发数据上位机用的是老得掉牙的串口调试助手,如下HYPERLINKhttp://space.ednchina.com/Upload/Blog/2008/8/29/f857221c-291a-4c41-9cd5-cc79e8caa49d.jpg\t_blankINCLUDEPICTUREhttp://space.ednchina.com/Upload/Blog/2008/8/29/f857221c-291a-4c41-9cd5-cc79e8caa49d.jpg\*MERGEFORMAT发送数据的波特率可选9600bps19200bps38400bps57600bps115200bps等,是可调的发送格式为1bit起始位,8bit数据,1bit停止位,无校验位以下的代码有比较详细的注释,经过下载验证,存在误码率(5%),仅供学习!代码如下(顶层模块)modulemy_uart_topclkrst_nrs232_rxrs232_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.clkclk//波特率选择模块,接收和发送模块复用,不支持全双工通信.rst_nrst_n.bps_startbps_start.clk_bpsclk_bps;my_uart_rxmy_uart_rx.clkclk//接收数据模块.rst_nrst_n.rs232_rxrs232_rx.clk_bpsclk_bps.bps_startbps_start.rx_datarx_data.rx_intrx_int;my_uart_txmy_uart_tx.clkclk//发送数据模块.rst_nrst_n.clk_bpsclk_bps.rx_datarx_data.rx_intrx_int.rs232_txrs232_tx.bps_startbps_start;endmodulemodulespeed_selectclkrst_nbps_startclk_bps;inputclk;//50MHz主时钟inputrst_n;//低电平复位信号inputbps_start;//接收到数据后,波特率时钟启动信号置位outputclk_bps;//clk_bps的高电平为接收或者发送数据位的中间采样点parameterbps9600=5207//波特率为9600bpsbps19200=2603//波特率为19200bpsbps38400=1301//波特率为38400bpsbps57600=867//波特率为57600bpsbps115200=433;//波特率为115200bpsparameterbps9600_2=2603bps19200_2=1301bps38400_2=650bps57600_2=433bps115200_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_nbeginif!rst_nbeginuart_ctrl=3d0;//默认波特率为9600bpsendelsebegincaseuart_ctrl//波特率设置3d0:beginbps_para=bps9600;bps_para_2=bps9600_2;end3d1:beginbps_para=bps19200;bps_para_2=bps19200_2;end3d2:beginbps_para=bps38400;bps_para_2=bps38400_2;end3d3:beginbps_para=bps57600;bps_para_2=bps57600_2;end3d4:beginbps_para=bps115200;bps_para_2=bps115200_2;enddefault:;endcaseendendalways@posedgeclkornegedgerst_nif!rst_ncnt=13d0;elseifcntbps_parabps_startcnt=cnt+1b1;//波特率时钟计数启动elsecnt=13d0;always@posedgeclkornegedgerst_nif!rst_nclk_bps_r=1b0;elseifcnt==bps_para_2bps_startclk_bps_r=1b1;//clk_bps_r高电平为接收或者发送数据位的中间采样点elseclk_bps_r=1b0;assignclk_bps=clk_bps_r;endmodulemodulemy_uart_rxclkrst_nrs232_rxclk_bpsbps_startrx_datarx_int;inputclk;//50MHz主时钟inputrst_n;//低电平复位信号inputrs232_rx;//RS232接收数据信号inputclk_bps;//clk_bps的高电平为接收或者发送数据位的中间采样点outputbps_start;//接收到数据后,波特率时钟启动信号置位output[7:0]rx_data;//接收数据寄存器,保存直至下一个数据来到outputrx_int;//接收数据中断信号接收到数据期间始终为高电平//----------------------------------------------------------------regrs232_rx0rs232_rx1rs232_rx2;//接收数据寄存器,滤波用wireneg_rs232_rx;//表示数据线接收到下降沿always@posedgeclkornegedgerst_nbeginif!rst_nbeginrs232_rx0=1b1;rs232_rx1=1b1;rs232_rx2=1b1;endelsebeginrs232_rx0=rs232_rx;rs232_rx1=rs232_rx0;rs232_rx2=rs232_rx1;endendassignneg_rs232_rx=rs232_rx2~rs232_rx1;//接收到下降沿后neg_rs232_rx置高一个时钟周期//----------------------------------------------------------------regbps_start_r;reg[3:0]num;//移位次数regrx_int;//接收数据中断信号接收到数据期间始终为高电平always@posedgeclkornegedgerst_nbeginif!rst_nbeginbps_start_r=1bz;rx_int=1b0;endelseifneg_rs232_rxbeginbps_start_r=1b1;//启动接收数据rx_int=1b1;//接收数据中断信号使能endelseifnum==4d12beginbps_start_r=1bz;//数据接收完毕rx_int=1b0;//接收数据中断信号关闭endendassignbps_start=bps_start_r;//----------------------------------------------------------------reg[7:0]rx_data_r;//接收数据寄存器,保存直至下一个数据来到//----------------------------------------------------------------reg[7:0]rx_temp_data;//但前接收数据寄存器regrx_data_shift;//数据移位标志always@posedgeclkornegedgerst_nbeginif!rst_nbeginrx_data_shift=1b0;rx_temp_data=8d0;num=4d0;rx_data_r=8d0;endelseifrx_intbegin//接收数据处理ifclk_bpsbegin//读取并保存数据接收数据为一个起始位,8bit数据,一个结束位rx_data_shift=1b1;num=num+1b1;ifnum=4d8rx_temp_data
[7]=rs232_rx;//锁存9bit(1bit起始位,8bit数据)endelseifrx_data_shiftbegin//数据移位处理rx_data_shift=1b0;ifnum=4d8rx_temp_data=rx_temp_data1b1;//移位8次,第1bit起始位移除,剩下8bit正好时接收数据elseifnum==4d12beginnum=4d0;//接收到STOP位后结束num清零rx_data_r=rx_temp_data;//把数据锁存到数据寄存器rx_data中endendendendassignrx_data=rx_data_r;endmodulemodulemy_uart_txclkrst_nclk_bpsrx_datarx_intrs232_txbps_start;inputclk;//50MHz主时钟inputrst_n;//低电平复位信号inputclk_bps;//clk_bps的高电平为接收或者发送数据位的中间采样点input[7:0]rx_data;//接收数据寄存器inputrx_int;//接收数据中断信号接收到数据期间始终为高电平在次利用它的下降沿来启动发送数据outputrs232_tx;//RS232发送数据信号outputbps_start;//接收或者要发送数据,波特率时钟启动信号置位//---------------------------------------------------------regrx_int0rx_int1rx_int2;//rx_int信号寄存器,捕捉下降沿滤波用wireneg_rx_int;//rx_int下降沿标志位always@posedgeclkornegedgerst_nbeginif!rst_nbeginrx_int0=1b0;rx_int1=1b0;rx_int2=1b0;endelsebeginrx_int0=rx_int;rx_int1=rx_int0;rx_int2=rx_int1;endendassignneg_rx_int=~rx_int1rx_int2;//捕捉到下降沿后,neg_rx_int拉地保持一个主时钟周期//---------------------------------------------------------reg[7:0]tx_data;//待发送数据的寄存器//---------------------------------------------------------regbps_start_r;regtx_en;//发送数据使能信号,高有效reg[3:0]num;always@posedgeclkornegedgerst_nbeginif!rst_nbeginbps_start_r=1bz;tx_en=1b0;tx_data=8d0;endelseifneg_rx_intbegin//接收数据完毕,准备把接收到的数据发回去bps_start_r=1b1;tx_data=rx_data;//把接收到的数据存入发送数据寄存器tx_en=1b1;//进入发送数据状态中endelseifnum==4d11begin//数据发送完成,复位bps_start_r=1bz;tx_en=1b0;endendassignbps_start=bps_start_r;//---------------------------------------------------------regrs232_tx_r;always@posedgeclkornegedgerst_nbeginif!rst_nbeginnum=4d0;rs232_tx_r=1b1;endelseiftx_enbeginifclk_bpsbeginnum=num+1b1;casenum4d0:rs232_tx_r=1b0;//发送起始位4d1:rs232_tx_r=tx_data
[0];//发送bit04d2:rs232_tx_r=tx_data
[1];//发送bit14d3:rs232_tx_r=tx_data
[2];//发送bit24d4:rs232_tx_r=tx_data
[3];//发送bit34d5:rs232_tx_r=tx_data
[4];//发送bit44d6:rs232_tx_r=tx_data
[5];//发送bit54d7:rs232_tx_r=tx_data
[6];//发送bit64d8:rs232_tx_r=tx_data
[7];//发送bit74d9:rs232_tx_r=1b0;//发送结束位default:rs232_tx_r=1b1;endcaseendelseifnum==4d11num=4d0;//复位endendassignrs232_tx=rs232_tx_r;endmoduleFPGA实现串行接口RS23212008-12-1711:38串行接口RS-232串行接口是连接FPGA和PC机的一种简单方式这个项目向大家展示了如果使用FPGA来创建RS-232收发器HYPERLINKhttp://www.dzkf.cn/upimg/allimg/0706/1_
29143208.JPG\t_blankINCLUDEPICTUREhttp://www.dzkf.cn/upimg/allimg/0706/1_
29143208.JPG\*MERGEFORMATHYPERLINKhttp://www.dzkf.cn/upimg/allimg/0706/1_
29143222.JPG\t_blankINCLUDEPICTUREhttp://www.dzkf.cn/upimg/allimg/0706/1_
29143222.JPG\*MERGEFORMAT整个项目包括5个部分RS232是怎样工作的如何产生需要的波特率发送模块接收模块应用实例RS-232接口是怎样工作的作为标准设备,大多数的计算机都有1到2个RS-232串口特性RS-232有下列特性:使用9针的DB-9插头旧式计算机使用25针的DB-25插头.允许全双工的双向通讯也就是说计算机可以在接收数据的同时发送数据.最大可支持的传输速率为10KBytes/s.DB-9插头你可能已经在你的计算机背后见到过这种插头HYPERLINKhttp://www.dzkf.cn/upimg/allimg/0706/1_
29143247.JPG\t_blankINCLUDEPICTUREhttp://www.dzkf.cn/upimg/allimg/0706/1_
29143247.JPG\*MERGEFORMAT它一共有9个引脚,但是最重要的3个引脚是:引脚2:RxD接收数据.引脚3:TxD发送数据.引脚5:GND地.仅使用3跟电缆,你就可以发送和接收数据.串行通讯数据以每次一位的方式传输;每条线用来传输一个方向的数据由于计算机通常至少需要若干位数据,因此数据在发送之前先“串行化”通常是以8位数据为1组的先发送最低有效位,最后发送最高有效位异步通讯RS-232使用异步通讯协议也就是说数据的传输没有时钟信号接收端必须有某种方式,使之与接收数据同步对于RS-232来说,是这样处理的:串行线缆的两端事先约定好串行传输的参数(传输速度、传输格式等)当没有数据传输的时候,发送端向数据线上发送1每传输一个字节之前,发送端先发送一个0来表示传输已经开始这样接收端便可以知道有数据到来了开始传输后,数据以约定的速度和格式传输,所以接收端可以与之同步每次传输完成一个字节之后,都在其后发送一个停止位1让我们来看看0x55是如何传输的:HYPERLINKhttp://www.dzkf.cn/upimg/allimg/0706/1_
29143348.JPG\t_blankINCLUDEPICTUREhttp://www.dzkf.cn/upimg/allimg/0706/1_
29143348.JPG\*MERGEFORMAT0x55的二进制表示为01010101但是由于先发送的是最低有效位,所以发送序列是这样的:1-0-1-0-1-0-1-
0.下面是另外一个例子:HYPERLINKhttp://www.dzkf.cn/upimg/allimg/0706/1_
29143415.JPG\t_blankINCLUDEPICTUREhttp://www.dzkf.cn/upimg/allimg/0706/1_
29143415.JPG\*MERGEFORMAT传输的数据为0xC4,你能看出来吗从图中很难看出来所传输的数据,这也说明了事先知道传输的速率对于接收端有多么重要数据传输可以多快数据的传输速度是用波特来描述的,亦即每秒钟传输的数据位,例如1000波特表示每秒钟传输100比特的数据或者说每个数据位持续1毫秒波特率不是随意的,必须服从一定的标准,如果希望设计123456波特的RS-232接口,对不起,你很不幸运,这是不行的常用的串行传输速率值包括以下几种1200波特.9600波特.38400波特.115200波特通常情况下是你可以使用的最高速度.在115200波特传输速度下每位数据持续1/115200=
8.7μs.如果传输8位数据共持续8x
8.7μs=69μs但是每个字节的传输又要求额外的“开始位”和“停止位”所以实际上需要花费10x
8.7μs=87μs的时间最大的有效数据传输率只能达到
11.5KBytes每秒在115200波特传输速度下一些使用了不好的芯片的计算机要求一个长的停止位
1.5或2位数据的长度,这使得最大传输速度降到大约
10.5KBytes每秒物理层电缆上的信号使用正负电压的机制:1用-10V的电压表示或者在-5V与-15V之间的电压.0用+10V的电压表示或者在5V与15V之间的电压.所以没有数据传输的电缆上的电压应该为-10V或-5到-10之间的某个电压FPGA实现串行接口RS23222008-12-1711:39波特率发生器这里我们使用串行连接的最大速度115200波特,其他较慢的波特也很容易由此产生FPGA通常运行在远高于115200Hz的时钟频率上(对于今天的标准的来说RS-232真是太慢了),这就意味着我们需要用一个较高的时钟来分频产生尽量接近于115200Hz的时钟信号从
1.8432MHz的时钟产生通常RS-232芯片使用
1.8432MHz的时钟,以为这个时钟很容易产生标准的波特率,所以我们假设已经拥有了一个这样的时钟源只需要将
1.8432MHz16分频便可得到115200Hz的时钟,多方便啊!reg[3:0]BaudDivCnt;always@posedgeclkBaudDivCnt=BaudDivCnt+1;wireBaudTick=BaudDivCnt==15;所以BaudTick每16个时钟周期需要置位一次,从而从
1.8432MHz的时钟得到115200Hz的时钟从任意频率产生早期的发生器假设使用
1.8432MHz的时钟但如果我们使用2MHz的时钟怎么办呢?要从2MHz的时钟得到115200Hz,需要将时钟
17.
361111111...分频,并不是一个整数我的解决办法是有时候17分频,有时候18分频,使得整体的分频比保持在
17.361111111这是很容易做到的下面是实现这个想法的C语言代码:while1//死循环{acc+=115200;ifacc=2000000printf*;elseprintf;acc%=2000000;}这段代码会精确的以平均每
17.
361111111...个时钟间隔打印出一个*为了从FPGA得到同样的效果,考虑到串行接口可以容忍一定的波特率误差,所以即使我们使用
17.3或者
17.4这样的分频比也是没有关系的FPGA波特率发生器我们希望2000000是2的整数幂,但很可惜,它不是所以我们改变分频比,2000000/115200约等于1024/59=
17.
356.这跟我们要求的分频比很接近,并且使得在FPGA上实现起来相当有效//10位的累加器[9:0]1位进位输出
[10]reg[10:0]acc;//一共11位!always@posedgeclkacc=acc[9:0]+59;//我们使用上一次结果的低10位,但是保留11位结果wireBaudTick=acc
[10];//第11位作为进位输出使用2MHz时钟BaudTick为115234波特跟理想的115200波特存在
0.03%的误差参数化的FPGA波特率发生器前面的设计我们使用的是10位的累加器,如果时钟频率提高的话,需要更多的位数下面是一个使用25MHz时钟和16位累加器的设计,该设计是参数化的,所以很容易根据具体情况修改parameterClkFrequency=25000000;//25MHzparameterBaud=115200;parameterBaudGeneratorAccWidth=16;parameterBaudGeneratorInc=BaudBaudGeneratorAccWidth/ClkFrequency;reg[BaudGeneratorAccWidth:0]BaudGeneratorAcc;always@posedgeclkBaudGeneratorAcc=BaudGeneratorAcc[BaudGeneratorAccWidth-1:0]+BaudGeneratorInc;wireBaudTick=BaudGeneratorAcc[BaudGeneratorAccWidth];上面的设计中存在一个错误:BaudGeneratorInc的计算是错误的因为Verilog使用32位的默认结果但实际计算过程中的某些数据超过了32位,所以改变一种计算方法parameterBaudGeneratorInc=BaudBaudGeneratorAccWidth-4+ClkFrequency5/ClkFrequency4;这行程序也使得结果成为整数,从而避免截断这就是整个的设计方法了现在我们已经得到了足够精确的波特率,可以继续设计串行接收和发送模块了FPGA实现串行接口RS23232008-12-1711:40RS-232发送模块下面是我们所想要实现的:它应该能像这样工作:发送器接收8位的数据,并将其串行输出TxD_start置位后开始传输.当有数传输的时候,使busy信号有效,此时“TxD_start”信号被忽略.RS-232模块的参数是固定的:8位数据2个停止位无奇偶校验.数据串行化假设我们已经有了一个115200波特的BaudTick信号.我们需要产生开始位、8位数据以及停止位用状态机来实现看起来比较合适reg[3:0]state;always@posedgeclkcasestate4b0000:ifTxD_startstate=4b0100;4b0100:ifBaudTickstate=4b1000;//开始位4b1000:ifBaudTickstate=4b1001;//bit04b1001:ifBaudTickstate=4b1010;//bit14b1010:ifBaudTickstate=4b1011;//bit24b1011:ifBaudTickstate=4b1100;//bit34b1100:ifBaudTickstate=4b1101;//bit44b1101:ifBaudTickstate=4b1110;//bit54b1110:ifBaudTickstate=4b1111;//bit64b1111:ifBaudTickstate=4b0001;//bit74b0001:ifBaudTickstate=4b0010;//停止位14b0010:ifBaudTickstate=4b0000;//停止位2default:ifBaudTickstate=4b0000;endcase注意看这个状态机是怎样实现当TxD_start有效就开始但只在BaudTick有效的时候才转换状态的.现在,我们只需要产生TxD输出即可.regmuxbit;always@state[2:0]casestate[2:0]0:muxbit=TxD_data
[0];1:muxbit=TxD_data
[1];2:muxbit=TxD_data
[2];3:muxbit=TxD_data
[3];4:muxbit=TxD_data
[4];5:muxbit=TxD_data
[5];6:muxbit=TxD_data
[6];7:muxbit=TxD_data
[7];endcase//将开始位、数据以及停止位结合起来assignTxD=state4|state
[3]muxbit;FPGA实现串行接口RS23242008-12-1711:41RS232接收模块下面是我们想要实现的模块:我们的设计目的是这样的
1.当RxD线上有数据时,接收模块负责识别RxD线上的数据
2.当收到一个字节的数据时,锁存接收到的数据到data总线,并使data_ready有效一个周期注意只有当data_ready有效时,data总线的数据才有效,其他的时间里不要使用data总线上的数据,因为新的数据可能已经改变了其中的部分数据过采样异步接收机必须通过一定的机制与接收到的输入信号同步(接收端没有办法得到发送断的时钟)这里采用如下办法
1.为了确定新数据的到来,即检测开始位,我们使用几倍于波特率的采样时钟对接收到的信号进行采样
2.一旦检测到开始位,再将采样时钟频率降为已知的发送端的波特率典型的过采样时钟频率为接收到的信号的波特率的16倍,这里我们使用8倍的采样时钟当波特率为115200时,采样时钟为921600Hz假设我们已经有了一个8倍于波特率的时钟信号Baud8Tick,其频率为921600Hz具体设计首先,接受到的RxD信号与我们的时钟没有任何关系,所以采用两个D触发器对其进行过采样,并且使之我我们的时钟同步reg[1:0]RxD_sync;always@posedgeclkifBaud8TickRxD_sync={RxD_sync
[0]RxD};首先我们对接收到的数据进行滤波,这样可以防止毛刺信号被误认为是开始信号reg[1:0]RxD_cnt;regRxD_bit;always@posedgeclkifBaud8TickbeginifRxD_sync
[1]RxD_cnt!=2b11RxD_cnt=RxD_cnt+1;elseif~RxD_sync
[1]RxD_cnt!=2b00RxD_cnt=RxD_cnt-1;ifRxD_cnt==2b00RxD_bit=0;elseifRxD_cnt==2b11RxD_bit=1;end一旦检测到开始位,使用如下的状态机可以检测出接收到每一位数据reg[3:0]state;always@posedgeclkifBaud8Tickcasestate4b0000:if~RxD_bitstate=4b1000;//startbitfound4b1000:ifnext_bitstate=4b1001;//bit04b1001:ifnext_bitstate=4b1010;//bit14b1010:ifnext_bitstate=4b1011;//bit24b1011:ifnext_bitstate=4b1100;//bit34b1100:ifnext_bitstate=4b1101;//bit44b1101:ifnext_bitstate=4b1110;//bit54b1110:ifnext_bitstate=4b1111;//bit64b1111:ifnext_bitstate=4b0001;//bit74b0001:ifnext_bitstate=4b0000;//stopbitdefault:state=4b0000;endcase注意,我们使用了next_bit来遍历所有数据位reg[2:0]bit_spacing;always@posedgeclkifstate==0bit_spacing=0;elseifBaud8Tickbit_spacing=bit_spacing+1;wirenext_bit=bit_spacing==7;最后我们使用一个移位寄存器来存储接受到的数据reg[7:0]RxD_data;always@posedgeclkifBaud8Ticknext_bitstate
[3]RxD_data={RxD_bitRxD_data[7:1]};FPGA实现串行接口RS23252008-12-1711:41怎样使用发送和接收模块这个设计似的我们可以通过计算机的串行口来控制FPGA的几个引脚具体来说,该设计完成以下功能
1.将FPGA的8个引脚作为输出(称为“通用输出”)FPGA收到任何数据时都会更新这8个GPout的值
2.将FPGA的8个引脚作为输入(称为“通用输入”)FPGA收到仁厚数据后,都会将GPin上的数值通过串行口发送出去通用输出可以用来通过计算机远程控制任何东西,例如FPGA板上的LED,甚至可以再添加一个继电器来控制咖啡机moduleserialfunclkRxDTxDGPoutGPin;inputclk;inputRxD;outputTxD;output[7:0]GPout;input[7:0]GPin;///////////////////////////////////////////////////wireRxD_data_ready;wire[7:0]RxD_data;async_receiverdeserializer.clkclk.RxDRxD.RxD_data_readyRxD_data_ready.RxD_dataRxD_data;reg[7:0]GPout;always@posedgeclkifRxD_data_readyGPout=RxD_data;///////////////////////////////////////////////////async_transmitterserializer.clkclk.TxDTxD.TxD_startRxD_data_ready.TxD_dataGPin;endmodule记得包含异步发送和接收模块的设计文件,并更新里面的时钟频率HYPERLINKhttp://www.dzkf.cn/upimg/soft/0706/1_
070629144239.zip\t_blankINCLUDEPICTUREhttp://www.dzkf.cn/plus/img/addon.gif\*MERGEFORMAT代码.zipmoduleserialclkrstrxdtxdenseg_datakey_inputlowbit;inputclkrst;inputrxd;//串行数据接收端inputkey_input;//按键输入output[7:0]en;output[7:0]seg_data;reg[7:0]seg_data;outputtxd;//串行数据发送端outputlowbit;////////////////////innerreg////////////////////reg[15:0]div_reg;//分频计数器,分频值由波特率决定分频后得到频率8倍波特率的时钟reg[2:0] div8_tras_reg;//该寄存器的计数值对应发送时当前位于的时隙数reg[2:0] div8_rec_reg;//该寄存器的计数值对应接收时当前位于的时隙数reg[3:0]state_tras;//发送状态寄存器reg[3:0]state_rec;//接受状态寄存器regclkbaud_tras;//以波特率为频率的发送使能信号regclkbaud_rec;//以波特率为频率的接受使能信号regclkbaud8x;//以8倍波特率为频率的时钟,它的作用是将发送或接受一个bit的时钟周期分为8个时隙regrecstart;//开始发送标志regrecstart_tmp;regtrasstart;//开始接受标志regrxd_reg1;//接收寄存器1regrxd_reg2;//接收寄存器2,因为接收数据为异步信号,故用两级缓存regtxd_reg;//发送寄存器reg[7:0]rxd_buf;//接受数据缓存reg[7:0]txd_buf;//发送数据缓存reg[2:0]send_state;//每次按键给PC发送Welcome字符串,这是发送状态寄存器reg[19:0]cnt_delay;//延时去抖计数器regstart_delaycnt;//开始延时计数标志regkey_entry1key_entry2;//确定有键按下标志////////////////////////////////////////////////parameterdiv_par=16h104;//分频参数,其值由对应的波特率计算而得,按此参数分频的时钟频率是波倍特率的8 //倍,此处值对应9600的波特率,即分频出的时钟频率是9600*8////////////////////////////////////////////////assigntxd=txd_reg;assignlowbit=0;assignen=8b11111110;//7段数码管使能信号赋值always@posedgeclkbegin if!rstbegin cnt_delay=0; start_delaycnt=0; end elseifstart_delaycntbegin ifcnt_delay!=20d800000begin cnt_delay=cnt_delay+1; end elsebegin cnt_delay=0; start_delaycnt=0; end end elsebegin if!key_inputcnt_delay==0 start_delaycnt=1; endendalways@posedgeclkbegin if!rst key_entry1=0; elsebegin ifkey_entry2 key_entry1=0; elseifcnt_delay==20d800000begin if!key_input key_entry1=1; end endendalways@posedgeclkbegin if!rst div_reg=0; elsebegin ifdiv_reg==div_par-1 div_reg=0; else div_reg=div_reg+1; endendalways@posedgeclk//分频得到8倍波特率的时钟begin if!rst clkbaud8x=0; elseifdiv_reg==div_par-1 clkbaud8x=~clkbaud8x;endalways@posedgeclkbaud8xornegedgerstbegin if!rst div8_rec_reg=0; elseifrecstart//接收开始标志 div8_rec_reg=div8_rec_reg+1;//接收开始后,时隙数在8倍波特率的时钟下加1循环endalways@posedgeclkbaud8xornegedgerstbegin if!rst div8_tras_reg=0; elseiftrasstart div8_tras_reg=div8_tras_reg+1;//发送开始后,时隙数在8倍波特率的时钟下加1循环endalways@div8_rec_regbegin ifdiv8_rec_reg==7 clkbaud_rec=1;//在第7个时隙,接收使能信号有效,将数据打入 else clkbaud_rec=0;endalways@div8_tras_regbegin ifdiv8_tras_reg==7 clkbaud_tras=1;//在第7个时隙,发送使能信号有效,将数据发出 else clkbaud_tras=0;endalways@posedgeclkbaud8xornegedgerstbegin if!rstbegin txd_reg=1; trasstart=0; txd_buf=0; state_tras=0; send_state=0; key_entry2=0; end elsebegin if!key_entry2begin ifkey_entry1begin key_entry2=1; txd_buf=8d119;//w end end else begin casestate_tras 4b0000:begin //发送起始位 if!trasstartsend_state7 trasstart=1; elseifsend_state7begin ifclkbaud_trasbegin txd_reg=0; state_tras=state_tras+1; end end elsebegin key_entry2=0; state_tras=0; end end 4b0001:begin//发送第1位 ifclkbaud_trasbegin txd_reg=txd_buf
[0]; txd_buf[6:0]=txd_buf[7:1]; state_tras=state_tras+1; end end 4b0010:begin//发送第2位 ifclkbaud_trasbegin txd_reg=txd_buf
[0]; txd_buf[6:0]=txd_buf[7:1]; state_tras=state_tras+1; end end 4b0011:begin//发送第3位 ifclkbaud_trasbegin txd_reg=txd_buf
[0]; txd_buf[6:0]=txd_buf[7:1]; state_tras=state_tras+1; end end 4b0100:begin//发送第4位 ifclkbaud_trasbegin txd_reg=txd_buf
[0]; txd_buf[6:0]=txd_buf[7:1]; state_tras=state_tras+1; end end 4b0101:begin//发送第5位 ifclkbaud_trasbegin txd_reg=txd_buf
[0]; txd_buf[6:0]=txd_buf[7:1]; state_tras=state_tras+1; end end 4b0110:begin//发送第6位 ifclkbaud_trasbegin txd_reg=txd_buf
[0]; txd_buf[6:0]=txd_buf[7:1]; state_tras=state_tras+1; end end 4b0111:begin//发送第7位 ifclkbaud_trasbegin txd_reg=txd_buf
[0]; txd_buf[6:0]=txd_buf[7:1]; state_tras=state_tras+1; end end 4b1000:begin//发送第8位 ifclkbaud_trasbegin txd_reg=txd_buf
[0]; txd_buf[6:0]=txd_buf[7:1]; state_tras=state_tras+1; end end 4b1001:begin//发送停止位 ifclkbaud_trasbegin txd_reg=1; txd_buf=8h55; state_tras=state_tras+1; end end 4b1111:begin ifclkbaud_trasbegin state_tras=state_tras+1; send_state=send_state+1; trasstart=0; casesend_state 3b000: txd_buf=8d101;//e 3b001: txd_buf=8d108;//l 3b010: txd_buf=8d99;//c 3b011: txd_buf=8d111;//o 3b100: txd_buf=8d109;//m 3b101: txd_buf=8d101;//e default: txd_buf=0; endcase end end default:begin ifclkbaud_trasbegin state_tras=state_tras+1; trasstart=1; end end endcase end endendalways@posedgeclkbaud8xornegedgerst//接受PC机的数据begin if!rstbegin rxd_reg1=0; rxd_reg2=0; rxd_buf=0; state_rec=0; recstart=0; recstart_tmp=0; end else begin rxd_reg1=rxd; rxd_reg2=rxd_reg1; ifstate_rec==0begin ifrecstart_tmp==1begin recstart=1; recstart_tmp=0; state_rec=state_rec+1; end elseif!rxd_reg1rxd_reg2//检测到起始位的下降沿,进入接受状态 recstart_tmp=1; end elseifstate_rec=1state_rec=8begin ifclkbaud_recbegin rxd_buf
[7]=rxd_reg2; rxd_buf[6:0]=rxd_buf[7:1]; state_rec=state_rec+1; end end elseifstate_rec==9begin ifclkbaud_recbegin state_rec=0; recstart=0; end end endendalways@rxd_buf//将接受的数据用数码管显示出来begin caserxd_buf 8h30: seg_data=8b0000_0011; 8h31: seg_data=8b1001_1111; 8h32: seg_data=8b0010_0101; 8h33: seg_data=8b0000_1101; 8h34: seg_data=8b1001_1001; 8h35: seg_data=8b0100_1001; 8h36: seg_data=8b0100_0001; 8h37: seg_data=8b0001_1111; 8h38: seg_data=8b0000_0001; 8h39: seg_data=8b0001_1001; 8h41: seg_data=8b0001_0001; 8h42: seg_data=8b1100_0001; 8h43: seg_data=8b0110_0011; 8h44: seg_data=8b1000_0101; 8h45: seg_data=8b0110_0001; 8h46: seg_data=8b0111_0001; default: seg_data=8b1111_1111; endcaseend endmodule。