PWM - Motor Control

A "new fashioned" televisor, using an Arduino to drive the motor and display.

Moderators: Dave Moll, Andrew Davie, Steve Anderson

Re: PWM - Motor Control

Postby Klaas Robers » Sun Mar 05, 2017 7:41 pm

Don't use the 1N4004, it is definitely too slow.
User avatar
Klaas Robers
"Gomez!", "Oh Morticia."
 
Posts: 1656
Joined: Wed Jan 24, 2007 8:42 pm
Location: Valkenswaard, the Netherlands

Re: PWM - Motor Control

Postby Andrew Davie » Sun Mar 05, 2017 7:49 pm

Well there's a LOT of reading about PWM on Arduino, some of it not entirely useful.
Here's probably the best link I've found... https://withinspecifications.30ohm.com/ ... AtMega328/
In essence, you can get up to 62.5KHz with 8-bit resolution on the duty cycle, or if you sacrifice bits in resolution you can up the frequency (all the way to 8Mhz!) but with just 1 bit for duty cycle. So, it's all highly configurable and I should be able to PWM the motor at quite a high rate.

Here's the example from that page for 250KHz PWM...

Code: Select all
// A sketch that creates an 8MHz, 50% duty cycle PWM and a 250KHz,
// 6bit resolution PWM with varying duty cycle (changes every 5μs
// or about every period.

#include <avr/io.h>
#include <util/delay.h>

int main(void)
{
  pinMode(3, OUTPUT); // output pin for OCR2B
  pinMode(5, OUTPUT); // output pin for OCR0B

  // Set up the 250KHz output
  TCCR2A = _BV(COM2A1) | _BV(COM2B1) | _BV(WGM21) | _BV(WGM20);
  TCCR2B = _BV(WGM22) | _BV(CS20);
  OCR2A = 63;
  OCR2B = 0;

  // Set up the 8MHz output
  TCCR0A = _BV(COM0A1) | _BV(COM0B1) | _BV(WGM01) | _BV(WGM00);
  TCCR0B = _BV(WGM02) | _BV(CS00);
  OCR0A = 1;
  OCR0B = 0;

  // Make the 250KHz rolling
  while (1) {
  _delay_us(5);
  if ( OCR2B < 63 )
    OCR2B += 5;
  else
    OCR2B = 0;
  }
}



Code actually sets up two PWM timers (250KHz on #2 and 8MHz on #1).

The code is just demonstrating a varying duty cycle for #2 by "rolling" a variable through the range 0-63 in a loop.
In the background the hardware is running at 250KHz doing the PWM writes, with 6 bits allocated to the duty cycle counter comparator.
The last while() loop is superfluous for my application, of course - but it will be replaced by the PLL code to set the OCR (counter) value.
Note that timer 2 is NOT available on the Nano, so a different timer needs to be selected.

Timer 0 is system and used for delay() and similar function calls, but I believe it can be hijacked if you don't need those just like shown here.

Edit: Unfortunately, millis() is affected by changing the frequency of timer 0, which means that the calculation of disc RPM won't work properly. Better perhaps to move the PWM to another timer other than 0
User avatar
Andrew Davie
"Gomez!", "Oh Morticia."
 
Posts: 1590
Joined: Wed Jan 24, 2007 4:42 pm
Location: Queensland, Australia

Re: PWM - Motor Control

Postby Andrew Davie » Sun Mar 05, 2017 7:52 pm

Klaas Robers wrote:Don't use the 1N4004, it is definitely too slow.


OK, noted.

First test is to demonstrate faster PWM frequencies and then try that with existing setup.
Then we'll know how much of that noise was from the low frequency PWM. I won't go back to the 1N4004 :)
User avatar
Andrew Davie
"Gomez!", "Oh Morticia."
 
Posts: 1590
Joined: Wed Jan 24, 2007 4:42 pm
Location: Queensland, Australia

Re: PWM - Motor Control

Postby Klaas Robers » Sun Mar 05, 2017 7:57 pm

You may replace the motor by a resistor, in that case the diode is not needed. Then you can scope the wave forms to see if they are what you expected. But if you go to the motor, you need a fast diode, 1A.
User avatar
Klaas Robers
"Gomez!", "Oh Morticia."
 
Posts: 1656
Joined: Wed Jan 24, 2007 8:42 pm
Location: Valkenswaard, the Netherlands

Re: PWM - Motor Control

Postby Andrew Davie » Sun Mar 05, 2017 10:10 pm

OK, so I have an operational 62.5KHz PWM signal!

duty4.jpg
duty4.jpg (187.52 KiB) Viewed 11556 times

