zoukankan      html  css  js  c++  java
  • 带FIFO的UART数据接收

    芯片手册

    某个Cortex-M4芯片带有1个UART,支持Tx,Rx 的FIFO功能,而且可以通过寄存器配置FIFO的阈值,芯片的datasheet并不完善,没有说明RX的FIFO具体有几个级别,每隔级别的阈值是多少。

    但是需要注意的是 TX, RX 的FIFO都可以通过UART 的 DR 寄存器进行访问。

    RX FIFO 阈值

    功夫不负有心人,终于在SDK的某段代码中窥见了RX的几个FIFO阈值:

    默认情况下RX FIFO 是收到32字节才会产生一次RX中断。

    如果收到的数据长度没有32字节,则一直等,等到满足32字节,才产生RX中断。

    RX FIFO 阈值 影响RX中断产生的频率

    之所以注意到这个问题,是因为下面的一件事情:

    调试的时候发现电脑发送6个字符,但是开发板并没有打印6个字符,再加6个字符,还是没有,继续加6个字符,直到满足了32个字节,这个时候才有了RX中断,把收到的数据打印了出来。

    RX 收到的数据不完整

    上面虽然收到了数据,但是并没有把32个字符都保存下来。原来在UART中断程序中,在RX中断条件下,每次都把前面的数据清空了。

    if (ui32Status & ( AM_REG_UART_IES_RXRIS_M | AM_REG_UART_IES_RTRIS_M)) {
        // 其他操作
        os_memset(g_uart_buf, '', sizeof(g_uart_buf));  // 后来才意识到在 uart 中断服务程序中每次接收都清空接收缓冲区是一个大错误。
        // 接收数据
        *(g_uart_buf + index++) = AM_REGn(UART, ui32Module, DR);
        
    }
    

    如上面的例子,会导致每一次中断中清空前面的数据,永远只能看到后一次FIFO中的数据,最多为32个字节。这样绝对是错误的,因为每次从FIFO中取走了一个字节,后面当FIFO满时又会产生新的RX中断。
    所以每次中断中尽量取走所有的数据。该怎么做呢?需要读UART寄存器状态,FIFO不空的情况下把所有的数据取走,例如下面:

        if(ui32Status & (AM_REG_UART_IES_RXRIS_M | AM_REG_UART_IES_RTRIS_M))
        {
            while ( !AM_BFRn(UART, 0, FR, RXFE) )
            {
                if (uart_data_index < UART_BUF_LENGTH) { // 不能超出 g_uart_buf 的长度
                    *(g_uart_buf + uart_data_index++) = AM_REGn(UART, ui32Module, DR);
                } else { // 超出部分直接丢弃
                    ui8Char = AM_REGn(UART, ui32Module, DR);
                }
            }
        }
    

    还需要注意,如果对端模块返回的UART数据超过了接收缓冲区的长度,需要丢弃。如果不丢弃则无法退出中断。

    总结

    UART 使用主要是初始化,中断服务程序以及应用程序对收发数据的处理。
    初始化部分,需要完成的工作:

    • 引脚配置
    • UART模块的时钟使能
    • UART的配置如波特率,数据位,停止位,奇偶校验,流控制
    • FIFO配置,一般重点关注RX的FIFO
    • UART模块使能
    • UART中断使能
    • UART中断子类别使能,例如 AM_HAL_UART_INT_RX 表示RX中断,AM_HAL_UART_INT_RX_TMOUT 表示超时中断

    中断服务程序,一般来说主要负责接收,可以按照下面的流程:

    • 读UART的中断状态,判定是哪一种UART中断,例如是RX还是RX TMOUT 中断
    • 根据具体的UART中断类型采取对应的操作
    • 读取DR寄存器,放到缓冲区,直到满足某个条件退出读DR的循环
    • 清除中断,修改某些标志位,如果跑了OS,可以发出信号量通知其他线程
    • 退出中断服务程序

    应用程序,在某个时候处理UART接收缓冲区,必要的时候需要清空缓冲区。
    如果不清空的话,UART中断会一直把数据拷贝到缓冲区,直到缓冲区满,后面的数据就浪费了。同时应用程序也会处理这些重复的数据可能无法进入正常的流程。

    按道理说在应用层清空缓冲区就可以了,但是我担心应用程序在操作缓冲区的时候突然被中断打断了,清空操作出错了或者把刚刚收到的数据清空了,我以为让中断程序在某些条件下清空或许是一个不错的选择。
    如下是我的实现,应用层发出清空缓冲区的请求uart_data_clear(),如果恰好下一次UART中断即时,就可以在中断一开始清空缓冲区。如果下一次UART中断来的不及时,那么第二次调用uart_data_clear()就可以在应用层清空缓冲区。

    extern unsigned char g_uart_buf[UART_BUF_LENGTH]; // UART 接收缓冲区
    extern uint16_t uart_data_index; // 当前写入UART接收缓冲区的数据的位置。
    extern uint8_t  uart_data_clear_flag; // 1 -- 表示需要清除数据; 0 -- 不需要清除数据
    
    /**
     * 第一次调用,可以让UART中断清空缓冲区,第二次调用可以在应用层清空缓冲区
     */
    void uart_data_clear(void)
    {
        // 说明还没有进入一次UART中断,这里在应用层主动清空UART数据
        if (uart_data_clear_flag) {
            os_memset(g_uart_buf, 0, sizeof(g_uart_buf));
            uart_data_index = 0;
            uart_data_clear_flag = 0;
        } else {
            // 在UART中断清空数据
            uart_data_clear_flag = 1;
        }
    }
    
    
    
    // 另一个文件中的UART中断服务程序
    
    void am_uart_isr(void)
    {
        uint32_t ui32Status;
        uint32_t ui32Module = 0;
        uint8_t  ui8Char = 0;
    
        //
        // Read the masked interrupt status from the UART.
        //
        ui32Status = am_hal_uart_int_status_get(true);
    
        if(ui32Status & (AM_REG_UART_IES_RXRIS_M | AM_REG_UART_IES_RTRIS_M))
        {
    
            // 用户已经处理完了上一次的UART接收数据,可以清空缓冲区以备下一次接收
            if (uart_data_clear_flag) {
                uart_data_clear_flag = 0;
                os_memset(g_uart_buf, '', sizeof(g_uart_buf));
                uart_data_index = 0;
            }
    
            while ( !AM_BFRn(UART, 0, FR, RXFE) )
            {
                if (uart_data_index < UART_BUF_LENGTH) { // 不能超出 g_uart_buf 的长度
                    *(g_uart_buf + uart_data_index++) = AM_REGn(UART, ui32Module, DR);
                } else { // 超出部分直接丢弃
                    ui8Char = AM_REGn(UART, ui32Module, DR);
                }
            }
        }
    
        // Clear the UART interrupts.
        //
        am_hal_uart_int_clear(ui32Status);
    
        // 发出信号量通知其他任务
       // do something...
    }
    
    

    声明

    欢迎转载,请注明出处和作者,同时保留声明。
    作者:LinTeX9527
    出处:https://www.cnblogs.com/LinTeX9527/p/9183656.html
    本博客的文章如无特殊说明,均为原创,转载请注明出处。如未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

  • 相关阅读:
    Ajax笔记(三)
    Ajax笔记(二)
    org.apache.commons.lang.StringUtils中常用的方法
    数位dp poj1850
    二分图 最小点覆盖 poj 3041
    poj 1789 prime
    c++三种进制格式
    c++面向行的输入getline()和get()
    最小生成树 prime算法 UVALive
    最短路 poj1125
  • 原文地址:https://www.cnblogs.com/LinTeX9527/p/9183656.html
Copyright © 2011-2022 走看看