December 28, 2015

High-Speed Quadrature Decoding with the STM32F4 Nucleo...Because Lasers and Bass

I've been using the STM32F4 Nucleo dev boards for everything lately, and needed to do high speed (edges at MHz frequency) quadrature decoding with one.  Here are the snippets of code to do this.

This setup uses PA_0 and PA_1 plus Timer 2 (one of the two 32-bit timers), which are on the Arduino-compatible A0 and A1 of the Nucleo board (pinout).

The setup code is mostly copy-and-pasted from here.  That code got me to encoder edges at ~600 Khz before it started losing counts.  Turns out there's a digital filter on the GPIO pins I had inadvertently turned on with the copy-pasta code.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
    // configure GPIO PA0 & PA1 as inputs for Encoder
    RCC->AHB1ENR |= 0x00000001;  // Enable clock for GPIOA
 
    GPIOA->MODER   |= GPIO_MODER_MODER0_1 | GPIO_MODER_MODER1_1 ;           //PA0 & PA1 as Alternate Function   /*!< GPIO port mode register,               Address offset: 0x00      */
    GPIOA->OTYPER  |= GPIO_OTYPER_OT_0 | GPIO_OTYPER_OT_1 ;                 //PA0 & PA1 as Inputs               /*!< GPIO port output type register,        Address offset: 0x04      */
    GPIOA->OSPEEDR |= 0x00000011;//|= GPIO_OSPEEDER_OSPEEDR0 | GPIO_OSPEEDER_OSPEEDR1 ;     // Low speed                        /*!< GPIO port output speed register,       Address offset: 0x08      */
    GPIOA->PUPDR   |= GPIO_PUPDR_PUPDR0_1 | GPIO_PUPDR_PUPDR1_1 ;           // Pull Down                        /*!< GPIO port pull-up/pull-down register,  Address offset: 0x0C      */
    GPIOA->AFR[0]  |= 0x00000011 ;                                          //  AF01 for PA0 & PA1              /*!< GPIO alternate function registers,     Address offset: 0x20-0x24 */
    GPIOA->AFR[1]  |= 0x00000000 ;                                          //                                  /*!< GPIO alternate function registers,     Address offset: 0x20-0x24 */
   
    // configure TIM2 as Encoder input
    RCC->APB1ENR |= 0x00000001;  // Enable clock for TIM2
 
    TIM2->CR1   = 0x0001;     // CEN(Counter ENable)='1'     < TIM control register 1
    TIM2->SMCR  = 0x0003;     // SMS='011' (Encoder mode 3)  < TIM slave mode control register
    TIM2->CCMR1 = 0x0101;     // CC1S='01' CC2S='01'         < TIM capture/compare mode register 1
    TIM2->CCMR2 = 0x0000;     //                             < TIM capture/compare mode register 2
    TIM2->CCER  = 0x0011;     // CC1P CC2P                   < TIM capture/compare enable register
    TIM2->PSC   = 0x0000;     // Prescaler = (0+1)           < TIM prescaler
    TIM2->ARR   = 0xffffffff; // reload at 0xfffffff         < TIM auto-reload register
    //Tim2->BDTR
    TIM2->CNT = 0x0000;  //reset the counter before we use it  

And to read the count, just


position = TIM2->CNT;

The critical change was in line 16, which used to read


    TIM2->CCMR1 = 0xF1F1;     

Changing "F" to "0" disables the filter for input capture 1 and 2.  "F" sets the filter as slow as possible, so I likely could have left some filtering and still gotten good enough results (with perhaps better noise-immunity).  More details about filtering can be found on page 358 of the 841 page reference manual for the STM32F4 series.

I was able to test up to ~5 Mhz edges using a 12,500 CPR optical encoder coupled to a DC motor, with no missed counts.  Probably could have gone faster, but I didn't have an easy way of testing.

%%  end of useful reference material, on to project-ramblings  %%
%%  proceed at your own risk  %%

