Skip to main content
Arduino

Return to Answer

Added missing constants.
Source Link
Edgar Bonet
  • 45.1k
  • 4
  • 42
  • 81
const int SENSOR_PIN = A0;
const int SAMPLE_COUNT = 8000;
const uint32_t SAMPLE_PERIOD = 125; // 125 us

void loop()
{
 long sum = 0;
 int lowest = INT_MAX, highest = 0;
 uint32_t lastTime = micros();
 for (int i = 0; i < SAMPLE_COUNT; i++) {
 // Wait until it's time.
 while (micros() - lastTime < SAMPLE_PERIOD) ;
 lastTime += SAMPLE_PERIOD;
 // Take and process the sample.
 int sensorValue = analogRead(SENSOR_PIN);
 sum += sensorValue;
 highest = max(highest, sensorValue);
 lowest = min(lowest, sensorValue);
 }
 uint32_t delay = micros() - lastTime;
 Serial.print("delay: "); Serial.print(delay); Serial.print(", ");
 float mean = (float) sum / SAMPLE_COUNT;
 Serial.print("mean: "); Serial.print(mean);
 Serial.print(", minimum: "); Serial.print(lowest);
 Serial.print(", maximum: "); Serial.println(highest);
 Serial.flush();
}
const int SENSOR_PIN = A0;
const int SAMPLE_COUNT = 8000;
void loop()
{
 long sum = 0;
 int lowest = INT_MAX, highest = 0;
 uint32_t lastTime = micros();
 for (int i = 0; i < SAMPLE_COUNT; i++) {
 // Wait until it's time.
 while (micros() - lastTime < SAMPLE_PERIOD) ;
 lastTime += SAMPLE_PERIOD;
 // Take and process the sample.
 int sensorValue = analogRead(SENSOR_PIN);
 sum += sensorValue;
 highest = max(highest, sensorValue);
 lowest = min(lowest, sensorValue);
 }
 uint32_t delay = micros() - lastTime;
 Serial.print("delay: "); Serial.print(delay); Serial.print(", ");
 float mean = (float) sum / SAMPLE_COUNT;
 Serial.print("mean: "); Serial.print(mean);
 Serial.print(", minimum: "); Serial.print(lowest);
 Serial.print(", maximum: "); Serial.println(highest);
 Serial.flush();
}
const int SENSOR_PIN = A0;
const int SAMPLE_COUNT = 8000;
const uint32_t SAMPLE_PERIOD = 125; // 125 us

void loop()
{
 long sum = 0;
 int lowest = INT_MAX, highest = 0;
 uint32_t lastTime = micros();
 for (int i = 0; i < SAMPLE_COUNT; i++) {
 // Wait until it's time.
 while (micros() - lastTime < SAMPLE_PERIOD) ;
 lastTime += SAMPLE_PERIOD;
 // Take and process the sample.
 int sensorValue = analogRead(SENSOR_PIN);
 sum += sensorValue;
 highest = max(highest, sensorValue);
 lowest = min(lowest, sensorValue);
 }
 float mean = (float) sum / SAMPLE_COUNT;
 Serial.print("mean: "); Serial.print(mean);
 Serial.print(", minimum: "); Serial.print(lowest);
 Serial.print(", maximum: "); Serial.println(highest);
 Serial.flush();
}
+ notes on controlling the sampling rate.
Source Link
Edgar Bonet
  • 45.1k
  • 4
  • 42
  • 81
const int SENSOR_PIN = A0;
const int SAMPLE_COUNT = 8000;
void loop()
{
 long sum = 0;
 int lowest = INT_MAX, highest = 0;
 uint32_t lastTime = micros();
 for (int i = 0; i < SAMPLE_COUNT; i++) {
 // Wait until it's time.
 while (micros() - lastTime < SAMPLE_PERIOD) ;
 lastTime += SAMPLE_PERIOD;
 // Take and process the sample.
 int sensorValue = analogRead(SENSOR_PIN);
 sum += sensorValue;
 highest = max(highest, sensorValue);
 lowest = min(lowest, sensorValue);
 }
 uint32_t delay = micros() - lastTime;
 Serial.print("delay: "); Serial.print(delay); Serial.print(", ");
 float mean = (float) sum / SAMPLE_COUNT;
 Serial.print("mean: "); Serial.print(mean);
 Serial.print(", minimum: "); Serial.print(lowest);
 Serial.print(", maximum: "); Serial.println(highest);
 Serial.flush();
}

Edit: I changed the code in order to keep a roughly constant sampling rate of 8000 samp/s. The main change is that, before taking a reading, we wait until it's time to do so (until micros() - lastTime >= SAMPLE_PERIOD).

Note that lastTime could be updated with lastTime = micros(), in which case it would have hold the time we last took a sample. By updating it with lastTime += SAMPLE_PERIOD it instead holds the time we shoud have taken the last sample. This is the right choice for enforcing a given average sampling rate.

Running on an Uno: I have no experience with the Due, but here are some remarks that would apply to an Uno, or any other AVR-based Arduino:

  • analogRead() takes about 110 μs, which means we cannot sample more than one channel, and we only have about 15 μs to process the sample. Here the processing is simple enough that the Arduino can do it in this limited time.
  • There is some jitter in the sampling, because micros() has a limited resolution of 4 μs, because of the timer interrupt kicking in from time to time, and because the while loop takes a finite amount of time.

