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?

Monday, July 26, 2010

wootstick 2.0: Hello, World.

If you're a hobbyist embedded developer, you've probably noticed the brewing 8/16-bit microprocessor low-cost development board arms race. (The super power: Arduino. The new contenders: MSP430 LaunchPad, dsPIC Microstick.) Even though the wootstick 1.1 was better than all of these (seriously), I've decided to let those other boards vie for the "best in class" title and move into an entirely different class: 32-bit microprocessor low-cost development boards. :)

ARM-based processors generally scare me, maybe because people tend to put Linux on them, but for whatever reason the STM32 lineup seemed inviting enough for me to try out as my first venture into 32-bit embedded processors. And by "inviting" I mean the reference manual is only 1,072 pages long. But I like that I can actually find the reference manual, and the memory and peripheral organization is similar to what I have gotten used to with other microprocessors. It was just familiar enough to me that, combined with some good recommendations, I decided to make the STM32F103 the microprocessor featured in the wootstick 2.0.


It looks very similar to the woostick 1.1. And for good reason: The wootstick 1.1 worked really well as a combined USB/XBee-programmable development board. I kept all the USB/XBee stuff and just changed the microprocessor. Because the STM32F103 has a few more pins than the MSP430F2274, I had to rearrange a few components. No big deal, everything fit perfectly...except...something's not right.... With more pins, the headers take up less space?...

I accidentally used 2mm headers. So much for sticking it on a breadboard. I should redesign it with 0.1" headers. I really should. It's not that hard to change, right?... Then again, something about 2mm headers not fitting into a breadboard is appealing to me. After all, the majority of my wootstick 1.1-based projects were custom carrier boards, to which the processor board attached. Custom boards that just as easily could have used 2mm headers. In fact, how useful is a board that can fit in a breadboard if it only leaves one row left on either side? Why not just use a single row female header? Or solder directly to it instead? 

Don't mind me...just debating with myself.

In the mean time, the biggest challenge of changing the wootstick microprocessor: implementing a new serial bootloader interface. Except, as I found out, ST already did that. It's called the Flash Loader, and you can get it here. The stock Flash Loader is not useful for wireless bootloading, because it doesn't let you manipulate the RTS and DTR lines. But, the command line version does. So, I wrote my own shell interface for it, which is way way way way easier than writing the entire bootloader interface from scratch, which I did for the MSP430. So much easier that it just worked right away:


It uses DTR and RTS to trigger the bootloader, then erases the flash and loads a new binary image. The binary image comes straight from the compiler, which is part of the IDE. As luck would have it, IAR makes a version of Embedded Workbench for ARM. I've been using IAR EW (MSP430 version) for the wootstick 1.1, since the KickStart edition is free. No learning of new IDEs. This is too easy. Well, I still have to learn how to write code for this microprocessor. The best I can do now is toggle a pin on and off, and even that I needed help with. I guess I should start reading that reference manual. After I learn how to use the timers: Harmonic Drive, the wootstick 2.0-based motor controller and 8-bit music engine.

For reference, here is the schematic for the wootstick 2.0:

Click to enlarge.

New base unit for future projects! Very exciting.

Wednesday, July 21, 2010

Cap Kart Summer Rebuild 3


In the previous post, we crossed the state of maximum entropy, having taken apart basically the entire rear of the kart and all the wiring. I'm happy to report that things are now being put back together, starting with the shiny new rear axle differential, which is pictured above (some assembly required). By virtue of bad machining good design, the precision shafts press tightly into one side of the case, but are a free fit in the other, so assembly is actually fairly easy. The finished product:


Savor the shiny multi-tone metallic look, because next time you see this it will probably be an indistinguishable cylinder of black grease. We were happy to see that it does in fact function as a differential should, even with all the screws tightened. It won't be the smoothest, straightest, or quietest differential in the world, but guess what, we made it and it works. Props to Charles for the simple concept and Max for the execution.

We had a few options for mounting the differential to the kart. There are two steel frame tubes under the rear axle that at first seemed like a prime target for welding on new bearing mounts. Upon further inspection, though, the rearmost of the two was attached to chassis ground structure with cracked aluminum clamps. So, we chose instead to mount everything to the aluminum shelf above the rear axle using our best friend, 80/20 t-slot extrusion. First, we got some of the nicest waterjet parts I have ever seen from Big Blue Saw's low-taper waterjet:

No joke, that's how they came out of the box.

 The boring part: finish-machining the bearing holes.

Angry brake caliper mount.

A new bag of t-nuts and some finicky alignment later, and we have a mounted rear differential:


All that's left to do, mechanically, is cut the rear axle and reattach the two halves to the outputs of the differential. The proposed method of attachment (34mm ID hollow shaft to 1-1/4" solid keyed shaft) involves thin-walled aluminum sleeves, 8-32 screws and small pattern nuts, and hot glue. I'll leave it at that.

