Analog signal sampling memory questions

U4R1A

Member
I'm working on a project where I want to sample a 100Khz 180-degree phase-shifted square wave. All I want from the square wave is the amplitude. The actual amplitude doesn't matter just that after the filtering the output is always consistent with the input. So, I have a Teensy 4.1 and have figured out the ADC sampling and some filtering that will magic the square wave into a constant value. My issue is the buffer/memory allocation. I'm not a programmer and normally cobble things together but I know what I want to do but am not sure how to execute it. Due to the averaging, I'd like to keep ~1K samples and then dump the oldest as new samples come in. Essentially a first in first out setup. I have no idea how to implement that and I'd welcome any suggestions. Thanks in advance

Cobbled together code:
#include <ADC.h>
#define testpin A9 // analog pin to be tested
#include <RunningMedian.h>
ADC *adc = new ADC();
/////////// Buffer declaration///////////////////////////////<--- This probably does nothing
#define BUFFER_SIZE (1L << 8)
uint16_t buffer[BUFFER_SIZE];
uint8_t buffer_count = BUFFER_SIZE;
////////////Averaging Setup//////////////////////////////////
const int numReadings = 100; //Number of data points to average
int readings[numReadings];
int readIndex = 0;
int total = 0;
int average = 0;
///////////////////Threshold/////////////////////////////////
const int numReadings1 = 100; //Number of data points to average
int readings1[numReadings1];
int readIndex1 = 0;
int total1 = 0;
int averageT = 0;
///////////////////Median Filter 1 Setup/////////////////////
RunningMedian samples = RunningMedian(5);
///////////////////Median Filter 2 Setup/////////////////////
RunningMedian samples1 = RunningMedian(5);
void setup() {
Serial.begin(1152000);
pinMode(testpin, INPUT);
//////////////////////ADC Setup///////////////////////////////////////////////////////////////////////////
adc->adc0->setResolution(12); // Sets Res
adc->adc0->setAveraging(1); // Causes heavy distortion if set nonzero
adc->adc0->setConversionSpeed(ADC_CONVERSION_SPEED::MED_SPEED); //Haven't noticed an advantage between MED or HIGH speed on errors
adc->adc0->setSamplingSpeed(ADC_SAMPLING_SPEED::HIGH_SPEED); // Same as above
adc->adc0->startContinuous(testpin); // starting continuous sampling
//////////////////Averaging//////////////////////////////////
for (int s = 0; s < numReadings; s++) {
readings = 0;}
/////////////////Threshold///////////////////////////////////
for (int average = 0; average < numReadings1; average++) {
readings[average] = 0;}
/////////////////////////////////////////////////////////////
}
void loop() {
test1();
}
void test1()
{
//uint16_t
float sample = adc->adc0->analogReadContinuous();
//////////////////Median Filter 1///////////////////////
samples.add(sample);
long h = samples.getHighest();
//////////////////Median Filter 2///////////////////////
samples1.add(h);
long s = samples1.getHighest();
///////////////////////////////////////////////////////

////////////////Averageing////////////////////////////
total = total - readings[readIndex];
readings[readIndex] = s;
total = total + readings[readIndex];
readIndex = readIndex + 1;
if (readIndex >= numReadings)
{readIndex = 0;
average = total / numReadings;}
///////////////////Threshold/////////////////////////////
total1 = total1 - readings1[readIndex1];
readings1[readIndex1] = average;
total1 = total1 + readings1[readIndex1];
readIndex1 = readIndex1 + 1;
if (readIndex1 >= numReadings1)
{readIndex1 = 0;
averageT = total1 / numReadings1;}
/////////////////////////////////////////////////////////
if (average > averageT*1.05);////////// Appiles the Threshold
{average=averageT;}//////////////// and creates a 10% window
if (average < averageT*1.05);////////// around the Threshold average
{average=averageT;}///////////////
///////////////////////////////////////////////////////////

Serial.print("\t");
Serial.print(sample);///origninal 100Khz signal
Serial.print("\t");
Serial.println(averageT);///Filtered and averaged signal
//
delay(1);
}
 
