FPGA-DE2-115-实验三-rs232串口通信


前言:
本文主要介绍了集成电路EDA这门课程的相关实验及代码。使用的软件是Quartus Ⅱ,该实验使用fpga芯片为cyclone IV EP4CE115F29C7。

1. 实验要求

串口接发送,设计实现接收PC机发送的信息,并发送信息给PC机
FPGA接收到hello后,回复ni,hao给PC机

2. 实验原理

实验采用用UART数据通信协议实现串口通信,UART是一种通用的数据通信协议,也是异步串行通信口(串口)的总称。
它在发送数据时将并行数据转换成串行数据来传输,在接收数据时将接收到的串行数据转换成并行数据。包括RS232、RS499、RS423、RS422和RS485等接口标准规范和总线标准规范。
本次实验采用RS232接口规范实现串口通信。
• PC机通过串口调试助手(上位机)向FPGA发送8bit数据,一位一位发送,首先接收最低位,最后最高位,之后FPGA拼凑成8bit数据
• FPGA向PC机发送8bit数据,通过串口调试助手(上位机),一位一位发送,首先接收最低位,最后最高位,之后串口调试助手拼凑成8bit数据
通信基于帧结构,一帧一帧发送数据
在这里插入图片描述
tx–发送端 串行发送
rx–接收端 并行接收
一帧总共10bit:
• 起始位/停止位-一个波特时间
• 每bit数据持续连续1个波特时间,可能发送很多次

3. 实验过程及源码

3.1 顶层模块rs232

对接收模块uart_rx与发送模块uart_tx的调用,并且存储接收的数据,当接收数据满足hello的ASCII码时,控制发送ni,hao的ASCII码给PC机通信

`timescale  1ns/1ns

module  rs232
(
    input   wire    sys_clk     ,    //系统时钟50MHz
    input   wire    sys_rst_n   ,   //全局复位
    input   wire    rx          ,   //串口接收数据
    
    output  wire    tx              //串口发送数据
);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//parameter define
parameter   UART_BPS    =   20'd9600        ,   //比特率
            CLK_FREQ    =   26'd50_000_000  ;   //时钟频率

localparam  BAUD_CNT_MAX    =   CLK_FREQ/UART_BPS   ;
//wire  define
wire            en_h_flag;
wire    [7:0]   po_data;    //接收的数据
wire            po_flag;    //接收完1字节数据标志位,高电平有效
wire            flag;       //识别到接收数据与密码对应标志位
wire            tx_flag;    //发送完1字节数据标志位,高电平有效
reg     [39:0]  datain_reg; //存储接收的数据,5字节
reg     [47:0]  dataout_reg;//存储的要发送的数据,6字节
reg     [1:0]   state;      //状态位
reg     [7:0]   data_tx;    //发送的1字节数据
reg             en_tx;      //发送允许标志位
reg     [2:0]   tx_cnt;     //发送字节计数器,发送6个后置0
reg             en;         //发送控制开关
reg     [12:0]  baud_cnt;   //收到发送成功的tx_flag后延迟1个波特
reg             bit_flag;   //计满1baud有效
reg             work;       //波特计数器baud_cnt有效
//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//
//------------------------ uart_rx_inst ------------------------
uart_rx
#(
    .UART_BPS    (UART_BPS  ),  //串口波特率
    .CLK_FREQ    (CLK_FREQ  )   //时钟频率
)
uart_rx_inst
(
    .sys_clk    (sys_clk    ),  //input             sys_clk
    .sys_rst_n  (sys_rst_n  ),  //input             sys_rst_n
    .rx         (rx         ),  //input             rx
            
    .po_data    (po_data    ),  //output    [7:0]   po_data
    .po_flag    (po_flag    )   //output            po_flag
);
always@(posedge sys_clk or negedge sys_rst_n)
    if(!sys_rst_n)
        en <= 1'b1;
    else if(en_h_flag)
        en <= 1'b1;
    else if(tx_cnt>=3'd5)
        en <= 1'b0;   
//接收数据寄存
always@(posedge sys_clk or negedge sys_rst_n)
    if(!sys_rst_n)
        datain_reg <= 40'd0;
    else if(po_flag)
        datain_reg <= {datain_reg[31:0],po_data[7:0]};
        
//接收到tx_flag后,延迟一个baud时间再发送下一个
always@(posedge sys_clk or negedge sys_rst_n)
    if(!sys_rst_n)
        work <= 1'b0;
    else if(tx_flag)
        work <= 1'b1;
    else if(state != 2'd2)
        work <= 1'b0;

always@(posedge sys_clk or negedge sys_rst_n)
    if(!sys_rst_n)
        baud_cnt <= 13'd0;
    else if((baud_cnt == BAUD_CNT_MAX - 1) || en_tx)
        baud_cnt <= 13'b0;
    else if(work)
        baud_cnt <= baud_cnt + 1'd1;

always@(posedge sys_clk or negedge sys_rst_n)
    if(!sys_rst_n)
        bit_flag <= 1'b0;
    else if(baud_cnt == BAUD_CNT_MAX - 1)
        bit_flag <= 1'b1;
    else if(state != 2'd2) 
        bit_flag <= 1'b0;

                              //hello的ASCII码
assign flag = (datain_reg == 40'h68656c6c6f)? 1'b1:1'b0;

always@(posedge sys_clk or negedge sys_rst_n)
    if(!sys_rst_n)
        begin
            state <= 2'd0;
            dataout_reg <= 48'h6e692c68616f;//ni,hao的ASCII码
            data_tx <= 8'd0;
            en_tx <= 1'b0;
            tx_cnt <= 3'd0;
        end
    else
        case(state)
            2'd0:
                begin
                    if(flag && en)
                        state <= 2'd1;
                    else
                        state <= 2'd0;
                end
            2'd1://发送数据
                begin
                    state <= 2'd2;
                    data_tx <= dataout_reg[47:40];
                    en_tx <= 1'b1;
                    dataout_reg <= dataout_reg << 8;
                end            
            2'd2://等待数据发送完成,并计数+1
                begin
                    if(bit_flag)
                        begin
                            if(tx_cnt>=3'd5)begin
                                state <= 2'd0;
                                tx_cnt <= 3'd0;                         
                            end
                            else begin
                                state <= 2'd1;
                                tx_cnt <= tx_cnt + 1'd1;                            
                            end 
                        end
                    else
                        begin
                            en_tx <= 1'b0;
                            state <= 2'd2;
                        end
                end 
            default : state <= 2'd0;
        endcase            
//------------------------ uart_tx_inst ------------------------
uart_tx
#(
    .UART_BPS    (UART_BPS  ),  //串口波特率
    .CLK_FREQ    (CLK_FREQ  )   //时钟频率
)
uart_tx_inst
(
    .sys_clk    (sys_clk    ),  
    .sys_rst_n  (sys_rst_n  ),  
    .pi_data    (data_tx    ),  
    .pi_flag    (en_tx      ),     
    
    .tx         (tx         ),   
    .tx_flag    (tx_flag    )
);

endmodule

3.2 接收模块uart_tx

时序图如下:
在这里插入图片描述
在这里插入图片描述

`timescale  1ns/1ns
//FPGA-->PC端    发送
module  uart_tx
#(
    parameter   UART_BPS    =   'd9600,         //串口波特率
    parameter   CLK_FREQ    =   'd50_000_000    //时钟频率
)
(
     input   wire            sys_clk     ,   //系统时钟50MHz
     input   wire            sys_rst_n   ,   //全局复位
     input   wire    [7:0]   pi_data     ,   //模块输入的8bit数据
     input   wire            pi_flag     ,   //并行数据有效标志信号

     output  reg             tx          ,   //串转并后的1bit数据
     output  reg             tx_flag         //发送一个字节完毕标志位
);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//localparam    define
localparam  BAUD_CNT_MAX    =   CLK_FREQ/UART_BPS   ;