The controller and new wiring have successfully survived power-up, so nothing is wired up backwards. We're taking the approach of writing the new software in its entirety without testing it piece by piece. It will just work on the first try. For my part, I became obsessed with having a flashy telemetry display after I watched the Solar Impulse videos. (Is it sad that after watching video of a solar plane, I decide the thing I want to do is write new telemetry software?) Anyway, I upgraded the existing telemetry system to have a scrolling plot:

 

Left-clicking on any variable sets it to the blue scope trace. Right-clicking sets it to the yellow trace. The telemetry is also recorded for later analysis and can be synced to video. In other words, we just might have the most over-instrumented go-kart in the world.

And yes, I created a GUI interface using Visual Basic. See if I can track a go-kart.

Next: movement.

Monday, July 12, 2010

Twitch, Jr. - Did I mention I like driving robots?

It's true. I think I like driving them more than I like building them. That was the real motivation behind this project. Some time ago, I saw this video of the original Twitch, made by FIRST Team #1565, and I decided that it would be just about the most fun robot to drive ever. It's not quite an omni drive, even though it has omni wheels. Nor is it a crab drive. It's a linkage drive. The wheels are constrained to move together by a set of linkages. I guess it doesn't offer many advantages over other omni drive configurations. But come on, it's awesome!

So I set out to build one a while back. A small one. With no particular use for it in mind. It's so arbitrary that I couldn't decide on what size to make the main chassis, so I chose letter 8.5"x11". (But is it portrait or landscape?) Anyway, I got to the point where I saw that it would all go together nicely and then realized that I should probably finish my thesis and get through 2.007. All that was left was...well, all the electronics and programming... Now that it's summer, I have no excuse not to finish.


I thought the wiring would be hard, given the number of moving parts inside the Twitch sandwich, but it actually wasn't that bad. I converged on a clam-shell-like wiring layout: you lay all the interconnects from the top panel to the bottom panel along one edge. This allows easy assembly and disassembly without detaching wires.


Here's another view.  The motor controllers are Pololu Trex modules, which I am very pleased with. The monster 1/4-scale servos are Vigor VS-11's. These are the servos we used for 2.007. They are ultra high-torque...like, over 1Nm. More than their equivalent Hitec servos. But as a consequence of that, they also draw a ton of current. Like, 2-3A each at full load. For that reason, I got a 7.5A BEC to power them. The only problem is, the microcontroller's LDO regulator is fed by the same 6V bus. And there isn't enough capacitance at the output of the BEC or in the servos to adequately decouple the switching. I found this out when Twitch started...well...twitching...under load.


Cue massive amounts of capacitance. 3300uF on the 6V bus, plus another 1000uF following the input diode on the line that feeds the microcontroller's LDO regulator.

Finally, it was time to close up the clam shell. Thanks to some serious Design For Assembly, I left myself an access hole so I could tighten the final servo horn screw with the clam all closed up. Unfortunately, I made the hole too small, so I still had to open everything, drill it out, and try again.


And that's that.. And this is the first test-drive:

Thursday, July 8, 2010

wootstick v2.0: I learn a 32-bit processor, finally.

Way back in 2007, I designed arguably the most useful piece of electronic hardware of my short and fake EE career, the wootstick v1.1:

Can you spot my reflection?

It's a 3.0"x1.1" board with all the things I thought I might find useful for developing embedded hardware. Namely, a microprocessor with USB and wireless connectivity. The processor is the Texas Instruments MSP430F2274. It's 16-bit, which puts it squarely between the 8-bit ATmega chips made popular by Arduino and 32-bit ARM processors that are now very commonplace in consumer electronics. "USB" really means UART serial through an FTDI chip, so it shows up as a COM port on my computer. And "wireless" means it can interface to an XBee radio.

The name wootstick came from the Wireless bOOTloader, a hardware and software combo that allows the microprocessor to be programed through the XBee radio in addition to the USB serial connection. Very few embedded development kits can match this feat, and none that I know of use a single board design that can switch between being the programmer and the remote target. Thanks to master programmer Cam, the wootloader for v1.1 has been ported to a cross-platform version as well, though we never did get around to distributing it...

In fact, my only real regret about the wootstick v1.1 was that we never did get it "out there." Not really to make money off of it, just because it is an interesting development platform and we could have used some extra third-party support. However, I've personally used it for just about everything, so in terms of return on my time investment, it's been more than worth it. It's one of the few things I've ever made that works well enough for me to rely on it as a unit in larger systems.

But, a few months ago, I finally reached its limit. Remember?

CPU Usage: 99.999999%

