LPC5516_SDK例程ADC_2Msps高速采集
来源:恩智浦MCU加油站 发布时间:2023-01-05 分享至微信

最近支持一个客户,需要在LPC5516下实现ADC 2Msps高速采集,根据数据手册描述:

  • ADC在12-bit模式下最高可以达到2.3Msps

  • ADC在16-bit模式下最高可以达到2.0Msps.

那么实际情况是否真如数据手册所述,能达到如此高的转换速率呢?小编这次就编写了测试代码进行了实测,结果为:

12-bit模式下ADC最快可达2.326Msps, 16-bit模式下2.083Msps, 结果还是和数据手册很吻合的。


代码设计

代码基于SDK的例程:

\SDK_2_12_0_LPCXpresso55S16\boards\lpcxpresso55s16\driver_examples\lpadc\dma

修改:

1. 为了实现最快速度ADC采集,我们需要将ADC配置为:

  • ADC输入时钟: ADCCLK = 48MHz

  • 无硬件平均: HWAVG=1

  • ADC采样时长设置为最短3xCLK: STS=0

  • ADC功率最大: PWRSEL=3

除此之外,还需要将ADC设置为连续转换模式:即将g_LpadcCommandConfigStruct.chainedNextCommandNumber指向自己,即完成当前转换后,自动开始下次转换。

以上所有配置对应SDK代码如下:

/* Configure ADC. */

LPADC_GetDefaultConfig(lpadcConfigStruct);

lpadcConfigStruct.enableAnalogPreliminary = true;

lpadcConfigStruct.conversionAverageMode = kLPADC_ConversionAverage1;

lpadcConfigStruct.powerLevelMode=kLPADC_PowerLevelAlt4;

lpadcConfigStruct.referenceVoltageSource = DEMO_LPADC_VREF_SOURCE;

lpadcConfigStruct.FIFO0Watermark = 2;



LPADC_GetDefaultConvCommandConfig(g_LpadcCommandConfigStruct);

g_LpadcCommandConfigStruct.channelNumber = DEMO_LPADC_USER_CHANNEL;

g_LpadcCommandConfigStruct.sampleTimeMode = kLPADC_SampleTimeADCK3;

g_LpadcCommandConfigStruct.loopCount = 1;

g_LpadcCommandConfigStruct.conversionResolutionMode = kLPADC_ConversionResolutionHigh;

//g_LpadcCommandConfigStruct.conversionResolutionMode =kLPADC_ConversionResolutionStandard;

g_LpadcCommandConfigStruct.chainedNextCommandNumber = DEMO_LPADC_USER_CMDID;


2. 配置DMA,使用DMA Ping-Pang buffer接收ADC数据,即定义两个DMA描述符,A和B:A传输完成后自动触发B,B传输完成后自动触发A。对应SDK代码为:

1.SDK_ALIGN(uint32_t s_dma_table[DMA_DESCRIPTOR_NUM * sizeof(dma_descriptor_t)], FSL_FEATURE_DMA_LINK_DESCRIPTOR_ALIGN_SIZE);

2.

3. const uint32_t g_XferConfig =

4. DMA_CHANNEL_XFER(true, /* Reload linkdescriptor after current exhaust, */

5. true, /* Clear trigger status.*/

6. true, /* Enable interruptA. */

7. false, /* Not enable interruptB. */

8. sizeof(uint32_t), /* Dma transfer width. */

9. kDMA_AddressInterleave0xWidth, /* Dma source address no interleave*/

10. kDMA_AddressInterleave1xWidth, /* Dma destination address nointerleave */

11. sizeof(uint32_t)*ADC_DMA_SIZE /* Dma transfer byte. */

12. );


static void DMA_Configuration(void)

