手机版

verilog串口通信程序

发布时间:2024-11-21   来源:未知    
字号:

FPGA实现RS-232串口收发的仿真过程(Quartus+Synplify+ModelSim)(2007-09-11

12:17:37)

就结合网上的参考资料和自己的琢磨,做了这个东西。

针对我这个小程序结合FPGA的开发流程,主要走了以下几步: 1. 文本程序输入(Verilog HDL)

2. 功能仿真(ModelSim,查看逻辑功能是否正确,要写一个Test Bench) 3. 综合(Synplify Pro,程序综合成网表)

网上关于RS-232的异步收发介绍得很多,最近没事学着摸索用ModelSim来做时序仿真,

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;

input[7:0] TxD_data; // 待发送的数据 output TxD, // 输出端口发送的串口数据 TxD_busy; reg TxD;

reg [7:0] TxD_dataReg; // 寄存器发送模式,因为在串口发送过程中输入端不可能一直保持有效电平 reg [3:0] state;

parameter ClkFrequency = 25000000; // 时钟频率-25 MHz

parameter Baud = 115200; // 串口波特率-115200

// 波特率产生

parameter BaudGeneratorAccWidth = 16;

reg [BaudGeneratorAccWidth:0] BaudGeneratorAcc; wire

[BaudGeneratorAccWidth:0]

BaudGeneratorInc

=

((Baud<<(BaudGeneratorAccWidth-4))+(ClkFrequency>>5))/(ClkFrequency>>4); wire BaudTick = BaudGeneratorAcc[BaudGeneratorAccWidth]; wire TxD_busy;

always @(posedge clk or negedge rst) if(~rst)

BaudGeneratorAcc <= 0; else if(TxD_busy)

BaudGeneratorAcc <= BaudGeneratorAcc[BaudGeneratorAccWidth-1:0] + BaudGeneratorInc; // 发送端状态

wire TxD_ready = (state==0); // 当state = 0时,处于准备空闲状态,TxD_ready = 1 assign TxD_busy = ~TxD_ready; // 空闲状态时TxD_busy = 0 // 把待发送数据放入缓存寄存器 TxD_dataReg always @(posedge clk or negedge rst) if(~rst)

TxD_dataReg <= 8'b00000000; else if(TxD_ready & TxD_start) TxD_dataReg <= TxD_data;

// 发送状态机

always @(posedge clk or negedge rst) 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; // bit 0 TxD <= TxD_dataReg[0]; end 4'b1001: if(BaudTick) begin

state <= 4'b1010; // bit 1 TxD <= TxD_dataReg[1]; end

4'b1010: if(BaudTick) begin

state <= 4'b1011; // bit 2 TxD <= TxD_dataReg[2]; end 4'b1011: if(BaudTick) begin

state <= 4'b1100; // bit 3 TxD <= TxD_dataReg[3]; end 4'b1100: if(BaudTick) begin

state <= 4'b1101; // bit 4 TxD <= TxD_dataReg[4]; end 4'b1101: if(BaudTick) begin

state <= 4'b1110; // bit 5 TxD <= TxD_dataReg[5]; end 4'b1110: if(BaudTick) begin

state <= 4'b1111; // bit 6 TxD <= TxD_dataReg[6]; end 4'b1111: if(BaudTick) begin

state <= 4'b0010; // bit 7 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 接收端: module rcv(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倍过采样 parameter Baud8 = Baud*8;

parameter Baud8GeneratorAccWidth = 16; wire

[Baud8GeneratorAccWidth:0]

Baud8GeneratorInc

((Baud8<<(Baud8GeneratorAccWidth-7))+(ClkFrequency>>8))/(ClkFrequency>>7); reg [Baud8GeneratorAccWidth:0] Baud8GeneratorAcc; always @(posedge clk or negedge rst) if(~rst)

Baud8GeneratorAcc <= 0; else

Baud8GeneratorAcc

<=

Baud8GeneratorAcc[Baud8GeneratorAccWidth-1:0]

Baud8GeneratorInc;

// Baud8Tick 为波特率的8倍 - 115200*8 = 921600