duty3.jpg
duty3.jpg (203.1 KiB) Viewed 11556 times

duty2.jpg
duty2.jpg (221.21 KiB) Viewed 11556 times

duty1.jpg
duty1.jpg (213.63 KiB) Viewed 11556 times

duty0.jpg
duty0.jpg (199.67 KiB) Viewed 11556 times


Trust me, these are square waves :) Based on the pulse repetition, I counted 16uS/pulse and 1/(16/1000000) = 62500Hz which is exactly what the program was supposed to be doing. Note that the CPU is 16MHz and there's a 256 value pulse counter, so 16000000/256 = 62500 and the prescalar is set to 1. All checks out.

Note also: This confirms the 5V output on the Arduino pins :)

Within the limitations of my $20 oscilloscope, I think it's done pretty well to show these waveforms!

I don't really expect I need more than 62.5K PWM frequency, so I'll leave it at that. Next job to perform the earlier test just to see how that changes the noise/spikes.

I have to find a suitable fast diode.
I have to put a capacitor across the motor terminals.

A small step forward, anyway. Here's the code...

Code: Select all
// CURRENTLY DOING 62.5K with max 255 counter.
// CAUTION: Uses Timer0 which can interfere with operation of delay() and other time-based operations/functions.

#define MOTOR_PIN 11

void setup() {

  pinMode(11, OUTPUT);
 
  // Set up the 62.5KHz output, scalar of 1
  TCCR0A = _BV(COM0A1) | _BV(COM0B1) | _BV(WGM01) | _BV(WGM00);
  TCCR0B = _BV(CS00);

  OCR0A = 0;     // duty cycle (0-255)
  OCR0B = 0;

  // Debug
  Serial.begin(9600);
  while (! Serial);
  Serial.println("Duty? (0-255)");
}

void loop() {

  if (Serial.available()) {

    int duty = Serial.parseInt();
    OCR0A = duty;
    Serial.print(duty);
  }
   
}
User avatar
Andrew Davie
"Gomez!", "Oh Morticia."
 
Posts: 1590
Joined: Wed Jan 24, 2007 4:42 pm
Location: Queensland, Australia

Re: PWM - Motor Control

Postby Andrew Davie » Sun Mar 05, 2017 11:47 pm

I did the noise test, but all I really saw on the collector was 12V bumps with the frequency of the PWM.
I'm going to have to lower the frequency to something my little scope can display properly, and then increase it once I've tested out OK.
User avatar
Andrew Davie
"Gomez!", "Oh Morticia."
 
Posts: 1590
Joined: Wed Jan 24, 2007 4:42 pm
Location: Queensland, Australia

Re: PWM - Motor Control

Postby Andrew Davie » Mon Mar 06, 2017 12:35 am

I reduced the frequency by randomly changing bits in the register :P I need to understand it a bit better than that. Edit: See here!
It would now be around 900Hz I'd say. Not sure how I managed to do that, but there it is - around 1.1ms/pulse.

IGNORE THE 9KHz written in the pictures. It's 900Hz... fingers crossed.

Duty = 0%
9kduty0.jpg
9kduty0.jpg (198.52 KiB) Viewed 11554 times


Duty = 25%
9kduty25.jpg
9kduty25.jpg (200.24 KiB) Viewed 11554 times


Duty = 50%
9kduty50.jpg
9kduty50.jpg (218.76 KiB) Viewed 11554 times


Duty = 75%
9kduty75.jpg
9kduty75.jpg (198.47 KiB) Viewed 11554 times


Duty = 100%
9kduty100.jpg
9kduty100.jpg (196.69 KiB) Viewed 11554 times


When I first "turned on" the 100% duty, from 0, there was some noticeable (about 5V?) noise until the disc got up to speed. Then it was mostly clean. Above picture is a "worst" noise picture once it's at 100% speed. When I then set duty at 0%, there was the following signal at the collector while the disc was rapidly spinning and slowing down. This is the inductance from the motor, right? The diode when correct will fix this, right?

9kslowdown.jpg
9kslowdown.jpg (201.61 KiB) Viewed 11554 times


I will try and find a fast 1A diode tomorrow and test again.
But it's looking much better already, I think.
User avatar
Andrew Davie
"Gomez!", "Oh Morticia."
 
Posts: 1590
Joined: Wed Jan 24, 2007 4:42 pm
Location: Queensland, Australia

Re: PWM - Motor Control

Postby Andrew Davie » Mon Mar 06, 2017 12:45 am

My local store carries the UF4004 Ultra-Fast Rectifier diode - rated at 1A, 400V.
I'll give that a go!
User avatar
Andrew Davie
"Gomez!", "Oh Morticia."
 
