AlexaZhou!

Here's my blog :)

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

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

最近做了一个物联网项目,目的是为原有的一个只能通过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芯片被广泛使用,这一类芯片的接收数据问题,都可以通过这样的思路来解决。