DC motors (Part 3)

Part 1, 2, 3

Now that we managed to get rid of (most of) the spike generated by the opening the supply line of the motor, let’s look at this plateau which happens just after the spike. From the pictures in the previous post, we have been able to observe that the voltage of this plateau looks proportional to the load applied to the shaft of the motor. Well, in fact this signal is related to the back-EMF (ElectroMotive Force) aka counter-EMF or CEMF. You probably remember that a DC-Motor can be reversed and act as a generator. So, when you interrupt the power supply to the motor, the motor will deliver its accumulated energy under the form of a voltage of the same sign than the power supply voltage. As the motor is hooked to VCC, the faster the revolution, the higher the CEMF, the lower the plateau ! And vice versa.

This is a very interesting property that we may exploit in order to build a rotational speed controller without additional sensors !

The ripple from the top picture or from unprotected power supplies looks exactly like the one observable at the output of a DC-motor used a generator. Using subtle DSP we might even calculate the rotational speed based on the analysis of this ripple signal. We will see that later.

Now that we managed to build a safe PWM power supply which is able to provide a clean feedback signal, let’s try to use the CEMF in order to control the speed of the motor. Firstly, we need to create a feedback line to Arduino in order to measure the CEMF, convert it, compare it to a set-point and adjust the PWM ratio accordingly.

pwm_schematics_5

The CEMF signal is taken from the switched power supply output through R2. Although this resistor may not be mandatory, it has some interesting advantages: using the D1 general purpose diode (1N4148 or so) wired to arduino +5V, it protects the analog input from excessive signal as R2 will absorb the generated current. In combination with the optional C1 capacitor it will act as a RC filter which will improve somewhat the filtering of the ripple.

Once the hardware ready, You may want to use the following code:

const uint8_t _outputPinMask = (1 << PINB5);
volatile uint8_t *_outputPort = &PORTB;

const int16_t _cycleTime = 10000; /* in micro seconds */
int16_t _onTime = 0; /* in micro seconds */
uint32_t _lastCycleTime; /* in micro seconds */
uint32_t _lastOnTime; /* in micro seconds */
const uint8_t _analogPin = 0;
/* user defined variable */
const int16_t _targetCEMF = 950; /* From 0 to 1024, high CEMF value = low speed */

void setup(void)
{
	/* Init pwm pin */
	*(_outputPort - 1) |= _outputPinMask; 
	/* Init analog pin */
	analogReference(DEFAULT);
	/* Init cycle time */
	_lastCycleTime = (micros() - _cycleTime);
}


void loop(void)
{
	uint32_t now = micros();
	if (((now - _lastCycleTime) >= _cycleTime)) {
		_lastCycleTime = now;
		_lastOnTime = now;
		/* Before switching the motor drive off, perform a one shot measurement 
		of the counter-electromotive force (abbreviated counter EMF, or CEMF) */
		int16_t val = analogRead(_analogPin);
		/* Compute the difference between the setpoint and the measured value */
		int16_t diff = (val - _targetCEMF);
		/* Apply a correction factor: damping (< 1) or accelerating (> 1) */
		diff *= 0.5;
		/* Update the on time and constrain it */
		_onTime += diff;
		if (_onTime < 0) {
			_onTime = 0;
		} else if (_onTime > _cycleTime) {
			_onTime = _cycleTime;
		}		
		/* Turn motor ON (MOFET gate high) */
		*(_outputPort) |= _outputPinMask; 
	} else if (((now - _lastOnTime) >= _onTime)) {
		/* Turn motor OFF (MOFET gate low) */
		*(_outputPort) &= ~_outputPinMask; 
	}
}

Now you understand why I decided to bit bang the PWM. The CEMF is measured just before resuming the ON state, far away from the spike (Tr in the following diagram).

cemf_sampling

(Very) serious games

From my primary school in the 60’s, I remember that we flew hundreds of paper planes in the school yard. Depending upon the way paper was folded, these planes would be fast or fly long distance or be able to perform some sorts of acrobatics figures (Mostly unexpected I must say).

