【MCXA156开发板测评】-6-EDMA3例程代码分析
[复制链接]
本帖最后由 慕容雪花 于 2025-1-12 20:16 编辑
例程:edma3_ping_pong_transfer
1. 首先进行EDMA3初始化:
/*!
* brief Initializes the eDMA peripheral.
*
* This function ungates the eDMA clock and configures the eDMA peripheral according
* to the configuration structure.
*
* param base eDMA peripheral base address.
* param config A pointer to the configuration structure, see "edma_config_t".
* note This function enables the minor loop map feature.
*/
void EDMA_Init(EDMA_Type *base, const edma_config_t *config)
{
assert(config != NULL);
assert(FSL_FEATURE_EDMA_INSTANCE_CHANNELn(base) != -1);
uint32_t tmpreg, i = 0U;
#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
/* Ungate EDMA peripheral clock */
CLOCK_EnableClock(s_edmaClockName[EDMA_GetInstance(base)]);
#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
#if defined(EDMA_RESETS_ARRAY)
RESET_ReleasePeripheralReset(s_edmaResets[EDMA_GetInstance(base)]);
#endif
#if defined(FSL_EDMA_SOC_IP_EDMA) && FSL_EDMA_SOC_IP_EDMA
/* clear all the enabled request, status to make sure EDMA status is in normal condition */
EDMA_BASE(base)->ERQ = 0U;
EDMA_BASE(base)->INT = 0xFFFFFFFFU;
EDMA_BASE(base)->ERR = 0xFFFFFFFFU;
/* Configure EDMA peripheral according to the configuration structure. */
tmpreg = EDMA_BASE(base)->CR;
tmpreg &= ~(DMA_CR_ERCA_MASK | DMA_CR_HOE_MASK | DMA_CR_CLM_MASK | DMA_CR_EDBG_MASK);
tmpreg |= (DMA_CR_ERCA(config->enableRoundRobinArbitration) | DMA_CR_HOE(config->enableHaltOnError) |
DMA_CR_CLM(config->enableContinuousLinkMode) | DMA_CR_EDBG(config->enableDebugMode) | DMA_CR_EMLM(1U));
EDMA_BASE(base)->CR = tmpreg;
#else
tmpreg = EDMA_MP_BASE(base)->MP_CSR;
#if defined FSL_FEATURE_EDMA_HAS_GLOBAL_MASTER_ID_REPLICATION && FSL_FEATURE_EDMA_HAS_GLOBAL_MASTER_ID_REPLICATION
tmpreg = (tmpreg & ~(DMA_MP_CSR_HAE_MASK | DMA_MP_CSR_ERCA_MASK | DMA_MP_CSR_EDBG_MASK | DMA_MP_CSR_GCLC_MASK |
DMA_MP_CSR_GMRC_MASK | DMA_MP_CSR_HALT_MASK)) |
DMA_MP_CSR_GMRC(config->enableMasterIdReplication) | DMA_MP_CSR_HAE(config->enableHaltOnError) |
DMA_MP_CSR_ERCA(config->enableRoundRobinArbitration) | DMA_MP_CSR_EDBG(config->enableDebugMode) |
DMA_MP_CSR_GCLC(config->enableGlobalChannelLink);
#else
tmpreg = (tmpreg & ~(DMA_MP_CSR_HAE_MASK | DMA_MP_CSR_ERCA_MASK | DMA_MP_CSR_EDBG_MASK | DMA_MP_CSR_GCLC_MASK |
DMA_MP_CSR_HALT_MASK)) |
DMA_MP_CSR_HAE(config->enableHaltOnError) | DMA_MP_CSR_ERCA(config->enableRoundRobinArbitration) |
DMA_MP_CSR_EDBG(config->enableDebugMode) | DMA_MP_CSR_GCLC(config->enableGlobalChannelLink);
#endif
EDMA_MP_BASE(base)->MP_CSR = tmpreg;
#if defined FSL_FEATURE_EDMA_HAS_CHANNEL_CONFIG && FSL_FEATURE_EDMA_HAS_CHANNEL_CONFIG
/* channel transfer configuration */
for (i = 0U; i < (uint32_t)FSL_FEATURE_EDMA_INSTANCE_CHANNELn(base); i++)
{
if (config->channelConfig[i] != NULL)
{
EDMA_InitChannel(base, i, config->channelConfig[i]);
}
}
#endif
#endif
}
2. 紧接着创建一个edma句柄,用于后续操作edma:
EDMA_CreateHandle(&g_DMA_Handle, EXAMPLE_DMA_BASEADDR, DEMO_DMA_CHANNEL_0);
/*!
* brief Creates the eDMA handle.
*
* This function is called if using the transactional API for eDMA. This function
* initializes the internal state of the eDMA handle.
*
* param handle eDMA handle pointer. The eDMA handle stores callback function and
* parameters.
* param base eDMA peripheral base address.
* param channel eDMA channel number.
*/
void EDMA_CreateHandle(edma_handle_t *handle, EDMA_Type *base, uint32_t channel)
{
assert(handle != NULL);
assert(FSL_FEATURE_EDMA_INSTANCE_CHANNELn(base) != -1);
assert(channel < (uint32_t)FSL_FEATURE_EDMA_INSTANCE_CHANNELn(base));
uint32_t edmaInstance;
edma_tcd_t *tcdRegs;
/* Zero the handle */
(void)memset(handle, 0, sizeof(*handle));
handle->channel = channel;
/* Get the DMA instance number */
edmaInstance = EDMA_GetInstance(base);
s_EDMAHandle[edmaInstance][channel] = handle;
/* Enable NVIC interrupt */
(void)EnableIRQ(s_edmaIRQNumber[edmaInstance][channel]);
handle->tcdBase = EDMA_TCD_BASE(base, channel);
handle->channelBase = EDMA_CHANNEL_BASE(base, channel);
handle->base = base;
/*
Reset TCD registers to zero. Unlike the EDMA_TcdReset(DREQ will be set),
CSR will be 0. Because in order to suit EDMA busy check mechanism in
EDMA_SubmitTransfer, CSR must be set 0.
*/
tcdRegs = handle->tcdBase;
EDMA_TCD_SADDR(tcdRegs, EDMA_TCD_TYPE(base)) = 0;
EDMA_TCD_SOFF(tcdRegs, EDMA_TCD_TYPE(base)) = 0;
EDMA_TCD_ATTR(tcdRegs, EDMA_TCD_TYPE(base)) = 0;
EDMA_TCD_NBYTES(tcdRegs, EDMA_TCD_TYPE(base)) = 0;
EDMA_TCD_SLAST(tcdRegs, EDMA_TCD_TYPE(base)) = 0;
EDMA_TCD_DADDR(tcdRegs, EDMA_TCD_TYPE(base)) = 0;
EDMA_TCD_DOFF(tcdRegs, EDMA_TCD_TYPE(base)) = 0;
EDMA_TCD_CITER(tcdRegs, EDMA_TCD_TYPE(base)) = 0;
EDMA_TCD_DLAST_SGA(tcdRegs, EDMA_TCD_TYPE(base)) = 0;
EDMA_TCD_CSR(tcdRegs, EDMA_TCD_TYPE(base)) = 0;
EDMA_TCD_BITER(tcdRegs, EDMA_TCD_TYPE(base)) = 0;
}
3. 设置传输完成后的回调函数:
EDMA_SetCallback(&g_DMA_Handle, DMA_Callback, NULL);
/* User callback function for EDMA transfer. */
void DMA_Callback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds)
{
if (transferDone)
{
g_transferDone = true;
}
}
4. 关联TCD 即传输控制描述符
EDMA_InstallTCDMemory(&g_DMA_Handle, g_DMA_Tcd, 2);
/*!
* brief Installs the TCDs memory pool into the eDMA handle.
*
* This function is called after the EDMA_CreateHandle to use scatter/gather feature. This function shall only be used
* while users need to use scatter gather mode. Scatter gather mode enables EDMA to load a new transfer control block
* (tcd) in hardware, and automatically reconfigure that DMA channel for a new transfer.
* Users need to prepare tcd memory and also configure tcds using interface EDMA_SubmitTransfer.
*
* param handle eDMA handle pointer.
* param tcdPool A memory pool to store TCDs. It must be 32 bytes aligned.
* param tcdSize The number of TCD slots.
*/
void EDMA_InstallTCDMemory(edma_handle_t *handle, edma_tcd_t *tcdPool, uint32_t tcdSize)
{
assert(handle != NULL);
assert(((uint32_t)tcdPool & 0x1FU) == 0U);
/* Initialize tcd queue attribute. */
/* header should initial as 1, since that it is used to point to the next TCD to be loaded into TCD memory,
* In EDMA driver IRQ handler, header will be used to calculate how many tcd has done, for example,
* If application submit 4 transfer request, A->B->C->D,
* when A finshed, the header is 0, C is the next TCD to be load, since B is already loaded,
* according to EDMA driver IRQ handler, tcdDone = C - A - header = 2 - header = 2, but actually only 1 TCD done,
* so the issue will be the wrong TCD done count will pass to application in first TCD interrupt.
* During first submit, the header should be assigned to 1, since 0 is current one and 1 is next TCD to be loaded,
* but software cannot know which submission is the first one, so assign 1 to header here.
*/
handle->header = 1;
handle->tcdUsed = 0;
handle->tcdSize = (int8_t)tcdSize;
handle->tcdPool = tcdPool;
}
5. 设置传输的源地址、目的地址、传输数据量等:
EDMA_PrepareTransfer(&transferConfig[0], &srcAddr[0], sizeof(srcAddr[0]), &destAddr[0], sizeof(destAddr[0]),
sizeof(uint32_t) * HALF_BUFFER_LENGTH, sizeof(uint32_t) * HALF_BUFFER_LENGTH,
kEDMA_MemoryToMemory);
EDMA_PrepareTransfer(&transferConfig[1], &srcAddr[4], sizeof(srcAddr[0]), &destAddr[4], sizeof(destAddr[0]),
sizeof(uint32_t) * HALF_BUFFER_LENGTH, sizeof(uint32_t) * HALF_BUFFER_LENGTH,
kEDMA_MemoryToMemory);
EDMA_SubmitLoopTransfer(&g_DMA_Handle, transferConfig, 2);
EDMA_StartTransfer(&g_DMA_Handle);
EDMA进行传输前,Destination Buffer内容为:
第一次DMA传输完成后:
第二次EDMA传输完成后:
|