I am using EFR32MG21 board and Gecko SDK Suite version 3.1.1. For my application I need to use ADC for 10 seconds. For this purpose I am using an example code provided by Silicon Labs: https://github.com/SiliconLabs/peripheral_examples/tree/master/series2/iadc/iadc_single_low_current . In this code I have done a few changes in main() function and LDMAIRQ_Handler() which I have written below. I am printing values on tera term as shown in code. I am getting output correctly (when I have connected input pin to high level, I am getting adc values around 4080 corresponding to 3.3V and same for logic 0). But the problem that I have is, the adc conversion happens instantly and hence the values are also printed almost instantly (or around 1 sec). However, the code should collect 100 samples/sec and it needs to collect 1024 sampes, which means about 10 sec. Then why doesn't it takes 10 seconds to complete conversions, why does it happen almost instantly??
#include "em_device.h"
#include "em_chip.h"
#include "em_cmu.h"
#include "em_emu.h"
#include "em_rtcc.h"
#include "em_iadc.h"
#include "em_ldma.h"
#include "em_gpio.h"
#include "em_usart.h"
#include "bsp.h"
#include "retargetserial.h"
#include "stdio.h"
#define HAL_VCOM_ENABLE (1)
/*******************************************************************************
******************************* DEFINES ***********************************
******************************************************************************/
// Use specified LDMA channel
#define IADC_LDMA_CH 0
// How many samples to capture
#define NUM_SAMPLES 1024
// Set HFRCOEM23 to lowest frequency (1 MHz)
//#define HFRCOEM23_FREQ cmuHFRCOEM23Freq_1M0Hz
// Set CLK_ADC to 1 MHz (set to max for shortest IADC conversion/power-up time)
#define CLK_SRC_ADC_FREQ 1000000 // CLK_SRC_ADC
#define CLK_ADC_FREQ 1000000 // CLK_ADC
// Set IADC timer cycles
#define TIMER_CYCLES 10000 // 10000 => 100 samples/second
// 1000 => 1000 samples/second
// 200 => 5000 samples/second
// 100 => 10000 samples/second
// 40 => 25000 samples/second
/*
* Specify the IADC input using the IADC_PosInput_t typedef. This
* must be paired with a corresponding macro definition that allocates
* the corresponding ABUS to the IADC. These are...
*
* GPIO->ABUSALLOC |= GPIO_ABUSALLOC_AEVEN0_ADC0
* GPIO->ABUSALLOC |= GPIO_ABUSALLOC_AODD0_ADC0
* GPIO->BBUSALLOC |= GPIO_BBUSALLOC_BEVEN0_ADC0
* GPIO->BBUSALLOC |= GPIO_BBUSALLOC_BODD0_ADC0
* GPIO->CDBUSALLOC |= GPIO_CDBUSALLOC_CDEVEN0_ADC0
* GPIO->CDBUSALLOC |= GPIO_CDBUSALLOC_CDODD0_ADC0
*
* ...for port A, port B, and port C/D pins, even and odd, respectively.
*/
#define IADC_INPUT iadcPosInputPortAPin6 // EXP header pin 14
#define ALLOCATE_ABUS (GPIO->ABUSALLOC |= GPIO_ABUSALLOC_AEVEN0_ADC0)
/*******************************************************************************
*************************** GLOBAL VARIABLES *******************************
******************************************************************************/
// LDMA link descriptor
LDMA_Descriptor_t descriptor;
// Buffer for IADC samples
uint32_t singleBuffer[NUM_SAMPLES]={0};
/**************************************************************************//**
* @brief GPIO Initializer
*****************************************************************************/
void initGPIO (void)
{
// Configure LED0 as output; will be set when LDMA completes transfers
GPIO_PinModeSet(BSP_GPIO_LED0_PORT, BSP_GPIO_LED0_PIN, gpioModePushPull, 0);
}
/**************************************************************************//**
* @brief IADC Initializer
*****************************************************************************/
void initIADC (void)
{
// Declare init structs
IADC_Init_t init = IADC_INIT_DEFAULT;
IADC_AllConfigs_t initAllConfigs = IADC_ALLCONFIGS_DEFAULT;
IADC_InitSingle_t initSingle = IADC_INITSINGLE_DEFAULT;
IADC_SingleInput_t initSingleInput = IADC_SINGLEINPUT_DEFAULT;
// Reset IADC to reset configuration in case it has been modified
IADC_reset(IADC0);
// Configure IADC clock source for use while in EM2
//CMU_ClockSelectSet(cmuClock_IADCCLK, cmuSelect_HFRCOEM23); // 1MHz
// Modify init structs and initialize
init.warmup = iadcWarmupNormal;
// Set the HFSCLK prescale value here
init.srcClkPrescale = IADC_calcSrcClkPrescale(IADC0, CLK_SRC_ADC_FREQ, 0);
// Set timer cycles to configure sampling rate
init.timerCycles = TIMER_CYCLES;
// Configuration 0 is used by both scan and single conversions by default
// Use unbuffered AVDD as reference
initAllConfigs.configs[0].reference = iadcCfgReferenceVddx;
// Divides CLK_SRC_ADC to set the CLK_ADC frequency
// Default oversampling (OSR) is 2x, and Conversion Time = ((4 * OSR) + 2) / fCLK_ADC
initAllConfigs.configs[0].adcClkPrescale = IADC_calcAdcClkPrescale(IADC0,
CLK_ADC_FREQ,
0,
iadcCfgModeNormal,
init.srcClkPrescale);
// Single initialization
initSingle.triggerSelect = iadcTriggerSelTimer;
initSingle.dataValidLevel = _IADC_SINGLEFIFOCFG_DVL_VALID1;
// Enable triggering of single conversion
initSingle.start = true;
// Set to run in EM2
//initSingle.fifoDmaWakeup = true;
// Configure Input sources for single ended conversion
initSingleInput.posInput = IADC_INPUT;
initSingleInput.negInput = iadcNegInputGnd;
// Initialize IADC
IADC_init(IADC0, &init, &initAllConfigs);
// Allocate the corresponding ABUS to the IADC
ALLOCATE_ABUS;
// Initialize Single
IADC_initSingle(IADC0, &initSingle, &initSingleInput);
}
/**************************************************************************//**
* @brief
* IADC Initializer
*
* @param[in] buffer
* pointer to the array where ADC data will be stored.
* @param[in] size
* size of the array
*****************************************************************************/
void initLDMA(uint32_t *buffer, uint32_t size)
{
LDMA_Init_t init = LDMA_INIT_DEFAULT;
// Configure LDMA for transfer from IADC to memory
// LDMA will loop continuously
LDMA_TransferCfg_t transferCfg =
LDMA_TRANSFER_CFG_PERIPHERAL(ldmaPeripheralSignal_IADC0_IADC_SINGLE);
// Set up descriptors for dual buffer transfer
descriptor = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_LINKREL_P2M_WORD(&IADC0->SINGLEFIFODATA, buffer, size, 1);
// One loop of NUM_SAMPLES, then complete
descriptor.xfer.decLoopCnt = 1;
descriptor.xfer.xferCnt = NUM_SAMPLES;
// Interrupt upon transfer complete
descriptor.xfer.doneIfs = 1;
descriptor.xfer.ignoreSrec = 0;
// Initialize LDMA with default configuration
LDMA_Init(&init);
// Start transfer, LDMA will sample the IADC NUM_SAMPLES time, and then interrupt
LDMA_StartTransfer(IADC_LDMA_CH, &transferCfg, &descriptor);
}
/**************************************************************************//**
* @brief LDMA Handler
*****************************************************************************/
void LDMA_IRQHandler(void)
{
// Clear interrupt flags
LDMA_IntClear(LDMA_IF_DONE0);
// Stop the IADC
IADC_command(IADC0, iadcCmdStopSingle);
for(int i=0;i<NUM_SAMPLES;i++){
printf("\nsample[%d]: %ld\n", i,singleBuffer[i]);
}
// Turn on LED0 to notify that transfers are complete
GPIO_PinOutToggle(BSP_GPIO_LED0_PORT, BSP_GPIO_LED0_PIN);
}
/**************************************************************************//**
* @brief Main function
*****************************************************************************/
int main(void)
{
CHIP_Init();
CMU_ClockEnable(cmuClock_GPIO, true);
initGPIO();
// Switch to running from the FSRCO
CMU_ClockSelectSet(cmuClock_SYSCLK, cmuSelect_FSRCO);
RETARGET_SerialInit();
printf("gagan\n");
initIADC();
initLDMA(singleBuffer, NUM_SAMPLES);
IADC_command(IADC0, iadcCmdEnableTimer);
while (1);
}
Discussion Forums
32-bit MCUs
Answered
Answered
You have another thread to address this use scenario:
I am using EFR32MG21 board and Gecko SDK Suite version 3.1.1. For my application I need to use ADC for 10 seconds. For this purpose I am using an example code provided by Silicon Labs: https://github.com/SiliconLabs/peripheral_examples/tree/master/series2/iadc/iadc_single_low_current .
>> Please elaborate what do you mean by "I need to use ADC for 10 seconds".
In this code I have done a few changes in main() function and LDMAIRQ_Handler() which I have written below. I am printing values on tera term as shown in code. I am getting output correctly (when I have connected input pin to high level, I am getting adc values around 4080 corresponding to 3.3V and same for logic 0).
>> Fine.
But the problem that I have is, the adc conversion happens instantly and hence the values are also printed almost instantly (or around 1 sec).
>> Sorry, I could not catch your point. please elaborate.
However, the code should collect 100 samples/sec and it needs to collect 1024 samples, which means about 10 sec.
>>
a. I guess you need adc sample speed to be 100 sampler per second.
b. I guess you meant to only sample 1024 samples and stop the ADC.
Then why doesn't it takes 10 seconds to complete conversions, why does it happen almost instantly?
it was not recommended to put printf in the ISR, especially you need to print some many samples data.
Then measure the time consumed to capture the 1024 adc data (via GPIO toggle).
My test result:
Correct Answer
0
Dear Delu,
Thank you for the reply and sorry for the confusion. I didn't use the previous thread because the problem is different even though the topic is same. I have explained below the confusing parts:
>> Please elaborate what do you mean by "I need to use ADC for 10 seconds".
I mean, I want ADC conversions to happen for 10.24 sec only and then ADC should stop.
>> Sorry, I could not catch your point. please elaborate.
According to the code, ADC conversions should only happen for 10.24 sec and then the ADC should stop and LED should toggle after 10.24 seconds. But when I am debugging this code, I receive the printf statements and LED is toggled after only about half a second after I press run. But in fact LED should be toggled/ADC conversions should stop after 10 seconds. Why does ADC conversions not happen for 10.24 seconds?
>>
a. I guess you need adc sample speed to be 100 sampler per second: Yes, and this is already defined in the code, I don't want to change it.
b. I guess you meant to only sample 1024 samples and stop the ADC: Yes and this is also already defined in code.
>> Then measure the time consumed to capture the 1024 adc data (via GPIO toggle): As, suggested, I removed the printf statements and I ran the code. Still after only 1 sec, the LEDs were toggled. But infact it should toggle after 10 seconds.
>> My plan is to get those ADC values and try to plot them in an excel sheet, to reconstruct and to verify if I am getting the correct output or not. I am not getting values if I print the values of buffer in main code after "IADC_command(IADC0, iadcCmdEnableTimer);" line. I think the values of buffer are getting erased. Could you suggest on how do I get those values then, if I shouldn't put them in the Interrupt handler?
0
>> Please elaborate what do you mean by "I need to use ADC for 10 seconds".
I mean, I want ADC conversions to happen for 10.24 sec only and then ADC should stop.
[Denver], current code do exactly what you need.
>> Sorry, I could not catch your point. please elaborate.
According to the code, ADC conversions should only happen for 10.24 sec and then the ADC should stop and LED should toggle after 10.24 seconds.
But when I am debugging this code, I receive the printf statements and LED is toggled after only about half a second after I press run.
But in fact LED should be toggled (ADC conversions should stop) after 10 seconds. Why does ADC conversions not happen for 10.24 seconds?
[Denver], Your measurement don't make sense. try to use a scope to measure the time.
>>
a. I guess you need adc sample speed to be 100 sampler per second: Yes, and this is already defined in the code, I don't want to change it.
b. I guess you meant to only sample 1024 samples and stop the ADC: Yes and this is also already defined in code.
>> Then measure the time consumed to capture the 1024 adc data (via GPIO toggle): As, suggested, I removed the printf statements and I ran the code. Still after only 1 sec, the LEDs were toggled. But in fact it should toggle after 10 seconds.
[Denver], your measurement result don't make sense. try to use a scope to measure the time consumed.
>> My plan is to get those ADC values and try to plot them in an excel sheet, to reconstruct and to verify if I am getting the correct output or not. I am not getting values if I print the values of buffer in main code after "IADC_command(IADC0, iadcCmdEnableTimer);" line.
[Denver]. Let us focus on the the 10.24 seconds issue first.
There are lot of method to dump the ADC data from the buffer, but that is a different topic.
I think the values of buffer are getting erased. Could you suggest on how do I get those values then, if I shouldn't put them in the Interrupt handler?
[Denver]. let us try to see if 1024 adc samples need 10 seconds to finish capture first.
0
Dear Delu,
Yes, I can see that the oscilloscope shows 10.20 sec. But, practically it doesn't make any sense. To explain what I mean, I have attached a short video of my result. Here you can see that LED gets toggled almost instantly but it should wait for atleast 10 sec.
Further, I got to know that by default, 2x oversampling is enabled. So, my guess is that by using the formula "Conversion Time = ((4 * OSR) + 2) / fCLK_ADC", the conversion time turns out to be 10 microseconds/conversion which gives me 10us*1024=0.012 sec and hence, It doesn't wait for 10 seconds. So, basically it samples for 0.012 seconds only.
Now, to sample for 10.24 complete seconds,
(1) I wanted to disable oversampling. But I couldn't find any register to disable it.
(2) Another option that I thought of was to use that formula to get 10 sec time and 2x oversampling, which would give me fCLK_ADC as 1 Hz, which I think is ridiculous.
Can you shed some light over this? Thanks
I think I am unable to add video here.
0
I noticed you have created a support case, let us continue using the support to address this issue.
0
I have attached the snapshot from the oscilloscope. I have connected LED0 (PB00) with oscilloscope to measure and that measurement is 2.28 sec (measured exact time using oscilloscope).In my code, LED0 is toggled after LDMA transfers are done. So this basically shows that the time for conversions is much less than 10 sec as far as I can see. Any help please.
ADC not taking samples for defined time
Hi,
I am using EFR32MG21 board and Gecko SDK Suite version 3.1.1. For my application I need to use ADC for 10 seconds. For this purpose I am using an example code provided by Silicon Labs: https://github.com/SiliconLabs/peripheral_examples/tree/master/series2/iadc/iadc_single_low_current . In this code I have done a few changes in main() function and LDMAIRQ_Handler() which I have written below. I am printing values on tera term as shown in code. I am getting output correctly (when I have connected input pin to high level, I am getting adc values around 4080 corresponding to 3.3V and same for logic 0). But the problem that I have is, the adc conversion happens instantly and hence the values are also printed almost instantly (or around 1 sec). However, the code should collect 100 samples/sec and it needs to collect 1024 sampes, which means about 10 sec. Then why doesn't it takes 10 seconds to complete conversions, why does it happen almost instantly??
You have another thread to address this use scenario:
https://www.silabs.com/community/mcu/32-bit/forum.topic.html/iadc_for_few_seconds-c9h3
I am using EFR32MG21 board and Gecko SDK Suite version 3.1.1. For my application I need to use ADC for 10 seconds. For this purpose I am using an example code provided by Silicon Labs: https://github.com/SiliconLabs/peripheral_examples/tree/master/series2/iadc/iadc_single_low_current .
>> Please elaborate what do you mean by "I need to use ADC for 10 seconds".
In this code I have done a few changes in main() function and LDMAIRQ_Handler() which I have written below. I am printing values on tera term as shown in code. I am getting output correctly (when I have connected input pin to high level, I am getting adc values around 4080 corresponding to 3.3V and same for logic 0).
>> Fine.
But the problem that I have is, the adc conversion happens instantly and hence the values are also printed almost instantly (or around 1 sec).
>> Sorry, I could not catch your point. please elaborate.
However, the code should collect 100 samples/sec and it needs to collect 1024 samples, which means about 10 sec.
>>
a. I guess you need adc sample speed to be 100 sampler per second.
b. I guess you meant to only sample 1024 samples and stop the ADC.
Then why doesn't it takes 10 seconds to complete conversions, why does it happen almost instantly?
>>
1024 (total samples) / 100 (sample per second) = 10.24 seconds.
Suggestion:
Please remove the printf in the ISR handler.
it was not recommended to put printf in the ISR, especially you need to print some many samples data.
Then measure the time consumed to capture the 1024 adc data (via GPIO toggle).
My test result:
Dear Delu,
Thank you for the reply and sorry for the confusion. I didn't use the previous thread because the problem is different even though the topic is same. I have explained below the confusing parts:
>> Please elaborate what do you mean by "I need to use ADC for 10 seconds".
I mean, I want ADC conversions to happen for 10.24 sec only and then ADC should stop.
>> Sorry, I could not catch your point. please elaborate.
According to the code, ADC conversions should only happen for 10.24 sec and then the ADC should stop and LED should toggle after 10.24 seconds. But when I am debugging this code, I receive the printf statements and LED is toggled after only about half a second after I press run. But in fact LED should be toggled/ADC conversions should stop after 10 seconds. Why does ADC conversions not happen for 10.24 seconds?
>>
a. I guess you need adc sample speed to be 100 sampler per second: Yes, and this is already defined in the code, I don't want to change it.
b. I guess you meant to only sample 1024 samples and stop the ADC: Yes and this is also already defined in code.
>> Then measure the time consumed to capture the 1024 adc data (via GPIO toggle): As, suggested, I removed the printf statements and I ran the code. Still after only 1 sec, the LEDs were toggled. But infact it should toggle after 10 seconds.
>> My plan is to get those ADC values and try to plot them in an excel sheet, to reconstruct and to verify if I am getting the correct output or not. I am not getting values if I print the values of buffer in main code after "IADC_command(IADC0, iadcCmdEnableTimer);" line. I think the values of buffer are getting erased. Could you suggest on how do I get those values then, if I shouldn't put them in the Interrupt handler?
>> Please elaborate what do you mean by "I need to use ADC for 10 seconds".
I mean, I want ADC conversions to happen for 10.24 sec only and then ADC should stop.
[Denver], current code do exactly what you need.
>> Sorry, I could not catch your point. please elaborate.
According to the code, ADC conversions should only happen for 10.24 sec and then the ADC should stop and LED should toggle after 10.24 seconds.
But when I am debugging this code, I receive the printf statements and LED is toggled after only about half a second after I press run.
But in fact LED should be toggled (ADC conversions should stop) after 10 seconds. Why does ADC conversions not happen for 10.24 seconds?
[Denver], Your measurement don't make sense. try to use a scope to measure the time.
>>
a. I guess you need adc sample speed to be 100 sampler per second: Yes, and this is already defined in the code, I don't want to change it.
b. I guess you meant to only sample 1024 samples and stop the ADC: Yes and this is also already defined in code.
>> Then measure the time consumed to capture the 1024 adc data (via GPIO toggle): As, suggested, I removed the printf statements and I ran the code. Still after only 1 sec, the LEDs were toggled. But in fact it should toggle after 10 seconds.
[Denver], your measurement result don't make sense. try to use a scope to measure the time consumed.
>> My plan is to get those ADC values and try to plot them in an excel sheet, to reconstruct and to verify if I am getting the correct output or not. I am not getting values if I print the values of buffer in main code after "IADC_command(IADC0, iadcCmdEnableTimer);" line.
[Denver]. Let us focus on the the 10.24 seconds issue first.
There are lot of method to dump the ADC data from the buffer, but that is a different topic.
I think the values of buffer are getting erased. Could you suggest on how do I get those values then, if I shouldn't put them in the Interrupt handler?
[Denver]. let us try to see if 1024 adc samples need 10 seconds to finish capture first.
Dear Delu,
Yes, I can see that the oscilloscope shows 10.20 sec. But, practically it doesn't make any sense. To explain what I mean, I have attached a short video of my result. Here you can see that LED gets toggled almost instantly but it should wait for atleast 10 sec.
Further, I got to know that by default, 2x oversampling is enabled. So, my guess is that by using the formula "Conversion Time = ((4 * OSR) + 2) / fCLK_ADC", the conversion time turns out to be 10 microseconds/conversion which gives me 10us*1024=0.012 sec and hence, It doesn't wait for 10 seconds. So, basically it samples for 0.012 seconds only.
Now, to sample for 10.24 complete seconds,
(1) I wanted to disable oversampling. But I couldn't find any register to disable it.
(2) Another option that I thought of was to use that formula to get 10 sec time and 2x oversampling, which would give me fCLK_ADC as 1 Hz, which I think is ridiculous.
Can you shed some light over this? Thanks
I think I am unable to add video here.
according the support case, you have made some change on the code.
let's continue using the support case to address the issue.
if you got conclusion /cause of the issue, you could consider to put it here.