paper_plane_a

At the same time clubs were flying the early model planes, powered by micro fuel engines and controlled by cables.

These archaic model planes became real serious games thanks to advanced technology and composite materials. the even get larger, heavier in order to match their sometimes deadly tasks.

Read more from WIRED post: Watch Darpa’s Creepy ‘Project SideArm’ Pluck a Drone Out of the Air

 

 

DC motors (Part 2)

Part 1, 2, 3

Time for code ! Let’s write a few lines of trivial code in order to drive our controller (Check previous post). You probably heard about the analogWrite() function which is very convenient for generating PWM. Well, we will not use it ! Is it because it is too easy ? It is because there is a limited number of pins capable of generating a PWM signal ? Or is it because of the limited choice of base frequency ? All these arguments must be taken into account. However on top of that, the reason why we will use bit bang code relates to the need for having full and easy access to all timed events from each cycle. Although this choice may not look obvious to you now, it will in the next posts.

So here is the basic code that we will use as a staring point:

/* set pwm pin */
const uint8_t _outputPinMask = (1 << PINB5);
volatile uint8_t *_outputPort = &PORTB;
/* set timing variables */
const uint16_t _cycleTime = 10000; /* in micro seconds */
uint16_t _onTime = 0; /* in micro seconds */
uint32_t _lastCycleTime; /* in micro seconds */
uint32_t _lastOnTime; /* in micro seconds */
/* User defined pwm ratio */
uint8_t _pwmRatio = 50; /* from 0 to 100 */

void setup(void)
{
	/* Init pwm pin */
	*(_outputPort - 1) |= _outputPinMask; 
	/* Constrain pwm ratio */
	if (_pwmRatio < 0) {
		_pwmRatio = 0;
	} else if (_pwmRatio > 100) {
		_pwmRatio = 100;
	}	
	/* Compute on-time */
	_onTime = (((uint32_t)_cycleTime * _pwmRatio) / 100);
	/* Init cycle time */
	_lastCycleTime = (micros() - _cycleTime);
}


void loop(void)
{
	uint32_t now = micros();
	if (((now - _lastCycleTime) >= _cycleTime)) {
		/* cycle-time has elapsed */
		/* Reset timing variables */
		_lastCycleTime = now;
		_lastOnTime = now;
		/* turn motor ON (MOFET gate high) */
		*(_outputPort) |= _outputPinMask; 
	} else if (((now - _lastOnTime) >= _onTime)) {
		/* on-time has elapsed */
		/* turn motor OFF (MOFET gate low) */
		*(_outputPort) &= ~_outputPinMask; 
	}
}

If you are unsure about your electronic skills, do not connect anything thing to your Arduino board and just observe the built in LED. Using various PWM ratio will dim the LED accordingly. Once you are ready, it might be a good idea to see what’s going on using an oscilloscope. Connect the probe A to TP1 (as per the schematic below) and probe B to TP2. The clean signal from channel A (yellow trace) will be used for synchronization, while we will pay attention to the signal on channel B (blue trace).

pwm_driver

Set a PWM ratio of 50 (50% on 50% off), upload your code and will get these kind of plots on your screen. This is the plot of the signal at TP2 when the motor in not loaded:

pwm_signal_1

Under a moderate load, this what you’ll get

pwm_signal_2

Ultimately, this is the resulting plot when the rotor is blocked

pwm_signal_3

Let’s discuss these plots starting from the ON state (5 V applied to the gate of the MOSFET as per channel A): there’s nothing much we can say about it. The MOSFET does the job perfectly and switches one of the motor ends to the ground or almost to the ground. The OFF state shows a more confusing signal. The pattern of the signal looks the same under various load conditions: a strong spike which exceed the supply voltage followed by a plateau which is equal or lower than the supply voltage. On the other hand, we observe that the higher the load, the more intense is the signal.