Now why did I need to read encoder edges at several megahertz?  Well, this term I took 2.171 (Digital Feedback Control Systems, with Prof. David Trumper and TA Will Bosworth.  A++, would highly recommend.), and for my final project built a subwoofer with closed-loop velocity control of the speaker cone from an audio signal reference.

Lots of possible velocity sensing methods exist, and they can basically all be broken down into three categories:  direct velocity measurement, position measurement with differentiation to get velocity, and acceleration measurement with integration to get velocity.

For direct velocity measurement, I thought of a few strategies.
  • Attach a second voice coil to the speaker cone.  Voice coil voltage will be proportional to velocity.  The only tricky bit is making sure the magnetics of the second voice coil are completely isolated from those of the driving coil.
  • Wrap a second coil of very thin sense-windings around the original voice coil windings.  This has been done.  The problem with this strategy is that, in addition to the back-emf from the permanent magnet, the driving coil and sense coil are coupled.  This can be sorted out with some math, but I preferred not to go that route.
  • Somehow measure the back-emf of the coil between switching transients.  During the dead-time in the switching of the h-bridge (or Class-D amplifier with no output filter, in audio-lingo), the voltage across the coil isn't railed at (±)power-supply voltage, and it should be possible to figure out the speaker's back emf during this period.  I stared at a scope trace of coil voltage for a few minutes and decided this would be really hard.  This would be the holy grail of closed-loop speaker nonsense, as it requires no external sensor.
Measuring position and differentiating can be risky business, because high-frequency noise gets amplified by differentiation.  A few linear position measuring methods are:
  • Laser + mirror + 1-D PSD, as suggested by Prof. Trumper.  Fix a laser, and point it at a little mirror on the speaker cone.  The reflected laser spot lands on the PSD, which gives you cone position.  Gain of the sensor can be adjusted by changing the angles of the laser, mirror, or PSD.  Sadly, PSDs proved difficult to find over a short timeframe.
  • Linear optical encoder strip.  These exist down to absurd resolution.
  • Magnet + hall effect sensor.  I built one of these, but it just didn't have the necessary resolution. 
  • And many more
And of course, for acceleration + integration to get velocity, just stick an accelerometer on the speaker cone.  This could be an attractive option- the usual drift problem associated with integrating an accelerometer can be solved by a slow high-pass filter, since audio is always AC anyway.  However, the accelerations are actually very large:  1 mm peak-to-peak excursion at 100 Hz is already 20 G's of acceleration, which would saturate a lot of cheap accelerometers.

So the path forward was unclear.  The most reasonable solutions are probably the secondary voice coil for sensing or the secondary sense winding, but I was talking to Peter about this, and he suggested using an interferometer for position feedback, and said he had all the parts to put one together.  Cut to a few days later, I show up at MITERS and Peter's built an interferometer in the back room.  The eventual output of the interferometer was to be a digital quadrature signal, hence the need for high-speed quadrature decoding.

So begins the story of the closed-loop subwoofer with interferometer feedback.  More details about the hardware and controls stuff to follow soon...

December 23, 2015

Metal Electric Ukulele

I like taking my gourd ukulele with me when I leave school for more than a week or so, but it's a little annoying to travel with, since it takes up pretty much an entire backpack and is somewhat fragile.  Inspired by Amy's lovely wooden travel ukulele and an awesome one-stringed electric instrument made out of a pipe made by Mike, I made this tiny electric travel ukulele.

The original intent was to basically make an aluimnum stick with four strings and a pickup on it, but after I machined a fretboard and it turned out really nicely, I figured out I should make the rest of the instrument nice while I was at it.

The entire project, from idea to finish, was completed between Thursday night and early Monday morning before I left back to Atlanta.  Some things could definitely be improved (the pickup and tuners, especially), but were done more simply for time.

I started out with this mystery strip of 1/8" x 1.75"  stainless steel found in the putz cruft closet.