wire Baud8Tick = Baud8GeneratorAcc[Baud8GeneratorAccWidth]; // next_bit 为波特率 - 115200 always @(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) begin

RxD_delay <= RxD;

RxD_start <= (Baud8Tick & RxD_delay & (~RxD)); end

// 状态机接收数据

always@(posedge clk or negedge rst) 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; // bit 0 4'b1001: if(next_bit) state <= 4'b1010; // bit 1 4'b1010: if(next_bit) state <= 4'b1011; // bit 2 4'b1011: if(next_bit) state <= 4'b1100; // bit 3 4'b1100: if(next_bit) state <= 4'b1101; // bit 4 4'b1101: if(next_bit) state <= 4'b1110; // bit 5 4'b1110: if(next_bit) state <= 4'b1111; // bit 6 4'b1111: if(next_bit) state <= 4'b0001; // bit 7 4'b0001: if(next_bit) state <= 4'b0000; // 停止位 default: state <= 4'b0000; endcase

// 保存接收数据到 RxD_data 中 always @(posedge clk or negedge rst) if(~rst)

RxD_data <= 8'b00000000;

else if(Baud8Tick && next_bit && state[3]) RxD_data <= {RxD, RxD_data[7:1]}; // RxD_data_ready 置位信号 always @(posedge clk or negedge rst) if(~rst)

RxD_data_ready <= 0; else

