使用双BUFF的首先注意事项:
有的硬件DMA带有存储切换功能,这个功能主要是在一个BUFF满了之后自动切换成另一个BUFF,主要实现了两个功能:一是自动切换buff和自动设置传输的长度;
这里不建议使用,因为我需要串口DMA接收不定长数据,既然是不定长,那么每一次从DMA Buff中搬运出数据时,需要计算出DMA传输的长度并设置,还要设置要传输的DMA Buff;如果是定长数据的接收那么可以使用存储切换功能;
DMA接收数据的实现过程:
1.获取DMA CNT的值,并和上次获取的值做比较得出这次需要从DMA BUFF中搬运的数量。此寄存器记录了能够通过DMA传输的数据个数,每传输一个数据该值变减一,直到该值为0,会自动加载,该初始值在初始化的时候设置好;
无论使用哪一个BUFF接收数据,DMA的CNT都会变化。
2.失能DMA,因为在失能的情况下才能修改寄存器的值;
3.获取上一次使用的BUFF,切换buff,使得DMA搬运的数据到正确的DMA BUFF;使用的哪个BUFF自己可以设置一个标志;
4.使能DMA
5.开启传输即把DMA BUFF中的数据搬运到外设,注意这就不是DMA自动搬运的过程了。
DMA双BUFF配置寄存器和从DMA BUFF中取数的过程如下:
if (pUartSioChan->SIOCHAN_iMethod == UART_DMA_MODE) {
dma_channel_disable(uiDmaX, iDmaRxChan); /* 通道失能 */
_G_uiDmaRxCnt = dma_transfer_number_get(uiDmaX, iDmaRxChan); /* 获取DMA的CNT剩余数据长度 */
if (_G_uiDmaRxCnt != _G_uiLastLength) { /* 和上一次CNT的值作比较 */
_G_uiLastLength = _G_uiDmaRxCnt;
uiReceivedCount = DMA_RXBUFF_SIZE - _G_uiDmaRxCnt; /* 获取接收数据量 */
// uiBuffUsed = dma_using_memory_get(uiDmaX, iDmaRxChan); /* 双BUFF时判断 */
/*
* 设置传输长度
*/
DMA_CHCNT(uiDmaX, iDmaRxChan) = DMA_RXBUFF_SIZE;
/*
* 设置传输BUFF
*/
dmaSwitchBuffer(uiDmaX, iDmaRxChan, _G_iRxNextBuff);
_G_iRxNextBuff = !(_G_iRxNextBuff);
_G_iBuffToTransfer = !(_G_iRxNextBuff);
dma_channel_enable(uiDmaX, iDmaRxChan);
/*
* 开始从内存中搬运到TTY设备
*/
while(uiCount < uiReceivedCount) {
if (_G_iBuffToTransfer == DMA_MEMORY_0) {
PUTRXCHAR(_G_ucRxbuffer1[uiCount]);
} else if (_G_iBuffToTransfer == DMA_MEMORY_1) {
PUTRXCHAR(_G_ucRxbuffer2[uiCount]);
}
++uiCount;
}
}
}
判断是否从DMA BUFF中取数的机制是获取DMA CNT的值,与上一次CNT值进行比较,两个值不同,则开始处理DMA BUFF中的数据。将CNT值存储为全局变量,每个通道存储在自己的全局变量里。