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

6 Comments

  1. paulfurze says:

    I like this but I have a question…
    why
    const int16_t _targetCEMF = 950
    as a const?

    Surely it would be better as just
    int16_t _targetCEMF = 950

    then we could play with it say using a pot to give the setpoint
    say…
    _targetCEMF = analogRead(_PotInputPin)

    and allow your code to control the PWM % depending on the load applied.

    or am I missing something here ?

    • Didier says:

      You got it perfectly right.
      Remember, the aim of my posts is to start with basic level and to reach more complex levels steps by steps. You are on your way ! Enjoy and keep us posted with your own works. 😉

      • paulfurze says:

        Well, I’m certainly pleased about that 🙂

        We have two motors.
        One is a simple DC brushless connected to a ESC unit.
        (I’ll be looking at that in more detail in a few weeks)
        The other is a 12V 90A monster 🙂
        Hence combined speed/load control is an ace approach.

        One wee small point.
        I know you’ve tended to use class type declarations of variables but that means its a tad more difficult for beginners. My C coding was 30 years ago so having to “Brush up” as i’m going along here.

        Have Fun,
        Looking forward to part 4.

        • Didier says:

          I see!
          Well, I am not a programmer by “accident”. In the very beginning, I wrote programs while preparing training material when I was teaching DSP applied to mass spectrometry to some professional students. The aim of these programs was to show how things work in real life. And I found it fun (my wife did not find it fun at all). It also helped me to seriously reduce the impact of dyslexia on my way of thinking because I had to picture my thoughts in a rational manner. So, this my way of coding and this why it is this way. In coding words, this translates in “What is general is general and goes on top” and “What is local goes as local as possible”. You may take it or leave it 😉 (Please take it)
          About this matter, I loved the Twitt from Massimo Banzi “Somebody just asked for “examples of good code” on the internet, violence is going to erupt soon…”

          BTW, what would you like to read in Part 4 ?

          • paulfurze says:

            Hi Didier,
            Don’t get me wrong here, I totally agree with your way of coding. I came up through industry in control systems as an apprentice and initially stated my software stuff on a commodore PET. (thats an age give away right there!). So initially I wrote assembler, and I wrote an awful lot of it across a lot of systems. Many years later I jumped to VB and ended up writing a fair bit of application stuff. My C/C++ time was sorta interspersed in the middle of that time. And like many things I was always “gonna get back to it” but never had the time. So i really get the importance of usability, and am really picky over layouts etc.
            My comment was a total beginner in C may struggle with the PORT B stuff.

            Our current project was, up until about 3 weeks ago, using an electronics design. It was suggested to use Arduino by a friend and initially I was against it citing I did not have development boards etc etc (remembering my old PIC programming days and the plethora of add-ons required to programme PICS). A simple demo of how it works today via a usb and two hours later I have the development IDE, a “pro mini” and am searching for examples 🙂 and trying to brush up my C as fast as possible.

            Don’t think there’s been a time in history when an engineers partner has been happy with ‘the work’
            I guess even Elsa Einstein said:
            “Put away all those damn equations as I’m trying to lay the table for tea. Who’s gonna want a new time system anyway ? :)”

            Part 4….
            Well, rotational speed measurement without sensors.

            On my project we’ve adding in dynamic, but will have to look at a “stall detector” asap. I think I have an idea but a heads up would be wicked.

            Have fun,
            Keep Rocking.
            Paul.

  2. Didier says:

    Hi Paul,
    No worries at all, I like a lot your comment. It sounds like we learned how to code the same way (As far as I remember, I started with basic on a HP9825, and then with Fortran on a HL1000 running RTEa).

    Let me see what we can do for the measurement of rotational speed without sensors …

Leave a Reply

You must be logged in to post a comment.