如何实现串口数据的接收呢?

电子说

1.2w人已加入

描述

00 简介

UART接收数据部分是接收另一个串口设备发送的数据,缓存到接收FIFO中。FIFO快要写满时,产生中断通知CPU拿取数据,实现串口数据的接收。

模块涉及到两个时钟域,ARM时钟和26MHz功能时钟。其中 接收FIFO读写逻辑是ARM时钟域,接收数据状态机和同步逻辑等是功能时钟域

01 模块接口与描述

时钟域

时钟域

02 实现

UART_RX模块主要由三部分组成: 配置信息同步接收状态机接收数据FIFO控制

配置信息是reg_if模块由APB总线配置寄存器产生,功能时钟域做两级同步处理。

接收状态机是根据串口协议划分,分为IDLE、START、RX_DATA、CHECK_DATA、STOP和SEND六种状态。

接收数据FIFO控制部分将接收完成的数据写入到FIFO,在CPU读取RX_FIFO寄存器时将FIFO数据读出送到RX_FIFO寄存器。

  • 配置信息同步

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

  • 接收状态产生

接收数据停止位状态指示st_error和接收数据校验位状态指示p_error是串口校验位和停止位检测状态指示。前者为1表示停止位错误;后者为1表示校验位错误。两个状态位都会传到reg_if模块,指示URAT当前状态,供CPU查询。当CPU发现停止位或者校验位错误时,会决定当前数据的处理方式(丢弃或者接受),清除状态位(即写1清0)。reg_if模块发现该状态位清除后,会回送一个ack到接收模块,即st_error_ack和p_error_ack。接收模块接收到ack后释放状态指示st_error和p_error,继续接收数据。

这个过程就是状态位的握手,本模块设计方式是握手期间,即发现校验位或者停止位错误后停止接收数据,直到CPU清除状态后才开始正常接收。读者可以根据自己的需要判断错误后是否继续接收数据。

  • 接收状态机

使用典型的三段式状态机设计,包含六种状态,IDLE、START、RX_DATA、CHECK_DATA、STOP和SEND。

时钟域

uart接收状态转移图

IDLE:状态机从IDLE状态开始,检测到uart_i的下降沿,进入START状态。

START:START状态起始位是否为低(避免毛刺触发状态机),起始位正常即进入RX_DATA开始接收数据。RX_DATA:接收满8bit后判断是否使能校验位,如使能,进入CHECK_DATA状态进行奇偶校验的判断;如不使能,直接进入停止状态STOP。

CHECK_DATA:CHECK_DATA状态判断奇偶校验是否正确,不正确则发出p_error信号,在状态寄存器指示校验错误,待CPU处理返回p_error_ack后回到IDLE状态。如果正确,判断是否使能停止位检查;使能停止位检查则跳转到STOP状态;不使能则跳转到SEND状态。

STOP:同样的,在STOP状态检测停止位是否是高电平。如果是,表示停止位正确,跳转到SEND状态;如果不是,则发出st_error信号,在状态寄存器指示停止位错误,待CPU处理返回st_error_ack后回到IDLE状态。

SEND:SEND状态主要是产生rx_start信号表示8bits数据接收正确,可以将数据写到接收FIFO。

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

