这几天看了下DMA,还真是只能给个粗略的认识,尝试配置DMA+PTC+ADC+ATC_ETC无果,就开始有点放弃,然后查看一下DMA例子,想到DMA还有个典型的应用就是串口,串口辣么慢,一个一个字符打又老中断CPU(或者堵塞),实在不环保.
才发现原来DMA+UART是不用自己写的LPUART_ReceiveEDMA和LPUART_SendEDMA都做好了,而且只需要LPUART_TransferCreateHandleEDMA初始化一下,是不是很贴心,然后查看其他代码,I2C,SPI也有同样的函数,唯独ADC_ETC没有?看来是我一开始选择了困难模式.
一般的DMA+外设初始化步骤大概如下(前提是官方做了函数):
- 初始化板子上的IO等配置.
- 初始化本身的外设(比如说串口,就可以先LPUART_GetDefaultConfig,填充参数,然后LPUART_Init就可以了.)
- 初始化DMAMUX,绑定通道.(比如官方例子绑定DMA CH0到kDmaRequestMuxLPUART1Tx,这里需要DMAMUX_Init初始化一下DMAMUX,DMAMUX_SetSource并DMAMUX_EnableChannel就可以了.)
- 初始化EDMA,绑定回调.(比如官方例子EDMA_Init初始化DMA0,然后EDMA_CreateHandle根据通道分别绑定g_lpuartTxEdmaHandle和g_lpuartRxEdmaHandle)
- 把EDMA和对应硬件外设连接起来.(比如官方例子中的LPUART_TransferCreateHandleEDMA,连接后得到了加料Handle g_lpuartEdmaHandle,以及绑定用户空间的处理回调LPUART_UserCallback)
- 完成
在官方的UART例子中,最终目的是得到g_lpuartEdmaHandle,可以用其他通道来进行,似乎没有什么限制.
在实际使用,就是收发,回调三个关键点,回调一般不携带数据,但是会回传状态等信息.
下面是官方给出的I2C,SPI,UART回调例子,可见回调是可复用(只要判断进来的Handle就能区分谁回调),并且主要携带status的一个函数.
static void lpi2c_master_callback(LPI2C_Type *base, lpi2c_master_edma_handle_t *handle, status_t status, void *userData) { /* Signal transfer success when received success status. */ if (status == kStatus_Success) { g_MasterCompletionFlag = true; } } void LPSPI_MasterUserCallback(LPSPI_Type *base, lpspi_master_edma_handle_t *handle, status_t status, void *userData) { if (status == kStatus_Success) { PRINTF("This is LPSPI master edma transfer completed callback. \r\n\r\n"); } isTransferCompleted = true; } void LPUART_UserCallback(LPUART_Type *base, lpuart_edma_handle_t *handle, status_t status, void *userData) { userData = userData; if (kStatus_LPUART_TxIdle == status) { txBufferFull = false; txOnGoing = false; } if (kStatus_LPUART_RxIdle == status) { rxBufferEmpty = false; rxOnGoing = false; } }
在函数方面,其实就是普通的收发函数,添加EDMA后缀,比如LPUART_Receive => LPUART_ReceiveEDMA,当然容错是没实现的,像这个函数,如果收不到特定字节数,是会停在那里的.
-----------------------------
用上面的知识,基本能把一些外设DMA化,但是如果要任意外设DMA化,暂时我没看懂,但是先往下分析代码.既然说到了LPUART_ReceiveEDMA,那么就看LPUART_ReceiveEDMA,在看的过程中,我还对比了其他类似的DMA化的函数,发现基本如下:
- (传输发起)EDMA_PrepareTransfer->EDMA_SubmitTransfer->EDMA_StartTransfer->LPUART_EnableRxDMA
- 交给EDMA后,EMA会传输,根据LPUART初始化设定的CallBack,会在传输完成后回调.
- 数据交到这个函数后,立马就返回了.
- (传输完成)LPUART_SendEDMACallback / LPUART_ReceiveEDMACallback
- (回调发生)LPUART_TransferAbortReceiveEDMA / LPUART_TransferAbortSendEDMA->用户回调函数()
而M2M(内存到内存)传输也是这样的,但是不知道为什么应用到ADC_ETC就不会了呢,是没触发哪个REQ吗?后续再看吧.