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

Leave a Reply

You must be logged in to post a comment.