verilog串口通信程序_第1页
verilog串口通信程序_第2页
verilog串口通信程序_第3页
verilog串口通信程序_第4页
verilog串口通信程序_第5页
已阅读5页,还剩29页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

FPGA实现RS-232串口收发的仿真过程(Quartus+Synplify+ModelSim)(2007-09-11 12:17:37) 网上关于RS-232的异步收发介绍得很多,最近没事学着摸索用ModelSim来做时序仿真,就结合网上的参考资料和自己的琢磨,做了这个东西。针对我这个小程序结合FPGA的开发流程,主要走了以下几步:1. 文本程序输入(Verilog HDL)2. 功能仿真(ModelSim,查看逻辑功能是否正确,要写一个Test Bench)3. 综合(Synplify Pro,程序综合成网表)4. 布局布线(Quartus II,根据我选定的FPGA器件型号,将网表布到器件中,并估算出相应的时延)5. 时序仿真(ModelSim,根据时延做进一步仿真)这里贴出我的程序和各个详细步骤,能和各位正在学习的新手们一起分享。0. 原理略一、文本程序输入(Verilog HDL)发送端:module trans(clk, rst, TxD_start, TxD_data, TxD, TxD_busy );input clk, rst, TxD_start;input7:0 TxD_data; / 待发送的数据output TxD, / 输出端口发送的串口数据 TxD_busy;reg TxD;reg 7:0 TxD_dataReg; / 寄存器发送模式,因为在串口发送过程中输入端不可能一直保持有效电平reg 3:0 state;parameter ClkFrequency = 25000000; / 时钟频率25 MHzparameter Baud = 115200; / 串口波特率115200/ 波特率产生parameter BaudGeneratorAccWidth = 16;reg BaudGeneratorAccWidth:0 BaudGeneratorAcc;wire BaudGeneratorAccWidth:0 BaudGeneratorInc = (Baud5)/(ClkFrequency4);wire BaudTick = BaudGeneratorAccBaudGeneratorAccWidth;wire TxD_busy;always (posedge clk or negedge rst) if(rst) BaudGeneratorAcc = 0;else if(TxD_busy) BaudGeneratorAcc = BaudGeneratorAccBaudGeneratorAccWidth-1:0 + BaudGeneratorInc;/ 发送端状态wire TxD_ready = (state=0); / 当state = 0时,处于准备空闲状态,TxD_ready = 1assign TxD_busy = TxD_ready; / 空闲状态时TxD_busy = 0/ 把待发送数据放入缓存寄存器 TxD_dataRegalways (posedge clk or negedge rst) if(rst) TxD_dataReg = 8b00000000;else if(TxD_ready & TxD_start)TxD_dataReg = TxD_data;/ 发送状态机always (posedge clk or negedge rst)if(rst) begin state = 4b0000; / 复位时,状态为0000,发送端一直发1电平 TxD = 1b1; endelsecase(state)4b0000: if(TxD_start) begin state = 4b0100; / 接受到发送信号,进入发送状态 end4b0100: if(BaudTick) begin state = 4b1000; / 发送开始位 - 0电平 TxD = 1b0; end4b1000: if(BaudTick) begin state = 4b1001; / bit 0 TxD = TxD_dataReg0; end4b1001: if(BaudTick) begin state = 4b1010; / bit 1 TxD = TxD_dataReg1; end4b1010: if(BaudTick) begin state = 4b1011; / bit 2 TxD = TxD_dataReg2; end4b1011: if(BaudTick) begin state = 4b1100; / bit 3 TxD = TxD_dataReg3; end4b1100: if(BaudTick) begin state = 4b1101; / bit 4 TxD = TxD_dataReg4; end4b1101: if(BaudTick) begin state = 4b1110; / bit 5 TxD = TxD_dataReg5; end4b1110: if(BaudTick) begin state = 4b1111; / bit 6 TxD = TxD_dataReg6; end4b1111: if(BaudTick) begin state = 4b0010; / bit 7 TxD = TxD_dataReg7; end4b0010: if(BaudTick) begin state = 4b0011; / stop1 TxD = 1b1; end4b0011: if(BaudTick) begin state = 4b0000; / stop2 TxD = 1b1; enddefault: if(BaudTick) begin state = 4b0000; TxD = 1b1; endendcaseendmodule接收端:module rcv(clk, rst, RxD, RxD_data, RxD_data_ready, );input clk, rst, RxD;output7:0 RxD_data; / 接收数据寄存器output RxD_data_ready; / 接收完8位数据,RxD_data 值有效时,RxD_data_ready 输出读信号parameter ClkFrequency = 25000000; / 时钟频率25MHzparameter Baud = 115200; / 波特率115200reg2:0 bit_spacing;reg RxD_delay;reg RxD_start;reg3:0 state;reg7:0 RxD_data;reg RxD_data_ready;/ 波特率产生,使用8倍过采样parameter Baud8 = Baud*8;parameter Baud8GeneratorAccWidth = 16;wire Baud8GeneratorAccWidth:0 Baud8GeneratorInc = (Baud88)/(ClkFrequency7);reg Baud8GeneratorAccWidth:0 Baud8GeneratorAcc;always (posedge clk or negedge rst) if(rst) Baud8GeneratorAcc = 0; else Baud8GeneratorAcc = Baud8GeneratorAccBaud8GeneratorAccWidth-1:0 + Baud8GeneratorInc;/ Baud8Tick 为波特率的8倍 115200*8 = 921600wire Baud8Tick = Baud8GeneratorAccBaud8GeneratorAccWidth;/ next_bit 为波特率 115200always (posedge clk or negedge rst)if(rst|(state=0) bit_spacing = 0;else if(Baud8Tick) bit_spacing = bit_spacing + 1;wire next_bit = (bit_spacing=7);/ 检测到 RxD 有下跳沿时,RxD_start 置1,准备接收数据always(posedge clk)if(Baud8Tick)beginRxD_delay = RxD; RxD_start = (Baud8Tick & RxD_delay & (RxD);end/ 状态机接收数据always(posedge clk or negedge rst)if(rst) state = 4b0000;elseif(Baud8Tick) case(state) 4b0000: if(RxD_start) state = 4b1000; / 检测到下跳沿 4b1000: if(next_bit) state = 4b1001; / bit 0 4b1001: if(next_bit) state = 4b1010; / bit 1 4b1010: if(next_bit) state = 4b1011; / bit 2 4b1011: if(next_bit) state = 4b1100; / bit 3 4b1100: if(next_bit) state = 4b1101; / bit 4 4b1101: if(next_bit) state = 4b1110; / bit 5 4b1110: if(next_bit) state = 4b1111; / bit 6 4b1111: if(next_bit) state = 4b0001; / bit 7 4b0001: if(next_bit) state = 4b0000; / 停止位 default: state = 4b0000; endcase/ 保存接收数据到 RxD_data 中always (posedge clk or negedge rst) if(rst) RxD_data = 8b00000000;else if(Baud8Tick & next_bit & state3)RxD_data = RxD, RxD_data7:1;/ RxD_data_ready 置位信号always (posedge clk or negedge rst) if(rst) RxD_data_ready = 0;else RxD_data_ready = (Baud8Tick & next_bit & state=4b0001);endmodule为了测试收发是否正常,写的Test Benchtimescale 1ns / 1nsmodule rs232_test;reg clk, rst, TxD_start;reg 7:0 TxD_data;wire7:0RxD_data;wire /RxD, TxD, TxD_busy, RxD_data_ready;trans trans(.clk(clk), .rst(rst), .TxD_start(TxD_start), .TxD_busy(TxD_busy), .TxD_data(TxD_data), .TxD(TxD);rcv rcv(.clk(clk), .rst(rst), .RxD(TxD), / 收发相接时 RxD = TxD .RxD_data(RxD_data), .RxD_data_ready(RxD_data_ready); initialbeginTxD_start = 0; TxD_data = 0; clk = 0; rst = 1; #54 rst = 0; #70 rst = 1; #40 TxD_start = 1b1; #10 TxD_data = 8b11011001; #100 TxD_start = 1b0;endalways begin #30 clk = clk; #10 clk = clk; endendmodule二、综合三、FPGA与PC串口自收发通信串口通信其实简单实用,这里我就不多说,只把自己动手写的verilog代码共享下。实现的功能如题,就是FPGA里实现从PC接收数据,然后把接收到的数据发回去。使用的是串口UART协议进行收发数据。上位机用的是老得掉牙的串口调试助手,如下:发送数据的波特率可选9600bps,19200bps,38400bps,57600bps,115200bps等,是可调的。发送格式为:1bit起始位,8bit数据,1bit停止位,无校验位。以下的代码有比较详细的注释,经过下载验证,存在误码率(5%),仅供学习!代码如下:(顶层模块):module my_uart_top(clk,rst_n,rs232_rx,rs232_tx);input clk;/ 50MHz主时钟input rst_n;/低电平复位信号input rs232_rx;/ RS232接收数据信号output rs232_tx;/RS232发送数据信号wire bps_start;/接收到数据后,波特率时钟启动信号置位wire clk_bps;/ clk_bps的高电平为接收或者发送数据位的中间采样点 wire7:0 rx_data;/接收数据寄存器,保存直至下一个数据来到wire rx_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);endmodulemodule speed_select(clk,rst_n,bps_start,clk_bps);input clk;/ 50MHz主时钟input rst_n;/低电平复位信号input bps_start;/接收到数据后,波特率时钟启动信号置位output clk_bps;/ clk_bps的高电平为接收或者发送数据位的中间采样点 parameter bps9600 = 5207,/波特率为9600bps bps19200 = 2603,/波特率为19200bpsbps38400 = 1301,/波特率为38400bpsbps57600 = 867,/波特率为57600bpsbps115200= 433;/波特率为115200bpsparameter bps9600_2 = 2603,bps19200_2= 1301,bps38400_2= 650,bps57600_2= 433,bps115200_2 = 216; reg12:0 bps_para;/分频计数最大值reg12:0 bps_para_2;/分频计数的一半reg12:0 cnt;/分频计数reg clk_bps_r;/波特率时钟寄存器/-reg2:0 uart_ctrl;/ uart波特率选择寄存器/-always (posedge clk or negedge rst_n) beginif(!rst_n) begin uart_ctrl = 3d0;/默认波特率为9600bpsendelse begincase (uart_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 (posedge clk or negedge rst_n)if(!rst_n) cnt = 13d0;else if(cntbps_para & bps_start) cnt = cnt+1b1;/波特率时钟计数启动else cnt = 13d0;always (posedge clk or negedge rst_n)if(!rst_n) clk_bps_r = 1b0;else if(cnt=bps_para_2 & bps_start) clk_bps_r = 1b1;/ clk_bps_r高电平为接收或者发送数据位的中间采样点 else clk_bps_r = 1b0;assign clk_bps = clk_bps_r;endmodulemodule my_uart_rx(clk,rst_n,rs232_rx,clk_bps,bps_start,rx_data,rx_int);input clk;/ 50MHz主时钟input rst_n;/低电平复位信号input rs232_rx;/ RS232接收数据信号input clk_bps;/ clk_bps的高电平为接收或者发送数据位的中间采样点output bps_start;/接收到数据后,波特率时钟启动信号置位output7:0 rx_data;/接收数据寄存器,保存直至下一个数据来到 output rx_int;/接收数据中断信号,接收到数据期间始终为高电平/-reg rs232_rx0,rs232_rx1,rs232_rx2;/接收数据寄存器,滤波用wire neg_rs232_rx;/表示数据线接收到下降沿always (posedge clk or negedge rst_n) beginif(!rst_n) beginrs232_rx0 = 1b1;rs232_rx1 = 1b1;rs232_rx2 = 1b1;endelse beginrs232_rx0 = rs232_rx;rs232_rx1 = rs232_rx0;rs232_rx2 = rs232_rx1;endendassign neg_rs232_rx = rs232_rx2 & rs232_rx1;/接收到下降沿后neg_rs232_rx置高一个时钟周期/-reg bps_start_r;reg3:0num;/移位次数reg rx_int;/接收数据中断信号,接收到数据期间始终为高电平always (posedge clk or negedge rst_n) beginif(!rst_n) beginbps_start_r = 1bz;rx_int = 1b0;endelse if(neg_rs232_rx) beginbps_start_r = 1b1;/启动接收数据 rx_int = 1b1;/接收数据中断信号使能endelse if(num=4d12) beginbps_start_r = 1bz;/数据接收完毕rx_int = 1b0;/接收数据中断信号关闭endend assign bps_start = bps_start_r;/-reg7:0 rx_data_r;/接收数据寄存器,保存直至下一个数据来到/-reg7:0rx_temp_data;/但前接收数据寄存器reg rx_data_shift;/数据移位标志always (posedge clk or negedge rst_n) beginif(!rst_n) beginrx_data_shift = 1b0;rx_temp_data = 8d0;num = 4d0;rx_data_r = 8d0;endelse if(rx_int) begin/接收数据处理if(clk_bps) begin/读取并保存数据,接收数据为一个起始位,8bit数据,一个结束位rx_data_shift = 1b1;num = num+1b1;if(num=4d8) rx_temp_data7 = rs232_rx;/锁存9bit(1bit起始位,8bit数据)endelse if(rx_data_shift) begin/数据移位处理rx_data_shift = 1b0;if(num=4d8) rx_temp_data 1b1;/移位8次,第1bit起始位移除,剩下8bit正好时接收数据else if(num=4d12) beginnum = 4d0;/接收到STOP位后结束,num清零rx_data_r = rx_temp_data;/把数据锁存到数据寄存器rx_data中endend endendassign rx_data = rx_data_r;endmodulemodule my_uart_tx(clk,rst_n,clk_bps,rx_data,rx_int,rs232_tx,bps_start);input clk;/ 50MHz主时钟input rst_n;/低电平复位信号input clk_bps;/ clk_bps的高电平为接收或者发送数据位的中间采样点input7:0 rx_data;/接收数据寄存器input rx_int;/接收数据中断信号,接收到数据期间始终为高电平,在次利用它的下降沿来启动发送数据output rs232_tx;/ RS232发送数据信号output bps_start;/接收或者要发送数据,波特率时钟启动信号置位/-reg rx_int0,rx_int1,rx_int2;/rx_int信号寄存器,捕捉下降沿滤波用wire neg_rx_int;/ rx_int下降沿标志位always (posedge clk or negedge rst_n) beginif(!rst_n) beginrx_int0 = 1b0;rx_int1 = 1b0;rx_int2 = 1b0;endelse beginrx_int0 = rx_int;rx_int1 = rx_int0;rx_int2 = rx_int1;endendassign neg_rx_int = rx_int1 & rx_int2;/捕捉到下降沿后,neg_rx_int拉地保持一个主时钟周期/-reg7:0 tx_data;/待发送数据的寄存器/-reg bps_start_r;reg tx_en;/发送数据使能信号,高有效reg3:0 num;always (posedge clk or negedge rst_n) beginif(!rst_n) beginbps_start_r = 1bz;tx_en = 1b0;tx_data = 8d0;endelse if(neg_rx_int) begin/接收数据完毕,准备把接收到的数据发回去bps_start_r = 1b1;tx_data = rx_data;/把接收到的数据存入发送数据寄存器tx_en = 1b1;/进入发送数据状态中endelse if(num=4d11) begin/数据发送完成,复位bps_start_r = 1bz;tx_en = 1b0;endendassign bps_start = bps_start_r;/-reg rs232_tx_r;always (posedge clk or negedge rst_n) beginif(!rst_n) beginnum = 4d0;rs232_tx_r = 1b1;endelse if(tx_en) beginif(clk_bps)beginnum = num+1b1;case (num)4d0:rs232_tx_r = 1b0; /发送起始位4d1:rs232_tx_r = tx_data0;/发送bit04d2:rs232_tx_r = tx_data1;/发送bit14d3: rs232_tx_r = tx_data2;/发送bit24d4: rs232_tx_r = tx_data3;/发送bit34d5: rs232_tx_r = tx_data4;/发送bit44d6: rs232_tx_r = tx_data5;/发送bit54d7:rs232_tx_r = tx_data6;/发送bit64d8: rs232_tx_r = tx_data7;/发送bit74d9: rs232_tx_r = 1b0;/发送结束位 default: rs232_tx_r = 1b1;endcaseendelse if(num=4d11) num = 4d0;/复位endendassign rs232_tx = rs232_tx_r;endmoduleFPGA实现串行接口 RS232(1)2008-12-17 11:38串行接口(RS-232)串行接口是连接FPGA和PC机的一种简单方式。这个项目向大家展示了如果使用FPGA来创建RS-232收发器。整个项目包括5个部分1. RS232是怎样工作的 2. 如何产生需要的波特率 3. 发送模块 4. 接收模块 5. 应用实例 RS-232接口是怎样工作的作为标准设备,大多数的计算机都有1到2个RS-232串口。特性RS-232有下列特性: 使用9针的DB-9插头(旧式计算机使用25针的DB-25插头). 允许全双工的双向通讯(也就是说计算机可以在接收数据的同时发送数据). 最大可支持的传输速率为10KBytes/s. DB-9插头你可能已经在你的计算机背后见到过这种插头它一共有9个引脚,但是最重要的3个引脚是: 引脚2: RxD (接收数据). 引脚3: TxD (发送数据). 引脚5: GND (地). 仅使用3跟电缆,你就可以发送和接收数据.串行通讯数据以每次一位的方式传输;每条线用来传输一个方向的数据。由于计算机通常至少需要若干位数据,因此数据在发送之前先“串行化”。通常是以8位数据为1组的。 。先发送最低有效位,最后发送最高有效位。异步通讯RS-232使用异步通讯协议。也就是说数据的传输没有时钟信号。接收端必须有某种方式,使之与接收数据同步。对于RS-232来说,是这样处理的:1. 串行线缆的两端事先约定好串行传输的参数(传输速度、传输格式等) 2. 当没有数据传输的时候,发送端向数据线上发送1 3. 每传输一个字节之前,发送端先发送一个0来表示传输已经开始。这样接收端便可以知道有数据到来了。 4. 开始传输后,数据以约定的速度和格式传输,所以接收端可以与之同步 5. 每次传输完成一个字节之后,都在其后发送一个停止位(1) 让我们来看看0x55是如何传输的:0x55的二进制表示为:01010101。但是由于先发送的是最低有效位,所以发送序列是这样的: 1-0-1-0-1-0-1-0.下面是另外一个例子 :传输的数据为0xC4,你能看出来吗?从图中很难看出来所传输的数据,这也说明了事先知道传输的速率对于接收端有多么重要。数据传输可以多快?数据的传输速度是用波特来描述的,亦即每秒钟传输的数据位,例如1000波特表示每秒钟传输100比特的数据, 或者说每个数据位持续1毫秒。波特率不是随意的,必须服从一定的标准,如果希望设计123456波特的RS-232接口,对不起,你很不幸运,这是不行的。常用的串行传输速率值包括以下几种: 1200 波特. 9600 波特. 38400 波特. 115200 波特 (通常情况下是你可以使用的最高速度). 在115200 波特传输速度下, 每位数据持续 (1/115200) = 8.7s. 如果传输8位数据,共持续 8 x 8.7s = 69s。但是每个字节的传输又要求额外的“开始位”和“停止位”,所以实际上需要花费10 x 8.7s = 87s的时间。最大的有效数据传输率只能达到 11.5KBytes每秒。在115200 波特传输速度下,一些使用了不好的芯片的计算机要求一个长的停止位(1.5或2位数据的长度),这使得最大传输速度降到大约10.5KBytes每秒物理层电缆上的信号使用正负电压的机制: 1 用 -10V 的电压表示(或者在 -5V 与 -15V之间的电压). 0 用 +10V 的电压表示(或者在 5V 与 15V之间的电压). 所以没有数据传输的电缆上的电压应该为-10V或-5到-10之间的某个电压。FPGA实现串行接口 RS232(2)2008-12-17 11:39波特率发生器这里我们使用串行连接的最大速度115200波特,其他较慢的波特也很容易由此产生。FPGA通常运行在远高于115200Hz的时钟频率上(对于今天的标准的来说RS-232真是太慢了),这就意味着我们需要用一个较高的时钟来分频产生尽量接近于115200Hz的时钟信号。从1.8432MHz的时钟产生通常RS-232芯片使用1.8432MHz的时钟,以为这个时钟很容易产生标准的波特率,所以我们假设已经拥有了一个这样的时钟源。只需要将 1.8432MHz 16分频便可得到 115200Hz的时钟,多方便啊!reg 3:0 BaudDivCnt;always (posedge clk) BaudDivCnt =2000000) printf(*); else printf( );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 (posedge clk)acc = acc9:0 + 59; /我们使用上一次结果的低10位,但是保留11位结果wire BaudTick = acc10; /第11位作为进位输出使用 2MHz 时钟, BaudTick 为 115234 波特, 跟理想的115200波特存在 0.03% 的误差。参数化的FPGA波特率发生器前面的设计我们使用的是10位的累加器,如果时钟频率提高的话,需要更多的位数。下面是一个使用 25MHz

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论