文档章节

CC2540通过UART接收115200bps数据的方法

A
 AlexaZhou
发布于 2014/11/30 19:14
字数 1549
阅读 483
收藏 1

这个问题比较有意思,而且具有一定的普遍性,写出来和大家一起分享。

最近做了一个物联网项目,目的是为原有的一个只能通过UART接口控制的设备添加蓝牙功能。

采用的主体结构是把CC2540模块和设备通过UART连接,然后在CC2540上实现相应的Profile,手机通过Profile和CC2540通讯,而CC2540通过UART接口和设备通讯。这样子经过CC2540的转换,就可以实现用手机APP对设备进行控制了。

cc2540是TI的 蓝牙4.0(BLE)SOC芯片,特性如下:

  • 256KB FALSH
  • 16KB RAM
  • 32MHz的单周期增强型51 Core
  • 蓝牙协议栈和用户的应用程序可以在单芯片上运行
  • 极低的功耗

其实整个项目都很普通,没有什么难度。除了一个地方,就是用CC2540捕捉UART数据。这里的设备数据包大小不定,最大不超过1KB。

###接收UART数据不是很简单嘛?单片机都能做,为嘛这个地方会有问题呢?

因为CC2540是一颗SOC芯片,也就是个System on chip。CC2540除了运行用户的应用程序以外,还运行蓝牙4.0的协议栈,收发无线数据包相关的事件都是由协议栈自动处理。

好的,问题来了。根据TI的资料,每次处理数据包相关事件可能需要占用CPU长达几个毫秒的时间,并且在这个时间中是完全关闭中断的。

而平时我们从UART接收数据,最常用就是通过中断。配置好设备的寄存器,使得每次UART收到数据就产生一次中断,然后在中断中对数据进行读取。

115200bps的速率下1秒大约有 115200/8=14400Byte的数据发送,1ms也就是14Byte。如果通过中断的方式来接收数据,假设CPU处理运行协议栈花了1ms时间,而这时恰好有数据过来。就会丢失14Byte,这肯定是没法用的。so,此路不通。

###幸好还有更好的方法,也就是通过DMA接收数据

DMA(Direct Memory Access,直接内存存取) 是所有现代电脑的重要特色,它允许不同速度的硬件装置来沟通,而不需要依于 CPU 的大量中断负载。否则,CPU 需要从来源把每一片段的资料复制到暂存器,然后把它们再次写回到新的地方。在这个时间中,CPU 对于其他的工作来说就无法使用。

DMA的特色就是可以不经过CPU来传输数据,正适合当前这种CPU分身乏术的情况。经查DataSheet,CC2540一共有4个DMA通道,无线协议栈用掉了一个。

通过把DMA的触发源设置为UART接收到数据,传输源地址设置为UART数据寄存器,传输目的地址设置为一片Buf,并设置为每次传输源地址不变,目的地址自增。就可以在DMA实现DMA自动搬运UART数据了。

不过不管开辟多大的DMA接收Buf,也总有满的时候。当DMA Buf满了,或者需要对DMA Buf中数据进行处理的时候,就需要暂停DMA传输。而这时如果有数据进入,就会丢失。

为了解决这个问题,这里使用经典的Ping-Pang DMA操作方法。 #####Ping-Pang通过开启双DMA通道,实现任意时刻都有一个DMA通道在接受数据中,不会丢数据。而且通过Ping-Pang切换,DMA Buf不会满,也就可以持续接收。

这里具体实现上先开辟两次要(a,b)一主要(x)三个缓冲区,并建立两个DMA通道,分别把DMA数据传输到两个次要缓冲区ab中。通过一个定时器在两个DMA通道之间来进行切换。每次切换到新的次要缓冲区之后,就把旧的次要缓冲区中的数据导出到主要接收缓冲x中。这样就可以在x中对数据进行处理,并且过程中不会丢失数据。

###不过这里还有一个坑,那就是怎样获得DMA传输数据的长度

使用Ping-Pang 方法操作DMA时,需要不断导出已经保存在DMA Buf中的数据。这时就需要知道DMA Buf中数据的长度。有些芯片的DMA控制器有相关接口,可以直接从寄存器中读取到传输计数值。不过CC2540没有这个功能。。。TI啊TI,你这样做事情做一半,真的好么-_-#

####这里显然不可以通过把DMA接收缓冲初始化为0,然后判断非0数据的长度来实现,因为UART接收到的数据可能是0。

