Showing posts with label arduino. Show all posts
Showing posts with label arduino. Show all posts

Wednesday, July 28, 2010

Arduino Interrupt-Driven Servo Routine

Why do I keep developing for Arduino?!? Anyway, unlike my recent endeavor into high-power Arduino shield design (HexBridge, HexBridge BLDC), this is post is pure software.

I've been tasked with creating an Arduino servo library that doesn't suck. I'm not saying that the existing servo libraries suck or don't suck; I'm just saying that my semi-self-imposed task was to make one that doesn't suck. In 2.007, we used BASIC Stamps to generate servo pulses with the wonderful PULSOUT command. It's a single-line command that generates a single servo pulse. But I should go back one step. This is a servo pulse:


It's a periodic signal sent to an RC servo or speed controller (on the white/yellow wire) that sets the position of the servo, or the speed if it's a continuous rotation servo or speed controller. A 1.0ms pulse represents full reverse, a 1.5ms pulse represents neutral, and a 2.0ms pulse represents full forward. Any position or speed in between can be achieved, though the output isn't linear. The pulse signal has no physical meaning; i.e. it does not represent the voltage being applied to the motor inside the servo. It's just conveying information.

One very important thing about this signal: It must be sent to each servo approximately every 20ms. Most servos aren't picky about the exact timing, but if you wait too long, they will time out and turn off. For this reason, a microcontroller commanding servos must dedicate some effort to continuously sending out these pulses. Furthermore, if you use a command like PULSOUT on the BASIC Stamp, the microcontroller can do nothing else for the duration of the pulse. So if, for example, you needed to control 8 servos using PULSOUT, the BASIC Stamp would be tied up for up to 16ms just generating pulses, leaving very little time to do anything else before the pulses have to be sent out again.

