芯片手册

某个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, '\0', 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, '\0', 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

本博客的文章如无特殊说明,均为原创,转载请注明出处。如未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

带FIFO的UART数据接收的更多相关文章

  1. 内核中用于数据接收的结构体struct msghdr(转)

    内核中用于数据接收的结构体struct msghdr(转) 我们从一个实际的数据包发送的例子入手,来看看其发送的具体流程,以及过程中涉及到的相关数据结构.在我们的虚拟机上发送icmp回显请求包,pin ...

  2. stm32 usb数据接收与数据发送程序流程分析

    http://blog.csdn.net/u011318735/article/details/17424349 既然学习了USB,那就必须的搞懂USB设备与USB主机数据是怎么通讯的.这里主要讲设备 ...

  3. MVC查询数据接收及校验

    本来想写一篇aspx的TreeView控件绑值的文章的,在写案例的时候,写了一半,发现有些地方还得考虑以下,就留待下次了. 这一篇的话,是最近在开发一个项目的时候,有大量的页面和数据表,需要花式查询, ...

  4. 65、Spark Streaming:数据接收原理剖析与源码分析

    一.数据接收原理 二.源码分析 入口包org.apache.spark.streaming.receiver下ReceiverSupervisorImpl类的onStart()方法 ### overr ...

  5. tcp输入数据 慢速路径处理 && oob数据 接收 && 数据接收 tcp_data_queue

    大致的处理过程 TCP的接收流程:在tcp_v4_do_rcv中的相关处理(网卡收到报文触发)中,会首先通过tcp_check_urg设置tcp_sock的urg_data为TCP_URG_NOTYE ...

  6. 酷友观点/经验:支付接口返回数据接收地址,session数据丢失(或者说失效)的问题浅析(原创文章)

    酷友观点/经验:支付接口返回数据接收地址,session数据丢失(或者说失效)的问题浅析(原创文章)   最近手头在开发一个游戏官网,在支付模块采用神州付技术支持,神州付数据表单中要求提供服务器返回地 ...

  7. Spark Streaming 数据接收过程

    SparkStreaming 源码分析 一节中从源码角度,描述了Streaming执行时代码的调用过程.下边就接收转化阶段过程再简单分析一下,为分析backpressure作准备. SparkStre ...

  8. UDP数据接收服务器

    简介 这是我在做一个要用UDP方式进行数据传输时,自己写的一个多线程的UDP数据接收服务器, 它能将接收到的UDP数据包存成文件,并提供数据包接收时间监测: 还支持键盘命令响应,以将数据写到新的文件, ...

  9. 【ALB技术笔记】基于多线程方式的串行通信接口数据接收案例

    基于多线程方式的串行通信接口数据接收案例 广东职业技术技术学院  欧浩源 1.案例背景 在本博客的<[CC2530入门教程-06]CC2530的ADC工作原理与应用>中实现了电压数据采集的 ...

随机推荐

  1. mysql 状态查询

    select  COUNT(case when info.State = '0' then State end  ) as daichuliCount, COUNT(case when info.St ...

  2. [ZOJ1610]Count the Colors

    Description 画一些颜色段在一行上,一些较早的颜色就会被后来的颜色覆盖了. 你的任务就是要数出你随后能看到的不同颜色的段的数目. Input 每组测试数据第一行只有一个整数n, 1 < ...

  3. Poj 3189 Steady Cow Assignment (多重匹配)

    题目链接: Poj 3189 Steady Cow Assignment 题目描述: 有n头奶牛,m个棚,每个奶牛对每个棚都有一个喜爱程度.当然啦,棚子也是有脾气的,并不是奶牛想住进来就住进来,超出棚 ...

  4. ACM_数数?诶?这么简单?

    数数?诶?这么简单? Time Limit: 2000/1000ms (Java/Others) Problem Description: 当看到GDUFE-GAME宣传海报上提到"场内人员 ...

  5. poj2573Bridge(过桥问题)

    链接 A,B为最快和次快 有两种方式可以使c,d过桥 一是a与c一起走,a回来接d再与d一起走,一直到对岸人为0为止 而是 a与b一起走 a回来送灯 c与d一起走 b回来送灯 重复此过程. 只剩2人时 ...

  6. Android学习笔记(十) Activity的生命周期

    一.如何在一个应用程序中定义多个Activity -定义一个类,继承Activity -复写onCreate() setContentView(R.layout.secondLayout):设定该Ac ...

  7. 4 Visual Effects 视觉效果 读书笔记 第四章

    4   Visual Effects    视觉效果        读书笔记 第四章 Well, circles and ovals are good, but how about drawing r ...

  8. 如何安装sql server2005 windows 8

    如何安装sql server2005   windows 8 1 从网上下载到本地文件 ,这里使用的是cs_sql_2005_dev_all_dvd 安装版. 2. 点击下图所表示进行安装   3. ...

  9. iOS - 事件处理全过程(补充)

    事件处理的完整过程 1> 先将事件对象由上往下传递(由父控件传递给子控件),找到最合适的控件来处理这个事件. 2> 调用最合适控件的touches….方法 3> 如果调用了[supe ...

  10. 关于Qt模态框总汇

    转载请注明出处:http://www.cnblogs.com/dachen408/p/7285710.html 父窗体为QMainWindow: 当子窗体为: 1.QWidget,需要设置 this- ...