Fast Signal Sampling (Part 10)

Part 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 11, 12
PlainADC looks like it is a live product with always room for improvements. Before releasing an other really amazing new version, we may review a few critical things that developers should feel confortable with prior to interfacing the library to any other software component.

This post deals essentially with the use that you can make of the acquired data. First of all, here is the bottom line for acquiring (double) data. The acquisition parameters are set as constants below the /* Acquisition parameters */ line. Capitalized names refer to library dependant constants.

#include 
PlainADC PADC; /* Create ADC object */
/* Acquisition parameters */
const uint16_t samples = 64;
const double samplingFrequency = 16.0E+3;
const uint16_t adcChannel = 0; /* From 0 to 5 on ATmega328 powered Arduinos */
const uint16_t refVoltage = ADC_REF_VOL_DEFAULT; /* VCC: 5V */
const uin8_t dataType = ADC_DAT_FMT_DBL;

uint8_t *vBuffer;

union {
	uint8_t vBytes[4];
	double Dbl32;
} cboData;

void setup() 
{ 
	Serial.begin(115200);
	/* Set data acquisition parameters */
	vBuffer = PADC.SetAcquisitionEngine(adcChannel, refVoltage,  samplingFrequency, samples, dataType);	
}

And next are the various ways to read the acquired data

The simplest way. If you go for this option, you do not even have to set and care for the vBuffer variable. Nor for the cboData variable.

	for (uint16_t i = 0; i < samples; i++) {
		double abscissa = (i / samplingFrequency);
		Serial.print(abscissa, 6);
		Serial.print("t");	
		double ordinate;
		ordinate = (((PADC.ReadDbl32Data(i)* 5.0) / 1024.0) - 2.5);
		Serial.print(ordinate , 6);
		Serial.println();
	}	

The somewhat brute way. Byte data are converted individually thanks the cboData variable.

	for (uint16_t i = 0; i < samples; i++) {
		double abscissa = (i / samplingFrequency);
		Serial.print(abscissa, 6);
		Serial.print("t");	
		double ordinate;
		for (uint8_t j = 0; j < 4; j++) {
			cboData.vBytes[j] = vBuffer[(i << 2) + j];
		}
		ordinate = (((cboData.Dbl32 * 5.0) / 1024.0) - 2.5);
		Serial.print(ordinate, 6);
		Serial.println();
	}

The smarter way. Same as above except that you do no longer iterate through the bytes in the vBytes vector from the cboData variable.


	for (uint16_t i = 0; i < samples; i++) {
		double abscissa = (i / samplingFrequency);
		Serial.print(abscissa, 6);
		Serial.print("t");	
		memcpy(&cboData.vBytes[0], &vBuffer[(i << 2)], 4);
		double ordinate = (((cboData.Dbl32 * 5.0) / 1024.0) - 2.5);
		Serial.print(ordinate, 6);
		Serial.println();
	}

An even nicer way. In the same spirit as above, but without the need for the cboData variable.


	for (uint16_t i = 0; i < samples; i++) {
		double abscissa = (i / samplingFrequency);
		Serial.print(abscissa, 6);
		Serial.print("t");	
		double ordinate;
		memcpy(&ordinate, &vBuffer[(i << 2)], 4);
		ordinate = (((ordinate * 5.0) / 1024.0) - 2.5);
		Serial.print(ordinate, 6);
		Serial.println();
	}	
	

All four options for reading data work. I did not run performance testing, but I intuitively think that options 1 and 4 are the quickest and use less memory.

And now is the time for an other awesome one! So far, we have to convert individual or groups of bytes into doubles. C++ brings a fancy but risky function which will tell the compiler: "Well, I know that this pointer points to a certain type of data (e.g. bytes). But I would like you to read it as doubles!"

And here comes the ultimate option!

	vReal = reinterpret_cast(vBuffer);
	for (uint16_t i = 0; i < samples; i++) {
		double abscissa = (i / samplingFrequency);
		Serial.print(abscissa, 6);
		Serial.print("t");	
		double ordinate;
		ordinate = vReal[i];
		ordinate = (((ordinate * 5.0) / 1024.0) - 2.5);
		Serial.print(ordinate , 6);
		Serial.println();
	}

Next post on same subject

Leave a Reply

You must be logged in to post a comment.