Posts: 1590
Joined: Wed Jan 24, 2007 4:42 pm
Location: Queensland, Australia

Re: PWM - Motor Control

Postby Andrew Davie » Mon Mar 06, 2017 1:59 am

I figured out why I was seemingly changing random bits to get my frequency changes to work.
The Arduino code I've seen changes bits something like this..

TCCR0B = _BV(CS00);

... and some of the documentation I've seen says that (for example) says ...

Setting Divisor Frequency
0x01 1 62500
0x02 8 7812.5
0x03 64 976.5625 <--DEFAULT
0x04 256 244.140625
0x05 1024 61.03515625

TCCR0B = (TCCR0B & 0b11111000) | <setting>;

I am now pretty certain this is correct, but also actually totally incorrect and buggy. Many of the programs use macros to set and clear bits. In particular, the _BV macro gets the value of a bit, and the bits are referenced by an encoding scheme (e.g, CSxy) where x is the timer # (0,1,2,3,4) and y is the... value (not bit) number. But it's the valueof the "CS", not bit number in the byte. Edit: OK, so I'm not so sure of this. Some of them are bit numbers in the conceptual "thing" - numbered from 0 for that "thing"So, from the above we could use CS14 to have timer 1, scalar of 256. Also, and here's the big gotcha, it seems that the registers are different in the different Arduinos and so these equates are designed to allow you to write code that will work on all the variations.

So to set the scaling divisor to "8" (0x02 in bit-terms), the correct way to do this is

TCCR0B = _BV(CS02)

What I was doing (from the very first example) was

TCCR0B = 2

because who wants to type all that obfusticating crap stuff, right? I know my binary, and I just needed to set bit #1 (counting from 0)
Only, it turns out that on the Arduino Micro, the scaling isn't bit 1 (value 2) - it's somewhere else. And that's what the _BV macro combined with the encoded bit descriptors actually does. Somewhere in the Arduino source code that gets linked to your program is a whole lot of defines for these values, which differ from processor to processor.

Lesson: USE THE PROVIDED NAMES FOR THINGS, because the hardware on different Arduinos is different, and the compiler will fix it automatically if you do.

I just re-tested with the macros and voila - I have a 7.8KHz PWM as expected.
User avatar
Andrew Davie
"Gomez!", "Oh Morticia."
 
Posts: 1590
Joined: Wed Jan 24, 2007 4:42 pm
Location: Queensland, Australia

Re: PWM - Motor Control

Postby Klaas Robers » Mon Mar 06, 2017 6:44 am

1 kHz is not really much higher than the 500 Hz first. If you go to 20 kHz you will see the first 5% of the wave forms that you see now in your last pictures. That is almost a square wave.

I would suggest to make the frequency somwhere between 16 kHz and 32 kHz. If you have 62 kHz it should also be possible to choose 31 kHz or 15.5 kHz. That is a decent frequency for PWM in a motor. May be you then have 512 steps or 1024, also not bad for the resolution. And now I see that your oscilloscope is really too low frequent to show the wave forms.

I got the impression that you bought 10 years ago, for your first monitor, a hardware oscilloscope... It that one still running? That should be able to give much better wave forms. Yes, it might be a big beast, but after you have done, you may put it again where it was.
User avatar
Klaas Robers
"Gomez!", "Oh Morticia."
 
Posts: 1656
Joined: Wed Jan 24, 2007 8:42 pm
Location: Valkenswaard, the Netherlands

Re: PWM - Motor Control

Postby Andrew Davie » Mon Mar 06, 2017 9:12 am

Klaas Robers wrote:1 kHz is not really much higher than the 500 Hz first. If you go to 20 kHz you will see the first 5% of the wave forms that you see now in your last pictures. That is almost a square wave.

I would suggest to make the frequency somwhere between 16 kHz and 32 kHz. If you have 62 kHz it should also be possible to choose 31 kHz or 15.5 kHz. That is a decent frequency for PWM in a motor. May be you then have 512 steps or 1024, also not bad for the resolution. And now I see that your oscilloscope is really too low frequent to show the wave forms.

I got the impression that you bought 10 years ago, for your first monitor, a hardware oscilloscope... It that one still running? That should be able to give much better wave forms. Yes, it might be a big beast, but after you have done, you may put it again where it was.


I gave the old oscilloscope to a young lad who was keen on learning electronics.
I tested last night on 8KHz, which the oscilloscope can display OK. The prescalars are not powers of two unfortunately - /1, /8, /64, /1024 and the /8 gives 8KHz. So getting other frequencies are a little trickier. Basically, what you have to do is change the range of the "duty" value from 0-255 to some other range, and keep the frequency prescalar, and you effectively modify your resultant frequency. I haven't fully learned that technique yet. But still, 8KHz... could be OK... better than 0.5K :)
User avatar
Andrew Davie
"Gomez!", "Oh Morticia."
 