I spent almost a month trying to implement field-oriented control on two brushless motor simultaneously.With no hardware multiplier, it was kind of like doing long division in your head. If your head could only store ones and zeros and could only add them to each other or shift them around. As it turns out, my theory that you can make software do anything was not disproved and I did get it to work, as described in this double-post. But the entire experience left kind-of a bad taste in my mouth and I realized that it was time to upgrade my dev kit. But spring semester kept me busy enough that I postponed any such upgrade.

Well, it's summer. No excuses now.

wootstick v2.0

My goal is pretty simple: Keep all of the functionality of the wootstick 1.1, but use a 32-bit processor. And the processor I've chosen for the job, based on a couple of strong recommendations and my own research, is the STM32F103. It's based on the ARM Cortex-M3, and has the following kick-ass features that suit my purposes very well:
  • 32-bit, single cycle multiply and hardware division
  • 16MHz external oscillator w/ PLL = faster than I ever need
  • Six timers. That includes three 16-bit ones with four PWMs each. One even does full synchronous three-phase output with dead time. That's just cheating.
  • Dual, simultaneous analog-to-digital conversion. I'm thinking true synchronous current measurement.
  • Factory-programmed bootloader activated through the UART.
The form factor and peripherals on the board are identical to the v1.1...XBee headers and an FTDI serial/USB converter, plus a small 3.3V regulator arranged on a stick-like board that can fit in a breadboard with one row to spare on each side. Hopefully it just works, but for some reason my vX.0 hardwares seem to have issues. So maybe it will actually be v2.1 that I wind up using for the next three years. We'll see.

As for the first test project, it's an idea I had a long time ago that actually utilizes the numerous and powerful timers: An 8-bit music engine running on top of a 3-phase motor controller. Imagine the musical scanner, but with three tracks. If you're unfamiliar with 8-bit music, here are a few gems:

Spintronics - No way to leave (featured in the awesome GLiP project demo)

Anything with three tracks will work. The basic premise is that each phase PWM will have its frequency controlled by the music engine. The duty cycle will still be handled by the field-oriented control logic so that, in theory at least, it will still be a fully-functional motor controller. Of what possible use is this? Well, besides having ring tones for your EV and alerting pedestrians of your presence...not much. It's just a really good performance benchmarking test for this chip. 

But first I need to learn how to use it... Schematic and board files to come if it works.

Tuesday, July 6, 2010

Cap Kart Summer Rebuild 2


Actually, that tiny little 2.2μF capacitor is of critical importance. It supplies the gate drivers, which are specialized chips that turn on and off the high-power transistors (MOSFETs) in the Cap Kart controller. They were formerly 1μF capacitors, but these new monster FETs have a gate capacitance of 0.25μF. So it's like if you have a bucket of water and a small garden hose to fill it and...well there's another taller, skinnier bucket and..... I don't know. It just needs more capacitance, okay?

And even with more buffer capacitance and smaller (5Ω) gate resistors, it still takes an eternity (i.e. two microseconds) to turn on and off these behemoths:

You can do it! Can you?

 It's not the world's snappiest gate drive, but the passive shoot-through protection integral to this half-bridge design is working. You can tell  by the delay between one gate turning off and the next one turning on. All that's left is some color-coordinated wiring:

Each gate gets a local pull-down resistor and 16V Zener diode for protection.

And the controller is pretty much ready to go:


Well, the hardware part anyway. The part that didn't change. As for the new control software...

What the...?

It's complicated. Or maybe it's simple. Either way, it's progressing. The goal is to implement constant-power field weakening on the SepEx motor. Before, we used fixed "gears" and a manual shifter to set the field strength. This is fun, but not as clean as a constant-power field weakening setup.

On the mechanical front, we've finally passed the state of maximum entropy and started putting things back on the kart. The battery trays were cut down to accommodate the smaller lithium-ion battery packs...

...which look damn good.

As it turns out, we have room for four more cells if we decide we need them. But for now we will be running 12S (39.6V nominal). This is a bit higher than the 36V nominal lead-acid setup, but it will be more than offset by a higher internal resistance per cell (more voltage draw-down). That's where an extra 12V from the capacitor may come in handy.

To accommodate the new motor orientation, a slot for the chain had to be added to the All-Purpose Mounting Shelf. (Seriously, everything we've added to the kart except battery trays mounts to this one quarter-inch plate.) This operation involved a bit of 80/20 mill fixture trickery.


The chain will pass down through the shelf to the custom-built axial differential. Speaking of which:


One side of DiffMaster Max's baby is done. The main radial bearing pressed cleanly into the side plate (on the side not visible). A thrust bearing (McMaster PN 5909K38) sits between the large output gear and the side plate. A spring clip holds the large gear in place on the output shaft. The four precision-ground gear shafts (three pictured) are to be screwed in from both sides. Two more shafts will provide structural support. Here's a video of the half-diff. Next: finish the second side plate, add spacers, and final assembly into the rear axle.

 Coming soon to a parking lot near you.