概述
本次实验目的:通过FPGA控制urat串口,接收上位机的数据并将接收到的数据发送给上位机,完成串口数据环回。
实验介绍:
UART是一种采用异步串行通信方式的通用异步收发传输器( universal asynchronousreceiver-transmitter), 它在发送数据时将并行数据转换成串行数据来传输,在接收数据时将接收到的串行数据转换成并行数据。异步串行通信的接
口标准有RS232、 RS422、 RS485,RS-232是单端输入输出(双线),而RS-422/485为差分输入输出。
异步串行通信数据格式
UART通信过程中的数据格式及传输速率是可设置的,为了正确的通信,收发双方应约定并遵循同样的设置。数据位可选择为5、 6、 7、 8位,其中8位数据位是最常用的, 在实际应用中一般都选择8位数据位;校验位可选择奇校验、偶校验或者无校验位;停止位可选择1位(默认),1.5或2位。 串口通信的速率用波特率表示,它表示每秒传输二进制数据的位数,单位是bps( 位
/秒) ,常用的波特率有9600、 19200、 38400、 57600以及115200等。(设置通过工作时钟与波特率之比可计算一位数据传输周期)
RS232接口,要用的一般为2,3,5引脚:
实验设计:接收模块,发送模块及顶层模块一共是四个模块。
urat_recv:接收外机传来的urat格式数据,并保存到寄存器中(串转并);
urat_send:发送urat格式数据(并转串)
顶层模块
//UART顶层模块
module URAT_RS232(sys_clk,sys_rst_n,uart_rxd,uart_txd);
input sys_clk;
input sys_rst_n;
input uart_rxd;
output uart_txd;
//设置波特率,时钟频率
parameter UART_BPS = 115200;
parameter CLK_FREQ = 50000000;
//内部变量声明
wire uart_en_w; //发送使能
wire [7:0] uart_data_w; //发送数据
wire clk_1m;
//发送模块
uart_send #(
.UART_BPS (UART_BPS),
.CLK_FREQ (CLK_FREQ))
u_uart_send(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.uart_din (uart_data_w),
.uart_txd (uart_txd),
.uart_en (uart_en_w)
);
//接收模块
uart_recv #(
.UART_BPS (UART_BPS),
.CLK_FREQ (CLK_FREQ))
u_uart_recv(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.uart_rxd (uart_rxd),
.uart_data (uart_data_w),
.uart_done (uart_en_w)
);
endmodule
接收模块:
//接收模块
module uart_recv(sys_clk, sys_rst_n,uart_rxd,uart_data,uart_done);
input sys_clk;
input sys_rst_n;
input uart_rxd; //起始信号为下降沿触发
output reg uart_done;
output reg [7:0] uart_data;
parameter UART_BPS = 115200;//设置波特率
parameter CLK_FREQ = 50000000;//时钟频率
localparam BPS_CNT = CLK_FREQ / UART_BPS;//当前波特率下传输移位数据所需的时钟周期
reg [7:0] rxdata;//暂存寄存器
reg [18:0] clk_cnt;//每传输一位数据周期计数器
reg [3:0] rx_cnt;//传输数据个数计数
wire start_flag;
reg uart_rxd_d0;
reg uart_rxd_d1;
reg rx_flag;//接受标志位
//使能信下降沿检测
assign start_flag = (~uart_rxd_d0) & uart_rxd_d1;
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
uart_rxd_d0 <= 1'b0;
uart_rxd_d1 <= 1'b0;
end
else begin
uart_rxd_d0 <= uart_rxd;
uart_rxd_d1 <= uart_rxd_d0;
end
end
//数据传输标志信号与数据暂存
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
rx_flag <= 1'b0;
end
else begin
if(start_flag)
rx_flag <= 1'b1;
else if((rx_cnt == 4'd9) && (clk_cnt == BPS_CNT /2)) //当计数到一位传输周期中间时数据有效
rx_flag <= 1'b0;
else begin
rx_flag <= rx_flag;
end
end
end
//时钟计数器与数据位数计数器
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
clk_cnt <= 19'd0;
rx_cnt <= 4'd0;
end
else if(rx_flag) begin
if(clk_cnt < BPS_CNT - 1'd1) begin
clk_cnt <= clk_cnt + 1'd1;
rx_cnt <= rx_cnt;
end
else begin
clk_cnt <= 19'd0;
rx_cnt <= rx_cnt + 1'd1;
end
end
else begin
clk_cnt <= 19'd0;
rx_cnt <= 4'd0;
end
end
//将接收端口的数据存到暂存器中,串转并
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
rxdata <= 8'd0;
end
else if(rx_flag)
if(clk_cnt == BPS_CNT / 2)begin
case(rx_cnt)
4'd1: rxdata[0] <= uart_rxd;
4'd2: rxdata[1] <= uart_rxd;
4'd3: rxdata[2] <= uart_rxd;
4'd4: rxdata[3] <= uart_rxd;
4'd5: rxdata[4] <= uart_rxd;
4'd6: rxdata[5] <= uart_rxd;
4'd7: rxdata[6] <= uart_rxd;
4'd8: rxdata[7] <= uart_rxd;
default: ;
endcase
end
else
rxdata <= rxdata;
else
rxdata <= 8'd0;
end
//数据寄存完毕给出标志信号,数据输出
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
uart_done <= 1'b0;
uart_data <= 8'd0;
end
else if(rx_cnt == 4'd9)begin
uart_done <= 1'b1;
uart_data <= rxdata;
end
else begin
uart_done <= 1'b0;
uart_data <= 8'd0;
end
end
endmodule
发送模块:
//发送模块
module uart_send(sys_clk,sys_rst_n,uart_din,uart_en,uart_txd);
input sys_clk;
input sys_rst_n;
input [7:0] uart_din;
input uart_en; //上升沿触发发送
output reg uart_txd;
parameter UART_BPS = 115200;//设置波特率
parameter CLK_FREQ = 50000000;//时钟频率
localparam BPS_CNT = CLK_FREQ / UART_BPS;//当前波特率下传输移位数据所需的时钟周期
wire en_flag;//上升沿检测起始位信号
reg uart_en_d0;
reg uart_en_d1;
reg [7:0] tx_data;//暂存寄存器
reg tx_flag;//数据传输中信号
reg[15:0] clk_cnt;//每传输一位数据周期计数器
reg[3:0] tx_cnt;//传输数据个数计数
assign en_flag = uart_en_d0 & (~uart_en_d1);//使能信号上升沿检测
//使能信号上升沿检测
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
uart_en_d0 <= 1'b0;
uart_en_d1 <= 1'b0;
end
else begin
uart_en_d0 <= uart_en;
uart_en_d1 <= uart_en_d0;
end
end
//时钟计数器与数据位数计数器
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
clk_cnt <= 19'd0;
tx_cnt <= 4'd0;
end
else if(tx_flag) begin
if(clk_cnt < BPS_CNT - 1'd1) begin
clk_cnt <= clk_cnt + 1'd1;
tx_cnt <= tx_cnt;
end
else begin
clk_cnt <= 19'd0;
tx_cnt <= tx_cnt + 1'd1;
end
end
else begin
clk_cnt <= 19'd0;
tx_cnt <= 4'd0;
end
end
//数据传输标志信号与数据暂存
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
tx_flag <= 1'b0;
tx_data <= 8'd0;
end
else if(en_flag)begin
tx_flag <= 1'b1;
tx_data <= uart_din;
end
else if((tx_cnt == 4'd9) && (clk_cnt == BPS_CNT /2))begin
tx_flag <= 1'b0;
tx_data <= 8'd0;
end
else begin
tx_flag <= tx_flag;
tx_data <= tx_data;
end
end
//根据数据的传输的当前个数输出数据,并转串
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
uart_txd <= 1'b1; //idle为高
end
else begin
if(tx_flag)
case(tx_cnt)
4'd0: uart_txd <= 1'b0; //起始位
4'd1: uart_txd <= tx_data[0];
4'd2: uart_txd <= tx_data[1];
4'd3: uart_txd <= tx_data[2];
4'd4: uart_txd <= tx_data[3];
4'd5: uart_txd <= tx_data[4];
4'd6: uart_txd <= tx_data[5];
4'd7: uart_txd <= tx_data[6];
4'd8: uart_txd <= tx_data[7];
4'd9: uart_txd <= 1'b1;
default: ;
endcase
else begin
uart_txd <= 1'b1;
end
end
end
endmodule
问题总结:
一、边沿检测写反了
发送模块是上升沿触发,接受使能信号,接收模块是下降沿触发,接收拉低的起始信号;
二、接受模块在数据存入寄存器时,第一位数据应当为1'b0作为起始信号;
即0:rx_data[0] <= 1'b0;
三、接收模块数据传输标志信号编写时,由于对if_else if,和begin_end的使用出错,导致两个并列关系的if语句变成了递进关系,最终使得rx_flag信号一直为1‘b0,无法接收数据,错误如下1,更正如2:
1:
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
rx_flag <= 1'b0;
end
else if(start_flag)begin
rx_flag <= 1'b1;
if((rx_cnt == 4'd9) && (clk_cnt == BPS_CNT /2))begin //当计数到一位传输周期中间时数据有效
rx_flag <= 1'b0;
end
else
rx_flag <= rx_flag;
end
end
2:
//当脉冲信号start_flag到达时,进入接收过程
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n)
rx_flag <= 1'b0;
else begin
if(start_flag) //检测到起始位
rx_flag <= 1'b1; //进入接收过程,标志位rx_flag拉高
else if((rx_cnt == 4'd9)&&(clk_cnt == BPS_CNT/2))
rx_flag <= 1'b0; //计数到停止位中间时,停止接收过程
else
rx_flag <= rx_flag;
end
end
实验总结:
通过两个计数器,数据位数的计数器和发送一位数据周期的计数器,在标志位的指示下,完成数据的串并转换收发。
最后
以上就是文静未来为你收集整理的FPGA学习日记(四)FPGA的RS232串口通信实验的全部内容,希望文章能够帮你解决FPGA学习日记(四)FPGA的RS232串口通信实验所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复