The spike is no big surprise as you may already know that opening a circuit loaded with an inductance will generate an intense reverse voltage. This is the principle of boost voltage converters or spark plug ignition coils. As this pulse may severely impact other components, we will get rid of it using a clamp diode in parallel with the motor which will absorb the reverse voltage. Once again, we will use a slightly different option by using an other MOSFET which features a built in protective diode… See why in the next pictures, starting with a zoom in the spike area

Protective diode (1N4007), blocked motor shaft

NewFile0

Protective MOSFET, blocked motor shaft

NewFile1

Are you convinced by my choice? Note that a Schottky diode would do the job as well. However, if you have one MOSFET, it is very likely that you also have its little brother !

Next post on same subject

DC-motors (Part 1)

Part 1, 2, 3

DC-motors are every where in many form factors, many performances and configurations. The aim of this series of posts is not to cover all aspects of DC-motors as there are so many publications available, most of them being of excellent quality.

Firstly, I will concentrate on the most common brushed DC-motors . Next picture illustrates a typical DC-motor of this kind.

dc_motor_1

And here is a picture of the dismantled motor. From left to right, the  brushes holder flange, the rotor and the cage holding the two magnets

dc_motor_2

Next is a picture of the rotor showing the collector and the three magnetic poles

dc_motor_3

You may find these types of motors in many, many applications, from hair dryers, to telephone vibrators, through cordless drills, toys and servo-motors. On one hand these motors are cheap, on the other hand they have poor torque and balance.

Feeding these motors is easy as far as loose specifications are concerned. In most applications, a fixed DC voltage is applied to the motor. Over-current protection is provided by a fuse (on the left), or even a thermal fuse (middle or right in the next picture) or by an electronic function when the driver features a voltage regulator.

fuses

Care must be taken with over-currents because the brushes are generally weak and consist in simple copper blades:

dc_motor_4

If we leave apart DC-motors fitted with a tachometer or an optical or magnetic encoder, controlling the speed of a DC-motor is not so easy. A method of choice consists in using PWM (Pulse Width Modulation) to power the motor. Next diagram illustrates the principle of PWM.

pwm

While t is constant, the on/off time ratio is proportional to required rotational speed. Although many publications describe how to wire such a controller, here is the “ultimate” schematic

pwm_driver

R1 is 1 kOhm and T1 is an IRF540 N-Channel MOSFET (or one of its numerous cousins). In my case, VCC was 6 V in order to comply with the motors specifications.

As I plan to go in more details in the next posts, I designed and printed a specific test bench as shown below…

dc_motor_bench_3

… featuring the following components: 2 identical motors, a rubber tubing which acts as a flexible coupling, a barrel which holds a reflective band (for future tests) and the mainframe with its two clips

dc_motor_bench_2

Both DC motors come from a defective CD player (diameter 24 mm, height 20 mm).

Next post on same subject

Incremental rotary encoders (Part 10)

Part 1234567, 8, 9, 10

Let’s have some fun with encoders. As I was thinking about an alternative solution for the stepping motors of our friend Valério’s puppet, I started playing around with some DC motors which are quieter than servo-motors and less greedy and less complex to drive than the steppers. Problem is that DC-motor require additional features for controlling the amount of rotation. And I remembered very, very old projects that used Portescap motors featuring tandem motors: the big one as the real motor and a smaller one acting as a magneto. As the voltage generated by the magneto is proportional to the rotational speed, it performs as a tachometer.

escap_motor

This option is not suitable for my application, but I though about bending the original idea … from a tachometer to an encoder, there are just a few steps to fulfill !

The hardware is very, very simple. R1 and R2 (1 kOhm both), decoupled by C2 (0.1 µF, 10 V) are biasing one end of the motor while its other end is connected to an analog input. The analog reference is wired to the 3.3 output, decoupled by C1 (1.0 µF, 10 V).

 encoder

Next figure illustrates the voltage read at the analog input A0 of arduino:

encoder2

