Position: Index > Unclassified >

The ATmega32 offers a rich set of peripherals useful for thi

2017-09-06 04:29  
Declaration:We aim to transmit more information by carrying articles . We will delete it soon, if we are involved in the problems of article content ,copyright or other problems.

 

The ATmega32 offers a rich set of peripherals useful for this sort of project, but inevitably once you start to use those peripherals, the availability of general-purpose I/O pins shrinks.To realize the maximum amenity from the ATmega32's peripheral set, you therefore need to categorize your I/O requirements, first according to whether a hardware feature of the microcontroller can carry out or accelerate the desired function, and secondly (in the case of functions that you will be implementing entirely in software), according to the specific timing limits for implementing the function. For example, something like a radio receiver input, with tight timing requirements, would be best connected directly to a micro input pin (preferably a pin with interrupt-on-change capability). Building I/O expansion You then need to build enough I/O expansion to get around the shortage of I/O pins on the microcontroller. You can achieve this goal in many ways, of which these are the top three:• Use external flip-flops (for example, 74HC373) to latch expansion output data and external tristatable buffers (for example, 74HC244) to scan in expansion inputs.• Use the ATmega32's on-chip SPI module to drive external I/O expanders.• Use the ATmega32's on-chip I2C (TWI, see sidebar1) module to drive external I/O expanders.Each of these methods has its own distinct advantages and disadvantages that are worth exploring.Using external logic is a simple route. It also offers the lowest possible latencies. However, the difficulty is that it directly consumes a relatively large number of I/Os. For example, if you wanted to add 24 I/Os to your device, you could add three 74HC373s and three 74HC244s. The inputs of the 373s and the outputs of the 244s would form a common data bus, routed to eight pins on the micro. But you would also need a read/write strobe line and at least three addressing pins -- bringing the total required I/O count up to 12.The simple external logic method also can't give you an interrupt when input states change; in some applications, that might be important. Of course, you could structure external interfaces with jellybean logic in many other ways, but it's a headache to design and debug and is often difficult to route on the printed circuit board (PCB) as well.Serial-connected I/O expanders utilize CPU pins much more effectively, and these chips also provide interrupt-on-change capabilities on the inputs, if you should require it -- they are also generally programmable, so you can remap inputs and outputs as necessary with software changes rather than needing to mess with physical wiring. All things considered, serial I/O expansion is definitely the way to go. On-board serial interfaces This article considers two very popular on-board serial interfaces: SPI and I2C (see sidebar1).SPI is usually described in fashionable literature as a three-wire interface, but in any system with more than one peripheral this is a bit of a smoke and mirrors act -- it really requires four lines: clock, data in, data out, and select. All SPI peripherals are connected to common clock, data in, and data out lines, and each peripheral has its own dedicated select line. (The two data lines are referred to as MOSI and MISO, acronyms for Master Out, Slave In; and Master In, Slave Out, respectively). To address an SPI device, you assert its _SS (Slave Select) pin and start clocking data out of your MOSI pin. The same clock signal pulls data out of the peripheral into your MISO pin, so after eight clocks you've simultaneously sent a byte and received a byte. The transaction ends when you deassert the _SS pin.SPI is very easy to use (especially if you have to implement it in pure software), but the requirement for individual _SS (Slave Select) lines means that you need to add extra wiring -- and an extra I/O -- for every peripheral, rather than having a true "bus" architecture. It is therefore slightly harder to route complex SPI buses over a PCB. The I2C serial bus I2C gets over this limitation and allows you to add extra peripherals to the bus simply by wiring them in parallel with existing devices. It achieves this at the expense of some implementation complexity. I2C is a two-wire interface; the signals are named SCL (Serial CLock) and SDA (Serial DAta). I2C peripherals have a 7-bit address -- generally, some of the address is fixed for a given part, and a few bits are configurable by means of resistor strapping or EEPROM setup. The protocol is, unfortunately, quite involved, with numerous meaningful states and state transitions. Luckily for us, practically all of the requisite magic is implemented in hardware inside the ATmega32's TWI module; this is a case of hardware support really saving you a lot of design and debug time. All you need to do is add two pull-up resistors selected according to the load capacitance of your I2C bus and the desired maximum data rate, and wire up all your devices in parallel.The principal disadvantage of I2C is that it's difficult to propagate over long distances through noisy environments. Because the data line (and, in multimaster systems, also the clock line) is bidirectional, you can't shape up the signals with a simple buffer. I2C was designed for, and works best in, communications on a single board or within a subassembly. It's commonly used in TV sets, PC motherboards, laptop batteries (and other smart battery types), and other consumer equipment.I have an ulterior motive for using I2C -- namely, the rich variety of peripherals available with I2C buses. Practically any sensor you can think of that might be of interest in an embedded application is available with an I2C-flavored interface -- for example (and this list is far from exhaustive), temperature sensors, pressure sensors, battery charge controllers, control interfaces for image sensors, television on-screen display chips, and even complex subassemblies like flux gate compasses, gyroscopes, and GPS receivers. Although some of these sensors are available with SPI-style interfaces, I2C is more widely supported.The revised circuit designWith all that background information digested, examine the schematic for the revised circuit: Figure 1. The VCM module 

