HÄRDBÖRD: Hardcore Electric Longboard

So I’m going to start using my blog as a build log for projects. All the cool kids are doing it.

Introducing HÄRDBÖRD, the new reason for my keyboard layout

Today, I want to turn your atten­tion to HÄRDBÖRD1, this wire­less, all-alu­minum elec­tric longboard/deathtrap project I got myself involved in. My room­mate Jami­son and I are the prin­ci­pal design­ers (engi­neers? build slaves?) on a pair of these for fun and… lever­age against The Man? The idea here is to get some learn­ing done, but we do end up with ridicu­lous­ly cool look­ing, pos­si­bly over­pow­ered boards to ride around cam­pus on:

Take that, cam­pus shut­tle!

I’m most­ly in charge of the elec­tron­ics, which are pret­ty sim­ple com­pared to the all the oth­er stuff. Most of it was done in about a week2. The plan is to use a Wii Nunchuk to wire­less­ly con­trol the motor con­trollers. Why a Nunchuk? Why wire­less? These tran­scen­den­tal ques­tions of painful grav­i­ty are end­less, but I can not answer them.

Jami­son has done much of the mechan­i­cal design and machin­ing, though I’ve been able to con­tribute a bit in the shop by learn­ing the trade (i.e. the OMAX water­jet cut­ter in the Inven­tion Stu­dio at Geor­gia Tech). The boards are almost all water­jet-cut 6061 alu­minum, with cus­tom trucks, wheel hang­ers, deck—the whole she­bang. We’ve run into a few snags where the wheel hang­ers don’t cen­ter with as much force as we’d like (in fact, not enough to be ride­able); I have an ele­gant solu­tion involv­ing one spring and a cam, but it’s unma­chine­able and appar­ent­ly patent­ed or licensed by Orig­i­nal.   

Pow­er runs from huge 4000mAh 6S1P pack lithi­um-poly­mer bat­ter­ies (22.2V nom­i­nal, 25.2V max). They’re real­ly not that big, but I haven’t built large bots for a long while so their size still sur­pris­es me. This juice runs to a pair of Turni­gy brush­less motors mount­ed with­in 4‑inch 30A durom­e­ter poly­mer-core wheels, though what goes in between the motors and the bat­ter­ies is still up for debate with the bean counters—no motor con­trol solu­tion for the pair of the boards costs less than $300.

For more infor­ma­tion on “get­ting the boards to move,” see Jamo’s posts on Härdbörd.

Mean­while, we’re think­ing of using a pair of Max­on DEC 50/5 (PDF) sen­sored brush­less con­trol mod­ules. Now I’d per­son­al­ly pre­fer to build my own brush­less con­trollers, but we real­ly want to see this on the road. These take in a few dig­i­tal sig­nals and a 0–5V ana­log sig­nal for speed. Unfor­tu­nate­ly, every­thing elec­tron­ic I cov­et and hold dear (includ­ing my low-MIPS micro­con­troller of choice, the Microchip dsPIC33FJ128GP802) use 3.3V, and I hate ana­log like that flat watered-down Sprite you get from real­ly stingy restau­rants in NYC’s Chi­na­town. No sir, no op-amps in my design; I’d rather drink diet.

An aside on my choice of µC

The nice thing about the dsPIC33FJ128GP802 is that it’s chock-full of every sort of periph­er­al, as described in its 386-page datasheet. If you don’t have a soft­ware solu­tion, it has a hard­ware solu­tion. If you don’t have a hard­ware solu­tion, it has a hard­ware solu­tion. If you already have a solu­tion, IT HAS A HARDWARE SOLUTION.

In my com­put­er archi­tec­ture class, the pro­fes­sor was talk­ing about the DEC VAX hav­ing a POLYD instruc­tion to eval­u­ate poly­no­mi­als and every­one laughed at the ridicu­lous­ly com­plex ISA co-designed by two Stuy alums. Then I real­ized, “dang, the dsPIC has a poly­no­mi­al eval­u­a­tor for CRC cal­cu­la­tions!” Hard­ware bloat is not fastest in Intel’s newest SIMD float­ing point SSE6.8d exten­sion, but in these lit­tle 8- and 16-bit bug­gers by means of the mem­o­ry-mapped periph­er­al.