The motor that I used is very popular in the scrap boxes: the motor was disassembled from a CD player. It produces a clean positive or negative +/- 1 V output which is fully compatible with the 3.3 V adc range. An obvious improvement  would consist in protecting the analog input against excessive voltages.

encoder3

The code is very simple and was written in a quick and dirty mode in order to validate the concept.

uint32_t _interval = 250; 	/* In ms */
uint32_t _lastTime; 		/* In ms */
uint16_t _meanValue = 512;
uint8_t _analogPin = 0;
int32_t _counter;


void Calibrate(void)
{
	uint8_t samples = 16;
	_meanValue = 0;
	for (uint8_t i = 0; i < samples; i++) {
		_meanValue += analogRead(_analogPin);
	}
	_meanValue /= samples;
}


void setup(void)
{
	Serial.begin(115200);
	analogReference(EXTERNAL);
	Calibrate();
	Serial.print("Mean value: ");
	Serial.print(_meanValue);
	Serial.println();
	_lastTime = (millis() - _interval);
}


void loop(void)
{
	uint32_t now = millis();
	if ((now - _lastTime) >= _interval) {
		_lastTime = now;
		int16_t val = analogRead(_analogPin);
		val -= _meanValue;
		val /= 3;
		/*  */
		if (abs(val) > 0) {
			_counter += val;
			if(_counter > 0) {
				Serial.print('+');
			}
			Serial.print(_counter);
			Serial.println();			
		}
	}
}

The setup routine initializes the serial port and the analog port. It performs an automatic calibration of the mean voltage which corresponds to the idle mode (Motionless motor). The loop routine periodically reads the analog port and converts the positive and negative swings in digital counts. Simple, cheap and easy! Although there is a lot of room for improvements, starting with the reading of the analog voltage which should be performed in an timed interrupt.

Random number generator (Part 3)

Part 1, 2, 3

The early tests presented in this series of posts were dealing with a limited number of data. The risk exist that a long term repeated pattern may not be seen using these statistical tools. Here is an other approach to testing the randomness of data.

It consists in creating a black and white bitmap made out of millions of data points, each of them representing a unique random value. Although the result is a flat image, using gray scale for each cell from the matrix allows a clean and easy 3D graphical representation of these data points. The size of the matrix depends on the range of the random values. If the random values ranges from 1 to 65536 (so as to say 2^16), the matrix shall be squared and each side will be 256 (so as to say 2^8) cells wide. Each time the random number matches a cell, the content of this cell is increased resulting in brighter pixels. The test stops when a cell achieves the maximum brightness.

Here is a sample from multiple tests that I ran using the previously described hardware random generator:

pixels_map_9

As you can see, this image looks like sand paper without any obvious pattern. Compare it to the next one that I obtained after tweaking the biasing mechanism:

pixels_map_7

Interesting, isn’t it ?

Ultimately, these are the few lines of code that I used in processing for reading data out of Arduino and plotting random values. Oh! By the way, the random() function gives a very… random bit map too !

import processing.serial.*;

Serial myPort;

int _plotWidth;
int _plotHeight;
long _counter = 0;
float _maxBright = 0;
volatile int _inByte;
volatile boolean _readStatus = false;


void setup() 
{
  /* Set graphics */
  size(256, 256);
  _plotWidth = width;
  _plotHeight = height;
  colorMode(RGB, 255);
  background(0); 
  frameRate(60);
  /* Set serial port */
  String portName = "COM29";
  myPort = new Serial(this, portName, 1000000);
}



void draw()
{  
  _counter= 0;
  _maxBright = 0;
  clear();
  background(0); 
  loadPixels();
  while(true) {
    int data = 0;
    for (int i = 0; i < 2; i++) {
      _readStatus = false;
      while (_readStatus == false);
      data <<= 8;
      data |= _inByte;
    }

    // data /= 64;
    /* Get the actual pixel color */
    float actColor = red(pixels[data]);
    float newColor = (actColor + 4.0);
    /* Record maximum brightness */
    if (newColor > _maxBright) {
      _maxBright = newColor;
    }
    /* constrain brightness */
    if (newColor > 255.0) {
      break;
    }
    int c = color(newColor);
    pixels[data] = c;
    _counter += 1;
   
    print(_counter); 
    print(";");
    print(data); 
    //print(";");
    //print((newColor)); 
    print(";");
    print((_maxBright)); 
    println();
  }
  updatePixels();
  save("pixels_map.tif");
  //delay(2000);
  while(true);
}


