Tinytroller: More Tinytrolling

Tinytroller sucks. It’s terrible and I wish I never designed it. Who the hell tries to make an Arduino powered brushless motor controller anyway?

Okay, so it isn’t quite that bad. I took today to finish knocking out some basic square wave commutation code in the manner of Melontroller (which, by the way, is finally on my Motor Controllers page). As with any new design, there are some issues both small and large. The board has been found to be quite noisy – at higher motor speeds, the Arduino tends to crash and reset. There is also a minor, possibly software-induced issue with running at low speeds which appears to cause current draw spikes (also causing the board to crash).

The first assembled Tinytroller board has been beaten to hell and back to observe the symptoms of the problems, so it might be worth my while to just make a new assembly to test from. Here’s the recap of the limited, and slightly disappointing, testing from today.

Well, the first fail was discovering that I misspelled my own website on my own motor controller. Seriously… what utterly epic fail. This just tells me I should stop sending boards out at 3 in the morning after hitting “APPROVE” on all my Design Rule Check errors without looking at what they actually are. Not that it would have mattered with some silkscreened text, but I think I got lucky that this board didn’t need any Little Blue Wires.

When I was confident that my software would do the thing I thought it did, I removed Kitmotter from the remains of its box mount (which sadly did not survive the return trip from Singapore) and chucked it up on my rotary indexing fiture as a mount. Kitmotter is about the most normal brushless motor there is, so if Tinytroller couldn’t run it, something was wrong.

On Tinytroller, I tried a little more software hackery than Melontroller. Melontroller’s commutation code is a dumb polling loop that runs as fast as possible (never clocked; after all, what I don’t see can’t hurt me in software, right?). If I planned on doing any closed-loop control with Tinytroller, I had to make sure that events occurred at a known interval. This time, I elected to use the Timer 1 Overflow native interrupt in the ATmega328 microcontroller. Timer 1 is clocked at 8kHz because it is the PWM clock for two of the phases, and an overflow bit is set every time a full PWM period passes (when Timer 1 is finished counting up to its post-prescaler overflow value and resets), and an interrupt can also be generated based on it. This is all in the ATMega328 manual, which I actually read for once… imagine that. It’s a little more hardcore than pure Arduino, but I needed the functionality in this case.

  [...setup() code here]
  TIMSK1 |= (1 << TOIE1); //Enable Timer 1 Overflow Interrupt
  Serial.begin(19200);
}

//Use Timer1 OVF interrupt at PWM frequency to switch
//state table
ISR(TIMER1_OVF_vect) {
  digitalWrite(LED,HIGH);
  //Bring in PORTD, which are digital pins
  //Only 5-7 are used (PORTD 5-7). Erase the lower bits not used.
  //Keep history of 2 motor states
  last_good_state = state;
  state = PIND & 0b11100000;
  [...rest of state table here]
  digitalWrite(LED,LOW);
}

So I moved the state table into the timer 1 overflow interrupt service routine. The controller therefore does something predictable 8000 times a second – right now, the only thing it is doing is reassigning which phase should be driving, which should be held low, and which phase should be left floating.

I took a hint from the most flamboyantly EE person I knew and timed how long the Interrupt Service Routine takes to run. At the very start, I set a pin (in this case the LED pin) high, then drop it low again after everything is done and the ISR is about to exit. This shows me how much “processor budget” I have remaining. From the scope shot above, the ISR takes up about 20% of the processor’s attention, leaving plenty of time to do other things.  If your interrupt triggered prodecures took up a significant amount of the processing time, then you’re boned.

I noticed I couldn’t get Kitmotter to run smoothly at all – it would run, but would be consistently stuttery, almost feeling like it was traveling backwards for one portion of the cycle, and draw tons of current. An attempt to increase the throttle (to beast it?) got me this blown trace. The FETs were fine – you can’t hurt the IRFS3107-7 with a 1oz copper trace.

After I pound all the bugs out of this board design, I should really get it remade in 4oz copper – at minimum 2oz, because 1oz is like, aluminum foil thickness.

This was about where I realized that high current spikes were causing the Arduino to crash and reset. The failure mode was sudden stopping of the motor (generating a huge reverse current spike which could have taken out this trace) and latchup of the microcontroller, requiring a reset button press. It would keep working fine after that.

So clearly the solution is to run the MITERS Public Etek, right? The Etek also had the same troubling stuttering. Normally, a sensor and phase wire combination can be found which (assuming properly timed sensors) results in a smoothly running motor. I could find no such thing for either Kitmotter or the MPE, and spent quite a few minutes pouring over my state table code looking to see where I might have forgotten to transition a pin properly.

