Hardware pages: where the bits meet the metal.

Open hardware analog I/O

Over at Software Carpentry, Greg Wilson just posted some thoughts about a hypothetical open science framework. He uses Ruby on Rails and similar web frameworks as examples where frameworks can leverage standards and conventions to take care of most of the boring boilerplate that has to happen for serving a website. Greg points out that it would be useful to have a similar open science framework that small projects could use to get off the ground and collaborate more easily.

My thesis is about developing an open source framework for single molecule force spectroscopy, so this is an avenue I'm very excited about. However, it's difficult to get this working for experimental labs with a diversity of the underlying hardware. If different labs have different hardware, it's hard to write a generic software stack that works for everybody (at least at the lower levels of the stack). Our lab does analog control and aquisition via an old National Instruments card. NI no longer sells this card, and developing Comedi drivers for new cards is too much work for many to take on pro bono. This means that new labs that want to use my software can't get started with off the shelf components; they'll need to find a second-hand card or rework the lower layers of my stack to work with a DAQ card that they can source.

I'd be happy to see an inexpensive, microprocessor-based open hardware project for synchronized, multi-channel, near-MHz analog I/O to serve as a standard interface between software and the real world, but that's not the sort of thing I can whip out over a free weekend (although I have dipped my toe in the water). I think the missing component is a client-side version of libusb, to allow folks to write the firmware for the microprocessor without dealing with the intricacies of the USB specs. It would also be nice to have a standard USB protocol for Comedi commands, so a single driver could interface with commodity DAQ hardware—much like the current situation for mice, keyboards, webcams, and other approved classes. Then the software stack could work unchanged on any hardware, once the firmware supporting the hardware had been ported to a new microprocessor. There are two existing classes (a physical interface device class and a test and measurement class), but I haven't had time to dig through those with an eye toward Comedi integration yet. So much to do, so little time…


I've been wanting to get into microcontroller programming for a while now, and last week I broke down and ordered components for a breadboard Arduino from Mouser. There's a fair amount of buzz about the Arduino platform, but I find the whole sketch infrastucture confusing. I'm a big fan of command line tools in general, so the whole IDE thing was a bit of a turn off.

Because the ATMega328 doesn't have a USB controller, I also bought a Teensy 2.0 from PJRC. The Teensy is just an ATMega32u4 on a board with supporting hardware (clock, reset switch, LED, etc). I've packed the Teensy programmer and HID listener in my Gentoo overlay, to make it easier to install them and stay up to date.

Arduinos (and a number of similar projects) are based on AVR microcontrollers like the ATMegas. Writing code for an AVR processor is the similar to writing code for any other processor. GCC will cross-compile your code once you've setup a cross-compiling toolchain. There's a good intro to the whole embedded approach in the Gentoo Embedded Handbook.

For all the AVR-specific features you can use AVR-libc, an open source C library for AVR processors. It's hard to imagine doing anything interesting without using this library, so you should at least skim through the manual. They also have a few interesting demos to get you going.

AVR-libc sorts chip-support code into AVR architecture subdirectories. For example, object code specific to my ATMega32u4 is installed at /usr/avr/lib/avr5/crtm32u4.o. avr5 is the AVR architecture version of this chip.


Since you will probably not want to build a version of GCC that runs on your AVR chip, you'll be building a cross comiling toolchain. The toolchain will allow you to use your development box to compile programs for your AVR chip. On Gentoo, the recommended approach is to use crossdev to build the toolchain (although crossdev's AVR support can be flaky). They suggest you install it in a stage3 chroot to protect your native toolchain, but I think it's easier to just make btrfs snapshots of my hard drive before doing something crazy. I didn't have any trouble skipping the chroot on my sytem, but your mileage may vary.

# emerge -av crossdev

Because it has per-arch libraries (like avr5), AVR-libc needs to be built with multilib support. If you (like me) have avoided multilib like the plague so far, you'll need to patch crossdev to turn on multilib for the AVR tools. Do this by applying Jess' patch from bug 377039.