Before I get yelled at: There is a servo library for Arduino and it is much more efficient than PULSOUT on a BASIC Stamp. Best I can tell (and please correct me if I'm wrong), it sorts the servo pulses from shortest to longest, starts them all simultaneously, and ends them in reverse length order using a hardware timer to keep track of the duration. So at most, it ties up your processor for 2ms (for all the servos). Pretty good. I would say this doesn't suck. But I would also say that I can do better.

There is obviously no reason to sit around doing nothing during the off time of a servo pulse. But there's also no reason to sit around doing nothing during the on time. It's only the transitions that require the processor to actually do anything. And with a hardware timer and interrupts, it's possible to set up a servo routine that runs entirely in the background, taking control only briefly to start and end a pulse. This is what I call an interrupt-driven servo routine. Here's the full code, only about 50 non-comment lines to run 8 servos in background interrupts. For the rest of the post, I'll step through it line-by line. It's pretty alpha, so feel free to yell at me for "doing it wrong."
// Interrupt-based Arduino Servo Template
// v1.0 | 7/28/2010
// scolton@mit.edu

// servo PWM value
//  |-----------|-----------|
// 250         375         500
// 1ms        1.5ms        2ms
unsigned int servopwm[10];

// servo index, used to multiplex Timer1
unsigned char servoindex;
Declaring some global variables. servopwm[10] is an array that stores the servo commands. Because of the way the timer will be set up, the value 250 corresponds to 1ms, 375 to 1.5ms, and 500 to 2.0ms. So, you effectively get 8-bit PWM resolution. But, an int data type is still required to hold numbers greater than 255. servoindex stores a looping index to which servo is being commanded at any instant in time.
// INTERRUPT SERVICE ROUTINES
// --------------------------
ISR(TIMER1_COMPA_vect)
{
  // interrupt on Timer1 compare match
  // (end of the servo pulse)
 
  // end servo pulse on the appropriate pin
  // faster version of digitalWrite(servoindex, LOW);
  if(servoindex <= 7)
  {
    PORTD &= ~(1 << servoindex);
  }
  else
  {
    PORTB &= ~(1 << (servoindex - 8));
  }
 
  // move on to the next servo (limit to 2-8)
  servoindex++;
  if(servoindex == 10)
  {
    servoindex = 2;
  }
 
  // set the compare match to the new servo pulse duration
  OCR1AH = servopwm[servoindex] >> 8;
  OCR1AL = servopwm[servoindex] & 0xFF;

  return;
}
This is the section that is not at all part of standard Arduino syntax. (As my friend pointed out, "You're not really programming an Arduino servo libarary; you're programming an AVR servo libary.") While Arduino does have some limited interrupt support, it's for external interrupts, such as a piece of code that is instantly triggered when a button is pressed.

The ATmega328 chip has the capability to generate internal interrupts as well. Here, I use one of Timer1's compare match interrupts. Timer1 is a hardware counter that ticks at a regular interval, which is configured later in the code. When it ticks to the 16-bit value stared in the OCR1AH and OCR1AL registers, it will trigger this compare match interrupt. Upon triggering, this bit of code takes over, interrupting whatever other code was running, and sets the servo pin low using a faster,more direct version of digitalWrite(). After setting the current servo pin low, it increments to the next servo and resets the compare match register to a new value. Then, the program returns to whatever code was previously running. The whole operation takes only a few microseconds.
ISR(TIMER1_OVF_vect)
{
  // interrupt on Timer1 overflow
  // start of the next servo pulse
 
  // start servo pulse on the appropriate pin
  // faster version of digitalWrite(servoindex, HIGH);
  if(servoindex <= 7)
  {
    PORTD |= (1 << servoindex);
  }
  else
  {
    PORTB |= (1 << (servoindex - 8));
  }
     
  return;
}
// --------------------------
This is another Timer1 interrupt that triggers on a Timer1 "overflow." As will later be configured, Timer1 counts to the number 511, then loops back around to 0. At the same time it loops, the new servo pin is set high. So, with the two interrupt routines, the servo pin turns on at 0, then turns off at some value between 250 and 500, which sets the pulse length. Because it loops through 8 servos, a servo that was just pulsed will sit idle while the next seven are commanded. Thus, the total sequence takes a bit over 16ms.
// REGULAR ARDUINO CODE
// --------------------

void setup()
{
  // TIMER1 SETUP FOR SERVOS
  // -----------------------
  // set Timer1 to clk/64 and 9-bit fast PWM
  // this is a period of 2.048ms for 16MHz clk
  // when multiplexed on 8 servos, this gives a 16ms period
  TCCR1A = 0x02;
  TCCR1B = 0x0B;
  // enable Timer1 compare match and overflow interrupts
  TIMSK1 = 0x03;
  // -----------------------
 
  // set initial servo index 
  servoindex = 2;
 
  // set all servos to neutral (375 = 1.500ms)
  // set all servo pins to outputs
  for(int i = 2; i <= 10; i++)
  {
    servopwm[i] = 375;
    pinMode(i, OUTPUT);
  }
}
Good old Arduino code. Oh wait, what's this hexadecimal crap? Well, it's required to setup up Timer1 at the right frequency, and resolution, and to request interrupts on compare match and overflow. It's basically setting some switches that define how Timer1 works. Arduino does this for you most of the time. If you really want to know more, RTFM (starting on page 114 of 562). This would be a good time to point out that because I take over Timer1, Arduino functions that depend on it, such as Pin 9 and 10 PWM, won't work.
void loop()
{
  // Your loop code here.
  // No need to worry about delays being longer than 20ms!
 
  // Set PWM values like this:
  //  |-----------|-----------|
  // 250         375         500
  // 1ms        1.5ms        2ms
 
  servopwm[2] = 250;
  servopwm[3] = 275;
  servopwm[4] = 300;
  servopwm[5] = 325;
  servopwm[6] = 350;
  servopwm[7] = 375;
  servopwm[8] = 400;
  delay(1000);
}
// --------------------
This is your job. Setting the value of servopwm[i] between 250 and 500 is only required once. The interrupts will continue generating servo pulses at that value until you command a new value. So, your loop is free to use delays of longer than 20ms, or functions that take a long time such as pulseIn() and Serial.print().

And that's it. Not that hard, right?

Saturday, June 26, 2010

HexBridge Shield BLDC and this silly thing from Texas Instruments

First, the Arduino HexBridge Shield BLDC challenge has been answered. In this post, Charles shows that it's a relatively simple matter to write control code for the Arduino that will turn the HexBridge into a viable brushless DC motor controller. As I mentioned (but was too lazy to prove) in the original post about the HexBridge concept, BLDC control involves setting one phase as high (or PWM), one as low, and one as off/floating. By cycling through all 3P3 = 6 permutations of this, you get rotational motion. The trick is linking this to the Hall effect sensors (or something else) that determine which state should be active. Here's the light load test video.

Dual BLDC control (when would you ever need that?) is simply replicating this on the second set of three half-bridges. Or you can run one BLDC controller and one reversible brushed DC controller using two out of the three remaining outputs. Or...well, you get the idea. Configuration flexibility proven, the next step is probably heavy load testing to see its limits. So far, I've tested to 20A. How about 50A?

Now for a bit of a rant...


This is the new Texas Instruments MSP-EXP430G2 LaunchPad. It's a $4.30 (clever) development board for the MSP430 value line devices. Does it look like a red Arduino? Can you imagine the developers' meeting? I can:
Hey guys, we're getting killed in the entry-level market by this Arduni thing. How are we going to get new engineers and embedded developers to use our product in the future?
How about we like...create something that looks like it, but with our cheapest chip. And we'll make it red. And we'll sell it at a loss, to get people hooked.
Now, I love the MSP430 microcontroller line. I use the MSP430F2274, specifically. It is significantly better in many ways than the ATmega328 used by the Arduino. But the chips they use on this board, the MSP430 value line, are actually somewhat lacking in features. For example, they have only one timer (vs. the ATmega328 with three). Timers are critical for the kind of stuff I do, like motor control, so that is a little disappointing. But I guess for the entry level, at $4.30, it's still a good way to get people engaged.

Except...do people use the Arduino because it's cheap? Or has a convenient form factor? Or is it because it's easy to program? I'm tempted to say it's the latter. And the MSP430 line, while being a pretty standard application of C programming, does not have the convenient (did I really just say that?) simplified programming language or the backing of a giant user group that Arduino now has. So I'm not sure. Maybe if they had come up with the idea of making an easy to use MSP430 dev board a few years ago, it would have caught on and been developed more.

Oh, wait.

How is that quarter not shorting out the 3V3 and GND pins?

That's right. Cam and I thought of putting an MSP430 chip on a dev board in 2007. (Seriously, look at the date on the board.) The thought process was essentially:
Hey check out this new Arduino thing. I wonder if we could make a better one.
How about using an MSP430F2274, and making it small enough to fit on a standard breadboard? And integrating an XBee header. And, heck, let's just make it USB and wirelessly programmable too.
We called it the 430stamp, or, informally, the wootstick (short for Wireless bOOTloading...sort of). It is arguably the most useful thing I have ever made. Seriously, I use it for everything. Doesn't it look familiar? It's the signal board for the 3ph Duo controller, where it executes simultaneous field-oriented control of two brushless motors. It's also the transmitter AND receiver for my RC car. There's one for programming the Cap Kart wirelessly. They were briefly used in 2.007. In other words, they work well enough that I rely on them. Very few things I've ever made have gotten to that status.

So, will I be buying any MSP430 LaunchPads? Probably not. So many people have told me about them now that I might just buy a few out of curiosity. But I already have a good, reasonably cheap (~$20) MSP430 solution. I wouldn't mind seeing more people use these as opposed to Arduino, just because they will have to learn how lower-level embedded programming works. (Not assembly...but low-level C. Fewer user-friendly libraries.) Overall, a $4.30 dev board from TI is a good thing, but I'm not necessarily impressed.

Saturday, June 12, 2010

Arduino HexBridge Shield v2.0

This is what I meant to do the first time.

Recap: I have lots of motor controllers. Some of them work, others do not. But they are almost all based on a combination of MSP430 microcontrollers and a totally isolated, totally modular gate drive solution. Recently I've been feeling the pressure to create a more...populist...solution, which basically means putting it on an Arduino shield. So that's what I did.

Even though v1.0 was a total failure, it gave me a chance to refine the design in ways other than making it not go crazy as soon as it was under any load. I had some extra time to sit around and think about exactly what I would want in an Arduino-based motor controller. Well, not what I would want, but what one would want...you know. ;) Here is the important functionality that I was targeting:

Six semi-independent MOSFET half-bridges. I say semi-independent because each set of three shares a single PWM output. So, at most it can control two motors. But these could be brushless (3-phase) or brushed motors (H-bridge). You can also put channels in parallel for higher current capacity. There should be a simple state selection for each bridge: PWM, low, or float:


This leaves a ton of flexibility, and only occupies one I/O pin per phase, plus the two PWM pins. The "float" state is important for BLDC control or for disabling a phase entirely. I talked about some of the possible configurations in the previous post. On to the new board:


The FETs are the same (IRFS3107 or IRFS3207 or any other D2PAK), but they are arranged differently this time. All the high-side FETs are on the top, and all the low side FETs are on the bottom. This let me move the entire power section out off the side of the board, away from the Arduino and the logic. To conserve horizontal space, I switched to 12-13mm vertical capacitors. They can face up , or down into what will likely be dead space next to the Arduino main board.

Speaking of dead space, I stuffed the DC/DC converter input and output capacitors in the gap between the arduino USB port and power jack and moved everything out of the way of a potential ProtoShield above the whole stack:

Tetris win. Isn't the Arduino supposed to disappear now or something?

Also, the whole board is 2.1" wide now...the width of the Arduino main board. So it is much more compact than the original version. Here's what it looks like all wired up:


Form factor aside, DOES IT WORK? I made some major changes to the gate drive, switching over to the IR21844 integrated half-bridge driver. This is a wonderful chip that automatically handles synchronous rectification with shoot through protection. It also has separate signal and power grounds that can float within ±5V of each other. Because of this, I abandoned optical isolation of the PWM signals for the first time in two years. This worried me, but it seems to work just fine. The ancillary benefit is that the old gate drive, which cost about $15 per half-bridge, is replaced by a $2.50 chip.

To execute the truth table I specified, it needs a bit of supporting logic, in the form of AND gates, comparators, and inverters, but these are dirt cheap. The cool part is the high-Z detector, a window comparator that checks to see if each selector pin is being driven:


If the input pin (which enters from the top of that schematic) is not being driven high or low, it will be pulled to 2.5V by the matched resistors. This is between the threshold of the window comparator, so the output will indicate the high impedance state, and the gate drive for that phase will be disabled. To get to this state from the Arduino, you just set that selector pin as an input.

I started by testing it as an H-bridge with a low-current DC motor, but I quickly got bored and scaled up:

 

This is the SepEx configuration that I described in the previous post. Three of the six outputs are tied together to power the high-current armature winding. Two of the remaining outputs are configured as an H-bridge to control the field. Reversing the field changed the direction of rotation, so the armature need not be reversed. The test motor is from the Cap Kart, which is getting a makeover soon. It's a wheels-up test, so the loads are fairly light (20A armature, 10-15A field current). None of the FETs got warm over the course of testing. If I had a legitimate way of doing current feedback, I would actually put it on the ground and drive it. But for now it's just a demo to show the flexibility afforded by having six output channels.

So, since I have no intention of making more or selling these it's up to you to make them! Here is probably everything you need:

HexBridge Shield Design Files (ZIP, 779kB)

In that folder you will find the schematic, EAGLE files, bill of materials, Gerber files, state table, and sample Arduino code for running the SepEx motor. I would especially like to see a brushless motor controller next...and I have a feeling it will happen very soon.

Thursday, May 27, 2010

Arduino Hex-Bridge Shield: Concept and v1.fail

So, I guess I am finally coming to terms with living in a world where Arduino rules. If you've been living under a rock, an Arduino is an AVR-based microcontroller development board with an array of peripherals, a huge library of code, and a large user base. They have become a staple of DIY projects and "Arduino compatibility" of DIY hardware is now a legitimate concern.

I'm faced with two options, then. One: I could continue to rant about how I designed most of my current TI MSP430-based hardware to do everything the Arduino does, but better, and how the Arduino is not a replacement for learning how to design circuit boards and program microcontrollers directly. Or, two: I could drink the Kool-Aid and make something useful that also happens to sits on top of an Arduino.

Cue the Hex-Bridge Shield. (Scientific name: Large Arduino Switching Shield with Field Effect Transistors.)