{

dma_channel_config_t dmaChannelConfigStruct;

#if defined (DEMO_DMA_HARDWARE_TRIGGER) DEMO_DMA_HARDWARE_TRIGGER

/* Configure INPUTMUX. */

INPUTMUX_Init(DEMO_INPUTMUX_BASE);

INPUTMUX_AttachSignal(DEMO_INPUTMUX_BASE, DEMO_DMA_ADC_CHANNEL, DEMO_DMA_ADC_CONNECTION);

#endif /* DEMO_DMA_HARDWARE_TRIGGER */

/* Configure DMA. */

DMA_Init(DEMO_DMA_BASE);

DMA_EnableChannel(DEMO_DMA_BASE, DEMO_DMA_ADC_CHANNEL);

DMA_CreateHandle(g_DmaHandleStruct, DEMO_DMA_BASE, DEMO_DMA_ADC_CHANNEL);

DMA_SetCallback(g_DmaHandleStruct, DEMO_DMA_Callback, NULL);

/* Prepare and submitthe transfer. */

DMA_PrepareChannelTransfer(dmaChannelConfigStruct, /* DMA channel transfer configuration structure. */

(void *)DEMO_LPADC_RESFIFO_REG_ADDR, /* DMA transfer source address.*/

(void *)adc_result, /* DMA transfer destination address. */

g_XferConfig, /* Xfer configuration */

kDMA_PeripheralToMemory, /* DMAtransfer type. */

NULL, /*DMA channel trigger configurations. */

(dma_descriptor_t *)(s_dma_table[0]) /* Address of next descriptor. */

);

DMA_SubmitChannelTransfer(g_DmaHandleStruct, dmaChannelConfigStruct);

/* Set two DMAdescripters to use ping-pong mode. */

DMA_SetupDescriptor((dma_descriptor_t *)(s_dma_table[0]), g_XferConfig, (void *)DEMO_LPADC_RESFIFO_REG_ADDR, (void *)adc_result, (dma_descriptor_t *)(s_dma_table[4]));

DMA_SetupDescriptor((dma_descriptor_t *)(s_dma_table[4]), g_XferConfig, (void *)DEMO_LPADC_RESFIFO_REG_ADDR, (void *)adc_result, (dma_descriptor_t *)(s_dma_table[0]));

}

3. 最后小编还使能了SysTick定时器用于记录转换时间,程序开始运行后,ADC会启动连续转换,DMA设置为传输100次ADC转换结果后触发DMA完成中断, DMA中断触发后(传输完成),程序会统计ADC转换时间,计算ADC转换结果的平均值和标准差,以及打印转换结果。


代码清单

最后为大家呈上完整代码清单(可以直接复制到lpadc_dma.c里运行):

/*
* Copyright 2018-2021 NXP
* All rights reserved.
*
*
* SPDX-License-Identifier: BSD-3-Clause
*/

#include "pin_mux.h"
#include "clock_config.h"
#include "board.h"
#include "fsl_debug_console.h"
#include "fsl_dma.h"
#include "fsl_inputmux.h"
#include "fsl_lpadc.h"
#include "stdio.h"
#include "math.h"
#include "fsl_power.h"
#include "fsl_anactrl.h"
/*******************************************************************************
* Definitions

******************************************************************************/
#define PF(a) ((a) * (a))
#define DEMO_LPADC_BASE ADC0
#define DEMO_LPADC_USER_CHANNEL 0U
#define DEMO_LPADC_USER_CMDID 1U /* CMD1 */
#define DEMO_LPADC_VREF_SOURCE kLPADC_ReferenceVoltageAlt2
#define DEMO_LPADC_DO_OFFSET_CALIBRATION true
#define DEMO_LPADC_RESFIFO_REG_ADDR (uint32_t)((ADC0->RESFIFO[0]))
#define DEMO_RESULT_FIFO_READY_FLAG kLPADC_ResultFIFO0ReadyFlag

#define DEMO_DMA_BASE DMA0
#define DEMO_DMA_ADC_CHANNEL 21U
#define DMA_DESCRIPTOR_NUM 2U
#define ADC_DMA_SIZE (100)

static void ADC_Configuration(void);
static void DMA_Configuration(void);

lpadc_conv_command_config_t g_LpadcCommandConfigStruct; /*
Structure to configure conversion command. */

dma_handle_t g_DmaHandleStruct; /* Handler structure for using DMA. */
uint32_t adc_result[ADC_DMA_SIZE]; /* Keep the ADC conversion resulut moved from ADC data register by DMA. */
static double adc_sum;
static double adc_mean, adc_std;
static double adc_sum_sqrt;

volatile bool g_DmaTransferDoneFlag = false; /* Flag of DMA transfer done trigger by ADC conversion. */
/* DMA descripter table used for ping-pong mode. */
SDK_ALIGN(uint32_t s_dma_table[DMA_DESCRIPTOR_NUM * sizeof(dma_descriptor_t)], FSL_FEATURE_DMA_LINK_DESCRIPTOR_ALIGN_SIZE);
const uint32_t g_XferConfig = DMA_CHANNEL_XFER(true, /* Reload link descriptor after current exhaust, */
true, /* Clear trigger status. */
true, /* Enable interruptA. */
false, /* Not enable interruptB. */
sizeof(uint32_t), /* Dma transfer width. */
kDMA_AddressInterleave0xWidth, /* Dma source address no interleave */
kDMA_AddressInterleave1xWidth, /* Dma destination address no interleave */
sizeof(uint32_t)*ADC_DMA_SIZE /* Dma transfer byte. */
);