# wget -O crossdev-avr-multilib.patch 'https://bugs.gentoo.org/attachment.cgi?id=304037'
# patch /usr/bin/crossdev < crossdev-avr-multilib.patch

If you're using a profile where multilib is masked (e.g. default/linux/x86/10.0/desktop) you should use Niklas' extended version of the patch from the duplicate bug 378387.

Despite claiming to use the last overlay in PORTDIR_OVERLAY, crossdev currently uses the first, so if you use layman to manage your overlays (like mine), you'll want to tweak your make.conf to look like:

source /var/lib/layman/make.conf

Now you can install your toolchain following the Crossdev wiki. First install a minimal GCC (stage 1) using

# USE="-cxx -openmp" crossdev --binutils 9999 -s1 --without-headers --target avr

Then install a full featured GCC (stage 4) using

# USE="cxx -nocxx" crossdev --binutils 9999 -s4 --target avr

I use binutils-9999 to install live from the git mirror, which avoids a segfault bug in binutils 2.22.

After the install, I was getting bit by bug 147155:

cannot open linker script file ldscripts/avr5.x

Which I work around with:

# ln -s /usr/x86_64-pc-linux-gnu/avr/lib/ldscripts /usr/avr/lib/ldscripts

Now you're ready. Go forth and build!

Cross compiler construction

Why do several stages of GCC need to be built anyway? From crossdev --help, here are the stages:

  1. Build just binutils
  2. Also build a bare C compiler (no C library/C++/shared GCC libs/C++ exceptions/etc…)
  3. Also build kernel headers
  4. Also build the C library
  5. Also build a full compiler
Aspire One 722



The processor is an AMD Fusion C-60 (dual core, x86-64, 1 GHz; CONFIG_MK8 option).

You can monitor the processor temperature uring the k10temp module (CONFIG_SENSORS_K10TEMP option), which communicates with the processor via the PCI bus:

$ lspci -k
00:18.3 Host bridge: Advanced Micro Devices [AMD] Device 1703
    Kernel driver in use: k10temp
    Kernel modules: k10temp

You can find read the relative processor temperature in degrees Celsius using sysfs. On my system, I can use:

$ cat '/sys/module/k10temp/drivers/pci:k10temp/0000:00:18.3/temp1_input'

You can also get there directly through the PCI interface:

$ cat '/sys/devices/pci0000:00/0000:00:18.3/temp1_input'

lm_sensors is a fancier way to monitor temperature:

$ sensors
Adapter: PCI adapter
temp1:        +69.0°C  (high = +70.0°C)
                       (crit = +115.0°C, hyst = +107.5°C)

Adapter: PCI adapter
temp1:        +69.0°C  


Radeon Evergreen IGP (HD 6290) integrated into the C-60 processor (radeon module, CONFIG_DRM_RADEON and CONFIG_DRM_RADEON_KMS options).


Builtin ATI soundcard and ATI Azalia. Both sound cards use Intel's HDA interface:

00:01.1 Audio device: ATI Technologies Inc Device 1314
    Subsystem: Acer Incorporated [ALI] Device 0598
    Kernel driver in use: snd_hda_intel
    Kernel modules: snd-hda-intel
00:14.2 Audio device: ATI Technologies Inc SBx00 Azalia (Intel HDA) (rev 40)
    Subsystem: Acer Incorporated [ALI] Device 0598
    Kernel driver in use: snd_hda_intel
    Kernel modules: snd-hda-intel

In order to get these working with ALSA, I had to adjust the default soundcard by adding /etc/asound.conf:

pcm.!default {
  type hw
  card SB

ctl.!default {
  type hw
  card SB

following this FAQ.

You may also want to enable the dmix plugin, to enable software mixing of multiple streams (e.g. to play two songs at the same time). I haven't bothered yet, but Pedro Venda discusses the setup here. If you haven't enabled the plugin, you may get errors like:

$ ogg123 -q test.ogg &
$ ogg123 -q test.ogg
=== Could not load default driver and no driver specified in config file. Exiting.


The wired network card is an Atheros/Attansic AR8152 (atl1c module, CONFIG_ATL1C option; Atheros aquired Attansic in 2007). The wireless network card is a Broadcom BCM4313 (brcmsmac module, CONFIG_BRCMSMAC option).

The Broadcom drivers require firmware, which you can get from git://git.kernel.org/pub/scm/linux/kernel/git/dwmw2/linux-firmware.git (or sys-kernel/linux-firmware on Gentoo). You'll need to install bcm43xx-0.fw and bcm43xx_hdr-0.fw into /lib/firmware/brcm. Despite the name similarity, this is not the firmware installed by Gentoo's net-wireless/b43-firmware package (which fills in /lib/firmware/b43).

There is also difficulty running both the atl1c and brmcsmac drivers at the same time. Upon associating with a wireless access point, the kernel (3.2.0-rc2) hangs, forcing a hard-reboot. To avoid this issue, it's wise to remove the atl1c module before attempting to connect to a wireless access point. For example:

# /etc/init.d/net.eth0 stop
# /etc/init.d/net.wlan0 stop
# modprobe -r atl1c
# modprobe -r brcmsmac
# modprobe brcmsmac
# /etc/init.d/net.wlan0 start

I'm not sure if re-loading the brcmsmac module accomplishes anything here, but it can't hurt ;).

Another brcmsmac limitiation is that it only supports managed and monitor interfaces (no master, ad-hoc, etc.).


The Suyin webcam is fairly standard, supporting the USB video class for video4linux (uvcvideo module, CONFIG_USB_VIDEO_CLASS option).

Card reader

I haven't tested the Realtek RTS51xxx card reader yet, but it uses the ums_realtek module (USB_STORAGE_REALTEK option).


$ lspci -k
00:14.0 SMBus: ATI Technologies Inc SBx00 SMBus Controller (rev 42)
    Subsystem: Acer Incorporated [ALI] Device 0598
    Kernel driver in use: piix4_smbus
    Kernel modules: i2c-piix4


What are these for?

00:14.3 ISA bridge: ATI Technologies Inc SB700/SB800 LPC host controller (rev 40)
    Subsystem: Acer Incorporated [ALI] Device 0598
00:14.4 PCI bridge: ATI Technologies Inc SBx00 PCI to PCI Bridge (rev 40)
00:15.0 PCI bridge: ATI Technologies Inc Device 43a0
    Kernel driver in use: pcieport
00:15.2 PCI bridge: ATI Technologies Inc Device 43a2
    Kernel driver in use: pcieport
00:15.3 PCI bridge: ATI Technologies Inc Device 43a3
    Kernel driver in use: pcieport
00:18.0 Host bridge: Advanced Micro Devices [AMD] Device 1700 (rev 43)
00:18.1 Host bridge: Advanced Micro Devices [AMD] Device 1701
00:18.2 Host bridge: Advanced Micro Devices [AMD] Device 1702
00:18.4 Host bridge: Advanced Micro Devices [AMD] Device 1704
00:18.5 Host bridge: Advanced Micro Devices [AMD] Device 1718
00:18.6 Host bridge: Advanced Micro Devices [AMD] Device 1716
00:18.7 Host bridge: Advanced Micro Devices [AMD] Device 1719
Comedi and NI triggering

Back in 2007 I submitted a patch to Comedi to support triggering analog output off the analog input start signal. The patch was accepted (commit 4284c2266987ad08a26f2758cd09fef06d1ce3cf), and is now old news, but I just ran across my notes from developing it, and thought I'd publish them for posterity (and as a reminder to myself in case I have to dig into the internals of Comedi or NI drivers in the future).

Patch notes

To understand the patch, you should understand some portion of National Instrument's Data Acquisition Systems Timing Controller (NI DAQ-STC, see “Understanding NI DAQ-STC” below and the manual for more details).

We are trying to set up the AO timing so it triggers off of the AI start signal. In NI terminology, that means we want AI_START1 to be the source of AO_START1. The possible sources are listed in the DAQ-STC manual in table 5-2, p6-7 (380), where we see that AO_START1's 19th input is AI_START1. To implement this, we need to write to the card's AO_Trigger_Select register (Appendix B, register 67, pB-21 (470)).

Sounds fairly simple, we should just be able to set the appropriate start_arg. However, looking into source of comedi/driver/ni_mio_common.c, we see that no external triggering information is written to the AO_Trigger_Select_register. Looking back through the STC manual, we come across their AO trigger example (, p3-23 (185)) which shows how to set up AO triggering. Comparing to ni_mio_common.c, we see that the AO triggering configuration in ni_ao_cmd() looks just like the “software triggered” case in NI's example. So it seems impossible to run externally triggered AO.

The patch edits ni_mio_common.c to make it more like NI's example and ni_mio_common.c's own AI code.

Understanding NI DAQ-STC

Consider the timing sequence shown in the Comedi manual. For output, all the same timings apply, but data is flowing the other way.

Here is a translation table between NI's terminology and Comedi's (NI references from STC manual):

Comedi AI NI AO Reference
seq_start AO_START1Fig 3-10, p3-12 (174)
scan_beginUPDATE Fig 3-2, p3-6 (168)
convert TMRDACWR Fig 3-2, p3-6 (168)

There are references to an AO_START as distinct from AO_START1, but it appears to be unsupported:

  • It does not show up in the manual's AO feature list (section 3.2, p3-2 (164), compare to the AI feature list, section 2.2, p2-2 (31))
  • It shows up in some contexts as unsupported (e.g. section 3.6.5, p3-36 (198))

Comedi is a Linux COntrol and MEasurement Device Interface. We use Comedi in our lab to contol experiments via a NI PCI-6052E. The basic architecture for a Comedi application is:

kernel space   |   user space
device         |
  |            |
kernel --------|-- libcomedi - application
  |            |
comedi module  |

To make matters more complicated, there are two version of the kernel module, one in Git at comedi.org and another in the kernel itself.

Luckily, many distributions now package versions of the in-kernel staging driver (e.g. Debian and Ubuntu). I was feeling left out on Gentoo, so I've just revived the old Gentoo packages and added them to my Gentoo overlay.

Photodiode signal access

Relying only on the measured deflection voltage to measure the AFM tip-surface interaction is risky, as the loss of the signal may not be obvious. In order to validate incoming photodiode signals, you should also monitor the total light hitting the photodiode. This is especially true in fluid cell measurements, as bubbles of air or buffer solution can deflect the laser enough to miss the photodiode completely.

As far as I can tell, the total photodiode voltage is not carried on one of the DB-25 lines connecting the MultiMode to the NanoScope controller (see the MultiMode internals page). It is displayed on the MultiMode LCD, so I monitored the lines connecting the main board to the LCD board to isolate the total photodiode line (LCD connector 2). I build a small connector to tap out that line (despite appearances, none of the huge solder blobs actually short anything out :p).

TODO: image of connector.

We sent the tapped line out through Auxilliary D (DB25 line 4), so we could tap it again at out main break-out box. Comparing this signal to the photodiode ground (DB25 line 12), the signal is actually inverted (-7V signal when there's lots of light, 0V when there's none).


Here are my current guesses about the role of various connector lines inside the MultiMode II

DB-25 line descriptions

DB-25 line Role To
1 Stepper A Main 3
2 Stepper B Main 20
3 +5 Volts Main 7 (also 1,2,18,19 through 20 Ohms)
4 Auxiliary D (role?) Jumper 2
5 HV to piezo (-Y) Piezo 5
6 NCAFMJM pin 2 (role?) Main 8
7 HV to piezo (-X) Piezo 9
8 Piezo/head chassis ground Peizo 1; Head 13; Jumper 8
9 -15 Volts Main 26
10 Analog 2 (gain select) Main 17, Jumper 6
11 Vertical cantilever deflection Main 15
12 Green loop (photodiode ground?) Main 27
13 BB2107 pin 5 (2+) though R (role?) Main 9
14 Manual switch mode AFM/LFM (+5 Volts?) Switch 10
15 Stepper C Main 4
16 Stepper D Main 21
17 Head Gnd? Head 1
18 HV to piezo (+Y) Piezo 4
19 Main board ground Main 5
20 HV to piezo (+X) Piezo 8
21 Horizontal deflection Main 33
22 HV to piezo (+Z) Piezo 6
23 NCAFMJM pin 3 (role?) Main 25
24 +15 Volts Main 10
25 AD620 pin 2 (in -) through R (role?) Main 31

Main board line descriptions

Main line Role To
1 Stepper C, from DS3658N pin 6 Stepper Black
2 Stepper A, from DS3658N pin 1 Stepper Brown
3 Stepper A? in DB-25 1
4 Stepper B? in DB-25 15
5 Main board ground (black loop) DB-25 19
6 Manual stepper switch direction Switch Purple
7 +5 Volts DB-25 3
8 NCAFMJM pin 2 DB-25 6
9 through R to BBOPA2107 pin 5 (+2) DB-25 13
10 +15 Volts DB-25 24, Head 5 though 10 Ohms
11 Photodiode D Head 11
12 Photodiode A Head 9
13 DG403 pin 10  
14 internal plane, but not ground loop?  
15 Vertical cantilever deflection 620 pin 6 (out) DB-25 11
16 DG403DJ pin 6 DB-25 11
17 Gain select DG 419pin 6 (through R) DB-25 10
18 Stepper D, from DS3658N pin 8 Stepper Orange
19 Stepper B, from DS3658N pin 3 Stepper Yellow
20 Stepper C? in DB-25 2
21 Stepper D? in DB-25 16
22 AD827(far) pin 7 (out) (Tapping-piezo voltage) Head 15, tipholder 6
23 Green loop Head 14
24 Manual stepper switch enable Switch grey
25 NCAFMJM pin 3 DB-25 23
26 -15 Volts DB-25 9, Head 7 through 10 Ohms
27 Green loop DB-25 12
28 Photodiode ground (green loop) Head 2
29 Photodiode C Head 10
30 Photodiode B Head 8
31 AD620 pin 2 (in -) through R DB-25 25
32 BBOPA2107 pin 1(out1) through R(total photodiode)  
33 DG403 pins 1 and 3 (drains from 1)(Horizontal deflection) DB-25 21

Chip list

(following this numbering)

Index Chip Description Role
1 DS3658N Current sink. Stepper motor current sink
2 H7555IBA CMOS timer. Manual stepper motor control clock
3 "NCAFMJM" ? Stepper motor
4 AD827JN Dual op amp. Far side amplifies tapping piezo signal (out Main 22)
5 Scratched out ? Stepper motor?
6 Scratched out ? Stepper motor?
7 AD534KD Difference multiplier (X1-X2)(Y1-Y2)/(10V)+Z2. Photo-diode?
8 AD734BQ Identical to AD534, but with control over denominator. Photo-diode?
9 SG531P 25.0000 M 25 MHz crystal oscillator. Photo-diode?
10 BBOPA 2107AU Dual op amp. Photo-diode?
11 DG419DJ CMOS analog switch. Photo-diode?
12 DG403DJ CMOS analog switch. Photo-diode?
13 AD534JD Identical to AD534KD, but with slightly more error. Photo-diode?
14 AD637JD RMS-to-DC converter. Photo-diode?
15 AD620AR Instrument amplifier. Photo-diode?
16 BBOPA 2604AU Op amp. Photo-diode?
17 DG411DJ Quad CMOS analog switch ?

Main board wiring

Vertical deflection (Main line 15) comes from pins 6 and 8 (shorted together) of the DG403 switch. Pin 6 drains pin 5 and pin 8 drains pin 9 when the switch is turned on. I'm not sure what pin 5 is attached to. Pin 9 is connected (through a resistor) to the output (pin 12) of AD534a.

Manual switch connector line descriptions

See my switch notes for details on understanding the stepper control switch.

Line Color Role To
1 Orange Laser LED ?  
2 White-red STM mode LED DB-25 17
3 Green Mode TM Main 13
4 Blue Mode STM 120 Ohms to DB-25 17
5 Purple Stepper direction Main 6
6 White-brown Stepper switch +5 Volts Main 7
7 White Laser LED? DB-25 17
8 Yellow Laser LED?  
9 White-brown STM mode LED 160 ohms to DB-25 17??
10 Red Mode AFM/LFM, +5 Volts DB-25 14
11 Grey Stepper enable Main 24
12 Black Stepper switch Gnd Main 5

AFM head micro-D 15 connector line descriptions

outside female from top-left: 8 7 6 5 4 3 2 1/15 14 13 12 11 10 9

inside male (on board) from bottom-right: 8 7 ... 1/9 10 11 ... 15 blank

Line Role To
1 Laser Ground DB-25 17
2 Photodiode ground Main 28
3 +5 Volts Laser Power no main, no DB-25 ??
5 +15 Volts DB-25 24 through 10 Ohms
6 STM current? Jumper 1, tipholder 2
7 -15 Volts DB-25 9 through 10 Ohms
8 Photodiode B Main 30
9 Photodiode A Main 12
10 Photodiode C Main 29
11 Photodiode D Main 11
12 AFM tip Jumper 3, tipholder 4
13 Chassis ground DB-25 8, Jumper 8
14 Tapping-piezo ground (main board shorts to photodiode ground) Main 23, tipholder 5
15 Tapping-piezo voltage Main 22, tipholder 6

(see, e.g. Glenair for Micro-D-Subminiature connector descriptions.)

AFM head to tipholder line descriptions

Labeled looking down on tipholder along laser, with MultiMode LCD at bottom from top-left: 1 2 3/4 5 6

Line Role To
1 +12 Volts ?
2 STM current? AFM connector 4
3 -12 Volts ?
4 AFM tip? AFM connector 12
5 Ground AFM connector 14, main 23, main board shorts to photodiode ground
6 Tapping-piezo voltage AFM connector 15, main 22

On our tapping-in-air tipholders, removing the connector-pad plate shows lines 1-4 are not connected to anything. Lines 5 and 6 have wires heading over towards the piezo, as you would expect. On our tapping-in-fluid tipholders, the pad is not removable, but the only wires leaving the pad area connect to the oscillating piezo region, so both tipholders have identical interfaces to the head, with 5/6 providing piezo oscillation and 1-4 as non-contacts.

Since both tapping mode pins are shorted through the head to the main board, but not to the DB-25 connector to the NanoScope, we assume the tapping oscillation is being generated on the MulitMode main board itself.

Piezo connector line descriptions

outside male from top-left: 1 2 3 4 5/6 7 8 9

inside male (on board) from top-left: same + blank

DB-25 labeled HV: 5,7,18,20,22

Line Role To
1 Ground DB-25 8, Jumper 8
2   no db
3   no db
4 +Y piezo DB-25 18
5 -Y piezo DB-25 5
6 +Z piezo DB-25 22
7 Piezo cap Jumper 7
8 +X piezo DB-25 20
9 -X piezo DB-25 7

Jumper pin descriptions

inside male from top-left: 1 2 3 4 5 6 7 8

Roles from MultiMode manual, 14-9

Pin Role To
1 STM current Head 6
2 Auxiliary D DB-25 4
3 AFM tip Head 12
4 Analog 2 DB-25 10
5 Gain Select Main 17
6 Analog 2 DB-25 10
7 Piezo Cap Piezo 7
8 Ground DB-25 8, Piezo 1, Head 13

LCD connector descriptions

inside male from top-left: 1 2 3 4 5 / 6 7 8 9 10

Pin Role To
1 Manual switch AFM/LFM, +5 V DB-25 14
2 Total photodiode voltage Main 32
3 -15 Volts Main 26
4 +15 Volts Main 10
5 Manual switch STM mode Switch 9
6 Head Gnd? DB-25 17
7   Main 16
8 Green loop Main 27, DB-25 12
9   Main 13
10 Horizontal deflection Main 33

LCD board

inside male from top-left: 1 2 3 4 5 / 6 7 8 9 10

Pin Role To
1 +5 V Big regulator
2 Total photodiode voltage  
3 -15 Volts  
4 +15 Volts  
5 Manual switch STM mode  
6 Head Gnd?  
7   Main 16
8 Ground Black loop
10 Horizontal deflection top center BBOPA though R

You may want to see my MultiMode wiring summary.

The manual stepper switch is a double pole double throw (DPDT) switch. I've labeled the contacts below

In my MultiMode, the switch contacts are wired as follows:

  • Contacts 1, 4, and 6 are shorted together, and fed by a white-brown striped wire,
  • Contact 2 is a purple wire,
  • Contact 3 is a black wire, and
  • Contact 5 is a grey wire.

Using a multimeter to measure the average voltages I got:

Wire Up DownOff Role MainDB-25
Black 0.2V0.2V0.0VGround UL+4none
Grey 5.0V5.0V0.0VEnable LL+6none
White 5.0V5.0V5.0VPower UL+63

So the white and black wires carry power and ground to the switch, and the purple and grey wires carry the switch information back to the electronics. The 'Main' column records the line's lead on the main circuit board in the MultiMode, and the 'DB-25' column records the line's lead on the DB-25 cable to the NanoScope controller.

From the main-connector, the grey and purple lines start spreading out, ending up at (partial list):

  • H7555IBA, a CMOS clock.
  • NCAFMJM, a top-secret something-or-other.

My guess is that the grey enable-wire from the switch turns on the H7555 timer, and the NCAFMJM is a stepper driver chip, which receives a direction input (the purple wire) and a pulse-to-step input (the H7555 output). The output of the NCAFMJM heads over to a DS3658N current driver which drives the motor.


There is a good deal of drive-train backlash in the stepper mechanism.

Detection procedure

  1. Step towards surface until there is plenty of surface contact.
  2. Step away until you leave the surface.
  3. Repeat until you have too much data :p.

Data from 17 counterclockwise approach-retreat cycles


About 47 steps of backlash. Since we never touch the stepper while any critcal surface interactions are going on, simply replace step back 1 step with step back 60 steps, then forward 59.

Supporting evidence

Noise on reversal

When we reversing the stepper (by any means), the movement is “clearly audible” for “a bit” and then becomes “almost silent”.

To give a rough idea, I usually have keep my finger on the drive-shaft to tell if the motor is moving in “almost silent” mode, but can hear the motor from around 5 ft away in “clearly audible” mode. “A bit” is about 0.5 s when controlling the motor using the manual Up/Down switch.

Stepper motor control


When I joined Prof. Yang's group, the LabVIEW software controlling the experiment lacked control of the stepper motor which provides coarse z axis control. This made it difficult to set up the fluid cell and meant that leaving the system running unattended for over an hour could place unnecessary strain on the z piezo (as it would need to compensate for thermal drift on its own).


After a staring into the bowels of the MultiMode AFM and watching some of the more suspicious lines on an oscilloscope, I saw that lines 1, 2, 15, and 16 were the stepper motor drive lines and guessed that they were TTL compatible. A short while later (wink), the LabVIEW code was up and running. The LabVIEW computer (via a PCI-6052E card and BNC-2090 box) writes TTL stepper motor currents to the relevent lines on the Nanoscope↔MultiMode cable using our break-in box.

Computer <-> Breakout box <-> MultiMode
NanoScope <------^

Part list (outdated commercial parts linked to the current generation):

Breakout box

Build a DB-25 breakout box, such as the ugly hack job shown below. This gives us access to the stepper motor drive-lines.

Break in box for Nanoscope-MultiMode cable

Control the motor

First, you need something to write to your new motor control lines. We are using the digital I/O lines from this National Instruments PCI-6052E.

Next, you need to send the proper signals down the lines. Prof. Douglas Jones has an excellent stepping motor tutorial, which explains how to drive a unipolar stepping motor. For basic operation, you just need something like this pseudo-code:

step( up? ) {
  static int index=0; // saved between function calls, only initialized once
  int output[] = {sink 1a, sink 2a, sink 1b, sink 2b}; // the output array
  if (up? == TRUE) {
    index = (index + 1) mod 4;
  } else { // down
    index = (index - 1) mod 4;
  write( output[index] );

For my MultiMode, the motor steps 'Up' when I equate:


I've written a Python stepper module which handles the stepper logic for you.


The stepper motor, like any other mechanical linkage, can have problems with backlash.

Figuring it out

The interior of the MultiMode chassis with the main PCB removed

The main board of the MultiMode

General stepper motor wiring

If you look back at the MultiMode interior and main board (above), you can see that there are wires coming from the stepper motor, going into the connector board, and (presumably) ending up at the main MultiMode board. Opening up your own MultiMode (until I get some better pictures up), you will see that the motor has 6 leads: green, brown, black, red, yellow, and orange. A few seconds with your ohm-meter, and you'll find the resistances between the leads are either 20 or 40 ohms. I've recorded the resistances in the following table:

BrownGreenBlackYellowRed Orange
Brown 20 40 Inf. Inf.Inf.
Green 20 Inf. Inf.Inf.
Black Inf. Inf.Inf.
Yellow 20 40
Red 20

Browsing through Prof. Jones' tutorial, we see that our motor is probably unipolar with a total winding resistance of 40 Ohms on each winding.

Looking closely at the stepper motor, you can see (upside down, near the right hand screw) that it is labeled 5 V DC, and 5 V over a 20 Ohm half-winding gives a current of I = V/R = 0.25 Amps. Following the motor leads back up the the main board (using the ohm-meter guess-and-check method :p), we find that they come from the DS3658N (chip 1). This chip takes care of all the details of sinking the large motor currents given a TTL driving pattern.

WARNING! I strongly suggest you don't do this on your own. The high voltage lines for driving the piezo are potentially dangerous.

The control for the DS3658N was too difficult for me to trace out on the board, so I put the main board back in the MultiMeter (leaving the base-plate off), connected the Multimeter to our NanoScope IIIa, and started clicking on the ‘raise’ and ‘lower’ tip buttons. At the same time, I watched the various DB-25 lines on the oscilloscope. DB-25 lines 1, 2, 15, and 16 oscillated, but only when the motor was turning, so I figured they must be direct TTL controls getting (somehow) to the DS3658N. I built a DB-25 breakout box to take control of those lines, and started writing software.

Line roles

Knowing the stepper control lines and how to control a unipolar motor, it was only a matter of matching the lines to the roles. I arrived at the matches that I pointed out in the How section through trial and error. It was fairly easy to get the motor moving macroscopically, and backlash testing convinced me that the microscopic motion is reproducible and smooth.


We measured the stepper stepsize by stepping the AFM tip closer to surface and sweeping the piezo in after each step. This produced the data shown below.

Measuring the stepsize

As the motor steps in, we record consecutive traces a, b, c, d, e, and f. Because we can calibrate the piezo by imaging a calibration sample, we can convert our piezo voltage into the distance shown on the x axis. Measuring the x distance between to traces, we see that the sample moves ~170 nm closer with each step.


There is a separate post with some details on the manual stepper switch.