Here are some pictures of v1.0, a failure for reasons which I will discuss below:


Hex-Bridge, Top Side: MOSFETs.

Sidecap.

Hex-Bridge Bottom Side: Gate Drive.

Okay, so calling it a shield is a bit of a stretch. It doesn't really sit on the Arduino so much as the Arduino sits under it. (Can you see where?) It's pretty compact for what it is, though: six semi-independent, high-power MOSFET half-bridges, with all the gate drive circuitry included. Each bridge was a version of my modular half-bridge design, which I've used in every motor controller I've built for the last two years.

The gate drive is based on a design we originally did for the Cap Kart long ago. Each FET gets its own 2.5A gate drive optocoupler (HCPL-3120). An optocoupler passes a signal through an LED and photosensor (packaged in a single chip) to electrically isolate a circuit, which is useful for protecting the microcontroller from high power switching noise. These particular optocouplers are designed to drive the gate of a MOSFET, on when the LED is on, off when the LED is off. By arranging two LEDs in anti-parallel and including a low-pass filter, passive shoot-through protection for each bridge is achieved.
One high-level half-bridge schematic.

Notice how the LEDs are arranged so that only one can be on at any given time. Also notice that the low-side gate drive optocoupler is fed by an inverted signal. This creates synchronous rectification, mirroring the PWM on the low-side to minimize diode losses and allow regenerative braking. This is just a high-level schematic, leaving out details such as the shoot-through delay filter and the gate resistors and protection components. But it should give the general idea. One down side of this method is that it requires a separate high-side supply. This makes it expensive (about $15 per bridge for all the gate drive components, which is more than the FETs themselves). Integrated half-bridge drivers such as the IR21844 provide a less expensive solution. v2.0 will use these.

