野火FPGA强化(1):串口

文章目录

    • 第31讲:串口RS232
      • 串口数据接收模块:Uart_rx
      • 串口数据发送模块:Uart_tx
      • 底层模块:rs232
    • 第32讲:使用SignalTap II嵌入式逻辑分析仪在线调试
    • 第33讲:串口RS485
      • key_filter
      • water_led
      • breath_led
      • led_ctrl
      • uart_rx
      • uart_tx
      • rs485
      • tb_rs485

第31讲:串口RS232

通用异步收发传输器(Universal Asynchronous Receiver/Transmitter,简称UART)
UART是一种通用的数据通信协议,也是异步串行通信口(串口)的总称,它在发送数据时将并行数据转换成串行数据来传输,在接收数据时将接收到的串行数据转换成并行数据。
包括RS232、RS499、RS423、RS422和RS485等接口标准规范和总线标准规范。

野火FPGA强化(1):串口_第1张图片
野火FPGA强化(1):串口_第2张图片
野火FPGA强化(1):串口_第3张图片
野火FPGA强化(1):串口_第4张图片


实验目标
野火FPGA强化(1):串口_第5张图片
顶层模块设计
野火FPGA强化(1):串口_第6张图片
子功能模块设计
野火FPGA强化(1):串口_第7张图片
系统框图
野火FPGA强化(1):串口_第8张图片

串口数据接收模块:Uart_rx

串口数据接收模块
野火FPGA强化(1):串口_第9张图片

`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         //串转并后的数据有效标志信号
);

//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     ;

//插入两级寄存器进行数据同步,用来消除亚稳态
//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

tb_uart_rx.v

`timescale  1ns/1ns
module  tb_uart_rx();

//reg   define
reg             sys_clk;
reg             sys_rst_n;
reg             rx;

//wire  define
wire    [7:0]   po_data;
wire            po_flag;

//初始化系统时钟、全局复位和输入信号
initial begin
        sys_clk    = 1'b1;
        sys_rst_n <= 1'b0;
        rx        <= 1'b1;
        #20;
        sys_rst_n <= 1'b1;
end

