CC2540协议栈高速串口通信解决(UART的DMA方式)

来源:互联网 发布:mysql视频教程 百度云 编辑:程序博客网 时间:2024/04/30 17:16

由于产品功能需求,要在CC2540协议栈里使用串口通信,115200的波特率

在裸机状态下115200完全没有问题,但在协议栈中,由于OSAL并不是一个实时的系统(或者说是平台),且效率并不高,导致大量串口数据过来时候会出现丢字节的现象

如果直接用中断函数,是处理不过来的,因为OSAL时不时的要进入临界区,从而关闭所有中断,那自然也就接收不到串口数据了~

为解决这个问题,我想到用DAM做。协议栈内置了uart的DMA传输,不过遗憾的是,这部分的代码效率同样不高,用来接收几十个byte没有问题,但不停的发送几百上千个字节就会出现部分字节丢失,甚至IAR报告溢出警告(估计也不稳定),将缓冲HAL_UART_DMA_RX_MAX调大,问题依旧

于是开始看DMA这块的代码,想办法直接从DMA的buf里面把数据取出来处理,省去OSAL轮询它,做些不必要的操作。。

在HalDmaInit和HalUARTInitDMA中基本是做DMA相关寄存器的配置,在HalUARTInitDMA中可以看到一处设置

HAL_DMA_SET_WORD_SIZE( ch, HAL_DMA_WORDSIZE_WORD );

这条语句有点诡异,我看到这儿的时候立马就注意到了,因为串口是一个字节一个字节传的,为何SIZE要设置为WORD,也就是两个字节,其上有一串注释

  /* The trick is to cfg DMA to xfer 2 bytes for every 1 byte of Rx.
   * The byte after the Rx Data Buffer is the Baud Cfg Register,
   * which always has a known value. So init Rx buffer to inverse of that
   * known value. DMA word xfer will flip the bytes, so every valid Rx byte
   * in the Rx buffer will be preceded by a DMA_PAD char equal to the
   * Baud Cfg Register value.
   */

在这儿,OSAL用了一个小技巧,因为串口数据接收的缓冲寄存器后面接的是波特率寄存器,见手册U0BUF的地址为0x70C1,而0x70C2正好是波特率寄存器U0BAUD,所以,DMA取一个字,也就是两个字节,会取出一个U0BAUD和U0BUF。取出U0BAUD来有何用?其实目的很简单——OSAL每次轮训,在DMA接收到数据后,将DMA目标缓冲中“有效”的数据取出来,并反馈给应用层,然后将多出来的、取自U0BAUD的字节,取反,这样就能分辨出DMA有没有接收到新的字节,因为已经处理过的数据有部分被取反了,也就不等于U0BAUD寄存器中的值了

举例说明,如果uart接收到的数据为,0x01,0x02,0x03,0x04,那么在DMA缓冲区里存的数据(115200波特率下)将会是:0xD8,0x01,0xD8,0x02,0xD8,0x03,0xD8,0x04,这样0xD8实际就是用来做标记的,处理过后给他设置为别的值,那就知道哪些是处理过,哪些没处理过了~~

而HAL_DMA_SET_TRIG_MODE( ch, HAL_DMA_TMODE_SINGLE_REPEATED );表明DMA是循环执行的,不用关心何时重启DMA

所以要做的修改为:

1.在Hal_ProcessPoll中注释掉HalUARTPoll();不让OSAL去轮询它

2.自己写一个处理DMA缓冲的函数,自己去分析dmaCfg.rxBuf中的内容

整理好以后重新烧到CC2540里面测试,一切OK,不停地发送了几万个字节没有一个字节丢了的。

0 0
原创粉丝点击