Hello,
I need to set the ADC to catch my signal about 25 kHz. I am wondering whether and how I can go with Spark Core. What is maximum analog sample rate of Spark Core?
Thanks a lot!
The datasheet for the micro used in the Spark Core (https://github.com/spark/core/blob/master/Datasheets/ST_STM32F103CB.pdf) says the sampling rate of the A/D converter as between 0.05 MHz and 1 MHz. Does that answer you question?
Thanks for your reply!
I try to run it in the loop(), but it can not reach that rate. Maybe because the chip has to run with wifi connection. I am very appreciate if you can help me optimal solution to get the signal 25kHz.
Thanks
Hi @tn0432
You say you have a 25kHz signal, so that absolute minimum sample rate you need would be 50kS/s but some oversampling is likely to be required to get good results.
Do you have an actual sample rate selected for your signal?
Another thing to note is that the input impedance of the ADC input depends on the sampling speed selected and is lower at high speeds.
You will almost certainly have to turn off the cloud connection at least while sampling.
There was some discussion, maybe even library on PJRC’s Teensy 3 forum about using ADC with DMA. Not sure if Spark’s chip supports it, but I believe it is worth checking.
http://forum.pjrc.com/threads/25532-ADC-library-update-now-with-support-for-Teensy-3-1
Hello @bko and @harrisonhjones ,
Thanks for your reply! I take the picture of the oscilloscope of my signal. The signal is ultrasonic signal to measure distance. I have turn off cloud connection as well as wifi in the code. I send ADC value to my laptop to check through serial connection. It does not work. Do you have any idea? Maybe I miss somewhere. Or can I increase the speed of void loop()?. Thanks a lot! I am very appreciated for your help.
[CODE] #include “application.h”
#define ADC1_DR_Address ((uint32_t) 0x4001244c)
uint16_t ADC_ConvertedValue;
void setup()
{
Spark.disconnect();
WiFi.off();
GPIO_InitTypeDef GPIO_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
Serial.begin(9600);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
DMA_DeInit(DMA1_Channel1);
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) &ADC_ConvertedValue;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = 1;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
DMA_Cmd(DMA1_Channel1, ENABLE);
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_1Cycles5);
ADC_DMACmd(ADC1, ENABLE);
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while (ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while (ADC_GetCalibrationStatus(ADC1));
while (!Serial.available());
}
void loop()
{
// long start = micros();
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while (!DMA_GetFlagStatus(DMA1_FLAG_TC1));
DMA_ClearFlag(DMA1_FLAG_TC1);
// long end = micros();
Serial.println(ADC_ConvertedValue);
//Serial.print(" micros ");
//Serial.println(end - start);
//delay(1000);
}[/CODE]
I would be willing to wager that Serial.println is slowing down your loop immensely.
What about using millis() and an interval to only send out data every 100ms or so?
Hi @harrisonhjones,
I do not really get your idea. Can you clarify? Thanks
I would try storing 1024 data points in an array and then printing them after the array is full. This will be like a sampling oscilloscope and will take the serial port out of the critical section.
something like this: (psudeo code)
void loop() {
doADCStuff();
saveADCData();
if(millis() - lastTime > interval)
{
printADCDataToSerial();
lastTime = millis();
}
}
Hello,
Done! Problem solve!
Here is my code, if anyone interested
[CODE]#include “application.h”
#define ADC1_DR_Address ((uint32_t) 0x4001244c)
uint16_t ADC_ConvertedValue;
int myArray[1024];
int lastTime = 0;
void setup()
{
//Spark.disconnect();
//WiFi.off();
GPIO_InitTypeDef GPIO_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
Serial.begin(9600);
//enable ADC1 clock
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
DMA_DeInit(DMA1_Channel1);
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) &ADC_ConvertedValue;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = 1;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
DMA_Cmd(DMA1_Channel1, ENABLE);
//ADC1 configuration
//select independent conversion mode (single)
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
//We will convert single channel only
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
//We will convert one time
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
//Select no external triggering
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
//Right 12-bit data alignment in ADC data register
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
//Single channel conversion
ADC_InitStructure.ADC_NbrOfChannel = 1;
//Load structure values to control and status registers
ADC_Init(ADC1, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_1Cycles5); // max 150KHz ---? 1Mhz?
ADC_DMACmd(ADC1, ENABLE);
//Enable ADC1
ADC_Cmd(ADC1, ENABLE);
//Enable ADC1 reset calibration register
ADC_ResetCalibration(ADC1);
//Check the end of ADC1 reset calibration register
while (ADC_GetResetCalibrationStatus(ADC1));
//Start ADC1 calibration
ADC_StartCalibration(ADC1);
//Check the end of ADC1 calibration
while (ADC_GetCalibrationStatus(ADC1));
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
//while (!Serial.available());
pinMode(D0, OUTPUT);
pinMode(D1, OUTPUT);
digitalWrite(D0, HIGH); // Turn ON the LED
}
void loop()
{
//for (int i=0; i<1024; i++)
//{
int threadhold = 1250;
while(1)
{
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while (!DMA_GetFlagStatus(DMA1_FLAG_TC1));
DMA_ClearFlag(DMA1_FLAG_TC1);
if(ADC_ConvertedValue<threadhold)
{
break;
}
}
for(int i=0; i<1024; i++)
{
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while (!DMA_GetFlagStatus(DMA1_FLAG_TC1));
DMA_ClearFlag(DMA1_FLAG_TC1);
myArray[i]=ADC_ConvertedValue;
}
for (int i=0; i<1024; i++)
{
Serial.println(myArray[i]);
}
}[/CODE]
Hello @bko and @harrisonhjones,
Thank you very much for helping me!
The next step, I will try with 8 ADC channels to detect the same signals. Hopefully, it is good enough to do this. Do you have any ideas or sample codes to do that with 8 ADC channels?
Thanks
Glad you got it working - I need to sample a 19kHz signal and your code looks to be a good starting point! I see from the code that you posted that you have the cloud disconnection stuff commented out…does that mean that you were able to achieve the sample rate you wanted but leave the spark core connected to the cloud?
Hi @405nm
As you can see in the code, the chip will run the loop() and after that send data to the cloud and run the loop(). In the loop(), we set condition to detect and to continuously sample signal in while and for functions. After finishing sampling signal, we sent data (myArray) to computer. We do that because we want to make sure the chip does not run anything else during sampling. So the connection or disconnection with cloud is not important.
Ah, ok. Do you know if the Core’s background wifi keep-alive stuff interferes with a constant sampling rate?
In the 0.4.0 firmware (available now on the photon, and in a few weeks on the Core) the loop iteration rate is over 200kHz, although there may be some jitter.
For a sample application, it would be best to use a timer interrupt to trigger regular sample taking, and then store the sample in a buffer for processing by the main loop.
I can get this code to compile for the Spark, but not the Photon…it doesnt know what a bunch of the RCC_* calls are. Any ideas?