• 三十而立学FPGA之UART


    三十而立学FPGA之UART

    UART介绍

    简介

    ​ 通用异步收发器(Universal Asynchronous Receiver/Transmitter),既UART

    时序

    在这里插入图片描述

    根据时序图可以了解到:

    1. uart在空闲的时候是高电平
    2. 当突变为低电平或者有一个下降沿,则是告知接收方uart要传数据了
    3. 这里实现为8bit数据传输,当数据传输完成,在1或1.5或2个时钟周期内将传输线拉高,表示停止传输
    
    • 1
    • 2
    • 3

    UART之RX实现

    状态转移图

    rx_pin==1'b0
    cycle_cnt==CYCLE__CNT_MAX-1
    cycle_cnt==CYCLE__CNT_MAX-1&&bit_cnt==4'd7
    cycle_cnt==CYCLE__CNT_MAX/2-1
    S0
    S1
    S2
    S3
    S4

    源代码

    状态转移实现

    always @(*)
    begin
        case (state)
            IDLE:begin
                if(rx_pin==1'b0)
                    nextstate <= START;
                else
                    nextstate <= IDLE;
            end
            START:begin
                if(cycle_cnt == CYCLE_CNT_MAX-1)//当一个BIT周期后,接收数据
                    nextstate <= REC_BYTE;
                else
                    nextstate <= START;
            end
            REC_BYTE:begin
                if(cycle_cnt == CYCLE_CNT_MAX-1&&bit_cnt==4'd7)//8位数据接收完成,跳转到检测停止位
                    nextstate <= STOP;
                else
                    nextstate <= REC_BYTE;
            end
            STOP:begin
                if(cycle_cnt == CYCLE_CNT_MAX/2-1)//半个bit周期,检测到停止位,将接收的数据发送到其他模块
                    nextstate <= DATA;
                else
                    nextstate <= STOP;
            end
            DATA:begin    //一个时钟周期后模块进入空闲态
                nextstate <= IDLE;
            end
        endcase 
    end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    时序描述

    //周期计数,既系统时钟内每个BIT所需要的周期数,通过波特率可计算出最大计数值
    always@(posedge clk or negedge rst_n)
    begin
        if(rst_n == 1'b0)
            cycle_cnt <= 16'd0;
        else if(state == REC_BYTE && (cycle_cnt == CYCLE_CNT_MAX-1||nextstate != state))//只需要在开始接收数据时开始计数,所以前提条件是状态在REC_BYTE,而且如果计数值达到最大值或者在状态跳转的时候都需要将计数值清零
            cycle_cnt <= 16'd0;
        else
           cycle_cnt <= cycle_cnt + 16'd1;
    end
    
    //位计数,以确认接收的位数
    always@(posedge clk or negedge rst_n)
    begin
       if(rst_n == 1'b0)
           bit_cnt <= 4'd0;
        else if(state == REC_BYTE)  //仅在接收数据状态时进行位计数
            if(cycle_cnt == CYCLE_CNT_MAX-1)
                bit_cnt <= bit_cnt + 4'd1;
            else
                bit_cnt <= bit_cnt;
        else
             bit_cnt <= 4'd0;
    end
    
    //数据接收
    always@(posedge clk or negedge rst_n)
    begin
       	if(rst_n == 1'b0)
           rx_data_r <=  8'd0;
        else if(state == REC_BYTE && cycle_cnt == CYCLE_CNT_MAX/2-1)
            rx_data_r[bit_cnt] <=  tx_pin;
        else
            rx_data_r <= rx_data_r;
    end
    
    //将数据传输给其他模块
    always@(posedge clk or negedge rst_n)
    begin
        if(rst_n == 1'b0)
           	rx_data <= 8'd0;
        else if(state == STOP || nextstate != state)
            rx_data <= rx_data_r;
    end
    
    //接收完成标志
    always@(posedge clk or negedge rst_n)
    begin
      	if(rst_n == 1'b0)
            rx_done <= 1'b0;
        else if(state == STOP )
            rx_done <= 1'b1;
        else
            rx_done <= 1'b0;
    end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    UART之TX实现

    实现TX就不用三段式状态机这么麻烦了,直接用序列机完全就可以了

    模块使能或者说发送请求

    always@(posedge clk or negedge rst_n)
    begin
       	if(rst_n == 1'b0)
            tx_en <= 1'b0;
        else if(tx_req_pos == 1'b1)
            tx_en <= 1'b1;
        else if(bit_cnt == 4'd11)
            tx_en <= 1'b0;
        else
           	tx_en <= tx_en; 
    end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    周期计数、位计数

    //周期计数,既系统时钟内每个BIT所需要的周期数,通过波特率可计算出最大计数值
    always@(posedge clk or negedge rst_n)
    begin
        if(rst_n == 1'b0)
            cycle_cnt <= 16'd0;
        else if(tx_en)
            if(cycle_cnt == CYCLE_CNT_MAX-1)//当模块使能时,开始计数,计数到最大值再从零开始
            	cycle_cnt <= 16'd0;
       		else
           		cycle_cnt <= cycle_cnt + 16'd1;
        else
            cycle_cnt <= 16'd0;
    end
    
    //位计数
    always@(posedge clk or negedge rst_n)
    begin
        if(rst_n == 1'b0)
           bit_cnt <= 4'd0;
        else if(cycle_cnt == 16'd1) //如果计数到最大值bit_cnt累加的话,那么数据发送需要多等一个bit周期
           bit_cnt <= bit_cnt + 4'd1;
        else if(bit_cnt == 4'd11)
           bit_cnt <= 4'd0; 
        else
           bit_cnt <= bit_cnt;
    end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    发送数据

    always@(posedge clk or negedge rst_n)
    begin
       	if(rst_n == 1'b0)
            tx_pin_r <= 1'b1;
        else
            case(bit_cnt)
                0:tx_pin_r <= 1'b1;         //这里需要避免bit_cnt=0的时候发送起始位,因为当复位的时候bit_cnt的值是零的,会在复位时就已经发出了起始位,而导致接收端的误判和发送的时序紊乱
                1:tx_pin_r <= START_BIT;
                2:tx_pin_r <= send_data[0];
                3:tx_pin_r <= send_data[1];
                4:tx_pin_r <= send_data[2];
                5:tx_pin_r <= send_data[3];
                6:tx_pin_r <= send_data[4];
                7:tx_pin_r <= send_data[5];
                8:tx_pin_r <= send_data[6];
                9:tx_pin_r <= send_data[7];
                10:tx_pin_r <= STOP_BIT;
                default: tx_pin_r <= 1'b1;
    end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
  • 相关阅读:
    CSS基础 2
    HCIA网络基础7-VRP和命令行基础
    数据仓库&数据库
    职场混沌:读书的好,别在变局到来才知道
    POJ 3264 Balanced Lineup 线段树 / 平方分割
    微信小程序地理位置接口使用流程
    防错与自働化的结合|优思学院・精益管理学会|CLMP
    使用 LoRA 和 QLoRA 对大型语言模型进行参数高效的微调
    一文详细拆解Agent工作原理
    Gin中间件开发
  • 原文地址:https://blog.csdn.net/kuxiao1991/article/details/124875842