Code:
#include <ADC.h>
#define testpin A9 // analog pin to be tested
#include <RunningMedian.h>
ADC* adc = new ADC();
/////////// Buffer declaration///////////////////////////////<--- This probably does nothing
#define BUFFER_SIZE (1L << 8)
uint16_t buffer[BUFFER_SIZE];
uint8_t buffer_count = BUFFER_SIZE;
////////////Averaging Setup//////////////////////////////////
const int numReadings = 100; //Number of data points to average
int readings[numReadings];
int readIndex = 0;
int total = 0;
int average = 0;
///////////////////Threshold/////////////////////////////////
const int numReadings1 = 100; //Number of data points to average
int readings1[numReadings1];
int readIndex1 = 0;
int total1 = 0;
int averageT = 0;
///////////////////Median Filter 1 Setup/////////////////////
RunningMedian samples = RunningMedian(5);
///////////////////Median Filter 2 Setup/////////////////////
RunningMedian samples1 = RunningMedian(5);
void setup() {
	Serial.begin(1152000);
	pinMode(testpin, INPUT);
	//////////////////////ADC Setup///////////////////////////////////////////////////////////////////////////
	adc->adc0->setResolution(12); // Sets Res
	adc->adc0->setAveraging(1); // Causes heavy distortion if set nonzero
	adc->adc0->setConversionSpeed(ADC_CONVERSION_SPEED::MED_SPEE D); //Haven't noticed an advantage between MED or HIGH speed on errors
	adc->adc0->setSamplingSpeed(ADC_SAMPLING_SPEED::HIGH_SPEED); // Same as above
	adc->adc0->startContinuous(testpin); // starting continuous sampling
	//////////////////Averaging//////////////////////////////////
	for (int s = 0; s < numReadings; s++) {
		readings[s] = 0;
	}
	/////////////////Threshold///////////////////////////////////
	for (int average = 0; average < numReadings1; average++) {
		readings[average] = 0;
	}
	/////////////////////////////////////////////////////////////
}
void loop() {
	test1();
}
void test1()
{
	//uint16_t
	float sample = adc->adc0->analogReadContinuous();
	//////////////////Median Filter 1///////////////////////
	samples.add(sample);
	long h = samples.getHighest();
	//////////////////Median Filter 2///////////////////////
	samples1.add(h);
	long s = samples1.getHighest();
	///////////////////////////////////////////////////////

	////////////////Averageing////////////////////////////
	total = total - readings[readIndex];
	readings[readIndex] = s;
	total = total + readings[readIndex];
	readIndex = readIndex + 1;
	if (readIndex >= numReadings)
	{
		readIndex = 0;
		average = total / numReadings;
	}
	///////////////////Threshold/////////////////////////////
	total1 = total1 - readings1[readIndex1];
	readings1[readIndex1] = average;
	total1 = total1 + readings1[readIndex1];
	readIndex1 = readIndex1 + 1;
	if (readIndex1 >= numReadings1)
	{
		readIndex1 = 0;
		averageT = total1 / numReadings1;
	}
	/////////////////////////////////////////////////////////
	if (average > averageT * 1.05);////////// Appiles the Threshold
	{average = averageT; }//////////////// and creates a 10% window
	if (average < averageT * 1.05);////////// around the Threshold average
	{average = averageT; }///////////////
	///////////////////////////////////////////////////////////

	Serial.print("\t");
	Serial.print(sample);///origninal 100Khz signal
	Serial.print("\t");
	Serial.println(averageT);///Filtered and averaged signal
	//
	delay(1);
}
Can I request that it future you enclose any code between CODE tags using the # button.
As I am sure you can see it makes your code so much more readable and therefore understandable
for someone who might be able to help you.
 
Thanks for your reply. It ended up not being a problem. I wasn't familiar with the Arduino platform and assumed when the serial plotter locked up the MC locked up. Not the case. However, if you google how an ADC works you will see that most ADCs already have the RC integrator (Low pass filter built-in). Naturally, you can put on another to adjust the time constant. Since I mentioned that I was measuring square waves using an RC integrator would cause a duty cycle dependence in the output. That method is more suitable for continuous analog signals.
 
A true square wave (you did not mention any duty cycle) is : (surprise) fixed square. 50%.
An ADC has an integrator, with parameters that are good for it's intended sampling frequency, not your square wave.
A low pass filter will give you your "continous" signal.

All I want from the square wave is the amplitude.

But just have fun. Why do you ask(??)
 
Thanks for your reply. A square wave is a class of waveforms that has several variables and one is duty cycle. Please google function generator for reference. Yes, the lowpass filter(RC integrator) will output a continuous signal but based upon the time constant of that circuit the input duty cycle of the squarewave(surprise) that might not be 50% will affect the output. As always I appreciate your feedback but just have fun.
 
Just listen. You are a new user. Your question does not indicate how much experience you have. There are beginners (as in every forum) who have never heard of a lowpass. And your question reads like this.

And no, the word "duty" does not appear in your post. "Amplitude," on the other hand, does.

Again, from your post:
All I want from the square wave is the amplitude
As you wrote later, obviously, both seem to be variable (or, not?). Amplitude and duty cyle. That's unusual, but if you say it does...ok, so it does. I don't know why you then want ONE value as output - but ok, accepted :) I'm not questioning this now.

For fun, here's a screenshot. Since you know everything about Google (other than about ADCs), surely a screenshot from Wikipedia will do. You can find the link yourself.

2022-05-10 19_40_15-Square wave - Wikipedia.png

That should be enough now.
Have fun :)
 
Bro, you seem to be fixated on the definition of a "true" squarewave. I enjoy the fact that you selectively cropped that picture from wiki to prove your point.
squarewave.PNG
Here is the full screenshot. It clearly talks about duty cycle. It is not a hard concept. Thus far you seemed to be very touched by the fact I have no interest in your low pass filter. Here is what my question was" Due to the averaging, I'd like to keep ~1K samples and then dump the oldest as new samples come in. Essentially a first in first out setup." For the love of god please show where I asked about a low pass filter option. Now this should be enough and have fun.
 
