本帖最后由 慕容雪花 于 2025-1-13 14:13 编辑
前面测评:
【MCXA156开发板测评】-1-开发环境搭建与串口回显 https://bbs.eeworld.com.cn/thread-1303370-1-1.html
【MCXA156开发板测评】-2-ADC https://bbs.eeworld.com.cn/thread-1303898-1-1.html
【MCXA156开发板测评】-3-LPTMR体验 https://bbs.eeworld.com.cn/thread-1304161-1-1.html
【MCXA156开发板测评】-4-LPI2C驱动OLED屏幕 https://bbs.eeworld.com.cn/thread-1304186-1-1.html
【MCXA156开发板测评】-5-LPADC中断模式快速配置 https://bbs.eeworld.com.cn/thread-1304340-1-1.html
【MCXA156开发板测评】-6-EDMA3例程代码分析 https://bbs.eeworld.com.cn/thread-1304375-1-1.html
MCX-A156的例程库提供了lpi2c, lpspi,lpuart等模块与edma3模块协作搬运数据的例程,但是没有看到adc与dma的例程。
所以本次实验来尝试在恩智浦mcuconfigtool里面配置edma3模块,并尝试去搬运adc获取到数据。
一、配置adc发送dma请求
在前面的测评文章中,提到了FIFO watermark可以用来作为一个阈值来发起中断请求或者DMA触发。
二、使能并配置dma3
本次实验使用dma0:
配置eDMA通道0的request source等参数:
三、适当修改代码
在生成的ADC1初始化代码中,看到已经使能了DMA请求:
static void ADC1_init(void) {
/* Initialize LPADC converter */
LPADC_Init(ADC1_PERIPHERAL, &ADC1_config);
/* Perform auto calibration */
LPADC_DoAutoCalibration(ADC1_PERIPHERAL);
/* Enable DMA request on FIFO watermark event */
LPADC_EnableFIFOWatermarkDMA(ADC1_PERIPHERAL, true);
/* Configure conversion command 1. */
LPADC_SetConvCommandConfig(ADC1_PERIPHERAL, 1, &ADC1_commandsConfig[0]);
/* Configure trigger 0. */
LPADC_SetConvTriggerConfig(ADC1_PERIPHERAL, 0, &ADC1_triggersConfig[0]);
/* Interrupt vector ADC1_IRQn priority settings in the NVIC. */
NVIC_SetPriority(ADC1_IRQN, ADC1_IRQ_PRIORITY);
/* Enable interrupts from LPADC */
LPADC_EnableInterrupts(ADC1_PERIPHERAL, (kLPADC_FIFO0WatermarkInterruptEnable));
/* Enable interrupt ADC1_IRQN request in the NVIC */
EnableIRQ(ADC1_IRQN);
}
查看dma0的初始化代码:
edma_config_t DMA0_config = {
.enableMasterIdReplication = false,
.enableGlobalChannelLink = true,
.enableHaltOnError = true,
.enableDebugMode = false,
.enableRoundRobinArbitration = false
};
/* Tansactional transfer configurations */
edma_transfer_config_t DMA0_CH0_Transfers_config[1];
edma_handle_t DMA0_CH0_Handle;
/* TCD pool initialization */
EDMA_ALLOCATE_TCD(DMA0_CH0_TCD_pool, DMA0_CH0_TCD_SIZE);
static void DMA0_init(void) {
status_t status;
(void)status;
/* Channel CH0 initialization */
/* Set the kDma0RequestMuxAdc1FifoRequest request */
EDMA_SetChannelMux(DMA0_DMA_BASEADDR, DMA0_CH0_DMA_CHANNEL, DMA0_CH0_DMA_REQUEST);
/* Create the eDMA DMA0_CH0_Handle handle */
EDMA_CreateHandle(&DMA0_CH0_Handle, DMA0_DMA_BASEADDR, DMA0_CH0_DMA_CHANNEL);
/* DMA0 channel 0 reset */
EDMA_ResetChannel(DMA0_DMA_BASEADDR, DMA0_CH0_DMA_CHANNEL);
/* DMA0 channel 0 peripheral request */
EDMA_EnableAsyncRequest(DMA0_DMA_BASEADDR, DMA0_CH0_DMA_CHANNEL, true);
/* Allocate TCD memory pool for scatter-gather mode */
EDMA_InstallTCDMemory(&DMA0_CH0_Handle, DMA0_CH0_TCD_pool, DMA0_CH0_TCD_SIZE);
/* DMA callback initialization */
EDMA_SetCallback(&DMA0_CH0_Handle, DMA_Callback, NULL);
/* DMA0 transfer CH0_TRANSFER0 configuration */
EDMA_PrepareTransferConfig(&DMA0_CH0_TRANSFER0_CONFIG, (void *) (uint32_t)(&(ADC1->RESFIFO)) , 1 << kEDMA_TransferSize4Bytes, 0, (void *) &destAddr[0], 1 << kEDMA_TransferSize4Bytes, sizeof(destAddr[0]), 4U, 80U);
DMA0_CH0_TRANSFER0_CONFIG.dstMajorLoopOffset = 4;
/* DMA0 loop transfer submit */
status = EDMA_SubmitLoopTransfer(&DMA0_CH0_Handle, DMA0_CH0_Transfers_config, 1U);
assert(status == kStatus_Success);
/* DMA0 hardware channel 0 request auto stop */
EDMA_EnableAutoStopRequest(DMA0_DMA_BASEADDR, DMA0_CH0_DMA_CHANNEL, true);
/* DMA0 channel 0 peripheral request */
EDMA_EnableChannelRequest(DMA0_DMA_BASEADDR, DMA0_CH0_DMA_CHANNEL);
}
定义相关的目标数组:
extern edma_handle_t DMA0_CH0_Handle;
volatile bool g_transferDone = false;
uint32_t destAddr[20];
/* DMA0 TCD CH0_TRANSFER0 destination address */
AT_NONCACHEABLE_SECTION_ALIGN_INIT(uint32_t destAddr[], 4);
/* DMA channel DMA0_CH0 callback function */
void DMA_Callback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds) {
/* Place your code here */
if (transferDone)
{
g_transferDone = true;
}
PRINTF("\r\n DMA_Callback Done");
}
启动DMA:
EDMA_StartTransfer(&DMA0_CH0_Handle);
之后等待DMA传输完成标志位在DMA CALLBAKC函数中置位:
/* Wait for EDMA transfer finish */
while (!g_transferDone)
{
LPADC_DoSoftwareTrigger(ADC1_PERIPHERAL, 1U);
}
/* Print destination buffer */
PRINTF("\r\n\r\nEDMA transfer ADC1 finish.\r\n\r\n");
PRINTF("Destination Buffer:\r\n");
for (uint8_t i = 0; i < 20; i++)
{
PRINTF("%d\t", (uint16_t)(destAddr[i]& ADC_RESFIFO_D_MASK));
}
PRINTF("\r\n END TEST");
while(1){
}
四、运行
ADC的输入信号是连接到3.3V电源上,这样简便些。
采集一下声音传感器数据:
五、总结
eDMA是恩智浦在MCXA156产品上提供的增强型DMA控制器,可以在不占用CPU资源来进行数据搬运。这种工作模式可以大大减轻CPU的负担。
借助恩智浦的MCU CONFIGTOOL,可以方便的配置eDMA模块和各种外设模式,自动生成驱动代码。用户只需要添加数行业务代码即可轻松使用eDMA模块。
参考:
Using Configtools to set up ADC and DMA from scratch - Sample Project LPC55S69 https://community.nxp.com/t5/MCUXpresso-Config-Tools/Using-Configtools-to-set-up-ADC-and-DMA-from-scratch-Sample/m-p/1183770