I mean seri­ous­ly, in addi­tion to the usu­al timers, ADCs, I²C, SPI, U(S)ARTs, CAN, and oth­er junk, it’s got freak­ing DMA and mul­ti-pri­or­i­ty inter­rupt sys­tem for all of those. Then they decide that was­n’t enough and crammed an extra ALU and bar­rel shifter in there with 40-bit accu­mu­la­tors and called it a DSP engine. But wait, there’s more! There’s more space on the die! Let’s shove a com­para­tor and a real-time clock in there! Now they had more hard­ware than they had pins, so what do they do? STICK MUXES AND TRISTATE DRIVERS EVERYWHERE. Yeah, that’s right, almost every periph­er­al on this thing can be mapped to any pin. This thing even solves your PCB lay­out issues because you can recon­fig­ure your pinout in soft­ware.

Oh yeah, this thing also does­n’t need a crys­tal to run at its full 40MIPS, has a free offi­cial GCC com­pil­er, and comes in 28-pin bread­boad­able through hole (as free sam­ples!). Life does­n’t get more per­fect.

The receiver

Aside aside, I want­ed to out­put  0–5V ana­log from my dsPIC33F. Well, first thing I see is that it’s got this DAC periph­er­al. Every­thing is fine and dandy unti—AUGH IT OUTPUTS A DIFFERENTIAL SIGNAL. In oth­er words, not only is it not 0–5V, it’s not even 0–3.3V. It’s some janky 0.8–2.8V (-ish. Don’t quote me.) swing for audio use. Darn, now I have to ghet­to-hack a DAC from the PWM hard­ware.

For­tu­nate­ly, this is extreme­ly straight­for­ward. The Out­put Com­pare mod­ule, as the “PWM” mod­ule is called in the dsPIC33F/PIC24H lines, is one of the most pleas­ant hard­ware periph­er­als I’ve ever used, on any com­put­er, ever. Put it like this: it’s eas­i­er than the soft­ware way. You set up a timer with some prescaler val­ues against sys­tem clock, as well as a peri­od match reg­is­ter for when it should reset the timer counter to 0. That’s right: not only can you set the fre­quen­cy of the timer tick, but also the peri­od by which it resets. Awsm.