void serialEvent(Serial myPort) 
{
  _inByte = myPort.read();
   //myPort.clear();
  _readStatus = true;
}

This is real raw code, however it does the job nicely and snappy. Note that the bitmap image is recorded in a compressed lossless TIF format so that it could be analyzed after acquisition.

Next is a copy of the code taken from one of the examples prepared for PlainRNG library. Setting the random number generator is plain easy. This code generates unsigned 2 bytes random numbers for ever and matches the processing code as shown above.

/*

	PlainRNG library, hardware random numbers generator library
	Copyright (C) 2016 Didier Longueville

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
	
*/

#include <PlainRNG.h>
PlainRNG RNG;

const uint8_t _adcPin = 0;

void setup()
{
	/* Initialize serial port */
	Serial.begin(1000000);
	/* Initialize analog port */
	RNG.InitializeRNG(_adcPin); 
	RNG.Calibrate(32768); 
}


void loop()
{
	uint8_t rn = (RNG.RandomNumber(8, BIA_REM_VON_NEUMANN) & 0xFF);
	Serial.write(rn);
}

 

Enjoy and keep me posted with your comments.

Last minute Xmas present… a storage box!

Time for tidying up my bench ! As electronic components are getting smaller and smaller, the need for suitable storage boxes emerges. Here is the description of an original storage cupboard that I designed for SMDs as well as for very small mechanical parts such as watches spare parts.

Here is the picture captured from FreeCad that I used to draw the box and bins. Each box features 10 bins.

storage_box

As you can see, multiple boxes can be stacked. Each bin fits in a cell and each cell features a spring loaded retaining restraint which prevents the bin from sliding and dropping while moving the storage cupboard. The carving on top is for sliding a  storage map as it is not possible to put labels in front of each bin. Here is a real life illustration of the box printed in 3D:

p1180296s

Do you like it ? Here are the > STL files <.

Random number generator (Part 2)

Part 1, 2, 3

Here is a noisy signal sampled at 100 kHz with an Arduino UNO. The signal has 8 bits of resolution and the divider bridge of the final amplifying stage has been set so that the ADC range matches (without exceeding) the input signal range. Here is the illustration of a vector of 1024 data points sampled from the noise generator.

noise

You may want to download these data in order to perform your own analysis, and I would be more than happy to publish your own results.

Firstly, let’s look at the distribution of data points versus their mean value. To do so, we compute the probability mass function:

pmf

This result is typical from thermal noise. The distribution of the number of data points from each bin looks like a Gaussian distribution. Starting from there it is possible to apply envelope modelers in order to get an overall picture of the distribution. Running an Hilbert transform might overwhelm our arduino UNO so I applied a simplified algorithm which gives pretty good results.

envelope

Now we know that we are really measuring what we expect. Let see if this signal is not contaminated by other signals… To do so, we apply a FFT in order to detect the possible presence of signals characterized by individualized frequencies (and possibly their harmonics) having significant strength (signal to noise ratio). Next is the frequency spectrum from the test signal:

fft

Well, at first glance, there is nothing special to say about it. Is there something near 8 kHz ? Running this test on multiple vectors of data does not confirm this hypothesis. Running the test on extra large vectors of data would bring us to some sort of flat spectrum.

An other interesting analysis tool is the Phase Portrait. Here is a plot of the phase portrait applied to the test data. The spot is centered on the 0;0 coordinate and no repeated trend line shows up from the plot, meaning that we are dealing with real white noise.

phase_portrait

Here and there are very good examples of phase portraits applied to clean and noisy signals.

