最近支持一个客户,需要在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_lpcxpresso55s16oardslpcxpresso55s16driver_exampleslpadcdma
修改:
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 ); 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 ); printf(adc clk:%d , clock_getadcclkfreq()); printf(core clk:%d , clock_getcoresysclkfreq()); /* configure peripherals. */ dma_configuration(); adc_configuration(); printf(adc full range: %d , g_lpadcfullrange); printf(adcresolution: %dbit , (g_lpadccommandconfigstruct.conversionresolutionmode == klpadc_conversionresolutionstandard)?(12):(16)); systick_config(0xffffff); int tick; printf(please press any key to trigger the conversion. ); 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) , time:, tick, (1 / (float)tick)*adc_dma_size); int i; adc_sum = 0; adc_sum_sqrt = 0; for(i=0; i> g_lpadcresultshift); adc_sum += (float)adc_result[i]; adc_sum_sqrt += (adc_result[i]*adc_result[i]); // printf(adc[%d]:%d , i, adc_result[i]); } // printf(sum:%.2f , adc_sum); // printf(ssum:%.2f , 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 , avg :, adc_mean); printf(%-16s%f , 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转换误差的各种因素。
关于存算融合的新型数据基础设施技术
精神航空公司承诺将订购100架空客A320neo系列飞机
苹果开启新一轮主板“革命” PCB厂商的未来在哪里?
大众汽车自建人工智能实验室
Google推出了超级强大的在线编辑器Colaboratory
LPC5516_SDK例程ADC_2Msps高速采集
继电器并联二极管的作用和电路图分析
苹果发布新专利,又一次刷新Apple Watch的操作体验
豪威科技发布了旗下一款型号为OV48C的4800万像素CMOS
led显示屏的优缺点_LED显示屏的主要功能
日本制造超越美国成功的秘诀是什么
江苏拓斯达(吴中)项目正式开工开业
昂科ic烧录器更新支持烧录BYD比亚迪半导体的8位微控制器BF7615CM32
音乐时钟DIY图解
从零开始到年赚190亿,这家半导体公司只用了5年
什么是主板ASUS插槽/AGP插槽
杜邦宣布将投资逾8000万美元在江苏省张家港市兴建全新制造基地
把握工业计算芯机遇 | 芯海科技受邀参加“2022中国工业计算机大会”
Versal GTY仿真:初始化,复位和速率变更
光电储能系统如何帮助电动车实现快充