Posts: 1590
Joined: Wed Jan 24, 2007 4:42 pm
Location: Queensland, Australia

Re: PWM - Motor Control

Postby Andrew Davie » Mon Mar 06, 2017 5:12 pm

Time for a bit of clarity on the difference between the MOSFET (IRF540) and the transistor (TIP31) I have been using in this circuit.

In short, I just got damn lucky that they both work in my circuit.

A more detailed explanation, assuming 12V available to the motor, and 0 or 5V at the arduino PWM digital pin.

Transistor:

A transistor is current-controlled. As you increase current at the base, the current to the collector increases.
There is assumed to be about an 0.7V drop across the transistor. So when the Arduino has its PWM pin (a digital 0 or 5V signal) at 5V, then across the resistor there is a (5-0.7)V voltage. With a gain of (say) 50x and requiring 1A at the motor, that's 1000mA, then we can decide the maximum current we want to allow through the Arduino pin. Let's just say 20mA (maximum is 40mA). So, R = V/I.. R = (5-0.7)/0.02 = 215 ohms.
But with PWM we're still effectively switching 0V or 5V - and current is 0mA or MAX (in this case 20mA). That in turn drives the motor at 0V or input voltage. Well, really it's driving the motor at 0mA or 1000mA at the supplied 12V, but I assume 0mA current is equivalent to 0V 0A. OFF in other words.

The power supply voltage to the motor is therefore not relevant to this calculation of resistor value. It's purely the current and gain that's relevant, and of course the voltage at the Arduino pin.

MOSFET:

MOSFETS are voltage driven. Changes in voltage at the gate affect the output voltage. There is no variation in current flow to the gate; we just need a very small current to drive the MOSFET and of course the voltage at the gate varies.