74HC373

 

As you see, I've chosen to use the Microchip MCP23008 8-bit I/O expander. This chip supports the three standard I2C data rates of 100kHz, 400kHz, and 1.7MHz. Note that there are other expander ICs, such as the Philips PCA95xx series parts, that offer more bits of I/O for roughly the same price as the MCP23008. I'm using the Microchip device in these developerWorks projects because it's available in a dual in-line package (DIP) and is therefore easier to hand-prototype; the PCA95xx parts are only available in SOIC and (T)SSOP. Moving to one of the higher pin-count devices is, however, a trivial modification, both in terms of hardware and code.

The MCP23008's outputs drive the inputs of a ULN2803A octal Darlington driver, which I use to drive two four-phase stepper motors. You'll find a link in Resources to some excellent background reading on stepper motors, as well as some handy hints on how to identify steppers scavenged from computer peripherals. I have amassed a huge collection of stepper motors simply by picking up my neighbors' unwanted inkjet printers and flatbed scanners off the curb on trash day. Because it is now cheaper to buy an entire new printer with fresh ink cartridges than to buy a set of replacement cartridges, I usually see two or three junked printers per week, each one a trove of motors and mechanical parts suitable for miscellaneous experiments.

For more formal projects, you can buy new, documented steppers from sources such as the suppliers mentioned in Resources. eBay is also a rich source of brand new steppers, usually with documentation -- although you often have to buy them a tray at a time.