If I were to do something similar on an Uno, I would forgo analogRead(), configure the ADC to be triggered by a timer, and set the timer to trigger the DC every 125 μs. Not only would this get rid of the jitter, it would also allow the CPU to do its job in parallel with the ADC, instead of just sitting idle and waiting until the ADC is done. This would allow for more complex per-sample processing.

About the Due: As I said, I have no experience with this board. It has a more capable processor, but this tells you nothing about the speed of its ADC. I would expect to face the same issues as with the Uno, though maybe to a lesser degree. I guess it should be possible to trigger the ADC from a timer, but I have no idea how to do so.

const int SENSOR_PIN = A0;
const int SAMPLE_COUNT = 8000;
void loop()
{
 long sum = 0;
 int lowest = INT_MAX, highest = 0;
 for (int i = 0; i < SAMPLE_COUNT; i++) {
 int sensorValue = analogRead(SENSOR_PIN);
 sum += sensorValue;
 highest = max(highest, sensorValue);
 lowest = min(lowest, sensorValue);
 }
 float mean = (float) sum / SAMPLE_COUNT;
 Serial.print("mean: "); Serial.print(mean);
 Serial.print(", minimum: "); Serial.print(lowest);
 Serial.print(", maximum: "); Serial.println(highest);
}
const int SENSOR_PIN = A0;
const int SAMPLE_COUNT = 8000;
void loop()
{
 long sum = 0;
 int lowest = INT_MAX, highest = 0;
 uint32_t lastTime = micros();
 for (int i = 0; i < SAMPLE_COUNT; i++) {
 // Wait until it's time.
 while (micros() - lastTime < SAMPLE_PERIOD) ;
 lastTime += SAMPLE_PERIOD;
 // Take and process the sample.
 int sensorValue = analogRead(SENSOR_PIN);
 sum += sensorValue;
 highest = max(highest, sensorValue);
 lowest = min(lowest, sensorValue);
 }
 uint32_t delay = micros() - lastTime;
 Serial.print("delay: "); Serial.print(delay); Serial.print(", ");
 float mean = (float) sum / SAMPLE_COUNT;
 Serial.print("mean: "); Serial.print(mean);
 Serial.print(", minimum: "); Serial.print(lowest);
 Serial.print(", maximum: "); Serial.println(highest);
 Serial.flush();
}

Edit: I changed the code in order to keep a roughly constant sampling rate of 8000 samp/s. The main change is that, before taking a reading, we wait until it's time to do so (until micros() - lastTime >= SAMPLE_PERIOD).

Note that lastTime could be updated with lastTime = micros(), in which case it would have hold the time we last took a sample. By updating it with lastTime += SAMPLE_PERIOD it instead holds the time we shoud have taken the last sample. This is the right choice for enforcing a given average sampling rate.

Running on an Uno: I have no experience with the Due, but here are some remarks that would apply to an Uno, or any other AVR-based Arduino:

  • analogRead() takes about 110 μs, which means we cannot sample more than one channel, and we only have about 15 μs to process the sample. Here the processing is simple enough that the Arduino can do it in this limited time.
  • There is some jitter in the sampling, because micros() has a limited resolution of 4 μs, because of the timer interrupt kicking in from time to time, and because the while loop takes a finite amount of time.

If I were to do something similar on an Uno, I would forgo analogRead(), configure the ADC to be triggered by a timer, and set the timer to trigger the DC every 125 μs. Not only would this get rid of the jitter, it would also allow the CPU to do its job in parallel with the ADC, instead of just sitting idle and waiting until the ADC is done. This would allow for more complex per-sample processing.

About the Due: As I said, I have no experience with this board. It has a more capable processor, but this tells you nothing about the speed of its ADC. I would expect to face the same issues as with the Uno, though maybe to a lesser degree. I guess it should be possible to trigger the ADC from a timer, but I have no idea how to do so.

Source Link
Edgar Bonet
  • 45.1k
  • 4
  • 42
  • 81

Just to as a complement to Majenko's answer (which I second), there are a couple of flaws in your program that may be related to your problem:

  • lowest is always zero, as it is initialized to 0 and cannot increase
  • your computation of the mean is incorrect: the mean is the sum of the readings divided by their number
  • you fail to reset lowest and highest between consecutive runs of 8000 samples
  • there is no point in putting all of loop() inside an infinite loop.

Assuming you really intend to print summary statistics on runs of 8000 samples, as your code suggests, I would write it like this:

const int SENSOR_PIN = A0;
const int SAMPLE_COUNT = 8000;
void loop()
{
 long sum = 0;
 int lowest = INT_MAX, highest = 0;
 for (int i = 0; i < SAMPLE_COUNT; i++) {
 int sensorValue = analogRead(SENSOR_PIN);
 sum += sensorValue;
 highest = max(highest, sensorValue);
 lowest = min(lowest, sensorValue);
 }
 float mean = (float) sum / SAMPLE_COUNT;
 Serial.print("mean: "); Serial.print(mean);
 Serial.print(", minimum: "); Serial.print(lowest);
 Serial.print(", maximum: "); Serial.println(highest);
}
default

AltStyle によって変換されたページ (->オリジナル) /