I machined it to size on the MITERS Bridgeport, drilled and tapped six M4 holes in it, and fixtured it up in the newly-revived MITERS CNC mill (now running a modern computer with linux CNC, instead of the old DOS-on-a-Pentium-III system).

Squaring up the stock on the CNC mill

The fretboard was roughed out with a 7/32" endmill, and finished with a 1/8" ballnose.  Here's a crappy video of the roughing:


I have very little experience machining stainless, so I just copied some feeds and speeds off the internet.  Seems to have worked out well enough.  I used a brand new endmill for the roughing, and the surface finish turned out fantastic.  Thank you, internet.


Apparently this stock had a lot of stress pre-baked into it, so it warped like crazy when I unscrewed it from its fixturing.  Fortunately it will get pulled flat again by the neck of the instrument.


I laid out the fretboard and some big bar bar magnets for the pickup on a sheet of 3/8" aluminum plate, and started sketching out the rest of the instrument.  For compactness it's headless and has tuners built into the body instead.


Most people I know would have run to a waterjet cutter to cut out the profile, but I have a perhaps somewhat unjustified dislike of waterjets.  Instead, I roughed out the profile on a bandsaw and brought it to shape with some files.  Here's the rough profile after bandsawing:


After shaping the body with some coarse files, I milled the neck to size.  Now I had some nice parallel surfaces for referencing the rest of the machining operations.


Milling slots for the tuners to slide in:


Tuners made from bronze:


The bronze tuners slide in slots, and are each actuated by a screw with a thumbwheel.  I'd have loved to use a proper acme thread screw, but all I could find of appropriate size was a 10-24 threaded rod.

Drilling the holes for the screws to ride in required some interesting fixturing.  I only had an extra 1/2" of vertical space on the bridgeport, after recessing my part in one of the t-slots and putting the drill bit in a collet:


At times like this I wish MITERS had the space for a horizontal mill.  Would make drilling holes like this so much easier.




I CNC milled a bridge of similar style to the fretboard, but out of brass, and made knurled thumbwheels for the tuners:


Milled a cutout for the pickup:



I need to run wires through the body to the pickup, but didn't want to mill a channel in the back of the body.  Instead, I drilled a long hole through the body.  Again, the setup was too tall to use with a chuck, and the particular drill bit I needed didn't fit any collets.  I made a simple split bushing to fit the bit in a 1/2" collet.  These are super simple to make and really handy.  Put round stock in the lathe, turn off a tiny pass to get the OD concentric, center drill, and then drill through with the drill bit you want to hold.  Finally, just bandsaw a slot, and you have a collet to drill bit adapter.


Drilling all the way through to the pickup area:



Next step was finishing body - cleaning up the profile and rounding the edges.  I did the very rough work with a belt sander, and finished with files and sandpaper.

 My selection of files:


Most people seem to shy away from files, but I really love using them.  They can actually remove aluminum remarkably quickly, and you can basically sculpt the metal with files.  It's much more freeform than other machining, which is really refreshing.


And here's the body post filing and sanding:


And here it is assembled.  Yes, that's a carbon fiber pickup cover.  There were some sheets of if lying around, and I thought the contrast with all the shiny metal would be nice.  I don't have any good pictures of winding the pickup, but I just took a big neodymium bar magnet, glued plastic flanges on the front and back, stuck it to a piece steel of stock in the lathe, and put several thousand turns of 36 gauge magnet wire around it.


Side by side with the gourd ukulele:


Here's a pile of nicer pictures:








The only unfinished bit is the headphone jack for plugging it in.  When I get back to a machine shop I'll make a nice cover for it, but right now the jack is just heatshrinked and stuffed into its cavity in the body.

Perhaps a clip of it in-action will follow soon.  I don't have a proper guitar amplifier, so I've just been plugging it into whatever set of speakers is nearby.  A headphone amp for it would be nice, so I can play without bothering people.