OOh, next time i have to quote the whole article? ehh, no , sorry.
Yes, the article mentions - in contrast to you - a duty cycle

EOD.
 
It is fairly sad that your obvious attempt to help was such a trash idea that you've been forced time and time again to defend your lack of critical reading skills. While your help and banter in this thread hasn't been useful it does remind me that petty people do seem to lurk in all hobbies. Since, I stated in my first reply that the issue was resolved... I think we can both walk away from this thread as a learning experience. Ohh and have fun
 
The OP posted a lot of code that has nothing to do with sampling and buffering the data from the ADC. It doesn't provide us with the algorithm for the median filters or tell us anything about the characteristics of the square wave being sampled other than that it is about 100KHz. Without information about the amplitude, ringing and noise levels, all the code about median filters, thresholds, etc. etc. is just a distraction.

To design an efficient buffering system we need to know a few more details:

1. How often do you get a measurement of the amplitude?
2. Do you want to store each measured value in the buffer, or just one value every millisecond?
3. What do you want to do with the buffered samples? How you store and use the samples can be quite different if you want to send a series of lines to the serial port, or plan to write the buffer to the SD card. For instance, writing to the SD card causes noise in the ADC results on a T4.1. Writing to the serial port can also cause noise, but not as much. If this is the case, you don't want to be sampling in the background while you are writing or displaying the results.
4. Is the ordering of the saved or displayed samples important? If it is not, writing or displaying the samples can be much simpler and more efficient.

Here is a simple program that illustrates the use of a circular buffer with simulated ADC data:

Code:
// Simple illustration of circular buffer storage and replay
#include <ADC.h>
#define testpin A9 // analog pin to be tested

ADC* adc = new ADC();

#define BUFFERSIZE 256  // Can be any value up to 65536 for uint16_t index

uint16_t bufferindex = 0;  // spot in array to store NEXT sample
uint16_t adcbuffer[BUFFERSIZE];

// set up the ADC and whatever else you need to collect amplitude values
void setup(void){
  delay(2000);  // wait for serial port to open
  Serial.begin(9600); // for USB serial, parameter value doesn't control baud rate
  pinMode(testpin, INPUT_DISABLE); // shut off pin-keeper resistors
  //////////////////////ADC Setup///////////////////////////////
  adc->adc0->setResolution(12); // Sets Res
  adc->adc0->setAveraging(1); // Causes heavy distortion if set nonzero
  adc->adc0->setConversionSpeed(ADC_CONVERSION_SPEED::MED_SPEED); //Haven't noticed an advantage between MED or HIGH speed on errors
  adc->adc0->setSamplingSpeed(ADC_SAMPLING_SPEED::HIGH_SPEED); // Same as above
  adc->adc0->startContinuous(testpin); // starting continuous sampling  
  // etc, etc.  
}

  // Collect a sample once per millisecond in foreground loop.
  // Playback the most recently collected samples when a 'p' key is
  // read.  (may need 'p' and <return> in serial monitor)
  // Samples are collected in foreground, so none are collected while
  // display is in progress.
void loop(void){
uint16_t ampsample;
char ch;
  ampsample = SimulatedAmplitude(); // I have no idea how long this takes for a real sample
  AddSample(ampsample);  // put the sample in the buffer
  
  if(Serial.available()){ // has the user pressed a key in the monitor
    ch = Serial.read();
    if(ch == 'p') PlaybackBuffer();  // was the key 'p'?
  }
  delay(1);   // collect and store at about 1KHz
}


// Calculate one simulated amplitude value
uint16_t SimulatedAmplitude(void){
  return 1000 + (random()%5); // about 3/4 Volt pk-pk with 5 counts noise
}


// Store the input sample in the circular buffer
void AddSample(uint16_t ampval){
  adcbuffer[bufferindex] = ampval;
  bufferindex++;
  //roll over to beginning when we reach the end of the buffer
  if(bufferindex >= BUFFERSIZE)bufferindex = 0;
}

// play back the most recent values in the buffer in order
void PlaybackBuffer(void){
uint16_t index, count;

  index = bufferindex;  // Get the global index
  // display buffer data 16 values per line on Serial monitor
  Serial.println("Buffer contents");
  for(count = 0; count < BUFFERSIZE; count++ ){
    if((count %16) == 0) Serial.printf("\n%4u: ",count);
    Serial.printf("%4u ", adcbuffer[index]);
    index++;
    if(index >= BUFFERSIZE) index = 0;
  }
  Serial.println();
}

This demo program doesn't worry about how you compute amplitude---it generates a simulated value. A global buffer and index value are used---which violates some strict best-practice programming standards. I'm aiming for a minimalist example, and didn't feel like writing a full C++ class example. Complain to your CS instructor if you object! ;-)
 
As I said above, the problem wasn't a problem. There wasn't a buffer or memory issue. The serial plotter would freeze after a few mins and I assumed the MC was freezing. So, I put a heart beat into the code and checked it on the scope. Turns out the arduino plotter will just stop. So, I appreciate the response but the problem is solved.
 
Back
Top