Voltage changes in the Arduino PWM pin (0V or 5V as it's digital) reflect in voltage changes to the motor. Assuming a 12V supply, then the PWM will result in 0 or 12V to the motor. And of course the motor uses whatever current it uses. There will be some threshold voltage at which the MOSFET is conductive, and below which it isn't (say, 2V). All we need to do is protect the Arduino from potential "backward" current from the MOSFET frying the Arduino - and so we put a resistor in there (say, 1K ohm).

Think of the internals of the MOSFET as having a capacitor which, as its driven at 5V... charges. When we switch the PWM pin to 0 (as part of the square wave), then that capacitor starts discharging - back into the Arduino. Arduino isn't designed for that, so that's why we put the resistor there for protection (not current limiting as per the Transistor implementation).



So, with the above my current understanding of the difference between the IRF540 (MOSFET) and the TIP31 (transistor), I think that I was just sort of fortunate that the circuit as designed actually worked for both of these very different devices! Now I am probably going with the TIP31 and I can safely change the voltage to the motor without worrying about the resistor value, as I set it once based on ardinuo output voltage (5V) and voltage drop across the component (0.7V) and desired maximum current (20mA).

Fingers crossed this time!

Edit: Also see this post
User avatar
Andrew Davie
"Gomez!", "Oh Morticia."
 
Posts: 1590
Joined: Wed Jan 24, 2007 4:42 pm
Location: Queensland, Australia

Re: PWM - Motor Control

Postby Andrew Davie » Mon Mar 06, 2017 7:56 pm

Just one image this time.

8khz.jpg
8khz.jpg (190.87 KiB) Viewed 11528 times


Here the PWM is running at 8KHz. Edit: It's actually 7812 Hz, so the pulse width is 128 us I did manage to get the 20KHz version working, and it was just fine, but the problem is that the range of duty-cycle values was reduced from 0-255 to something like 0-50. What I'd like to experiment with now is increasing the motor voltage because the motor response is too "weak" when starting up at full power. I want to find a balance between motor voltage, PWM frequency, and the duty-cycle range of values (so that it's as broad as possible, giving fine control).

I purchased a new diode - the UF4004 (1A rated). This did clean up the signal an awful lot (NO sign of the spike on top of the square wave anymore). However, there's still a little bit in the valley when motor is under stress spinning up. It's not bad, but I guess I could find a diode rated for even higher amperage.
User avatar
Andrew Davie
"Gomez!", "Oh Morticia."
 
Posts: 1590
Joined: Wed Jan 24, 2007 4:42 pm
Location: Queensland, Australia

Re: PWM - Motor Control

Postby Andrew Davie » Mon Mar 06, 2017 9:52 pm

Whilst it's really really good now, I am going to try a more capable diode - 1N5822 Schottky 40V 3A - to see how that affects the last of the 'glitches' in the waveform. In particular, when the motor is way-slow and you gun the accellerator there's a small ramp in the valley of the square wave PWM just before the rising edge, and when you set the PWM to 0 and the motor is spinning fast, there's a lot of noise on the trace from the motor (acting as a dynamo) that has to be bypassing that 1A diode. So I hope to quell that.

And then it's on to the transfer from the prototype board onto a breadboard. And it will be done.

Source code is pretty much in the final state...

Code: Select all
// PWM testbed
// Creates a PWM on an output pin

void setup() {

  // Only enable the pin that's being used. Here are all the options for the Micro though...
  pinMode(3, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(11, OUTPUT);
  pinMode(13, OUTPUT);

  // Setup the PWM
  // WGM02|WGM01|WGM00  = waveform generation
  //                    = fast PCM with OCRA controlling the counter maximum
  // CS01               = clock scalar /8
  // COMA               = toggle on compare match (01)

  TCCR0A = _BV(COM0A0) | _BV(COM0B1) | _BV(WGM01) | _BV(WGM00);
  TCCR0B =  _BV(WGM02) | _BV(CS01);

  OCR0A = 255;          // the compare limit (reduce this for higher frequency PWM)
 
  // frequency  = 16MHz/scalar/(OCR0A+1)
  //            = 16000000/8/256
  //            = 7812 Hz
 
  // if we change the scalar to 1 and OCR0A to 255...
  // frequency = 16000000/1/256
  //                 = 62.5KHz

  // if we change the scalar to 8 and OCR0A to 99...
  // frequency = 16000000/8/100
  //                 = 20KHz

  // and note, Output A is fixed frequency - use B instead
  // Output B is based on the duty cycle written to OCR0B;  i.e., (OCRB+1)/(OCRA+1)%
 
  OCR0B = 0;            // start with duty 0%

  // PWW is already running at this point.
  // Code after here is just testing it works

  // Debug
  Serial.begin(9600);
  while (! Serial);
  Serial.println("Duty?");
}

void loop() {
  // Testing - set the duty value from 0 to OCR0A
  if (Serial.available()) {
    int duty = Serial.parseInt();
    OCR0B = duty;
    Serial.print(duty);
  }
}
User avatar
Andrew Davie
"Gomez!", "Oh Morticia."
 
Posts: 1590
Joined: Wed Jan 24, 2007 4:42 pm
Location: Queensland, Australia

Re: PWM - Motor Control

Postby Klaas Robers » Tue Mar 07, 2017 5:02 am

Andrew, it is somewhat different than you explained.

1. The transistor can be given the full 40 mA of base current (Edit: Note the Arduino Micro limit is 20 mA), for 5-0.7 volt that is 107 ohm.... take 120. Then the collector current can be the amplification factor beta larger, if you assume 50 x, that is 2A 1 A. The motor will not draw that. The voltage then willl be a square wave with the duty cycle that the Arduino gives. The motor runs on the mean value of the PWM, so at the higher numbers about the supply voltage. The motor will define the taken current, the transistor can never conduct more, because during conducting the voltage on the collector is zero. This is called "Saturated", a voltage of zero and the load defines the current. If you choose for a higher supply voltage you end up at the lower numbers.

2. The MOSFET does the same, also a saturated square wave. The gate is insulated, so no fear for "delivering back". The gate has a quite high capacitance to ground (source) one or more nanofarads. The Arduino has to charge and discharge the gate as fast as possible, to make the switching times short. A resistor may limit the charging and discharging currents, may be to 40 mA, so the same 120 ohm is sensible. Place your little scope to the gate and see the charging and discharging slopes. If not, even the better. the output of the Arduino is designed to deliver a current of 40 mA 20 mA when the output voltage is high, 5V, and also to sink a current of at least 40 mA 20 mA when the output voltage is low, 0V. It is likely that in the low state the current can be much higher. Read your specification sheet.

Only during the slopes the transistor / MOSFET is dissipating heat. In the flat areas, or the voltage is almost zero (bottom), or the current is almost zero (top). the faster the switching, the cooler the device stays.
User avatar
Klaas Robers
"Gomez!", "Oh Morticia."
 
Posts: 1656
Joined: Wed Jan 24, 2007 8:42 pm
Location: Valkenswaard, the Netherlands

PreviousNext

Return to Andrew Davie's Arduino Televisor

Who is online

Users browsing this forum: No registered users and 1 guest