…but not before managing to bomb another trace. Oops. Time to patch it up with copper braid…

It turns out the true cause of the stuttering was caused by an innocuous logic error in my variables:

[...]
#define SDA 2 //PD2
#define SDB 3 //PD3
#define SDC 4 //PD4
[...]
#define AIN0 4 //PC3
#define AIN1 3 //PC4
[...]
void setup() {
  [...]
  pinMode(SDA,OUTPUT);
  pinMode(SDB,OUTPUT);
  pinMode(SDC,OUTPUT);
  [...]
  pinMode(AIN0,INPUT);
  pinMode(AIN1,INPUT);
  [...]
}

It looks like I forgot that #define is just a hard find-and-replace, such that my Analog Input pins just paved right over the gate driver shutdown pins. The shutdowns are critical because one phase has to be completely off (as in, both switches off) at some point in the commutation cycle. I was first clued into this problem when I noticed the SDB and SDC pins failing to drive low when viewed on the oscilloscope. When the ATmega pin is in INPUT mode, it is left floating or internally pulled high. The IR21844 chip has a default internal pullup state, so I was unable to drive the pin to low logic level.

Oops. Commenting out the pin mode change for the analog and digital input pins fixed that problem, and I was able to get a smoothly running Etek.

But it didn’t fix the problem of the program tripping over noise at high speeds. At 25 volts input and running full throttle, the program crashed and the Etek came to a very, very quick halt. The 6 pound rotor of the Etek suddenly became a huge voltage and current spike, and it detonated my negative bus trace.

Solution: More busbraid.

There were more problems, though. Being punched in the face by an Etek actually caused 2 of the FETs to fail short – the sudden spike in voltage and current likely greatly exceeded their Vds. It also blew a different trace that I didn’t see before since it was still hidden under the solder mask. Time to pop out the hot air station…

Further testing on A Certain Tiny Kart revealed that even on a well-timed motor, something was behaving erratically at low speed (10-15% throttle). The current draw was abnormally high, but after the weirdness went away, the controller would work smoothly until the high speed noise problems caused it to crash and latch up.

I’m fairly certain the issues are twofold: that the low speed roughness is caused by some weird software effect that I did not account for, and that the high speed problems are caused by noise coupling into the logic board.

Things that will be tried:

  1. Not using the new interrupt-based code for now, and using Melontroller’s dumb loop to see if the Timer1 interrupt is doing something I am not anticipating
  2. Possibly switching to the Timer2 interrupt – Timer1 is a 16 bit timer-counter, Timer2 is a more average 8 bit timer-counter with fewer modes of operation.  Phase C is actually being run on Timer 2, so it should still yield an 8kHz loop.
  3. Isolating the signal ground and power ground more. Right now, they are tied together directly. Some noise isolation hacks involve using a low resistance of 10 to 20 ohms to tie them together, or a RC type snubber network – this was used on Melontroller 2 and it eliminated some noise problems. This ground tie resistor is R15 in the signal board schematic.
  4. Adding a fast zener diode to the 5 volt bus in case spikes are feeding back into the system, and also adding epic bus capacitance to the 5v bus.
  5. Possibly increasing my switching time (currently about 150-200ns) to decrease the dV/dt and dI/dt that the phases see – those rapid changes in current and voltage can couple over to signal pins, etc. and crash the processor.

3 thoughts on “Tinytroller: More Tinytrolling”

  1. Isn’t Timer 1 used by arduino for something? PWM or the millis() function? Might be worth having a look…

  2. millis() and delay() use Timer 0, which is why I explicitly put the PWM pins on 1 and 2. However, Timer 1 is the 16 bit timer which can do a million things. I might not have noticed what else activating the overflow interrupt did.

  3. I have to say I find that ISR service time intriguing. Assuming you are running the Nano at 16MHz, an ISR executing 8000 times per second at 1 MIPS/MHz and taking up ~20% runtime would imply ~400 instructions. Now I obviously have no idea what else do you have under ‘[…rest of state table here]’, but unless you’re calculating Pi in there this sounds strange. Have you tried looking at the generated code directly? Does it look kosher?

    Also, I’m not that familiar with the Arduino’s internal housekeeping; is there any other timing-critical interrupt you might be upsetting with the fairly long ISR? Is there any that might be interrupting yours?

Comments are closed.