如何根据UART传输协议将数据发送出去呢?

电子说

1.3w人已加入

描述

00 简介

和接收部分相反,UART发送数据部分是CPU将需要发送的数据写到发送数据寄存器(TX_DATA),发送模块进行数据的发送。由于系统时钟速率一般会比UART发送数据快,所以发送数据将缓存到发送数据FIFO(TX_FIFO)。当TX_FIFO非空时,发送数据模块会根据UART传输协议将数据发送出去,直到TX_FIFO为空。

类似的,发送模块也涉及到ARM时钟和26MHz功能时钟。其中 发送FIFO读写逻辑是ARM时钟域,发送数据状态机和同步逻辑等是功能时钟域

01 模块接口与描述

有限状态机

有限状态机

02 实现

UART_TX模块主要由三部分组成: 配置信息同步TX_FIFO读控制部分发送状态机

配置信息是reg_if模块由APB总线配置寄存器产生,功能时钟域做两级同步处理。(比如校验位使能、奇偶校验控制、停止位使能等)

发送数据FIFO控制部分将FIFO中的数据读出,通过发送数据状态机将数据发送。

发送状态机是根据串口协议划分,分为IDLE、IRQ、START_BIT、TX_DATA、CHECK_BIT、STOP和DELAY六种状态。

配置信息同步

和接收模块类似,由于配置信息是由APB时钟产生,到接收模块的功能时钟域需要做同步处理。配置信息是电平信号,通常不会像数据一样变化快,所以只需要两级同步。

接收状态机

使用典型的三段式状态机设计,状态转移图如下:

有限状态机

uart发送状态转移图

IDLE: 发送状态机的IDLE状态会产生tx_start的发送请求信号,FIFO读逻辑部分会判断读空信号tx_fifo_rempty和tx_start,如果TX_FIFO非空且发送请求有效,则产生发送响应tx_ack,并发出FIFO读使能。IDLE状态机内发现tx_ack有效,跳转到IRQ状态。IRQ: IRQ状态目的是等待FIFO数据读出。进入IRQ后使能波特率时钟,tx_start信号disable,跳转到START_BIT。

START_BIT: 发送起始位。拉低utxd_o后跳转到发送数据状态TX_DATA。

TX_DATA: 发送从TX_FIFO读出的8bit数据,发送完8bit数据后判断校验位是否使能(check_syn2),使能则进入CHECK_BIT,不使能则进入STOP状态。

CHECK_BIT: 8bit数据按位异或(偶校验)或同或(奇校验),计算出校验位发送,判断是否使能停止位(stop_bit_syn2),如使能停止位则进入STOP状态,不使能则进入DELAY状态。

这里提一下 奇偶校验

所谓奇校验,就是判断发送的数据位中1的个数是否是奇数,如果数据位中1的个数是偶数,那就给校验位赋值1;如果数据位中1的个数是奇数,那就给校验位赋值0。目的是确保发送的数据中1的个数是奇数。

偶校验则相反,判断发送的数据位中1的个数是否是偶数,如果数据位中1的个数是偶数,那就给校验位赋值0;如果数据位中1的个数是奇数,那就给校验位赋值1。目的是确保发送的数据中1的个数是偶数。

实现时,奇偶校验可以用同或和异或操作计算,相同的8bit数奇偶校验的值一定是相反的。

STOP: STOP状态拉高utxd_o,然后进入DELAY状态。

DELAY: DELAY状态控制相邻两次发送之间的间隔,间隔时间以波特率时钟为单位,受CPU控制(配置字two_tx_delay),默认delay两个波特率时钟周期。延时后回到IDLE状态进行等待或下一Byte数据传输。

前两段状态机,状态跳转:

// state to nextstate with clk in this block.
always@(posedge clk26m ornegedge rst26m_) begin
    if(!rst26m_) begin
        state <= IDLE;
    end
    elsebegin
        state <= nextstate;
    end
end