const uint32_t g_LpadcFullRange = 65536U;
const uint32_t g_LpadcResultShift = 0U;

void DEMO_DMA_Callback(dma_handle_t *handle, void *param, bool transferDone, uint32_t tcds)
{
//printf("DEMO_DMA_Callback\r\n");
if (true == transferDone)
{
g_DmaTransferDoneFlag = true;
}
}

int main(void)
{
/* Initialize board hardware. */
/* set BOD VBAT level to 1.65V */
POWER_SetBodVbatLevel(kPOWER_BodVbatLevel1650mv, kPOWER_BodHystLevel50mv, false);
/* attach main clock divide to FLEXCOMM0 (debug console) */ CLOCK_AttachClk(BOARD_DEBUG_UART_CLK_ATTACH); BOARD_InitBootPins(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); /* Set clock source for ADC0 */ CLOCK_SetClkDiv(kCLOCK_DivAdcAsyncClk, 2U, true); CLOCK_AttachClk(kFRO_HF_to_ADC_CLK); /* Disable LDOGPADC power down */ POWER_DisablePD(kPDRUNCFG_PD_LDOGPADC); ANACTRL_Init(ANACTRL); ANACTRL_EnableVref1V(ANACTRL, true); PRINTF("LPADC DMA Example\r\n"); PRINTF("ADC CLK:%d\r\n", CLOCK_GetAdcClkFreq()); PRINTF("CORE CLK:%d\r\n", CLOCK_GetCoreSysClkFreq()); /* Configure peripherals. */ DMA_Configuration(); ADC_Configuration(); PRINTF("ADC Full Range: %d\r\n", g_LpadcFullRange); PRINTF("ADCResolution: %dbit\r\n", (g_LpadcCommandConfigStruct.conversionResolutionMode == kLPADC_ConversionResolutionStandard)?(12):(16)); SysTick_Config(0xFFFFFF); int tick; PRINTF("Please press any key to trigger the conversion.\r\n"); while (1) { /* Get the input from terminal and trigger the converter by software. */ GETCHAR(); g_DmaTransferDoneFlag = false; LPADC_DoSoftwareTrigger(DEMO_LPADC_BASE, 1UL); /* Trigger the ADC and start the conversion. */ DMA_StartTransfer(g_DmaHandleStruct); /* Enable the DMA every time for each transfer. */ tick = SysTick->VAL; /* Wait for the converter transfer to be done. */ while (false == g_DmaTransferDoneFlag) {}; tick = tick - SysTick->VAL; tick = tick / (CLOCK_GetCoreSysClkFreq() / (1000*1000)); printf("%-16s%dus(%.3fMS/s)\r\n", "TIME:", tick, (1 / (float)tick)*ADC_DMA_SIZE); int i; adc_sum = 0; adc_sum_sqrt = 0; for(i=0; i<ADC_DMA_SIZE; i++) { adc_result[i] = ((uint16_t)(adc_result[i] ADC_RESFIFO_D_MASK) >> g_LpadcResultShift); adc_sum += (float)adc_result[i]; adc_sum_sqrt += (adc_result[i]*adc_result[i]); // PRINTF("ADC[%d]:%d\r\n", i, adc_result[i]); } // printf("SUM:%.2f\r\n", adc_sum); // printf("SSUM:%.2f\r\n", adc_sum_sqrt); adc_mean = adc_sum / ADC_DMA_SIZE; adc_std = (adc_sum_sqrt - PF(adc_sum)/ADC_DMA_SIZE) / (ADC_DMA_SIZE-1); adc_std = sqrt(adc_std); printf("%-16s%f\r\n", "AVG :", adc_mean); printf("%-16s%f\r\n", "STD :", adc_std); }}static void ADC_Configuration(void){ lpadc_config_t lpadcConfigStruct; lpadc_conv_trigger_config_t lpadcTriggerConfigStruct; /* Configure ADC. */ LPADC_GetDefaultConfig(lpadcConfigStruct); lpadcConfigStruct.enableAnalogPreliminary = true; lpadcConfigStruct.conversionAverageMode = kLPADC_ConversionAverage1; lpadcConfigStruct.powerLevelMode=kLPADC_PowerLevelAlt4; lpadcConfigStruct.referenceVoltageSource = DEMO_LPADC_VREF_SOURCE; lpadcConfigStruct.FIFO0Watermark = 2; LPADC_Init(DEMO_LPADC_BASE, lpadcConfigStruct); LPADC_DoOffsetCalibration(DEMO_LPADC_BASE); LPADC_DoAutoCalibration(DEMO_LPADC_BASE); /* Set conversion CMD configuration. */ LPADC_GetDefaultConvCommandConfig(g_LpadcCommandConfigStruct); g_LpadcCommandConfigStruct.channelNumber = DEMO_LPADC_USER_CHANNEL; g_LpadcCommandConfigStruct.sampleTimeMode = kLPADC_SampleTimeADCK3; g_LpadcCommandConfigStruct.loopCount = 1; g_LpadcCommandConfigStruct.conversionResolutionMode = kLPADC_ConversionResolutionHigh; // g_LpadcCommandConfigStruct.conversionResolutionMode = kLPADC_ConversionResolutionStandard; g_LpadcCommandConfigStruct.chainedNextCommandNumber = DEMO_LPADC_USER_CMDID; LPADC_SetConvCommandConfig(DEMO_LPADC_BASE, DEMO_LPADC_USER_CMDID, g_LpadcCommandConfigStruct); /* Set trigger configuration. */ LPADC_GetDefaultConvTriggerConfig(lpadcTriggerConfigStruct); lpadcTriggerConfigStruct.targetCommandId = DEMO_LPADC_USER_CMDID; lpadcTriggerConfigStruct.enableHardwareTrigger = true; LPADC_SetConvTriggerConfig(DEMO_LPADC_BASE, 0U, lpadcTriggerConfigStruct); /* Configurate the trigger0. */ /* DMA request enabled. */ LPADC_EnableFIFO0WatermarkDMA(DEMO_LPADC_BASE, true);}static void DMA_Configuration(void){ dma_channel_config_t dmaChannelConfigStruct;#if defined(DEMO_DMA_HARDWARE_TRIGGER) DEMO_DMA_HARDWARE_TRIGGER /* Configure INPUTMUX. */ INPUTMUX_Init(DEMO_INPUTMUX_BASE); INPUTMUX_AttachSignal(DEMO_INPUTMUX_BASE, DEMO_DMA_ADC_CHANNEL, DEMO_DMA_ADC_CONNECTION);#endif /* DEMO_DMA_HARDWARE_TRIGGER */ /* Configure DMA. */ DMA_Init(DEMO_DMA_BASE); DMA_EnableChannel(DEMO_DMA_BASE, DEMO_DMA_ADC_CHANNEL); DMA_CreateHandle(g_DmaHandleStruct, DEMO_DMA_BASE, DEMO_DMA_ADC_CHANNEL); DMA_SetCallback(g_DmaHandleStruct, DEMO_DMA_Callback, NULL); /* Prepare and submit the transfer. */ DMA_PrepareChannelTransfer(dmaChannelConfigStruct, /* DMA channel transfer configuration structure. */ (void *)DEMO_LPADC_RESFIFO_REG_ADDR, /* DMA transfer source address. */ (void *)adc_result, /* DMA transfer destination address. */ g_XferConfig, /* Xfer configuration */ kDMA_PeripheralToMemory, /* DMA transfer type. */ NULL, /* DMA channel trigger configurations. */ (dma_descriptor_t *)(s_dma_table[0]) /* Address of next descriptor. */ ); DMA_SubmitChannelTransfer(g_DmaHandleStruct, dmaChannelConfigStruct); /* Set two DMA descripters to use ping-pong mode. */ DMA_SetupDescriptor((dma_descriptor_t *)(s_dma_table[0]), g_XferConfig, (void *)DEMO_LPADC_RESFIFO_REG_ADDR, (void *)adc_result, (dma_descriptor_t *)(s_dma_table[4])); DMA_SetupDescriptor((dma_descriptor_t *)(s_dma_table[4]), g_XferConfig, (void *)DEMO_LPADC_RESFIFO_REG_ADDR, (void *)adc_result, (dma_descriptor_t *)(s_dma_table[0]));}void SysTick_Handler(void){}



下期,将重点聊聊影响ADC转换误差的各种因素。



恩智浦MCU加油站


这是由恩智浦官方运营的公众号,着重为您推荐恩智浦MCU的产品信息、开发技巧、教程文档、培训课程等内容。


长按二维码,关注我们



END



更多恩智浦AI-IoT市场和产品信息,邀您同时关注“NXP客栈”微信公众号




NXP客栈


恩智浦致力于打造安全的连接和基础设施解决方案,为智慧生活保驾护航。





长按二维码,关注我们

[ 新闻来源:恩智浦MCU加油站,更多精彩资讯请下载icspec App。如对本稿件有异议,请联系微信客服specltkj]
存入云盘 收藏
举报
全部评论

暂无评论哦,快来评论一下吧!