Note that the ATmega32 allows you to perform I2C operations asynchronously, using interrupts (though it can be quite complex to write an interrupt service routine (ISR) that covers all the bases if you're using several different I2C peripherals that have radically different protocols). The code I'm presenting here uses a simpler polled method. Note that this I2C code is not reentrant! If you plan to use the TWI module from an interrupt routine as well as main process code, you must erect suitable barriers to prevent an interrupt from attempting to use I2C in the middle of a main process I2C transaction. The firmware in this article is carefully designed to avoid this sort of contention.

Speaking of code, it's time to unpack the source code archive for this episode and build the VCM code in the vcm_399 directory and the SCM code in the scmd directory.

Observe first, and most importantly, that we've tightened up the serial packet format. The previous article in this series used some very simple demo code that you could talk to with a terminal emulator, just to prove that the communications were working bidirectionally.

The new packet format is as follows:

Table 1. New packet format

Name

Type

Description

STRT

1 byte

Start character, '!'

CSUM

1 byte

Unsigned checksum of all bytes in packet from CSUM+1 to end of BODY (or, all bytes except STRT and CSUM)

TXSN

1 byte

Serial number of transmission (see below)

IRSN

1 byte

'In Response To' serial number (see below)

CMND

1 byte

Command or data ID

BLEN

1 byte

Number of bytes in BODY or zero if no BODY

BODY

Variable

Additional data, if required

Each end waits for a "!" character to begin buffering data. The packet is deemed complete when the requisite number of BODY bytes has been received. Bytes received beyond the capacity of the serial-receive buffer are discarded. Since the start character might occur in the middle of real data, the CSUM field is used to verify that the data block just received was in fact a complete packet.

But what are these "serial numbers" I mention? They are a means of identifying specific question-answer pairs in a transmission stream, as well as for spotting dropped packets. At power up, both the VCM and the Kuro Box initialize their internal serial number counter to 1 (0 is a reserved value). When one end of the link wants to send a packet, it sets the TXSN field to the current serial number value, then increments the serial number counter (if the counter overflows to 0, it is reset to 1). If the packet being sent requires a response, when the other end replies, it sets the IRSN field to the TXSN from the packet to which it is responding.

An "originated" packet, or, one that is not a response to a request from the other side of the link, has the IRSN field set to 0.

This very simple system allows either end to keep a history of pending data requests and track which responses belong to which outstanding requests (as well as, if necessary, implementing time-outs and retries on packets that simply don't get a response).

An example illustrates this most easily. Suppose you defined command #99 to be "Get current vehicle attitude," and command #100 to be "Get current vehicle position from GPS." Furthermore, suppose that the current serial number in the VCM was 56 and the serial number in the Kuro Box was 80.

Then say the Kuro Box wants to know the vehicle's current attitude and position as soon as possible. It might send the following packets in quick succession:

A packet with CMND=100 (get GPS position), TXSN=80, IRSN=0, and no BODYA packet with CMND=99 (get attitude), TXSN=81, IRSN=0, and no BODY

The GPS position can take some time for the VCM to acquire, so the attitude could easily be ready for transmission before the position data. Assuming this scenario takes place, the VCM would respond with:

A packet with CMD=99 (get attitude), TXSN=56, IRSN=81, and attitude data in the BODY. Note that the nonzero IRSN implicitly indicates that this is a response rather than a request.(Later, when data is available) A packet with CMD=100 (get position), TXSN=57, IRSN=80, and the position data in the BODY.

If for some reason the VCM didn't respond to one of these packets, the Kuro Box could keep a record of which serial numbers are outstanding, and after some time-out period, reissue the request. (Note that this system breaks down if the serial number wraps and comes back to the same number as the earliest pending request. For this reason, your time-out code should also trigger if TXSN wraps to the earliest pending serial number).

Some more rules wrap these kinds of queued requests, but I'll discuss those protocol details once you've got some of the appropriate hardware attached.

Before you run the code, you should understand what it does and how to wire up your stepper motor(s). The links in Resourcesdescribe in detail how a stepper motor (synchronous DC motor) is constructed and driven, but here's a thumbnail: A stepper motor has fixed coils and a permanent magnet rotor. The types of motors I use have four coils (phases), each of which has one line run to a common wire and the other wire run to the outside world. (The common wire is also run to the outside world.) When a given phase is energized, it pulls the rotor towards a certain position. The four phases are physically positioned so that if you energize them in repeated sequence (1, 2, 3, 4, 1, 2, 3, 4, 1, ...) the motor will turn continuously in a given direction.

The stepper outputs on the VCM board are labeled SAP1 through 4 (Stepper A Phase 1 through 4) and SBP1 through 4 (Stepper B Phase 1 through 4). You should connect the phase lines up to the SxPx lines as appropriate, and the common line(s) from your motor(s) to the +24V line. On the VCM, I use a 9-position terminal block to take these signals off-board.

Attention! Observe the series resistors on the stepper drive lines. The 0ohm resistors I've specified are placeholders; you should substitute suitable current-limiting resistors based on your drive voltage and the stepper motor's coil resistance and rated current.

This code and hardware are designed to provide very simple control of two four-phase unipolar stepper motors, Stepper A and Stepper B. The step functions are driven, by default, off a 100Hz software timer. This is a moderate speed for junkbox steppers, and a fairly safe arbitrary choice on my part. The hardware is capable of considerably higher step rates, however. You should be aware of the following two issues as you increase step rates:

Without going into the detailed mechanics of it, a stepper can't start from zero and instantly begin operating at its maximum rated step frequency. You need to implement "acceleration profiles" if you intend to get the most out of your motors. In brief, this means that if you need to make a speed change on the motor, instead of just jumping to a new step rate, you ramp the step rate towards the target speed. Acceleration profiles need to be calibrated for the motor and the mechanical load being driven.As you increase step rates, torque decreases. Cunning hardware designs are necessary to squeeze the maximum possible performance out of a given motor.

This particular design is nowhere near to pushing the envelope in either of these respects -- you have plenty of headroom to increase step rates, for instance.

Referring to stepper.c, you'll see that the software keeps track of the absolute position of each stepper, checking it at the step speed interval. If the position ordered by the Kuro Box is not the same as the current position, the appropriate motor is stepped either forwards or backwards. The serial interface code listens for "seek to..." packets and updates the target values appropriately.

The VCM code, for the moment, assumes that the power-up position of each stepper motor is its known absolute zero position. Finding the zero reference is normally accomplished by having a hardware zero position detector attached to the motor's drive shaft (or the mechanism it operates). For example, in floppy disk drives, the track 0 sensor is an opto-interrupter gate, or (in older drives) sometimes a microswitch that the head engages when it is stepped to the track 0 position.

The two packet types supported by the VCM firmware in the vcm_399 directory are CMD_STEP_A and CMD_STEP_B (defined in vcmpacket.h). If you build the Kuro Box side in the scmd directory, you'll see the Kuro Box printing out status info from the VCM and, once every four status packets, sending the VCM commands to seek both stepper motors to random locations. Please note that the Kuro Box side of the code is quick and dirty, since at the moment I'm concentrating on the VCM side. A little later on, after we start integrating the two more closely, I'll tidy and tighten the Kuro Box-side code.

However, while on the Kuro code, note that the terminal setup is slightly different in this version than in the scmd provided with the previous article. Specifically, I removed the IGNCR bit on the input flags, otherwise every 0x0d byte would be stripped by the terminal layer.

Here's some sample output from the scmd program:


Listing 1. Stepper motor demo output

root@KURO-BOX:/mnt/share/article8/scmd# ./scmd

IBM developerWorks Kuro Box to VCM Demo Applet #2 - Stepper Demo

Waiting for VCM to start sending...

Rx packet (TXSN 0x01, BLEN 0x05) - MTIME 0x000005b8, FLAGS1 0x00

Rx packet (TXSN 0x02, BLEN 0x05) - MTIME 0x00000b70, FLAGS1 0x00

Rx packet (TXSN 0x03, BLEN 0x05) - MTIME 0x00001128, FLAGS1 0x00

Rx packet (TXSN 0x04, BLEN 0x05) - MTIME 0x000016e0, FLAGS1 0x00

Tx 2 packets: CMD_STEP_A->00000167, CMD_STEP_B->FFFFFF97

Rx packet (TXSN 0x05, BLEN 0x05) - MTIME 0x00001c98, FLAGS1 0x00

Rx packet (TXSN 0x06, BLEN 0x05) - MTIME 0x00002250, FLAGS1 0x00

Rx packet (TXSN 0x07, BLEN 0x05) - MTIME 0x00002808, FLAGS1 0x00

Rx packet (TXSN 0x08, BLEN 0x05) - MTIME 0x00002dc0, FLAGS1 0x00

Tx 2 packets: CMD_STEP_A->FFFFFFAF, CMD_STEP_B->0000004A

Rx packet (TXSN 0x09, BLEN 0x05) - MTIME 0x00003378, FLAGS1 0x01

Rx packet (TXSN 0x0a, BLEN 0x05) - MTIME 0x00003930, FLAGS1 0x00

Rx packet (TXSN 0x0b, BLEN 0x05) - MTIME 0x00003ee8, FLAGS1 0x00

Rx packet (TXSN 0x0c, BLEN 0x05) - MTIME 0x000044a0, FLAGS1 0x00

Tx 2 packets: CMD_STEP_A->FFFFFED7, CMD_STEP_B->FFFFFF46

Rx packet (TXSN 0x0d, BLEN 0x05) - MTIME 0x00004a58, FLAGS1 0x00

Rx packet (TXSN 0x0e, BLEN 0x05) - MTIME 0x00005010, FLAGS1 0x00

Rx packet (TXSN 0x0f, BLEN 0x05) - MTIME 0x000055c8, FLAGS1 0x00

Rx packet (TXSN 0x10, BLEN 0x05) - MTIME 0x00005b80, FLAGS1 0x00

Tx 2 packets: CMD_STEP_A->FFFFFE0E, CMD_STEP_B->000001E3

Rx packet (TXSN 0x11, BLEN 0x05) - MTIME 0x00006138, FLAGS1 0x02

Rx packet (TXSN 0x12, BLEN 0x05) - MTIME 0x000066f0, FLAGS1 0x00

Rx packet (TXSN 0x13, BLEN 0x05) - MTIME 0x00006ca8, FLAGS1 0x00

 

Observe that the VCM informs the Kuro Box if it's busy stepping every time the VCM sends status; this information is contained in FLAGS1 in the CMD_STATUS_REPORT packet. FLAGS1 bit 0 set means that Stepper A was running when the status packet was generated; likewise, bit 1 means that Stepper B was running.