FPGA学习日记(四)FPGA的RS232串口通信实验

本次实验目的:通过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

实验总结:

通过两个计数器,数据位数的计数器和发送一位数据周期的计数器,在标志位的指示下,完成数据的串并转换收发。


 

 

  • 7
    点赞
  • 59
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值