// 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(neg_urxd_i) begin
            nextstate = START;
        end
        elsebegin
            nextstate = IDLE;
        end
    end
    START: begin
        if(start_right) begin// start bit is right,then reserve data
            nextstate = RX_DATA;
        end
        elsebegin
            nextstate = IDLE;
        end
    end
    RX_DATA: begin
        if(data_cnt < 4'd8) begin// reserve 8 datas
            nextstate = RX_DATA;
        end
        elsebegin
            if(rx_bpsclk) begin
                if(check_syn2) begin
                    nextstate = CHECK_DATA;
                end
                elsebegin
                    nextstate = STOP;
                end
            end
            elsebegin
                nextstate = RX_DATA;
            end
        end
    end
    CHECK_DATA: begin
        if(p_error_ack_delay2) begin
            nextstate = IDLE;
        end
        elsebegin
            if(rx_bpsclk) begin
		            // p_error:1:parity bit error,0:parity bit error
		            if(p_error) begin
		                nextstate = CHECK_DATA;
		            end
		            elsebegin
		                // st_check:1:check stop bit,0:don't check stop bit
		                if(st_check_syn2) begin
		                    nextstate = STOP;
		                end
		                elsebegin
		                    nextstate = SEND;
		                end
		            end
		        end
		        elsebegin
		            nextstate = CHECK_DATA;
		        end
        end
    end
    STOP: begin
        if(st_error_ack_delay2) begin
            nextstate = IDLE;
        end
        elsebegin
            if(rx_bpsclk) begin
		            // st_error:1:stop bit error,0:stop bit error
		            if(st_error) begin
		                nextstate = STOP;
		            end
		            elsebegin
		                nextstate = SEND;
		            end
		        end
		        elsebegin
		            nextstate = STOP;
		        end
        end
    end
    SEND: begin
        if(rx_ack_delay2) begin
            nextstate = IDLE;
        end
        elsebegin
            nextstate = SEND;
        end
    end
    default: begin
        nextstate = IDLE;
    end
    endcase
end

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

// output signal state
always@(posedge clk26m ornegedge rst26m_) begin
    if(!rst26m_) begin
        rx_bpsen <= 1'b0;
        data_rx  <= 8'd0;
        data_cnt <= 4'd0;
        p_error  <= 1'b0;
        st_error <= 1'b0;
        rx_start <= 1'b0;
        start_right <= 1'b0;
    end
    elsebegin
        case(nextstate)
        IDLE: begin
            rx_bpsen    <= 1'b0;
            data_cnt    <= 4'd0;
            st_error    <= 1'b0;
            p_error     <= 1'b0;
            rx_start    <= 1'b0;
            start_right <= 1'b0;
        end
        START: begin
            rx_bpsen    <= 1'b1;
            if(rx_bpsclk) begin
                if(urxd_i == 1'b0) begin
                    start_right <= 1'b1;
                end
                elsebegin
                    start_right <= 1'b0;
                end
            end
        end
        RX_DATA: begin
            if(rx_bpsclk) begin
                data_rx[data_cnt] <= urxd_i;
                data_cnt <= data_cnt + 1'b1;
            end
        end
        CHECK_DATA: begin
            if(rx_bpsclk) begin
                // odd check
                if(parity_syn2) begin
                    if(^data_rx == urxd_i) begin// odd check is wrong
                        p_error  <= 1'b1;
                        rx_bpsen <= 1'b0;
                    end
                end
                // even check
                elsebegin
                    if(^data_rx == !urxd_i) begin// even check is wrong
                        p_error  <= 1'b1;
                        rx_bpsen <= 1'b0;
                    end
                end
            end
        end
        STOP: begin
            if(rx_bpsclk) begin
                if(urxd_i == 1'b0) begin// stop bit is wrong
                    st_error <= 1'b1;
                    rx_bpsen <= 1'b0;
                end
            end
        end
        SEND: begin
            rx_start <= 1'b1;
        end
        endcase
    end
end
  • 接收数据FIFO控制

接收状态机的SEND状态表示1Byte数据接收完成,此状态会产生一个rx_start信号。

FIFO写控制部分通过监控此信号产生写使能rx_fifo_winc(1个ARM时钟周期)和接收响应rx_ack,SEND状态发现rx_ack后释放rx_start,回到IDLE状态。由于FIFO写控制是在ARM时钟域进行,握手的时间很短,不会对接收下一Byte数据产生影响。

// this state machine to send data to RX FIFO
always@(posedge clk ornegedge rst_) begin
    if(!rst_) begin
        rx_ack       <= 1'b0;
        rx_fifo_winc <= 1'b0;
        wdata_state  <= 2'b0;
    end
    elsebegin
        case(wdata_state)
        2'b00: begin
            if(!rx_fifo_wfull && rx_start_delay2) begin
                rx_ack       <= 1'b1;
                rx_fifo_winc <= 1'b1;
                wdata_state  <= 2'b01;
            end
        end
        2'b01: begin
            rx_fifo_winc    <= 1'b0;
            if(!rx_start_delay2) begin
                rx_ack      <= 1'b0;
                wdata_state <= 2'b10;
            end
        end
        2'b10: begin
            wdata_state <= 2'b0;
        end
        endcase
    end
end

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

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

全部0条评论

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

×
20
完善资料,
赚取积分