0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

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

冬至子 来源:两猿社 作者:IC猿 2023-06-05 15:59 次阅读

00 简介

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

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

01 模块接口与描述

1.jpg

2.jpg

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中。

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

    关注

    134

    文章

    8653

    浏览量

    361847
  • 寄存器
    +关注

    关注

    30

    文章

    5037

    浏览量

    117764
  • 有限状态机
    +关注

    关注

    0

    文章

    51

    浏览量

    10270
  • FIFO存储
    +关注

    关注

    0

    文章

    102

    浏览量

    5895
  • UART接口
    +关注

    关注

    0

    文章

    123

    浏览量

    15068
收藏 人收藏

    评论

    相关推荐

    如何算得的数据(10进制)转换为16进制通过串口发送出

    我用公式节点算得一组数据,好比是1212,1313,1414等,这些数据转为16进制就是04BC,0521,0586,如何十进制的数据转换为十六进制然后通过串口
    发表于 01-30 14:05

    程序是ADC采集的数据和串口接收的数据通过ble蓝牙发送出去,请问为什么打开uart后找不到搜索蓝牙?如何解决?

    我写的程序是ADC采集的数据和串口接收的数据通过ble蓝牙发送出去,不知道为什么,打开uart后,手机无法搜索蓝牙,通过调试发现卡在0x0
    发表于 06-29 16:27

    请问用光敏实验采集的数据,怎么样通过WIFI发送出去

    比如用光敏实验采集的数据,怎么样通过WIFI发送出去?怎么样通过WIFI用电脑控制开发板上面的LED灯,这类的问题,谢谢解答。
    发表于 06-03 21:35

    lwip中数据不能发送出去

    我现在在stm32上用lwip和上位机通讯,正常情况下数据每次能发送出去,并且lwip中tcp_output函数里 pcb->unacked == NULL的,但是当出现异常情况时,数据发送
    发表于 07-04 04:35

    请问特征值发送出去用什么函数?

    请问下,我想把特征值 里面的值发送出去需要用什么函数呢?还是说我只要用SimpleProfile_SetParameter()这个函数设置好要发的特征值就可以了,让主机自己去读呢?小白请教大家。谢谢。
    发表于 03-05 10:30

    CC2541的串口没法发送出去

    你好,在使用CC2541时目前遇到一个很困惑的问题,我的程序希望通过串口发送,同样的程序在开发板上运行能够正常的收发,程序下载到我们自己的目标板上,就没法发送出去了,跟踪到
    发表于 03-18 08:32

    请问用NRF2401温度数据发送出去怎么在电路板上把天线做出来?

    用NRF2401温度数据发送出去,怎么在电路板上就把天线做出来,求原理图和PCB布线!谢谢各位大神!
    发表于 04-13 04:35

    NRF24L01如何才能将所有数据发送出去呢

    利用NRF24L01通信发送数据,资料说的是可以发送最长32个字节但是我只能发送16个字节,只能发送一半,后面的全部都为了0了,请问这是为什
    发表于 06-10 09:25

    DSP处理结果如何通过SCI发送出去

    在工程实际应用中需要将DSP处理结果通过SCI发送出去,但是所要传输数据是浮点float型,无法通过SCI发送,该如何处理?谢谢~
    发表于 07-23 06:27

    为什么文章无法提交 发送出去

    为什么文章无法提交 发送出去
    发表于 07-22 14:23

    一个浮点型的数通过串口发送出去 精选资料分享

    可能大家对发送字符串,整数,数组等待的没有问题,也想的明白,可是对于浮点型的数,或许有些不知所措(大佬绕过,小白我是这样)今天搞懂了,就记录下!其实发送原理还是通过字符串的形式发送出去的。只不过
    发表于 08-18 07:14

    如何一个浮点型的数通过串口发送出去

    如何一个浮点型的数通过串口发送出去
    发表于 11-22 06:16

    如何printf()函数的数据用STM32的串口发送出去呢

    如何去使用C语言中的printf()函数呢?如何printf()函数的数据用STM32的串口发送出去呢
    发表于 12-01 07:58

    Linux如何操作将数据发送出去

      Linux 服务器收到网络数据包,需要经过哪些处理,一步步将数据传给应用进程的呢?应用进程发送数据包时,Linux 又是如何操作将数据
    的头像 发表于 06-17 16:00 717次阅读
    Linux如何操作将<b class='flag-5'>数据</b>包<b class='flag-5'>发送出去</b>

    stm32 HAL库spi接收的同时为什么有数据发送出去

    stm32 HAL库spi接收的同时为什么有数据发送出去? 在STM32 HAL库中,SPI接口的数据收发是通过DMA传输完成的。因此,通过SPI接收
    的头像 发表于 10-26 17:42 919次阅读