后来想了个方法来解决。思路就是开始每一轮DMA传输之前把DMA接收缓冲区的数据初始化为固定值,比如0x00。然后把DMA控制器配置为每次传输双字节数据^1。通过查询DataSheet,UART控制器的数据寄存器后面一字节是波特率寄存器的一部分,这是一个固定值而且不为零。这样DMA每次传输会传输1字节UART接收的数据+1字节固定数据到接收Buf中。通过分析收到固定数据的长度,就可以知道DMA收到数据的长度。

####这里一共有三个地方需要注意的:

  • 通过DMA接收数据
  • DMA Ping-Pong操作
  • 通过给数据添加"尾巴"来实现DMA传输计数

###通过以上方法,就完美的解决了UART接收数据的问题。现在带无线协议栈的Soc芯片被广泛使用,这一类芯片的接收数据问题,都可以通过这样的思路来解决。

AlexaZhou.xyz版权所有,转载请注明

© 著作权归作者所有

A

AlexaZhou

粉丝 7
博文 3
码字总数 3018
作品 1
朝阳
程序员
私信 提问
S5PV210开发 -- 串口驱动开发

上篇文章讲的 UART,更多的是硬件相关的知识。接下来进入正题了,串口驱动开发。 一、阅读原理图 我们用的是 UART2 串口,则接收管脚 XuRXD2 复用 GPA10,发送管脚 XuTXD2 复用 GPA11 二、S...

qq_29350001
2017/11/20
0
0
使用Arduino UART-WiFi模块做web服务器

一、硬件准备 1.选择硬件,主要有2种: 2.UART-WIFI模块介绍 3.UART-WIFI模块配置 UART-WIFI模块需要3.3V和300mA的输入,而Arduino只能提供3.3V和50 mA,直接连接到Arduino,会导致arduino的...

开源机器人
2012/12/01
20.4K
3
STM32 UART串口驱动程序

示例1.通过UART1进行数据发送 UART 1 的初始化 将数据0xBA通过UART1发送出 示例2.通过对UART1进行接收中断配置,收到外部数据后进入中断读取数据 UART 1 的初始化 开启接收中断 中断入口函数,...

sheeptech
2018/01/31
0
0
4 UART串口(RS232)相关内容

4 UART串口(RS232)相关内容 1、首先交代:计算机中,硬件层面仅仅包括CPU和外设 只要一上电,CPU核一天到晚都在跟外设进行数据的交互。既然CPU核和外设要进行数据交互,明确常见几种数据通信...

uperficialyu
2018/01/15
0
0
stm32F030C8单片机串口利用USART_IT_IDLE+DMA接收完整的一帧

之前用串口的USARTITIDLE中断和USARTITRXNE(此中断每接收一个字节就会进一次中断),然后利用USARTITIDLE检测空闲接收完整的一帧数据,但是此方法需要频繁进入中断,影响CPU效率,所以利用USA...

gmq_syy
2018/03/16
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Vue warn]: Computed property "activeNames" was assigned to but it has no setter.

在使用 vue,element-ui时,如下代码 <template> <el-form :model="numberValidateForm" ref="numberValidateForm"> <el-form-item> <el-tabs v-model="activeNames" @tab-cl......

牧云橙
25分钟前
3
0
重构-改善既有代码的设计-6.2内联函数

6.2内联函数 动机 本书经常以简短的函数表现动作意图,这样会使代码更清晰易读。但有时候你会遇到某些函数,其内部代码和函数名称同样清晰易读。也可能你充够了该函数的内部实现,使其内容和...

还仙
26分钟前
5
0
Less 混入

混合类似于编程语言中的函数。 Mixins 是一组CSS属性,允许我们将一个类的属性嵌套于另一个类,被嵌入的类可以看作是变量,并且包含类名作为其属性,也就是说我们可以用一个类定义样式然后把...

凌兮洛
28分钟前
5
0
频繁FGC的真凶原来是它

频繁FGC的真凶原来是它 上周排查了一个线上问题,主要现象是CPU占用过高,jvm old区占用过高,同时频繁fgc,我简单排查了下就草草收场了,但是过后我对这个问题又进行了复查,发现问题没有那...

每天晒白牙
29分钟前
5
0
简单的树形菜单如何写

业务需求 数据结构中含有图片、名称、children的树形结构,需要展示出每一级的图片名称和图片,找了些树形图的插件,都没有展示大的图片的,一般都是小图标,就自己试着写一个包含图的简单的...

tianyawhl
31分钟前
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部