RxD_data_ready <= (Baud8Tick && next_bit && state==4'b0001); endmodule

为了测试收发是否正常,写的Test Bench `timescale 1ns / 1ns module rs232_test; reg clk, rst, TxD_start; reg [7:0] TxD_data; wire[7:0] RxD_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) );

initial begin TxD_start = 0; TxD_data = 0; clk = 0; rst = 1; #54 rst = 0; #70 rst = 1;

#40 TxD_start = 1'b1; #10 TxD_data = 8'b11011001; #100 TxD_start = 1'b0; end always begin #30 clk = ~clk; #10 clk = ~clk; end endmodule

二、综合

三、

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主时钟

// RS232接收数据信号

RS232发送数据信号

input rst_n; //低电平复位信号 input rs232_rx; output rs232_tx; //

wire bps_start; wire clk_bps;

//接收到数据后,波特率时钟启动信号置位

// clk_bps的高电平为接收或者发送数据位的中间采样点

wire[7:0] rx_data; //接收数据寄存器,保存直至下一个数据来到 wire rx_int; //接收数据中断信号,接收到数据期间始终为高电平 //---------------------------------------------------- speed_select 全双工通信

my_uart_rx

my_uart_tx

endmodule

module speed_select(clk,rst_n,bps_start,clk_bps);

input clk;

// 50MHz主时钟

input rst_n; //低电平复位信号

my_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) );

my_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) );

.rst_n(rst_n), .bps_start(bps_start), .clk_bps(clk_bps) );

speed_select( .clk(clk), //波特率选择模块,接收和发送模块复用,不支持

input bps_start; output clk_bps;

parameter

parameter

//接收到数据后,波特率时钟启动信号置位

// clk_bps的高电平为接收或者发送数据位的中间采样点

= 5207, //波特率为9600bps

= 2603, //波特率为19200bps = 1301, //波特率为38400bps = 867, //波特率为57600bps = 433; //波特率为115200bps

bps9600

bps19200 bps38400 bps57600 bps115200

bps9600_2 = 2603,

bps19200_2 = 1301, bps38400_2 = 650, bps57600_2 = 433, bps115200_2 = 216;

//分频计数最大值 //分频计数 //波特率时钟寄存器

reg[12:0] bps_para; reg[12:0] cnt; reg clk_bps_r;

reg[12:0] bps_para_2; //分频计数的一半

//---------------------------------------------------------- reg[2:0] uart_ctrl; // uart波特率选择寄存器 //----------------------------------------------------------

always @ (posedge clk or negedge rst_n) begin

if(!rst_n) begin

end

case (uart_ctrl)

3'd0: begin

bps_para <= bps9600; bps_para_2 <= bps9600_2; end

bps_para <= bps19200; bps_para_2 <= bps19200_2; end

//波特率设置

uart_ctrl <= 3'd0; //默认波特率为9600bps

else begin

3'd1: begin

end

end

3'd2: begin

bps_para <= bps38400; bps_para_2 <= bps38400_2; end

bps_para <= bps57600; bps_para_2 <= bps57600_2; end

bps_para <= bps115200; bps_para_2 <= bps115200_2; end

3'd3: begin

3'd4: begin

default: ; endcase

always @ (posedge clk or negedge rst_n)

always @ (posedge clk or negedge rst_n)

assign clk_bps = clk_bps_r;

endmodule

module my_uart_rx(clk,rst_n,rs232_rx,clk_bps,bps_start,rx_data,rx_int);

input clk;

// 50MHz主时钟

// RS232接收数据信号

input rst_n; //低电平复位信号 input rs232_rx;

if(!rst_n) clk_bps_r <= 1'b0;

else if(cnt==bps_para_2 && bps_start) clk_bps_r <= 1'b1; // clk_bps_r高电平为接收else clk_bps_r <= 1'b0; if(!rst_n) cnt <= 13'd0;

else if(cnt<bps_para && bps_start) cnt <= cnt+1'b1; else cnt <= 13'd0;

//波特率时钟计数启动

或者发送数据位的中间采样点

input clk_bps; // clk_bps的高电平为接收或者发送数据位的中间采样点 output bps_start; //接收到数据后,波特率时钟启动信号置位 output[7:0] rx_data;

//---------------------------------------------------------------- reg rs232_rx0,rs232_rx1,rs232_rx2; wire neg_rs232_rx;

always @ (posedge clk or negedge rst_n) begin end

assign neg_rs232_rx = rs232_rx2 & ~rs232_rx1; //接收到下降沿后neg_rs232_rx置高一个时钟周期

//---------------------------------------------------------------- reg bps_start_r; reg[3:0] num; reg rx_int;

always @ (posedge clk or negedge rst_n) begin

if(!rst_n) begin

end

bps_start_r <= 1'b1; rx_int <= 1'b1;

//启动接收数据

bps_start_r <= 1'bz; rx_int <= 1'b0;

//移位次数

//接收数据中断信号,接收到数据期间始终为高电平

if(!rst_n) begin

end end

rs232_rx0 <= rs232_rx; rs232_rx1 <= rs232_rx0; rs232_rx2 <= rs232_rx1; rs232_rx0 <= 1'b1; rs232_rx1 <= 1'b1; rs232_rx2 <= 1'b1;

//接收数据寄存器,滤波用

//表示数据线接收到下降沿

//接收数据寄存器,保存直至下一个数据来到

output rx_int; //接收数据中断信号,接收到数据期间始终为高电平

else begin

else if(neg_rs232_rx) begin

//接收数据中断信号使能

end

end

bps_start_r <= 1'bz; rx_int <= 1'b0;

//数据接收完毕 //接收数据中断信号关闭

else if(num==4'd12) begin

end

assign bps_start = bps_start_r;

//----------------------------------------------------------------

reg[7:0] rx_data_r; //接收数据寄存器,保存直至下一个数据来到 //----------------------------------------------------------------

reg[7:0] rx_temp_data; //但前接收数据寄存器 reg rx_data_shift; //数据移位标志

always @ (posedge clk or negedge rst_n) begin

if(!rst_n) begin

end

//接收数据处理

if(clk_bps) begin //读取并保存数据,接收数据为一个起始位,8bit数据,一个结束

end

rx_data_shift <= 1'b0;

if(num<=4'd8) rx_temp_data <= rx_temp_data >> 1'b1; //移位8次,第1else if(num==4'd12) begin

num <= 4'd0; //接收到STOP位后结束,num清零

rx_data_r <= rx_temp_data; //把数据锁存到数据寄存器rx_data

rx_data_shift <= 1'b1; num <= num+1'b1;

if(num<=4'd8) rx_temp_data[7] <= rs232_rx; //锁存9bit(1bit起始位,8rx_data_shift <= 1'b0; rx_temp_data <= 8'd0; num <= 4'd0; rx_data_r <= 8'd0;

else if(rx_int) begin

bit数据)

else if(rx_data_shift) begin //数据移位处理

bit起始位移除,剩下8bit正好时接收数据

中 end

assign rx_data = rx_data_r;

endmodule

module my_uart_tx(clk,rst_n,clk_bps,rx_data,rx_int,rs232_tx,bps_start);

input clk;

// 50MHz主时钟

// clk_bps的高电平为接收或者发送数据位的中间采样点

//接收数据中断信号,接收到数据期间始终为高电平,在次利用它的下降沿

input rst_n; //低电平复位信号 input clk_bps; 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) begin end

if(!rst_n) begin

end end

rx_int0 <= rx_int; rx_int1 <= rx_int0; rx_int2 <= rx_int1; rx_int0 <= 1'b0; rx_int1 <= 1'b0; rx_int2 <= 1'b0;

input[7:0] rx_data; //接收数据寄存器

end

end

end

else begin

assign neg_rx_int = ~rx_int1 & rx_int2; //捕捉到下降沿后,neg_rx_int拉地保持一个主时钟周期

//--------------------------------------------------------- reg[7:0] tx_data; //待发送数据的寄存器 //--------------------------------------------------------- reg bps_start_r; reg tx_en;

always @ (posedge clk or negedge rst_n) begin end

assign bps_start = bps_start_r;

//--------------------------------------------------------- reg rs232_tx_r;

always @ (posedge clk or negedge rst_n) begin

if(!rst_n) begin

end

num <= 4'd0; rs232_tx_r <= 1'b1;

if(!rst_n) begin

end

//接收数据完毕,准备把接收到的数据发回去

//把接收到的数据存入发送数据寄存器 //进入发送数据状态中

end end

bps_start_r <= 1'bz; tx_en <= 1'b0; bps_start_r <= 1'b1; tx_data <= rx_data; tx_en <= 1'b1;

bps_start_r <= 1'bz; tx_en <= 1'b0; tx_data <= 8'd0;

//发送数据使能信号,高有效

reg[3:0] num;

else if(neg_rx_int) begin

else if(num==4'd11) begin //数据发送完成,复位

end

end

if(clk_bps)

end

//复位

begin num <= num+1'b1; case

(num)

4'd0: rs232_tx_r <= 1'b0; //发送起始位 4'd1: rs232_tx_r <= tx_data[0]; //发送bit0 4'd2: rs232_tx_r <= tx_data[1]; //发送bit1 4'd3: rs232_tx_r <= tx_data[2]; //发送bit2 4'd4: rs232_tx_r <= tx_data[3]; //发送bit3 4'd5: rs232_tx_r <= tx_data[4]; //发送bit4 4'd6: rs232_tx_r <= tx_data[5]; //发送bit5 4'd7: rs232_tx_r <= tx_data[6]; //发送bit6 4'd8: rs232_tx_r <= tx_data[7]; //发送bit7 4'd9: rs232_tx_r <= 1'b0; //发送结束位 default: rs232_tx_r <= 1'b1; endcase

else if(num==4'd11) num <= 4'd0;

assign rs232_tx = rs232_tx_r;

endmodule

FPGA实现串行接口 RS232(1) 2008-12-17 11:38

串行接口(RS-232)

串行接口是连接FPGA和PC机的一种简单方式。这个项目向大家展示了如果使用FPGA来创建RS-232收发器。

verilog串口通信程序.doc 将本文的Word文档下载到电脑,方便复制、编辑、收藏和打印
    ×
    二维码
    × 游客快捷下载通道(下载后可以自由复制和排版)
    VIP包月下载
    特价:29 元/月 原价:99元
    低至 0.3 元/份 每月下载150
    全站内容免费自由复制
    VIP包月下载
    特价:29 元/月 原价:99元
    低至 0.3 元/份 每月下载150
    全站内容免费自由复制
    注:下载文档有可能出现无法下载或内容有问题,请联系客服协助您处理。
    × 常见问题(客服时间:周一到周五 9:30-18:00)