//模拟发送8次数据,分别为0~7
initial begin
        #200
        rx_bit(8'd0);  //任务的调用,任务名+括号中要传递进任务的参数
        rx_bit(8'd1);
        rx_bit(8'd2);
        rx_bit(8'd3);
        rx_bit(8'd4);
        rx_bit(8'd5);
        rx_bit(8'd6);
        rx_bit(8'd7);
end

//sys_clk:每10ns电平翻转一次,产生一个50MHz的时钟信号
always #10 sys_clk = ~sys_clk;

//定义一个名为rx_bit的任务,每次发送的数据有10位
//data的值分别为0~7由j的值传递进来
//任务以task开头,后面紧跟着的是任务名,调用时使用
task rx_bit(
    //传递到任务中的参数,调用任务的时候从外部传进来一个8位的值
        input   [7:0]   data
);
        integer i;      //定义一个常量
//用for循环产生一帧数据,for括号中最后执行的内容只能写i=i+1
//不可以写成C语言i=i++的形式
        for(i=0; i<10; i=i+1) begin
            case(i)
                0: rx <= 1'b0;
                1: rx <= data[0];
                2: rx <= data[1];
                3: rx <= data[2];
                4: rx <= data[3];
                5: rx <= data[4];
                6: rx <= data[5];
                7: rx <= data[6];
                8: rx <= data[7];
                9: rx <= 1'b1;
            endcase
            #(5208*20); //每发送1位数据延时5208个时钟周期
        end
endtask         //任务以endtask结束

//------------------------uart_rx_inst------------------------
uart_rx 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
);

endmodule

串口数据发送模块:Uart_tx

野火FPGA强化(1):串口_第10张图片
野火FPGA强化(1):串口_第11张图片

`timescale  1ns/1ns

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数据
);

//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 ;

//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

endmodule

tb_uart_tx.v

`timescale  1ns/1ns
module  tb_uart_tx();

//reg   define
reg         sys_clk;
reg         sys_rst_n;
reg [7:0]   pi_data;
reg         pi_flag;

//wire  define
wire        tx;

//初始化系统时钟、全局复位
initial begin
        sys_clk    = 1'b1;
        sys_rst_n <= 1'b0;
        #20;
        sys_rst_n <= 1'b1;
end

//模拟发送7次数据,分别为0~7
initial begin
        pi_data <= 8'b0;
        pi_flag <= 1'b0;
        #200
        //发送数据0
        pi_data <= 8'd0;
        pi_flag <= 1'b1;
        #20
        pi_flag <= 1'b0;
//每发送1bit数据需要5208个时钟周期,一帧数据为10bit
//所以需要数据延时(5208*20*10)后再产生下一个数据
        #(5208*20*10);
        //发送数据1
        pi_data <= 8'd1;
        pi_flag <= 1'b1;
        #20
        pi_flag <= 1'b0;
        #(5208*20*10);
        //发送数据2
        pi_data <= 8'd2;
        pi_flag <= 1'b1;
        #20
        pi_flag <= 1'b0;
        #(5208*20*10);
        //发送数据3
        pi_data <= 8'd3;
        pi_flag <= 1'b1;
        #20
        pi_flag <= 1'b0;
        #(5208*20*10);
        //发送数据4
        pi_data <= 8'd4;
        pi_flag <= 1'b1;
        #20
        pi_flag <= 1'b0;
        #(5208*20*10);
        //发送数据5
        pi_data <= 8'd5;
        pi_flag <= 1'b1;
        #20
        pi_flag <= 1'b0;
        #(5208*20*10);
        //发送数据6
        pi_data <= 8'd6;
        pi_flag <= 1'b1;
        #20
        pi_flag <= 1'b0;
        #(5208*20*10);
        //发送数据7
        pi_data <= 8'd7;
        pi_flag <= 1'b1;
        #20
        pi_flag <= 1'b0;
end

//sys_clk:每10ns电平翻转一次,产生一个50MHz的时钟信号
always #10 sys_clk = ~sys_clk;

//------------------------uart_rx_inst------------------------
uart_tx uart_tx_inst(
        .sys_clk    (sys_clk    ),  //input           sys_clk
        .sys_rst_n  (sys_rst_n  ),  //input           sys_rst_n
        .pi_data    (pi_data    ),  //output  [7:0]   pi_data
        .pi_flag    (pi_flag    ),  //output          pi_flag

        .tx         (tx         )   //input           tx
);

endmodule

底层模块:rs232

`timescale  1ns/1ns

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

    output  wire    tx              //串口发送数据
);

//parameter define
parameter   UART_BPS    =   14'd9600        ,   //比特率
            CLK_FREQ    =   26'd50_000_000  ;   //时钟频率

//wire  define
wire    [7:0]   po_data;
wire            po_flag;

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

//------------------------ uart_tx_inst ------------------------
uart_tx
#(
    .UART_BPS    (UART_BPS  ),  //串口波特率
    .CLK_FREQ    (CLK_FREQ  )   //时钟频率
)
uart_tx_inst
(
    .sys_clk    (sys_clk    ),  //input             sys_clk
    .sys_rst_n  (sys_rst_n  ),  //input             sys_rst_n
    .pi_data    (po_data    ),  //input     [7:0]   pi_data
    .pi_flag    (po_flag    ),  //input             pi_flag
                
    .tx         (tx         )   //output            tx
);

endmodule

tb_rs232.v

`timescale  1ns/1ns
module  tb_rs232();

//wire  define
wire    tx          ;

//reg   define
reg     sys_clk     ;
reg     sys_rst_n   ;
reg     rx          ;

//初始化系统时钟、全局复位和输入信号
initial begin
    sys_clk    = 1'b1;
    sys_rst_n <= 1'b0;
    rx        <= 1'b1;
    #20;
    sys_rst_n <= 1'b1;
end

//调用任务rx_byte
initial begin
    #200
    rx_byte();
end

//sys_clk:每10ns电平翻转一次,产生一个50MHz的时钟信号
always #10 sys_clk = ~sys_clk;

//创建任务rx_byte,本次任务调用rx_bit任务,发送8次数据,分别为0~7
task    rx_byte();  //因为不需要外部传递参数,所以括号中没有输入
    integer j;
    for(j=0; j<8; j=j+1)    //调用8次rx_bit任务,每次发送的值从0变化7
        rx_bit(j);
endtask

//创建任务rx_bit,每次发送的数据有10位,data的值分别为0到7由j的值传递进来
task    rx_bit(
    input   [7:0]   data
);
    integer i;
    for(i=0; i<10; i=i+1)   begin
        case(i)
            0: rx <= 1'b0;
            1: rx <= data[0];
            2: rx <= data[1];
            3: rx <= data[2];
            4: rx <= data[3];
            5: rx <= data[4];
            6: rx <= data[5];
            7: rx <= data[6];
            8: rx <= data[7];
            9: rx <= 1'b1;
        endcase
        #(5208*20); //每发送1位数据延时5208个时钟周期
    end
endtask

//------------------------ rs232_inst ------------------------
rs232   rs232_inst
(
    .sys_clk    (sys_clk    ),  //input         sys_clk
    .sys_rst_n  (sys_rst_n  ),  //input         sys_rst_n
    .rx         (rx         ),  //input         rx

    .tx         (tx         )   //output        tx
);

endmodule



第32讲:使用SignalTap II嵌入式逻辑分析仪在线调试

看视频讲解:https://www.bilibili.com/video/BV17z411i7er



第33讲:串口RS485

RS485是双向、半双工通信协议,信号采用差分传输方式,允许多个驱动器和接收器挂接在总线上,其中每个驱动器都能够脱离总线。
野火FPGA强化(1):串口_第12张图片
野火FPGA强化(1):串口_第13张图片


实现目标
野火FPGA强化(1):串口_第14张图片
野火FPGA强化(1):串口_第15张图片
系统框图
野火FPGA强化(1):串口_第16张图片
控制板波形
野火FPGA强化(1):串口_第17张图片
被控制板波形
野火FPGA强化(1):串口_第18张图片

key_filter

`timescale  1ns/1ns

module  key_filter
#(
    parameter CNT_MAX = 20'd999_999 //计数器计数最大值
)
(
    input   wire    sys_clk     ,   //系统时钟50Mhz
    input   wire    sys_rst_n   ,   //全局复位
    input   wire    key_in      ,   //按键输入信号

    output  reg     key_flag        //key_flag为1时表示消抖后检测到按键被按下
                                    //key_flag为0时表示没有检测到按键被按下
);

//reg   define
reg     [19:0]  cnt_20ms    ;   //计数器

//cnt_20ms:如果时钟的上升沿检测到外部按键输入的值为低电平时,计数器开始计数
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_20ms <= 20'b0;
    else    if(key_in == 1'b1)
        cnt_20ms <= 20'b0;
    else    if(cnt_20ms == CNT_MAX && key_in == 1'b0)  //为低电平且已经达到最大值,不再进行清零
        cnt_20ms <= cnt_20ms;
    else
        cnt_20ms <= cnt_20ms + 1'b1;

//key_flag:当计数满20ms后产生按键有效标志位
//且key_flag在999_999时拉高,维持一个时钟的高电平
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        key_flag <= 1'b0;
    else    if(cnt_20ms == CNT_MAX - 1'b1)
        key_flag <= 1'b1;
    else
        key_flag <= 1'b0;

endmodule

water_led

`timescale  1ns/1ns

module  water_led
#(
    parameter CNT_MAX = 25'd24_999_999
)
(
    input   wire            sys_clk     ,   //系统时钟50Mh
    input   wire            sys_rst_n   ,  //全局复位

    output  wire    [3:0]   led_out        //输出控制led灯
);

//reg   define
reg     [24:0]  cnt         ;
reg             cnt_flag    ;
reg     [3:0]   led_out_reg ;

//cnt:计数器计数1s
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt <= 25'b0;
    else    if(cnt == CNT_MAX)
        cnt <= 25'b0;
    else
        cnt <= cnt + 1'b1;

//cnt_flag:计数器计数满1s标志信号
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_flag <= 1'b0;
    else    if(cnt == CNT_MAX - 1)
        cnt_flag <= 1'b1;
    else
        cnt_flag <= 1'b0;

//led_out_reg:led循环流水
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        led_out_reg <=  4'b0001;
    else    if(led_out_reg == 4'b1000 && cnt_flag == 1'b1)
        led_out_reg <=  4'b0001;
    else    if(cnt_flag == 1'b1)
        led_out_reg <=  led_out_reg << 1'b1; //左移

assign  led_out = ~led_out_reg;

endmodule

breath_led

`timescale  1ns/1ns

module  breath_led
#(
    parameter CNT_1US_MAX = 6'd49   ,
    parameter CNT_1MS_MAX = 10'd999 ,
    parameter CNT_1S_MAX  = 10'd999
)
(
    input   wire    sys_clk     ,   //系统时钟50Mhz
    input   wire    sys_rst_n   ,   //全局复位

    output  reg     led_out         //输出信号,控制led灯
);

//reg define
reg [5:0]   cnt_1us     ;
reg [9:0]   cnt_1ms     ;
reg [9:0]   cnt_1s      ;
reg         cnt_1s_flag ;

//cnt_1us:1us计数器
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_1us <= 6'b0;
    else    if(cnt_1us == CNT_1US_MAX)
        cnt_1us <= 6'b0;
    else
        cnt_1us <= cnt_1us + 1'b1;

//cnt_1ms:1ms计数器
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_1ms <= 10'b0;
    else    if(cnt_1ms == CNT_1MS_MAX && cnt_1us == CNT_1US_MAX)
        cnt_1ms <= 10'b0;
    else    if(cnt_1us == CNT_1US_MAX)
        cnt_1ms <= cnt_1ms + 1'b1;

//cnt_1s:1s计数器
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_1s <= 10'b0;
    else    if(cnt_1s == CNT_1S_MAX && cnt_1ms == CNT_1MS_MAX 
                                        && cnt_1us == CNT_1US_MAX)
        cnt_1s <= 10'b0;
    else    if(cnt_1ms == CNT_1MS_MAX && cnt_1us == CNT_1US_MAX)
        cnt_1s <= cnt_1s + 1'b1;

//cnt_1s_flag:1s计数器标志信号
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_1s_flag <= 1'b0;
    else    if(cnt_1s == CNT_1S_MAX && cnt_1ms == CNT_1MS_MAX 
                                            && cnt_1us == CNT_1US_MAX)
        cnt_1s_flag <= ~cnt_1s_flag;

//led_out:输出信号连接到外部的led灯
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        led_out <= 1'b0;
    else    if((cnt_1s_flag == 1'b1 && cnt_1ms < cnt_1s) || 
                            (cnt_1s_flag == 1'b0 && cnt_1ms > cnt_1s))
        led_out <= 1'b0;
    else
        led_out <= 1'b1;

endmodule

led_ctrl

`timescale  1ns/1ns

module  led_ctrl
(
    input   wire            sys_clk         ,   //模块时钟,50MHz 
    input   wire            sys_rst_n       ,   //复位信号,低有效
    input   wire            water_key_flag  ,   //流水灯按键有效信号
    input   wire            breath_key_flag ,   //呼吸灯按键有效信号
    input   wire    [3:0]   led_out_w       ,   //流水灯
    input   wire            led_out_b       ,   //呼吸灯
    input   wire    [7:0]   po_data         ,   //接收数据

    output  wire            pi_flag         ,   //发送标志信号
    output  wire    [7:0]   pi_data         ,   //发送数据
    output  reg     [3:0]   led_out             //输出led灯
);

//reg   define
reg     water_led_flag  ;   //流水灯标志信号,作为pi_data[0]发送
reg     breath_led_flag ;   //呼吸灯标志信号,作为pi_data[1]发送

//按下呼吸灯按键或流水灯按键时,开始发送数据
assign  pi_flag =   water_key_flag | breath_key_flag;

//低两位数据为led控制信号
assign  pi_data =   {6'd0,breath_led_flag,water_led_flag};

//water_key_flag:串口发送的控制信号,高时流水灯,低时停止(按键控制)
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        water_led_flag  <=  1'b0;
    else    if(breath_key_flag == 1'b1)
        water_led_flag  <=  1'b0;
    else    if(water_key_flag == 1'b1)
        water_led_flag  <=  ~water_led_flag;

//breath_key_flag:串口发送的控制信号,高时呼吸灯灯,低时停止(按键控制)
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        breath_led_flag  <=  1'b0;
    else    if(water_key_flag == 1'b1)
        breath_led_flag  <=  1'b0;
    else    if(breath_key_flag == 1'b1)
        breath_led_flag  <=  ~breath_led_flag;

//led_out:当传入的流水灯有效时,led灯为流水灯,同理呼吸灯也是如此
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        led_out <=  4'b1111;
    else    if(po_data[0] == 1'b1 )
        led_out <=  led_out_w;
    else    if(po_data[1] == 1'b1 )
    //使四个led灯都显示呼吸灯状态
        led_out <=  {led_out_b,led_out_b,led_out_b,led_out_b};
    else
        led_out <=  4'b1111; 

endmodule

uart_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         //串转并后的数据有效标志信号
);

//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     ;

//插入两级寄存器进行数据同步,用来消除亚稳态
//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

uart_tx

`timescale  1ns/1ns

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     ,   //并行数据
    input   wire            pi_flag     ,   //并行数据有效标志信号
            
    output  reg             work_en     ,   //发送使能,高有效
    output  reg             tx              //串口发送数据
);

//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     ;    

//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计数到5207
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 */baud_cnt == BAUD_CNT_MAX - 1 )
        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 */work_en == 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
                
endmodule

rs485

`timescale  1ns/1ns

module  rs485
(
    input   wire            sys_clk     ,   //系统时钟,50MHz
    input   wire            sys_rst_n   ,   //复位信号,低有效
    input   wire            rx          ,   //串口接收数据
    input   wire    [1:0]   key         ,   //两个按键

    output  wire            work_en     ,   //发送使能,高有效
    output  wire            tx          ,   //串口接收数据
    output  wire    [3:0]   led             //led灯
);

//parameter define
parameter   UART_BPS    =   14'd9600;       //比特率
parameter   CLK_FREQ    =   26'd50_000_000; //时钟频率

//wire  define
wire    [7:0]   po_data         ;   //接收数据
wire    [7:0]   pi_data         ;   //发送数据
wire            pi_flag         ;   //发送标志信号
wire            water_key_flag  ;   //流水灯按键有效信号
wire            breath_key_flag ;   //呼吸灯按键有效信号
wire    [3:0]   led_out_w       ;   //流水灯
wire            led_out_b       ;   //呼吸灯

//--------------------uart_rx_inst------------------------
uart_rx
#(
    .UART_BPS    (UART_BPS  ),   //串口波特率
    .CLK_FREQ    (CLK_FREQ  )    //时钟频率
)
uart_rx_inst(
    .sys_clk     (sys_clk   ),   //系统时钟50Mhz
    .sys_rst_n   (sys_rst_n ),   //全局复位
    .rx          (rx        ),   //串口接收数据

    .po_data     (po_data   ),   //串转并后的8bit数据
    .po_flag     (          )    //接收数据完成标志信号没用到可不接
);

//--------------------uart_tx_inst------------------------
uart_tx
#(
    .UART_BPS    (UART_BPS  ),   //串口波特率
    .CLK_FREQ    (CLK_FREQ  )    //时钟频率
)
uart_tx_inst(
    .sys_clk     (sys_clk   ),   //系统时钟50Mhz
    .sys_rst_n   (sys_rst_n ),   //全局复位
    .pi_data     (pi_data   ),   //并行数据
    .pi_flag     (pi_flag   ),   //并行数据有效标志信号

    .work_en     (work_en   ),   //发送使能,高有效
    .tx          (tx        )    //串口发送数据
);

//--------------------key_filter_inst------------------------
//两个按键信号例化两次
key_filter  key_filter_w
(
    .sys_clk        (sys_clk        ),    //系统时钟50Mhz
    .sys_rst_n      (sys_rst_n      ),    //全局复位
    .key_in         (key[0]         ),    //按键输入信号

    .key_flag       (water_key_flag)  //key_flag为1时表示消抖后按键有效
);
key_filter  key_filter_b
(
    .sys_clk        (sys_clk        ),    //系统时钟50Mhz
    .sys_rst_n      (sys_rst_n      ),    //全局复位
    .key_in         (key[1]         ),    //按键输入信号

    .key_flag       (breath_key_flag) //key_flag为1时表示消抖后按键有效
);

//--------------------key_ctrl_inst------------------------
led_ctrl    led_ctrl_inst
(
    .sys_clk         (sys_clk        ),   //模块时钟,50MHz
    .sys_rst_n       (sys_rst_n      ),   //复位信号,低有效
    .water_key_flag  (water_key_flag ),   //流水灯按键有效信号
    .breath_key_flag (breath_key_flag),   //呼吸灯按键有效信号
    .led_out_w       (led_out_w      ),   //流水灯
    .led_out_b       (led_out_b      ),   //呼吸灯
    .po_data         (po_data        ),   //接收数据

    .pi_flag         (pi_flag        ),   //发送标志信号
    .pi_data         (pi_data        ),   //发送数据
    .led_out         (led            )    //输出led灯
);

//--------------------water_led_inst------------------------
water_led   water_led_inst
(
    .sys_clk         (sys_clk   ),   //系统时钟50Mh
    .sys_rst_n       (sys_rst_n ),   //全局复位

    .led_out         (led_out_w )    //输出控制led灯
);

//--------------------breath_led_inst------------------------
breath_led  breath_led_inst
(
    .sys_clk         (sys_clk   ),   //系统时钟50Mhz
    .sys_rst_n       (sys_rst_n ),   //全局复位

    .led_out         (led_out_b )    //输出信号,控制led灯
);

endmodule

tb_rs485

`timescale  1ns/1ns
module  tb_rs485();

//wire  define
wire            rx1         ;
wire            work_en1    ;
wire            tx1         ;
wire    [3:0]   led1        ;
wire            work_en2    ;
wire            tx2         ;
wire    [3:0]   led2        ;

//reg   define
reg             sys_clk     ;
reg             sys_rst_n   ;
reg     [1:0]   key1        ;
reg     [1:0]   key2        ;

//对sys_clk,sys_rst赋初值,并模拟按键抖动
initial
    begin
            sys_clk     =   1'b1 ;
            sys_rst_n   <=  1'b0 ;
            key1        <=  2'b11;
            key2        <=  2'b11;
    #200    sys_rst_n   <=  1'b1 ;
//按下流水灯按键
    #2000000    key1[0]      <=  1'b0;//按下按键
    #20         key1[0]      <=  1'b1;//模拟抖动
    #20         key1[0]      <=  1'b0;//模拟抖动
    #20         key1[0]      <=  1'b1;//模拟抖动
    #20         key1[0]      <=  1'b0;//模拟抖动
    #200        key1[0]      <=  1'b1;//松开按键
    #20         key1[0]      <=  1'b0;//模拟抖动
    #20         key1[0]      <=  1'b1;//模拟抖动
    #20         key1[0]      <=  1'b0;//模拟抖动
    #20         key1[0]      <=  1'b1;//模拟抖动
//按下呼吸灯按键
    #2000000    key1[1]      <=  1'b0;//按下按键
    #20         key1[1]      <=  1'b1;//模拟抖动
    #20         key1[1]      <=  1'b0;//模拟抖动
    #20         key1[1]      <=  1'b1;//模拟抖动
    #20         key1[1]      <=  1'b0;//模拟抖动
    #200        key1[1]      <=  1'b1;//松开按键
    #20         key1[1]      <=  1'b0;//模拟抖动
    #20         key1[1]      <=  1'b1;//模拟抖动
    #20         key1[1]      <=  1'b0;//模拟抖动
    #20         key1[1]      <=  1'b1;//模拟抖动
//按下呼吸灯按键
    #2000000    key1[1]      <=  1'b0;//按下按键
    #20         key1[1]      <=  1'b1;//模拟抖动
    #20         key1[1]      <=  1'b0;//模拟抖动
    #20         key1[1]      <=  1'b1;//模拟抖动
    #20         key1[1]      <=  1'b0;//模拟抖动
    #200        key1[1]      <=  1'b1;//松开按键
    #20         key1[1]      <=  1'b0;//模拟抖动
    #20         key1[1]      <=  1'b1;//模拟抖动
    #20         key1[1]      <=  1'b0;//模拟抖动
    #20         key1[1]      <=  1'b1;//模拟抖动
//按下呼吸灯按键
    #2000000    key1[1]      <=  1'b0;//按下按键
    #20         key1[1]      <=  1'b1;//模拟抖动
    #20         key1[1]      <=  1'b0;//模拟抖动
    #20         key1[1]      <=  1'b1;//模拟抖动
    #20         key1[1]      <=  1'b0;//模拟抖动
    #200        key1[1]      <=  1'b1;//松开按键
    #20         key1[1]      <=  1'b0;//模拟抖动
    #20         key1[1]      <=  1'b1;//模拟抖动
    #20         key1[1]      <=  1'b0;//模拟抖动
    #20         key1[1]      <=  1'b1;//模拟抖动
//按下流水灯灯按键
    #2000000    key1[0]      <=  1'b0;//按下按键
    #20         key1[0]      <=  1'b1;//模拟抖动
    #20         key1[0]      <=  1'b0;//模拟抖动
    #20         key1[0]      <=  1'b1;//模拟抖动
    #20         key1[0]      <=  1'b0;//模拟抖动
    #200        key1[0]      <=  1'b1;//松开按键
    #20         key1[0]      <=  1'b0;//模拟抖动
    #20         key1[0]      <=  1'b1;//模拟抖动
    #20         key1[0]      <=  1'b0;//模拟抖动
    #20         key1[0]      <=  1'b1;//模拟抖动
//按下流水灯灯按键
    #2000000    key1[0]      <=  1'b0;//按下按键
    #20         key1[0]      <=  1'b1;//模拟抖动
    #20         key1[0]      <=  1'b0;//模拟抖动
    #20         key1[0]      <=  1'b1;//模拟抖动
    #20         key1[0]      <=  1'b0;//模拟抖动
    #200        key1[0]      <=  1'b1;//松开按键
    #20         key1[0]      <=  1'b0;//模拟抖动
    #20         key1[0]      <=  1'b1;//模拟抖动
    #20         key1[0]      <=  1'b0;//模拟抖动
    #20         key1[0]      <=  1'b1;//模拟抖动
    end

//sys_clk:模拟系统时钟,每10ns电平取反一次,周期为20ns,频率为50Mhz
always #10 sys_clk = ~sys_clk;

//重新定义参数值,缩短仿真时间仿真
//发送板参数
defparam    rs485_inst1.key_filter_w.CNT_MAX         =   5      ;
defparam    rs485_inst1.key_filter_b.CNT_MAX         =   5      ;
defparam    rs485_inst1.uart_rx_inst.UART_BPS        =   1000000;
defparam    rs485_inst1.uart_tx_inst.UART_BPS        =   1000000;
defparam    rs485_inst1.water_led_inst.CNT_MAX       =   4000   ;
defparam    rs485_inst1.breath_led_inst.CNT_1US_MAX  =   4      ;
defparam    rs485_inst1.breath_led_inst.CNT_1MS_MAX  =   9      ;
defparam    rs485_inst1.breath_led_inst.CNT_1S_MAX   =   9      ;
//接收板参数
defparam    rs485_inst2.key_filter_w.CNT_MAX         =   5      ;
defparam    rs485_inst2.key_filter_b.CNT_MAX         =   5      ;
defparam    rs485_inst2.uart_rx_inst.UART_BPS        =   1000000;
defparam    rs485_inst2.uart_tx_inst.UART_BPS        =   1000000;
defparam    rs485_inst2.water_led_inst.CNT_MAX       =   4000   ;
defparam    rs485_inst2.breath_led_inst.CNT_1US_MAX  =   4      ;
defparam    rs485_inst2.breath_led_inst.CNT_1MS_MAX  =   99     ;
defparam    rs485_inst2.breath_led_inst.CNT_1S_MAX   =   99     ;

//发送板
//-------------rs485_inst1-------------
rs485   rs485_inst1
(
    .sys_clk     (sys_clk    ),   //系统时钟,50MHz
    .sys_rst_n   (sys_rst_n  ),   //复位信号,低有效
    .rx          (rx1        ),   //串口接收数据
    .key         (key1       ),   //两个按键

    .work_en     (work_en1   ),   //发送使能,高有效
    .tx          (tx1        ),   //串口发送数据
    .led         (led_tx1    )    //led灯
);

//接收板
//-------------rs485_inst2-------------
rs485   rs485_inst2
(
    .sys_clk     (sys_clk    ),   //系统时钟,50MHz
    .sys_rst_n   (sys_rst_n  ),   //复位信号,低有效
    .rx          (tx1        ),   //串口接收数据
    .key         (key2       ),   //两个按键

    .work_en     (work_en2   ),   //发送使能,高有效
    .tx          (tx2        ),   //串口发送数据
    .led         (led_rx2    )    //led灯
);
endmodule

你可能感兴趣的:(FPGA,fpga开发)