The MOSFETs are IRFS3107 surface mount packages. They have a typical Rds of 2.5mΩ. (For reference, that's about the same as one foot of 14AWG copper wire.) These are monstrous 75V FETs that can, with proper heat sinking, probably handle "high-double-digit" currents. Throw a fan on and I don't even want to speculate how much current you could pass through it.

IRFS3107 Output Characteristic. 
0.2V at 85A...way better than an IGBT.

Everything on the board is sized for an input voltage of 18 to 48V. On the input, there is 990μF worth of 63V electrolytic capacitors. (These could easily be replaced with 50V or 35V capacitors of higher capacitance for lower-voltage, higher-current applications.) An LM2575HV switching regulator provides 15V @ 1A to power the Arduino and the gate drivers.

An here's where the failure comes in. Take a look at an Arduino. (I know you have one.) All of the digital signal pins are on the top row of headers, but the +5V, VIN, and GND pins are on the bottom row. I made the horrible mistake of running lone +5V and VIN lines from the LM2575 and the logic ICs, around the entire high current switching board, to the bottom headers. Bad bad bad bad bad bad. I can't even describe how bad it was.

By eliminating those traces and running an external connection for +5V, GND, and VIN, I was able to eliminate at least the most dramatic of the failure modes. While in this hacked state, it was able to run 20A continuously through a single phase with just natural convection. The resulting FET temperature was about 90ºC. Extrapolate as you see fit for a current rating with paralleled FETs, heat sinking, and/or fans.

So it has the potential to work, but in terms of a board design, this one is total scrap. Version 2.0, which is already in the works, will be better in every way. (Smaller, cheaper, better mechanical and thermal layout, easier to stack additional shields on.) But since I have nothing much to show for it yet, I can only tell you the design intent:

It would be great if the Arduino had six completely independent high-resolution PWM channels like some chips do. Then you could control two brushless motors. You could even do sinusoidal field-oriented control! Well, maybe not. But anyway, it would be cool if this shield could be used to control two brushless DC motors using square wave drive, at least. With six phases to play with, that should be possible. The trickiness is in multiplexing the PWM signal from Arduino pin 9 and 10 out to each set of three phases. Thinking about NAND gates makes my head hurt, but fortunately I got some help in coming up with a creative solution using an analog switch:

Chuxxorplexer

Actually, the real key is using one selector pin to completely control each bridge, including being able to disable it. This is where tri-stating comes in. If the selector pin is driven high, the relevant PWM from pin 9 or 10 is ANDed in and the bridge executes synchronous rectification. If the pin is driven low, the low-side FET is steady on, grounded that phase. If the pin is set as an input (high impedance), neither FET is on and the phase floats. This allows full four-quadrant control while still preserving the "off" state. Don't ask me how the analog switch accomplishes all that in one shot...but it does. Just stare at it until it make sense. That part totally works. This is what comes out of the magic box:


Digital pins 2-7 are used to select a state for each of the bridges. The bridges are labeled {A, B, C, U, V, W} from left to right. Each bridge can have one of three states:
  • When its state pin is written HIGH, the bridge executes synchronous rectification based on the PWM present on pin 9 for A, B, and C, or pin 10 for U, V, and W. This means the high side FET will be PWMed with a duty cycle set by analogWrite(9 or 10), while the low side FET does the opposite.
  • When its state pin is written LOW, the low side FET of the bridge will be steady on and the high side will be off. This is effectively like grounding that phase. The PWM does not matter.
  • When its state pin is set as an INPUT using pinMode(), it will turn off both FETs and the phase will float. This is useful for disabling the controller or for the undriven phase in BLDC control. Note that even in this state, diode flyback can occur.
Unfortunately, the Chuxxorplexer won't make it into v2.0, which uses a different gate drive configuration entirely, but the functionality will be preserved using a much less elegant brute force set of logic chips. The important thing is that the three-state control in the table above will be preserved. This is, IMO, the cool part, giving almost complete flexibility of bridge configuration for one or two motors.

Here are a few examples:

 Monster H-Bridge

The monster H-bridge can be achieved by setting {A,B,C}={1,1,1} and {U,V,W}={0,0,0} for forward, or {A,B,C}={0,0,0} and {U,V,W}={1,1,1} for reverse. This parallels three bridges to make a super half-bridge, then combines two super half-bridges to make the full H-Bridge. This gives the highest current reversible control with regenerative braking.

Dual H-Bridge

The dual H-Bridge, also known as the drive controller. This can be used to drive the left and right sides of a tank steer robot. Since each bridge only uses one FET per leg now, the current capability is lower than the single monster H-bridge. To get this configuration, {A,B,C} = {1,0,X} or {0,1,X} for forward and reverse and {U,V,W} = same idea. X means that the unused phase is set as an INPUT, left floating. Hey, why not use the two unused phases to make a third H-bridge? Because there are only two PWM channels. Sorry. But the next one should be obvious...

Hey now...

The dual-channel BLDC controller, something I've seen before somewhere. Basically, you would cycle through every permutation of {1,0,X}, one phase driven, one grounded, and one floating. Except now you're on your own for how to achieve electronic commutation. One idea would be to use the floating phase and an analog pin to do back EMF-based sensorless control. Another idea would be to use Hall effect sensors and interrupts.

Monster Half-Bridge

The monster half-bridge, also known as the weapon or spin controller. It can only go in one direction, but it parallels all the MOSFETs for the highest possible current capability. This is actually not a "safe" configuration, since it is possible (and likely) to shoot-through from ABC to UVW if the PWMs are not in sync. To get this configuration, use only one PWM, 9 or 10. Set the other as an input and tie them together with a resistor. If you don't do this, instant destruction will occur. Along the same vein, there is also a monster BLDC controller configuration that ties A to U, B to V, and C to W.

Lastly, my favorite configuration: the SepEx controller:

SepEx Controller

This combines a super half-bridge with a regular H-bridge. The super half-bridge controls the high-current armature winding of a separately-excited DC motor. (By the way, don't bother reading the description of the SepEx motor's regenerative braking method on that page...it's totally wrong.) The smaller H-bridge controls the low-current field windings, allowing the field to be varied and reversed. Now, I don't want to pretend that this controller is powerful enough to drive a D&D SepEx motor under full load...it's not. But conceptually it can be configured for the SepEx by using {A,B,C} = {1,1,1} and {U,V,W} = {1,0,X} or {0,1,X}. Actually, don't I have a SepEx motor somewhere that I can try this with?

Oh right, there it is.

I'll leave off with a teaser for v2.0:

 Can you spot the layout improvements?