Next post on same subject

Random number generator (Part 1)

Part 1, 2, 3

As I was stumbling the web looking for advanced information on thermal noise, I found some very interesting papers on RNGs, aka Random Number Generators. These devices feature hardware components which are responsible for a generating unpredictable random numbers !

This is a very interesting matter as it involves physics, electronics, mathematics, software and … ultimately cryptography. Credit is due to people smarter than I am which publications can be found at the end of this post. The idea here is to described a surprisingly simple and yet powerful method for generating random numbers of various length, actually from 1 to 32 bits.

The principle of operation relys on the amplification of the signal produced on the base of a reverse-biased transistor. The emitter is saturated with electrons and occasionally they will “tunnel” through the band gap and exit via the base. This signal is then amplified through two transistors one of them behind biased through a decoupling capacitor in order to get rid of the DC component of the signal.

Here is the electronic section of the project: All you need is a trio of general purpose NPN transistors such as the popular 2N3904 a pair of capacitor and few resistors. The schematic is rather simple.

schematics

I brought a little refinement to the original design by feeding the amplifying stage with a stable +5 V. Note that both Vin and +5 V should be decoupled by 10 µF capacitors (Watch out the operating voltage of the capacitor attached to Vin !). Under these conditions, the circuit outputs a 0 to 1  V signal which fits very well the analog to digital conversion using the 1.1  V internal reference. Please note that 5 V will not suffice to create a reverse bias condition on the base-emetter junction of T1. I managed to get a stable running state starting with Vin = 9 V.

scope

I also brought few refinements to the original code originally written by Rob Seward (v1.0 dated 4/20/2009 ). Firstly, the analog read function is replaced by a custom one which is way faster, thanks to its 8 bits resolution, low prescaler and refined code. the SetADC() function must be inserted within the setup() part of the code

/* Set adc parameters for 8 bits resolution, fast mode */
void SetADC(uint8_t adcChannel) 
{
	/* Clear ADC control registers */
	ADCSRA = 0x00;
	ADCSRB = 0x00;
	ADMUX  = 0x00;
	// ADMUX |= (1 << REFS0); /* DEFAULT 5 V*/
	ADMUX |= (1 << REFS1) | (1 << REFS0); /* INTERNAL 1.1 V */
	ADMUX |= adcChannel; /* Set channel */
	ADMUX |= (1 << ADLAR); /* Left align adc value, so as to say 8 bits resolution */
	ADCSRA |= 3; /* Set ADC prescaler */
	ADCSRA |= (1 << ADEN); /* Enable ADC */
	ReadADC(); /* Run one mock read */
}

inline uint8_t ReadADC(void) 
{
	const uint8_t ADSCmask = (1 << ADSC);
	/* Start conversion */
	ADCSRA |= ADSCmask; 
	/* Wait for conversion */
	while (ADCSRA & ADSCmask);
	/* Read digital value */
	uint8_t result = ADCH;
	/* Returned value */
	return(result);
}

Please note that in spite of its higher sampling rate capacity (about 100 kHz), we are still far from optimal sampling rate (1 Mhz or better). However, that will be enough for an experiment.

I also brought few refinements to the application specific functions:

/* Porcess the input value, so as to say the adc reading */
void ProcessInput(uint8_t adcValue)
{
	/* Set default value */
	uint8_t input = 0;
	if (adcValue > _adcThreshold) {
		input = 1;
	}
	switch(_biasRemovalType){
	case VON_NEUMANN:
		VonNeumann(input); 
		break;
	case EXC_OR:
		ExclusiveOr(input);
		break;
	case NO_BIAS_REMOVAL:
		BuildRandomNumber(input);
		break;
	}
}

/* Apply exclusive or to pairs of bits (thanks to the flip flop flag) */
void ExclusiveOr(uint8_t input)
{
	static uint8_t flipFlop = 0;
	static uint8_t previousInput = 0;
	if (flipFlop) {
		BuildRandomNumber(previousInput ^ input);
	}
	previousInput = input;
	flipFlop = !flipFlop;
}


