Design Article

IMG1

Open-Source Robotics and Process Control Circuit Examples - Part 2: Stepper Motor Controller

Lewin Edwards

8/26/2008 2:40 PM EDT

[Part 1 begins a look at how to interface with some common robotics-type sensors and actuators, and how these can be tied into an example system (in this case, the author's E-2 Autonomous Submarine Project).]

3.4 Stepper Motor Controller
Stepper motors are useful for relatively low-speed, intermediate-torque drive and positioning applications, particularly where accurate sub-revolution rotor position control is necessary. Motors of this type are commonly used to drive the reels on electromechanical slot machines (one-armed bandits), to position floppy disk drive heads, operate trainable camera platforms, and to power the drive wheels of small mobile robots. In times of yore, they were also used to position hard disk heads, though such applications have long ago been taken over by voice-coil type mechanisms.

Stepper motors are simple and cheap to use, and you don't need to have a fully closed-loop controller to use them accurately. Servomotors are much faster, but for guaranteeable positioning accuracy, you need to have a position encoder on the shaft to provide feedback on the actuator's position. By contrast, as long as you don't stray outside your system's nominal acceleration profile (see the following), a stepper-based system can reliably maintain its position indefinitely without recalibration.

There are several types of stepper motors, with varying electrical drive requirements. However, by far the most common type of motor to be found on the surplus market (or scavenged from unwanted computer equipment) is the four-pole unipolar type14, so this is the type our circuit is designed to use. Without further ado, here's the schematic15:

Figure 3-5: Stepper motor control circuit.

This project uses the ULN2803 octal high-voltage, high-current Darlington array to switch the stepper coils. This chip is readily available for around $0.75 in small quantities, and it is a handy solution for driving moderate loads. Until recently, one could often find this chip, or its close relatives, in commercial stepper motor applications such as inkjet printers and both sheet-fed and flatbed scanners. At present, however, it appears to be in decline as application-specific microcontrollers with high-current drivers on-chip take over its market space.

On the subject of prices, you'll notice that I've specified an NTSC colorburst crystal as the clock source, despite the fact that the tiny26L is rated at up to 8 MHz for a 5 V supply voltage. I chose the 3.579545 MHz value, although it's not a nice integer to work with, because these crystals are available everywhere and are often cheaper than other speeds. Chances are you have several in your junkbox already, in fact. You'll also find that application notes for microcontrollers almost always give precalculated example timing constant values (e.g., for setting the baud rate of a UART) for this base clock speed.

Our example stepper controller module also has two active-low limit switch inputs. These are optionally used to signal end-of-travel in the increment and decrement step directions. Note that JP4, which selects between 5 V or 12 V drive for the stepper coils, is intended to be a wire link for factory configuration, rather than a user-changeable jumper. If you are using the device in 5 V drive mode, you should alter or remove ZD1; you can also omit C2, since it serves no function if you're driving the motor off the +5 V rail.

The controller operates in one of two modes: "drive" or "train." In drive mode, you simply specify a speed and direction, and the motor turns in that direction until commanded to stop. Optionally, you can request that it travel until either of the limit switches is triggered. Train mode is intended for positioning applications. In this mode, you command the stepper controller to seek to a specific offset from the current position, and it will automatically seek to that position while you carry out other tasks. The stepper will automatically cut off if it hits the high limit switch while seeking forwards, or the low limit switch while seeking backwards.

Note that the limit switches are permanently associated with specific seek directions. The "low" limit switch is only enforced for "backwards" seeking, and the "high" limit switch is only enforced for "forwards" seeking. The reasons for this are twofold: First, an external force - say, water rushing past a submarine's rudder - might turn the stepper past the make-point for the limit switch, before it reaches a mechanical stop.

Second, switches are practically never perfect - in other words, the displacement required to make a contact isn't necessarily the displacement required to break it. You might need to push the arm of a microswitch two steps in to penetrate the oxide layer on its contacts; the first step in the other direction might leave the cleaned metal contact surfaces still touching. Or you might be using a reed switch—you need to bring the magnet to a certain proximity to close the switch, but a weaker field will suffice to hold the switch closed. In any of these sorts of cases, it could require one or more "extra" reverse steps to clear the limit condition.

The stepper controller accepts 8-bit command bytes, optionally followed by additional data. Essentially the same serial reception code is used in all the projects in this book, so it deserves a little additional study here. To begin with, please note that my choice of I/O pin assignments was by no means arbitrary. The AVR's pin-state-change interrupts are useful, but not very intelligent. On the tiny26L, there are only two such interrupts: PCINT0, which (if enabled) fires on state-changes for pins PB0-PB3, and PCINT1, which fires on state-changes for pins PA3, PA6, PA7, and PB4-PB7. When one of these interrupts fires, there is no direct way of determining which pin caused the interrupt; you have to maintain a shadow copy of the port registers and compare them to determine which pin(s) changed state.

Fortunately, when an alternate function is enabled for a pin, that pin will no longer generate state-change interrupts (note that there are a couple of exceptions to this rule). Even more fortunately, the three USI signals used for SPI-style communications are mapped to pins PB0-PB2. Thus, by configuring the USI in three-wire mode, PCINT0 will fire only if PB3 changes state. Since the USI in the tiny26L doesn't implement slave select logic in hardware, we need to do it in software—and as a result of all the discussion in the previous paragraph, it makes excellent sense to use PB3 as the SPI select line, since it has a state-change interrupt all to itself.

Footnotes:
14. When faced with an unknown stepper motor of small to intermediate size, a very reliable gamble to play is as follows: if it has more than four wires, it's probably a four-pole unipolar motor, 0.9 degrees per step, and likely rated for either 5 V or 12 V operation. There are vast numbers of motors constructed with these characteristics.

15. The alternate function for pin 1 is misprinted here as NOSI—it should be MOSI. This is an unimportant typographical error in the atmel.lbr library supplied by Cadsoft as part of the EAGLE package.

The stepper code
The entire meat of the stepper code is contained in three interrupt handlers: USI overflow, timer 0 overflow, and PCINT0. PCINT0 is probably the single most important function in the firmware - it is responsible for checking the state of PB3 and disabling the output driver on MISO (PB1) when the stepper controller is deselected (so we don't fight with anything else on the bus), or enabling it if _SSEL is asserted.

When the device is deselected, this ISR also disables USI interrupts, because we don't care about other transactions that may be occurring on the bus, and having to service USI interrupts causes timing jitter in any step operation we happen to be running in the background. Here's the code in this handler:

;====================================================================
; I/O pin change interrupt
; The only valid source of this interrupt is PB3, which is used as
; the 3-wire slave select line.
entry_iopins:
          push r0
          push r16
          push r17
          in r0, SREG

          ; Check state of select line, which is the only line that should
          ; have generated this interrupt.
          sbic PINB, PORTB_SEL
          rjmp usi_disable

          ; SEL line is LOW. Enable and reset USI and switch PB1 to output
          ldi r24, FSMS_RXCMD
          ldi r16, $00
          out USIDR, r16     ; Empty USI data register
          out USISR, r16     ; Clear USI status (including clock count!)
          sbi DDRB, PORTB_DO     ; set PB1 to output

          sbi USISR, USISIF     ; Clear start condition status
          sbi USISR, USIOIF     ; Clear overflow status
          sbi USICR, USIOIE     ; Enable USI overflow interrupts

          rjmp iopin_exit

          ; SEL line is HIGH. Disable USI and switch PB1 to input to take
          ; us off-bus
usi_disable:

          ; disable USI start and overflow interrupts
          cbi USICR, USISIE
          cbi USICR, USIOIE

          ; Disable output driver on PB1 (DO)
          cbi DDRB, PORTB_DO     ; set PB1 to input
iopin_exit:

          out SREG, r0
          pop r17
          pop r16
          pop r0
reti

Actual stepping operations are performed in the timer 0 overflow interrupt. Timer 0, which has an 8-bit count register, is clocked through the prescaler at CK/256, which is approximately 14.053 kHz. When the overflow interrupt fires, the first thing the handler does is to reload the timer register with a step speed value. The default speed value is $00. Since timer 0 counts upwards, this means that by default the step speed is roughly 55 Hz, which is the slowest configurable speed.

You can configure faster speeds by using the CMD_STEP_SETTICK command, followed by an 8-bit parameter that sets a new (larger) reload value. For instance, if you configure a reload value of $E0, the timer will overflow every 33rd ($21) tick instead of every 256th, thereby yielding a step speed of approximately 425 Hz.

Theoretically, you could specify a reload value of $FF, resulting in an overflow on every tick and a 14.053 kHz step speed, but in practice there is an upper boundary on legal values for the timer reload figure. This boundary is set by the number of CPU instruction cycles required to service an incoming interrupt and make ready for the next, and it caps the step speed at about 7.1 kHz (reload value $FE) for the cheap NTSC colorburst clock crystal I specified. This shouldn't be a serious impediment: although many stepper motors are rated for as much as 10,000 steps/sec, real applications rarely exceed 2,000 steps/sec (300rpm) due to the fact that the torque of a stepper motor rapidly decreases as step speed increases.

Timer 0 handler
The code for Timer 0 handler, along with the subroutines it calls, is as follows:

;====================================================================
; Timer 0 overflow
entry_timer0:
          push r0
          push r16
          push r17
          in r0, SREG

          ; Reset TMR0 counter to start position for next tick

          lds r16, tick_speed
          out TCNT0, r16

          ; Update state of limit switch flags in machine
          ; status byte (for the benefit of the main thread only)
          sbr r25, (1 << LIM_H)
          sbic PINA, PORTA_LIM_H
          cbr r25, (1 << LIM_H)

          sbr r25, (1 << LIM_L)
          sbic PINA, PORTA_LIM_L
          cbr r25, (1 << LIM_L)

          ; Load current tick-command and see what we should be doing
          cpi r19, TICK_FWD
          breq tick_seek_fwd
          cpi r19, TICK_REV
          breq tick_seek_rev
          cpi r19, TICK_POWERDOWN
          breq tick_poweroff
          cpi r19, TICK_SEEK
          breq tick_seekto
          cpi r19, TICK_SEEK_FWEND
          breq tick_seek_fwdend
          cpi r19, TICK_SEEK_RVEND
          breq tick_seek_revend

          ; Note - TICK_SLEEP falls through to here
          rjmp tick_done

;====================================================================
; Tick event - Seek forward, ignoring limit switch
tick_seek_fwd:
          rcall seek_fwd
          rjmp tick_done

;====================================================================
; Tick event - Seek backward, ignoring limit switch
tick_seek_rev:
          rcall seek_rev
          rjmp tick_done

;====================================================================
; Tick event - Seek forward, honoring limit switch
tick_seek_fwdend:
          sbis PINA, PORTA_LIM_H
          rjmp seekto_finished

          rcall seek_fwd
          rjmp tick_done

;====================================================================
; Tick event - Seek backward, honoring limit switch
tick_seek_revend:
          sbis PINA, PORTA_LIM_L
          rjmp seekto_finished

          rcall seek_rev
          rjmp tick_done

;====================================================================
; Tick event - Power down motor
tick_poweroff:
          andi r25, ~(1 << SEEKING)          ; Turn off busy flag
          in r16, PORTA
          andi r16, $F0
          out PORTA, r16
          ldi r19, TICK_SLEEP
          rjmp tick_done

;====================================================================
; Tick event - Generic seek operation
tick_seekto:

          ; First check if the step count is 0 - if it is, then there's
          ; nothing left to do and we should go back to sleep.
          cpi r23, $00
          brne seekto_nz
          cpi r22, $00
          brne seekto_nz
          cpi r21, $00
          brne seekto_nz
          cpi r20, $00
          brne seekto_nz

          rjmp seekto_finished

seekto_nz:
          sbrc r25, DIRECTION
          rjmp seekto_fwd

          ; Seekto - REVERSE
          ; Check limit switch. If it's active, we stop.
          sbis PINA, PORTA_LIM_L          ; Check limit switch
          rjmp seekto_finished

          rcall seek_rev
          rjmp seekto_update_count

          ; Seekto - FORWARD

seekto_fwd:
          sbis PINA, PORTA_LIM_H          ; Check limit switch
          rjmp seekto_finished

          rcall seek_fwd
          rjmp seekto_update_count

seekto_update_count:
          dec r23
          cpi r23, $FF
          brne seekto_notz
          dec r22
          cpi r22, $FF
          brne seekto_notz
          dec r21
          cpi r21, $FF
          brne seekto_notz
          dec r20

; No terminal conditions have been encountered - continue stepping
seekto_notz:
          rjmp tick_done

seekto_finished:
          ldi r19, TICK_SLEEP
          andi r25, ~(1 << SEEKING)          ; Turn off busy flag
          rjmp tick_done

tick_done:
          out SREG, r0
          pop r17
          pop r16
          pop r0
          reti

;====================================================================
; SUBROUTINE - Seek forward one step
; Destroys R16, R17, SREG
; Updates X,Y
; Implicitly powers up motor and leaves it in powered state
seek_fwd:
          lds r16, stepper_phase
          inc r16
          andi r16, $03          ; Only the lower two bits interest us
          sts stepper_phase, r16          ; Store new current phase
          ldi r17, $01

sf_lp:
          cpi r16, $00
          breq sf_lp_done
          dec r16
          lsl r17
          rjmp sf_lp

sf_lp_done:
          in r16, PORTA
          andi r16, $F0
          or r16, r17
          out PORTA, r16

sf_update:
          inc r29          ; increment step position
          brne sf_done
          inc r28
          brne sf_done
          inc r27
          brne sf_done
          inc r26

sf_done:
          ret

;====================================================================
; SUBROUTINE - Seek backward one step
; Destroys R16, SREG
; Updates X,Y
; Implicitly powers up motor and leaves it in powered state
seek_rev:
          lds r16, stepper_phase
          dec r16
          andi r16, $03          ; Only the lower two bits interest us
          sts stepper_phase, r16          ; Store new current phase

          ldi r17, $01

sr_lp:
          cpi r16, $00
          breq sr_lp_done
          dec r16
          lsl r17
          rjmp sr_lp

sr_lp_done:
          in r16, PORTA
          andi r16, $F0
          or r16, r17
          out PORTA, r16

sr_update:
          ; Check for zero condition.
          sbic PINA, PORTA_LIM_L
          rjmp sb_notz
          clr r26
          clr r27
          clr r28
          clr r29
          ret

sb_notz:
          dec r29          ; decrement step position
          cpi r29, $FF
          brne sr_done
          dec r28
          cpi r28, $FF
          brne sr_done
          dec r27
          cpi r27, $FF
          brne sr_done
          dec r26
          cpi r26, $FF
          brne sr_done

sr_done:
          ret

Firmware commands
The most complex code segment, at least in terms of code volume, is the USI handler. This handler implements a simple state machine. The first byte received after the select line goes active is a command byte. This byte either causes the USI receive ISR to modify the system state directly, or to transit the ISR's state machine through further intermediate states either to receive more data, or to transmit a multi-byte response back to the host. I won't reproduce the code here, because it's largely a very long switch ... case statement implemented in assembly language.

The theoretical transfer speed between the stepper controller and its master is limited by a couple of factors. First, hardware absolutely limits the USI to transfer clock rates of fCK/4 in three-wire slave mode. In our case, this is approximately 895 kHz, and we have to be careful that the SPI master doesn't exceed this speed.

In the case of a PC parallel port master, as we are discussing in this book, it is unlikely (however, not entirely impossible) that you will be able to outrun the USI. The reason for this is tied up in the ancient PC architecture of twenty years ago, and the fact that the parallel interface is designed for backwards compatibility with 9-pin dot-matrix printers run by slow 8-bit microcontrollers. Because of these compatibility issues, the parallel port registers are, conceptually or physically, on the other end of an ISA bridge; they are limited to ISA bus clock speeds (nominally 8 MHz) and may even have additional wait states inserted.

Furthermore, the layers we traverse when making calls to change the parallel port registers add extra delays, the constant SMM interrupts on the Geode platform are stealing cycles from us regularly—and to cap it all off, the application layer in Linux is inherently so nonreal-time as to make the idea of accurately marking off 1 µS delay steps in userland quite silly indeed. You will observe, therefore, that the SPI code I provide in e2bus.c does not have explicit timing instructions throughout all of the state-changes. This code is tested on the 300 MHz Geode platform with Advantech's BIOS 1.23; if you need to rely on it to work faultlessly, it will need, at minimum, testing and requalification on other systems.

Following is a complete list of commands recognized by the stepper controller's firmware. (The mnemonic names are defined for you in e2bus.h):

Mnemonic Value Description
STEP_CMD_STOP 0 Aborts any current step operation. Note that the current step position, and the steps-remaining counter, are not altered—you can resume the step operation later, if necessary. The stepper motor coils remain powered.
STEP_CMD_SLEEP 1 Aborts the current step operation and de-energizes all stepper coils. The motor may move a step or two unpredictably when you re-energize it; a calibration may be necessary.
STEP_CMD_SETTICK 2 Set the step rate (larger values = faster rate). This command should be followed immediately by a one-byte step rate.
STEP_CMD_DRIVE_FWD 3 Starts the motor driving forwards at the configured step speed. The motor will continue to spin until another step command is received, ignoring the limit switches.
STEP_CMD_DRIVE_REV 4 Works the same as STEP_CMD_DRIVE_ FWD but steps backwards instead of forwards.
STEP_CMD_STEP_FWD 5 Steps forwards a specified number of steps, or until the "high" limit switch is closed. This command should be followed immediately by a four-byte step count (MSB first).
STEP_CMD_STEP_REV 6 Works the same as STEP_CMD_STEP_ FWD but steps backwards instead of forwards, and monitors the "low" limit switch. The current step position is set to 00000000 when this switch is closed.
STEP_CMD_FWD_END 7 Steps forwards continuously until the "high" limit switch is asserted.
STEP_CMD_REV_END 8 Steps backwards continuously until the "low" limit switch is asserted. The current step position is set to 00000000 when this switch is closed.
STEP_CMD_READ_STATUS 254 Reads back the controller status byte and four-byte step position.
STEP_CMD_RESET 255 Performs a soft reset.

Firmware commands (cont.)
The status byte returned by STEP_CMD_READ_STATUS is formatted as follows:

Bit Function
7 Unexpected interrupt or internal error detected.
6 Step error, e.g., attempt to seek beyond calibrated range. (LED2 " ERR " tracks the state of this bit).
5 Reserved.
4 Reserved.
3 Reserved.
2 High limit switch asserted.
1 Low limit switch asserted.
0 Currently seeking to requested position (LED1 " BSY " tracks the state of this bit).

This is an extremely simple stepper control design; it is intended for low-speed positioning and simple low-torque drive applications only. The E-2 project uses these modules to position its rudder and dive planes, and to swivel a camera platform, neither of which are particularly demanding applications. For a little more understanding of the subtleties of stepper motor control, try running this little code snippet, which assumes that you have a stepper module connected as E2BUS device #0. (You'll find this sourcecode, with a makefile, in the stepper1 directory of the sample sourcecode archive):

#include <stdio.h>
#include "e2bus.h"
int main (int _argc, char *_argv[]) {
          unsigned char pkt[2];

          // Open port and start stepper motor
          if (E2B_Acquire()) {
                    printf("Error opening E2BUS.\n");
                    return -1;
          }

          E2B_Reset();

          pkt[0] = STEP_CMD_DRIVE_FWD;
          E2B_Tx_Bytes(pkt, 1, 0, 1);

          // Speed motor up gradually
          pkt[0] = STEP_CMD_SETTICK;
          for (pkt[1] = 0; pkt[1]<255; pkt[1]++) {
                    printf("Setting speed: %d\n", pkt[1]);
                    E2B_Tx_Bytes(pkt, 2, 0, 1);
                    sleep(1);
          }

          // Stop motor and de-energize it
          pkt[0] = STEP_CMD_SLEEP;
          E2B_Tx_Bytes(pkt, 1, 0, 1);

          return 0;
}

This will take a few minutes to complete its run. While it's proceeding, listen to and watch your motor. You will observe two things:

  1. Certain step speeds are very noisy, but there will be a range of speeds - typically, the faster speeds - for which the motor is comparatively silent.
  2. Depending on your stepper motor, at some point on the speed ramp, the motor will probably stop spinning and will simply begin to hum. Take a note of the approximate speed value when this happens; for the motors I am using (under no mechanical load), this is about 240.

The second point in particular is important, and needs elucidation. Try running this second code snippet (this project is located in the stepper2 directory):

#include <stdio.h>
#include "e2bus.h"

int main (int _argc, char *_argv[])
{
          unsigned char pkt[2];

          // Open port and start stepper motor
          if (E2B_Acquire()) {
                    printf("Error opening E2BUS.\n");
          }
          E2B_Reset();

          // Set the maximum speed your motor can withstand
          pkt[0] = STEP_CMD_SETTICK;
          pkt[1] = 230;
          printf("Setting speed: %d\n", pkt[1]);
          E2B_Tx_Bytes(pkt, 2, 0, 1);

          printf("Starting motor.\n");
          pkt[0] = STEP_CMD_DRIVE_FWD;
          E2B_Tx_Bytes(pkt, 1, 0, 1);

          sleep(2);

          // Stop motor and de-energize it
          pkt[0] = STEP_CMD_SLEEP;
          E2B_Tx_Bytes(pkt, 1, 0, 1);

          printf("Finished.\n");

          return 0;
}

Here we attempt to set a speed just shy of the fastest possible step rate we observed in the first test, while the motor is stopped - and then we try to start the stepper. You will observe, however, that it doesn't rotate at all - it just stalls and whines like an engineer awakened by his wife's alarm clock. (If you don't see this behavior, then increase the speed value a little, recompile, and try again).

The stepper motor mechanics
To understand what's going on here, you need to think about the mechanics of the situation. Each position of the stepper's rotor is a stable mechanical state for a certain corresponding electrical (magnetic) state of the coils. This state can be visualized as a valley between two hills of a plot of net force vs. angle; the motor tries to seek the low point in the valley, where the clockwise and counterclockwise forces on the rotor are equal. If you put an external mechanical force on the rotor, in (say) a clockwise direction, you are pushing up one side of the hill. If you push all the way to the top, then when you release the rotor, it will fall down into the next valley, i.e., the next stable step position.

When we advance the step phase in software, we alter the electrical state of the coils, which creates a new position of mechanical equilibrium. Effectively, we move the two hills and valley along a quarter-phase, which means the motor is no longer sitting in the middle of the valley. Since the clockwise and counterclockwise forces on the rotor are no longer equal, the rotor turns until it is in an equilibrium position once again; the step operation is then complete. However, this process doesn't happen instantly, because the rotor " and whatever mechanical load it's driving—has inertia. The time required for the rotor to find its new equilibrium point depends, among other things, on the inertia (which is determined by the mechanical load on the motor) and the force exerted by the coils - which is directly proportional to coil current.

The above explains both why we observe unreasonable stepper noise at certain combinations of load and step rate, and the fact that we can't necessarily go from stationary to maximum achievable step speed instantly. At slow speeds and/or with light loads, the motor will snap to each new equilibrium point very quickly compared to the step rate, and will in fact stop at the bottom of the equilibrium valley waiting for the next change of magnetic state.

This is horrifically wasteful of energy - every single step, we're injecting enough energy to overcome static friction forces and impart some amount of angular momentum to the stepper shaft and its attached load. The motor responds quickly and reaches its new stable state, but our control software isn't ready to move on to the next step state yet. The motor's stored inertia causes it to start climbing the slope of the next "hill," which turns the stepper temporarily into a generator. The energy we just pumped in is shorted through the transient protection circuitry and turned into heat. The motor slumps back down to the stable position until the next step pulse comes along, whereupon the entire process is repeated. This, along with the possibility of simple mechanical resonances in the motor and other components at certain step speeds, creates a loud and objectionable noise.

The most efficient way to drive the motor is to transit to the next step state as soon as the rotor reaches its new equilibrium position; this way, you are always pushing the load in the desired direction. Knowing exactly when this occurs is difficult, and it leads nicely into the next issue, which explains my point #2 on p. 66: We're not exerting a constant force on the rotor; rather, we are kicking it periodically. In order for our kicks to do the most good, we have to time them to coincide with certain rotor positions.

At slow speeds, the motor responds to the step pulses faster than we issue them (so the motor can be said to be "led" by the step pulses). However, as the speed increases, we start to rely increasingly on the fact that the motor's inertia will carry it to the equilibrium point by the time we kick it next. This is why you can't simply start the motor at its maximum possible step speed - starting from rest, the rotor won't have time to reach equilibrium point before we transit to the next state.

On the flip side of the coin, once the motor is whirling madly at high speeds, simply stopping the step pulses dead will very likely lead to mechanical overshoot. You probably won't observe that phenomenon on a bare motor - at least, not the small, light motors you're likely to be using - but it's a very real issue under load. The most common approach to this is to implement an "acceleration profile," which is a table of step rates describing how to get from one motor speed to another most efficiently and reliably. These sorts of tables are easily handled in small microcontrollers. Advanced stepper designs will also use higher drive current (in other words, steeper hills) to achieve faster speed changes.

Completely general solutions to these problems (and others not mentioned) are possible - for example, I should point out that as soon as the motor overshoots the current equilibrium position, it will start generating a back EMF that can be measured via a suitably delicate circuit. However, such complex solutions are not required for simple, low-speed stepper applications, and they lie beyond the scope of this text.

Coming up in Part 3: Speed-controlled DC motor with tach feedback and thermal cutoff.

Printed with permission from Newnes, a division of Elsevier. Copyright 2004. "Open-Source Robotics and Process Control Cookbook" by Lewin Edwards. For more information about this title and other similar books, please visit www.elsevierdirect.com.

Related links:
Open-Source Robotics and Process Control: Sensor, Actuator and Control Circuit Examples - Part 1
Choosing a microcontroller and other design decisions - Part 1 | Part 2 | Part 3
Building flexible architectures for configurable UAV systems
Rolling your own
The lowdown on Embedded Linux and its use in real-time applications: Part 1 | Part 2
How to roll your own Linux


print

email

rss

Bookmark and Share

Joinpost comment




Please sign in to post comment

Navigate to related information

Most Popular

Product Parts Search

Enter part number or keyword
PartsSearch


FeedbackForm