static inline void configTimer2(void) {
	PR2 = usToU16Ticks(PWM_PERIOD, getTimerPrescale(T2CONbits)) - 1;
	TMR2 = 0; //clear timer2 value
	_T2IF = 0;
	_T2IP = 1;
	_T2IE = 1; //enable the Timer2 interrupt

Any­ways, next you just set up the out­put com­pare mod­ule to use that timer.

static inline void configOutputCompare1(void) {
	T2CONbits.TON = 0; //disable Timer when configuring Output compare
	CONFIG_OC1_TO_RP(14); //map OC1 to RP14/RB14
	//assumes TIMER2 initialized before OC1 so PRE bits are set
	OC1RS = 0; //initially off
	//turn on the compare toggle mode using Timer2
	OC1CON = OC_TIMER2_SRC | //Timer2 source
			OC_PWM_FAULT_PIN_DISABLE; //PWM, no fault detection

The way this work is basi­cal­ly described by this tim­ing dia­gram:

When the timer counter is less than reg­is­ter OC1RS, the OC1 pin (recon­fig­ured by soft­ware as described above, to RP14) is out­put high. Oth­er­wise, it’s out­put low. The peri­od of rollover is defined by the timer’s peri­od reg­is­ter PR2. In oth­er words, the duty cycle (and thus aver­age volt­age) is exact­ly3 OC1RS/PR2.

Aside: you know why this would be use­less on your ATMEGATRON9000KFLASH4BYTERAM AVRDUINO CAPPUCCINO2001 device? It’s 8‑bit. All of these reg­is­ters would be 8‑bit, and you’d either have crap­py 8‑bit timers that can’t do straight­for­ward PWM like this or some janky con­cate­nat­ed 2×8‑bit mon­ster with ter­ri­ble laten­cy and 40 pages in erra­ta. End aside.

OK, so now we know how the PWM works. But how do we get 5V out­put? Sure­ly we’ll need an op amp to up our range to fiv—CATSLAP. We don’t like your kind here.

Yeah, so there’s a dsPIC periph­er­al solu­tion here. The dig­i­tal pins on the dsPIC have an open-drain oper­a­tion mode4. A pin in open-drain out­put mode is hard pull to low (drain) when its port reg­is­ter bit is 0, and tris­tat­ed (high-imped­ance, Z, float­ing) when its bit is 1. This means you can hook up 5V pull-up resis­tor to it, and get 5V out­put from a 3.3V device.

Now all I need to do is feed that 5V sig­nal to a RC fil­ter tuned to atten­u­ate the PWM fre­quen­cy, and I’ll have a clean 5V DAC out­put. I feel like freak­ing 诸葛亮 right here.

The test

I’m work­ing on all of this in the Inven­tion Stu­dio because I want an oscil­lo­scope for this. Now the Inven­tion Stu­dio is an Mechan­i­cal Engi­neer­ing shop, so the o‑scope is a bit dinky, but hey, it’s dig­i­tal, there are work­ing probes around, and it works… for the most part.

“Try a USB flash dri­ve which has a small­er mem­o­ry size.” OK, what?

Any­ways, I don’t have real o‑scope cap­tures, so bear with my cell­phone cam­era pho­tos of the var­i­ous test cas­es. So here’s the cir­cuit under test:

The trans­mit­ter and receiv­er ghet­toed up to a pow­er sup­ply and scope

Now let’s get some data:

This is the PWM out­put from the dsPIC, with­out open-drain

Being able to adjust the duty cycle of the wave­form with a joy­stick is some hot stuff, espe­cial­ly if you fid­dle with the trig­ger set­tings a bit. Now let’s hook up a 100KOhm×0.1µF RC fil­ter on that. This has a cut­off fre­quen­cy of about 16Hz, at which point half the sig­nal is atten­u­at­ed. High­er fre­quen­cy sig­nals are atten­u­at­ed at 20dB per decade, so the 3.28kHz PWM fre­quen­cy is atten­u­at­ed by at least 40dB. Good enough for me to play Etch-A-Sketch on the scope with my Nunchuk:

With RC fil­ter and slooow sweep scope sam­ples

And just for the hell of it, I mea­sured the rip­ple of the ana­log:

About 30mV. Not bad. Tak­ing this mea­sure­ment was a real pain: I had to keep my joy­stick fin­ger steady while zooming/panning on a tiny sliv­er of the ver­ti­cal scale, throw in mea­sure­ments, and pray­ing that a lit­tle twitch won’t throw the wave­form off the trig­ger­ing lev­el.

So, that con­cludes this real­ly bor­ing part of “reduc­ing part count of every­thing to just a dsPIC.” Next time, I’ll show how I did this with the trans­mit­ter side.

  1. Cred­it goes to Charles for this awe­some name. Note that the Umlauts are met­al umlauts, not actu­al Ger­man. []
  2. Much of that time was spent cod­ing with my shirt off while Ramm­stein blast­ed from my work­sta­tion. []
  3. Actu­al­ly, there’s a fen­ce­post error here I’m gloss­ing over, but which is cov­ered by my code snip­pet. []
  4. Well, all of the I/O pins on the equiv­a­lent PIC24H do, but you’re not sup­posed to use the dsPIC ana­log pins in open-drain since they’re not 5V tol­er­ant. Yes, it does work, but this puts your app out­side of spec. []