/* Apply Von Neumann to pairs of bits (thanks to the flip flop flag) */
void VonNeumann(uint8_t input)
{
	static uint8_t flipFlop = 0;
	static uint8_t previousInput = 0;
	if (flipFlop) {
		if (input & ~previousInput){
			BuildRandomNumber(0);
		} else if (~input & previousInput){
			BuildRandomNumber(1); 
		}
	}
	previousInput = input;
	flipFlop = !flipFlop;
}


/* Append new bit to the currently built random number */
void BuildRandomNumber(uint8_t input)
{
	/* Shift previous bits */
	_outputBuffer <<= 1UL;
	/* Append input bit as LSB */
	_outputBuffer |= (input & 0x01);
	/* Update bits counter */
	_bitsCounter += 1;
	if (_bitsCounter == _outputBits) {
		/* Reset bytes counter */
		_bitsCounter = 0;
		/* Output data */
		_mainCounter += 1;
		Serial.print(_mainCounter, DEC);
		Serial.print(';');
		switch(_outputFormat){
		case BINARY:
			Serial.print(_outputBuffer, BIN); 
			break;
		case DECIMAL:
			Serial.print(_outputBuffer, DEC);
			break;
		case HEXADECIMAL:
			Serial.print(_outputBuffer, HEX);
			break;
		}
		Serial.println();
		_outputBuffer = 0x00;
	}
}

/* Record occurences of adc values in their corresponding bins */
void SortAdcValue(uint8_t byteValue)
{
	/* Update occurences counter of the current adc vlaue */
	_vSorter[byteValue] += 1; 
	/* Update counter of sorted values */
	_sortedValuesCounter += 1;
}

/* 
Compute the adc threshold which is the median of the counts of occurences for 
each adc value void 
*/
void ComputeAdcThreshold(void)
{
	uint32_t sum = 0;
	/* Compute the sum of adc occurences in all bins */
	for (uint16_t i = 0; i < _sorterSize; i++){
		sum += _vSorter[i];
	}	
	/* Compute half the sum of all observations */
	uint32_t halfSum = (sum >> 1);
	/* Reset sum */
	sum = 0;
	/* Find the adc value corresponding to half the sum of occurences */
	_adcThreshold = 0;
	while (sum < halfSum) {
		sum += _vSorter[_adcThreshold];
		_adcThreshold += 1;
	} 
}

This is how the code was looking like when I performed early tests which were not so bad in the end !

Next picture illustrates the probability mass function (aka pmf) from 65536 random numbers ranging from 0 to 65535:

pmf

As you can see, the spectrum is pretty homogeneous, showing a fine spreading of random numbers over the expected range. Next is the FFT from the raw data:

fft

Once again, the spectrum is pretty homogeneous, showing no particular recurring pattern. Additional statistics show an average value of 32711.29 which turns to a -0.17 % error. The skewness (symetry of data versus average) is 0.003. All these statistics are very encouraging. However, I might have a use the Dieharder, “A Random Number Test Suite” which looks it is the perfect tool for checking randomness of numbers.

Next steps ? I think that I will pack this material in a library (Why not PlainRNG ?) and run some long term testing…

Links:

Next post on same subject

Trick of the day

This is a funny although very serious one: When you are looking for something very special, pretty complex such as papers on thermodynamics, astronomy, advanced electronics, etc. and you fail to find some information of interest, just insert the “arduino” word to your sequence of searched words.

From my own experience, the chances of finding the information of interest jumps to 90%.

Hum, great, just great  will you say, but “what about the 10% cases when it fails ?”

Well, the information found in these “failing” cases might just strike you as it did recently. I am more and more amazed by what the people manage to get from this insignificantly slow, rough, cheap piece of glass and silicon called arduino.

arduino316

Much more than just a piece of hardware and a bunch of lines of code, this is an awesome concept and the common denominator for a whole social network of makers. Long live arduino !