// nextstate transform
always@(*) begin
    case(state)
    IDLE: begin
        if(tx_ack_delay2) begin
            nextstate = IRQ;
        end
        elsebegin
            nextstate = IDLE;
        end
    end
    IRQ: begin
        if(tx_bpsclk) begin
            nextstate = START_BIT;
        end
        elsebegin
            nextstate = IRQ;
        end
    end
    START_BIT: begin
        if(tx_bpsclk) begin
            nextstate = TX_DATA;
        end
        elsebegin
            nextstate = START_BIT;
        end
    end
    TX_DATA: begin
        // send 8 bit data
        if(data_cnt < 4'd8) begin
            nextstate = TX_DATA;
        end
        elsebegin
            if(tx_bpsclk) begin
                if(check_syn2) begin
                    nextstate = CHECK_BIT;
                end
                elsebegin
                    nextstate = STOP;
                end
            end
            elsebegin
                nextstate = TX_DATA;
            end
        end
    end
    CHECK_BIT: begin
        if(tx_bpsclk) begin
            if(stop_bit_syn2) begin
                nextstate = STOP;
            end
            elsebegin
                nextstate = DELAY;
            end
        end
        elsebegin
            nextstate = CHECK_BIT;
        end
    end
    STOP: begin
        if(tx_bpsclk) begin
            nextstate = DELAY;
        end
        elsebegin
            nextstate = STOP;
        end
    end
    DELAY: begin
        if(baud_cnt < two_tx_delay_syn2) begin
            nextstate = DELAY;
        end
        elsebegin
            nextstate = IDLE;
        end
    end
    default: begin
        nextstate = IDLE;
    end
    endcase
end

第三段状态机,信号赋值:

// output signal
always@(posedge clk26m ornegedge rst26m_) begin
    if(!rst26m_) begin
        tx_bpsen <= 1'b0;
        tx_start <= 1'b0;
        utxd_o   <= 1'b1;
        data_cnt <= 4'd0;
        baud_cnt <= 4'd0;
    end
    elsebegin
        case(nextstate)
        IDLE: begin
            tx_start   <= 1'b1;
            baud_cnt   <= 4'd0;
            data_cnt   <= 4'd0;
        end
        IRQ: begin
            tx_bpsen   <= 1'b1;
            tx_start   <= 1'b0;
        end
        START_BIT: begin
            if(tx_bpsclk) begin
                utxd_o <= 1'b0;
            end
        end
        TX_DATA: begin
            if(tx_bpsclk) begin
                utxd_o   <= data_tx[data_cnt];
                data_cnt <= data_cnt + 1'b1;
            end
        end
        CHECK_BIT: begin
            if(tx_bpsclk) begin
                // odd check
                if(parity_syn2) begin
                    utxd_o <= ^data_tx;
                end
                // even check
                elsebegin
                    utxd_o <= ^~data_tx;
                end
            end
        end
        STOP: begin
            if(tx_bpsclk) begin
                utxd_o <= 1'b1;
            end
        end
        DELAY: begin
            if(tx_bpsclk) begin
                baud_cnt <= baud_cnt + 1'b1;
                utxd_o   <= 1'b1;    // the delay between twice send data is 1
            end
        end
        endcase
    end
end

接收数据FIFO控制

TX_FIFO使用同步FIFO设计,工作在APB时钟域。

TX_FIFO的读逻辑使用一段式状态机产生。首先判断FIFO是否为空,发送状态机的发送请求tx_start是否有效。两个信号都有效时将产生读FIFO使能和发送数据响应,通知发送状态机可以开始发送数据。

注意读FIFO使能仅有效一个APB时钟周期,待tx_start不使能时disable发送数据响应。等待一个状态后状态机回到初始态(等待一个状态是为了确保发送状态机已经进行跳转,避免tx_ack和FIFO读使能一直有效)。

// this state machine to receive data from RX FIFO
always@(posedge clk ornegedge rst_) begin
    if(!rst_) begin
        tx_ack       <= 1'b0;
        tx_fifo_rinc <= 1'b0;
        rdata_state  <= 2'b0;
    end
    elsebegin
        case(rdata_state)
        2'b00: begin
            if(!tx_fifo_rempty && tx_start_delay2) begin
                tx_ack       <= 1'b1;
                tx_fifo_rinc <= 1'b1;
                rdata_state  <= 2'b01;
            end
        end
        2'b01: begin
            tx_fifo_rinc <= 1'b0;
            if(!tx_start_delay2) begin
                tx_ack      <= 1'b0;
                rdata_state <= 2'b10;
            end
        end
        2'b10: begin
            rdata_state <= 2'b0;
        end
        endcase
    end
end

TX_FIFO写逻辑放在reg_if模块,APB写TX_DATA寄存器时,产生写使能信号,将寄存器数据放到TX_FIFO中。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分