sixcircle

十而立学FPGA之UART

1
阅读(223)

UART介绍

简介

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

时序

image.png 

 

根据时序图可以了解到:

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

UART之RX实现

状态转移图

 image.png

 

源代码

状态转移实现

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

时序描述

//周期计数,既系统时钟内每个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

 

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

 

周期计数、位计数

//周期计数,既系统时钟内每个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

发送数据

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