//reg   define
reg [12:0]  baud_cnt;
reg         bit_flag;
reg [3:0]   bit_cnt ;
reg         work_en ;

//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//work_en:接收数据工作使能信号
always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            work_en <= 1'b0;
        else    if(pi_flag == 1'b1)
            work_en <= 1'b1;
        else    if((bit_flag == 1'b1) && (bit_cnt == 4'd9))
            work_en <= 1'b0;

//baud_cnt:波特率计数器计数,从0计数到BAUD_CNT_MAX - 1
always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            baud_cnt <= 13'b0;
        else    if((baud_cnt == BAUD_CNT_MAX - 1) || (work_en == 1'b0))
            baud_cnt <= 13'b0;
        else    if(work_en == 1'b1)
            baud_cnt <= baud_cnt + 1'b1;

//bit_flag:当baud_cnt计数器计数到1时让bit_flag拉高一个时钟的高电平
always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            bit_flag <= 1'b0;
        else    if(baud_cnt == 13'd1)
            bit_flag <= 1'b1;
        else
            bit_flag <= 1'b0;

//bit_cnt:数据位数个数计数,10个有效数据(含起始位和停止位)到来后计数器清零
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        bit_cnt <= 4'b0;
    else    if((bit_flag == 1'b1) && (bit_cnt == 4'd9))
        bit_cnt <= 4'b0;
    else    if((bit_flag == 1'b1) && (work_en == 1'b1))
        bit_cnt <= bit_cnt + 1'b1;

//tx:输出数据在满足rs232协议(起始位为0,停止位为1)的情况下一位一位输出
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        tx <= 1'b1; //空闲状态时为高电平
    else    if(bit_flag == 1'b1)
        case(bit_cnt)
            0       : tx <= 1'b0;
            1       : tx <= pi_data[0];
            2       : tx <= pi_data[1];
            3       : tx <= pi_data[2];
            4       : tx <= pi_data[3];
            5       : tx <= pi_data[4];
            6       : tx <= pi_data[5];
            7       : tx <= pi_data[6];
            8       : tx <= pi_data[7];
            9       : tx <= 1'b1;
            default : tx <= 1'b1;
        endcase

always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        tx_flag <= 1'b0;
    else if((bit_flag == 1'b1) && (bit_cnt == 4'd9))
        tx_flag <= 1'b1;
    else
        tx_flag <= 1'b0;
endmodule

3.3 发送模块uart_rx

时序图:
在这里插入图片描述


//rx对于时钟信号是异步信号,经过寄存器同步之后,会引入亚稳态问题,多次打拍解决

`timescale  1ns/1ns

module  uart_rx
#(
    parameter   UART_BPS    =   'd9600,         //串口波特率
    parameter   CLK_FREQ    =   'd50_000_000    //时钟频率
)
(
    input   wire            sys_clk     ,   //系统时钟50MHz
    input   wire            sys_rst_n   ,   //全局复位
    input   wire            rx          ,   //串口接收数据

    output  reg     [7:0]   po_data     ,   //串转并后的8bit数据
    output  reg             po_flag         //串转并后的数据有效标志信号
);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//localparam    define
localparam  BAUD_CNT_MAX    =   CLK_FREQ/UART_BPS   ;

//reg   define
reg         rx_reg1     ;
reg         rx_reg2     ;
reg         rx_reg3     ;
reg         start_nedge ;
reg         work_en     ;
reg [12:0]  baud_cnt    ;
reg         bit_flag    ;
reg [3:0]   bit_cnt     ;
reg [7:0]   rx_data     ;
reg         rx_flag     ;

//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//插入两级寄存器进行数据同步,用来消除亚稳态
//rx_reg1:第一级寄存器,寄存器空闲状态复位为1
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rx_reg1 <= 1'b1;
    else
        rx_reg1 <= rx;

//rx_reg2:第二级寄存器,寄存器空闲状态复位为1
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rx_reg2 <= 1'b1;
    else
        rx_reg2 <= rx_reg1;

//rx_reg3:第三级寄存器和第二级寄存器共同构成下降沿检测
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rx_reg3 <= 1'b1;
    else
        rx_reg3 <= rx_reg2;

//start_nedge:检测到下降沿时start_nedge产生一个时钟的高电平
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        start_nedge <= 1'b0;
    else    if((~rx_reg2) && (rx_reg3))
        start_nedge <= 1'b1;
    else
        start_nedge <= 1'b0;

//work_en:接收数据工作使能信号
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        work_en <= 1'b0;
    else    if(start_nedge == 1'b1)
        work_en <= 1'b1;
    else    if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
        work_en <= 1'b0;

//baud_cnt:波特率计数器计数,从0计数到BAUD_CNT_MAX - 1
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        baud_cnt <= 13'b0;
    else    if((baud_cnt == BAUD_CNT_MAX - 1) || (work_en == 1'b0))
        baud_cnt <= 13'b0;
    else    if(work_en == 1'b1)
        baud_cnt <= baud_cnt + 1'b1;

//bit_flag:当baud_cnt计数器计数到中间数时采样的数据最稳定,
//此时拉高一个标志信号表示数据可以被取走
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        bit_flag <= 1'b0;
    else    if(baud_cnt == BAUD_CNT_MAX/2 - 1)
        bit_flag <= 1'b1;
    else
        bit_flag <= 1'b0;

//bit_cnt:有效数据个数计数器,当8个有效数据(不含起始位和停止位)
//都接收完成后计数器清零
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        bit_cnt <= 4'b0;
    else    if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
        bit_cnt <= 4'b0;
     else    if(bit_flag ==1'b1)
         bit_cnt <= bit_cnt + 1'b1;

//rx_data:输入数据进行移位
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rx_data <= 8'b0;
    else    if((bit_cnt >= 4'd1)&&(bit_cnt <= 4'd8)&&(bit_flag == 1'b1))
        rx_data <= {rx_reg3, rx_data[7:1]};

//rx_flag:输入数据移位完成时rx_flag拉高一个时钟的高电平
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rx_flag <= 1'b0;
    else    if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
        rx_flag <= 1'b1;
    else
        rx_flag <= 1'b0;

//po_data:输出完整的8位有效数据
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        po_data <= 8'b0;
    else    if(rx_flag == 1'b1)
        po_data <= rx_data;

//po_flag:输出数据有效标志(比rx_flag延后一个时钟周期,为了和po_data同步)
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        po_flag <= 1'b0;
    else
        po_flag <= rx_flag;

endmodule

4. SignalTap仿真图

SignalTap图:
接收图:
在这里插入图片描述
发送图:
在这里插入图片描述

5. 实现现象

PC机发送hello,板子返回ni,hao
在这里插入图片描述

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值