BMOW title
Floppy Emu banner

Archive for May, 2011

Tiny CPU Rebirth?

With the Backcountry Data Logger more or less finished (just waiting for the PCB), my thoughts have returned to my neglected Tiny CPU project. I did a huge amount of design work on Tiny CPU about a year ago, finishing the complete CPU design in Verilog, as well as a companion design called Tiny Device for bank switching and peripheral I/O. I even built a test board using the target CPLD (a 128-macrocell Altera EPM7128), so I could experiment with JTAG programming and various construction issues. But then progress stalled, and there’s been nothing further for a year.

I lost interest in Tiny CPU. Why? I don’t fully understand it myself, but two reasons in particular are big contributors:

Questionable Value – There are lots of small soft CPU cores out there, with probably 100 different designs on opencores.org alone. Some of the better ones like PicoBlaze provide a very capable CPU that still fits a pretty small device. Or on the other end of the scale, soft CPUs like MCPU are academic masterpieces, fitting into the smallest devices, even if they aren’t very capable or practical as CPUs. Tiny CPU occupies an awkward middle ground where it requires nearly as large a device as PicoBlaze, yet provides not much more capability than MCPU. So what’s the point?

Construction Fatigue – After completing the Verilog design work, all that remained was to actually build a computer using Tiny CPU. I say “all” with some sarcasm, because designing the required PCB and working out all the necessary connections between the twin CPLDs and SRAM and ROM is a huge job, not to mention writing all the software needed to make it work. At the time, I was really much more interested in the CPU design work than the construction aspect of the project.

Not too much has changed since then, except that I’m more interested in custom PCB design and construction than I was before. I also hate to see a project abandoned half-finished, so I’m considering returning to Tiny CPU with a different focus. I can’t do much about the questionable value of Tiny CPU, but honestly, who ever said any of my projects had value? I can do something about the construction question, though, so that’s where I’ll focus my attention.

Enter the Max II

The original Tiny CPU design called for an Altera EPM7128, a comparatively old 5V device in a PLCC package, allowing for through-hole soldering with a PLCC socket. Tiny CPU and Tiny Device each required a separate CPLD, so there would be two of them on the computer board. I insisted on the EPM7128 because I wasn’t confident I could solder the surface mount packages that most newer devices use, and also because I liked the challenge of fitting 128 macrocells.

I’m now looking at using an Altera Max II, which is a much more modern CPLD with the equivalent of roughly 192 or 440 macrocells in the two smallest sizes. The Max II is a 3.3V device, so this change would involve moving all the other components in the system to 3.3V as well. Fortunately 3.3V SRAM, Flash ROM, and LCDs are pretty easy to find, so that’s not an issue. The only challenge would be the PS/2 keyboard interface. The keyboard data and clock signals are unidirectional in the Tiny CPU design, and use an open collector and pull-up resistor instead of actively driving 5V, so that wouldn’t be a problem. However, the keyboard itself would require a 5V supply.

Switching to the Max II would entail combining Tiny CPU and Tiny Device into a single CPLD, creating something that’s a bit more like a microcontroller than a traditional CPU. The Max II comes in a 100-pin TQFP package with 0.5mm pin spacing, so it would be a real challenge to solder, but I’ve been told repeatedly that it can be done.

The Max II also has a few other advantages. It has a built-in clock generator and power-on reset circuitry, allowing me to delete two external parts from my original design. It also has 1KB of internal Flash memory, which might be useful for bootstrapping.

The final advantage of the Max II, especially the 440 macrocell-equivalent version, is that it would provide some breathing room to recover from mistakes. The original Tiny CPU and Tiny Device designs both just barely fit the EPM7128, and any bugs discovered after construction might require fixes that would push them over the capacity of the device. Some combination of the tight fit and age of the device also gave Altera’s synthesis software problems: if I synthesized, back-annotated the result, and synthesized again, it would fail to fit the second time. That meant that I couldn’t use back-annotation or pin constraints, and any trivial Verilog change might result in new pin assignments that no longer matched the board.

Of course, with a larger device, the very resource constraints that motivated the original CPU design would no longer apply. It might now be possible to increase the address space beyond 10 bits, or add some of the additional address modes that didn’t fit before. I’ve decided I would ignore these opportunities, though, and instead focus on implementing the original design in new hardware. I don’t really have the enthusiasm to revisit the whole CPU design part of the project, so the project would be an example of a generic “small” soft CPU realized in hardware, rather than an exercise of optimizing a CPU design to the size of a particular device.

Read 9 comments and join the conversation 

Living on the Edge

I just applied 4 years worth of WordPress updates in one go, without making any backups first. What could go wrong?

Be the first to comment! 

Micropower Data Collection

Hallelujah! After substituting an external 32768 Hz crystal as the timer clock source for my hiking data logger, I’ve reduced the sleep mode (LCD off) supply current to less than 0.01 mA! It’s actually below the resolution of my multimeter– the meter just reads zero. Previously I was using the microcontroller’s internal clock as the timer clock source, which required it to keep running during sleep mode, and resulted in a sleep supply current of 0.11 mA.

0.01 mA: that is one small number. 10 microamps! It’s hard to believe a real circuit can run on that little current. Gotta love these AVR microcontrollers.

When “on”, with the LCD enabled and the user pressing buttons, the supply current is 0.41 mA without the backlight, or 1.2 mA with the backlight. Every time a new pressure/temperature sample is taken (once a minute or so), there’s an additional short current spike of about 0.15 mA above the baseline. How short, I’m not sure, but probably less than 50 ms according to the datasheet.

Assuming the logger is fully powered with LCD and backlight for about 15 minutes a day, that’s about 1% of the day in high power 1.2 mA mode, and 99% in low power 0.01 mA mode. That’s an overall average current of about 0.02 mA or 20 microamps. At that rate, a CR2032 coin cell with typical 220 mAh capacity will have a battery life of more than a year.

Read 2 comments and join the conversation 

Pressure and Temperature Demo

In my last post, I described an idea for a hiking data analyzer based around an old cell phone LCD, a pressure/temperature chip, and an ATmega microcontroller. Since then, I’ve received the parts and built a simple demo that exercises everything. It’s not very exciting yet, but it demonstates that all the parts are working as expected, so now the real work can begin.

In the photo, clockwise from the upper-left, you can see the ATmega, LED used for debugging, Nokia 5110 LCD, Bosch BMP085 pressure/temperature sensor, and the ISP programming cable. The BMP085 is mounted upside-down, since direct light on the sensor can skew its measurements. A small 3.3v power supply is to the left of the board, not visible.


The demo circuit polls the sensor once per second, and displays the results on the LCD. The photo shows a current report of 70.8 degrees Farenheit, a station pressure (actual pressure reading) of 999.02 millibars, and an equivalent pressure at sea level of 29.84 inches of mercury. The equivalent sea level pressure matches almost exactly with local weather reports, but the temperature is about 2 degrees higher than what’s reported by three other thermometers in the room. Suspecting that the power supply or other nearby components might be slightly warm and affecting the reading, I tried insulating the sensor by putting a cup over it, but the results were the same with and without the cup.

I wrote the demo software last week, before the parts arrived. Fortunately it only had a few small bugs, and it was just a couple of hours from when I opened the parts box to when I had everything working, although I did have to resort to logic analyzer debugging to resolve a problem with my I2C code.

Some thoughts, before I move on to the software design:

LCD resolution – I think I didn’t fully appreciate just how low-res 84×48 is. I can forget about drawing complex graphs with labeled reference lines. Small graphs should still be possible, but I’ll probably need to move a cursor over the graph to see times and values, which is a little clunky. The 5×7 font with one pixel of space to the right and below will allow for 6 lines of 14 characters each. A 5×5 font would allow 8 lines, but would also complicate the LCD display update, since the display must be written in units of 8-pixel tall columns.

program size – The demo program is 8294 bytes, which is already 25% of the capacity of the ATmega 328. Much of that is the floating point math library and the LCD font, which are one-time memory costs. Still, after more interesting program logic is added along with a few more bitmap images, I’m concerned it may not fit in 32K of program space.

temperature offset – Why does the sensor report a temperature 2 degrees higher than the room’s air temperature? Is it being warmed from a nearby component? Does the sensor itself generate a small amount of heat during its operation, biasing the result? Should I subract 2 degrees from the sensor reading to get the “true” air temperature?

supply current – The demo circuit draws about 1.35 mA. That’s not much, but it’s a lot more than it should, according to the datasheets. Even allowing for the busy loop in the microcontroller that keeps it constantly active, the circuit should only be drawing about 0.3 mA for the microcontroller, 0.24 mA for the display, and 0.005 mA average for the sensor. I measured the microcontroller drawing a bit less than 1 mA, and the LCD drawing 0.47 mA. I’m unsure why those measurements are so much higher than the datasheet values.

Read 10 comments and join the conversation 

Hiking Data Analyzer

It’s new project time, and I’ve decided to try my hand at building a data logger/grapher/analyzer for long-distance hikers. In August I’ll be hiking the John Muir Trail, a 211 mile wilderness trail through remote parts of California’s Sierra Nevada mountains. My goal is to build a small battery-powered device that measures altitude, temperature, and other data, and take it with me on the trip. Sure, I could probably buy a commercial unit that does all this and more, but where’s the fun in that?


This project will be a first for me in several ways. It will be my first project intended for practical use instead of simply being an interesting gizmo, so design considerations like size, weight, and reliability will be important. It will also be my first portable battery-based project, and first 3.3v project. Most importantly, it will be my first low-power project, where designing for the lowest possible current draw will be of central importance.

Overview – The plan is to collect periodic pressure and temperature samples, and use them to calculate useful data displayed on an LCD, like a beefed-up version of an altimeter wristwatch. When calibrated, the current air pressure can be used to determine elevation above sea level. Combining pressure, elevation, temperature, time of day, and possibly other data like moving time and distance traveled will provide endless options for graphing and analyzing the data for hiking geeks.

Some of the features I imagine are:

  • current temperature display
  • high/low daily temperature
  • graph of temperature over time
  • current altitude display
  • graph of altitude over time
  • rate of ascent/descent display
  • estimated time remaining to reach a specified goal elevation
  • total ascent/descent distance
  • current air pressure at sea level display
  • weather forecast
  • storm warning alarm (rapidly dropping air pressure)
  • clock/stopwatch/timer/alarm
  • battery voltage display, estimated remaining battery life
  • day trip/movement timer*
  • total accumulated trip/movement timer*
  • graph of moving vs stopped periods over time*
  • step counter/pedometer**
  • estimated distance traveled display**
  • estimated current speed display**
  • day trip average speed display**
  • graph of distance traveled over time**
  • estimated time remaining to reach a specified goal trip distance**
  • graph of speed over time**
  • inclinometer**

* requires a shake/motion sensor to detect when you’re walking vs standing still
** requires an accelerometer and some questionable software methods

The core of the hike logger design is a graphical LCD display, air temperature/pressure sensor, and a microcontroller. To ease construction, I’ll be using break-out boards for all the SMD parts, and mounting the break-outs onto a main board containing the microcontroller and miscellaneous hardware.

84×48 monochrome graphical LCD – SparkFun sells these inexspensive Nokia 5110 displays. The break-out board is 4.5 cm square. These displays operate on a 2.7 to 3.3v supply, use an SPI interface for communication with the microcontroller, and draw about 240 microamps when active and 1.5 microamps when powered-down.


Bosch BMP085 digital pressure sensor – These micro sensors measure both air pressure and temperature. The SparkFun break-out board is a paltry 1.5 cm square. The sensors operate on a 1.8 to 3.6v supply, use I2C for communication with the microcontroller, and draw about 5 microamps when sampling at 1 Hz or 0.1 microamps when on standby. Temperature accuracy is +/- 0.5 degree C, and pressure accuracy is +/- 1 hPa, which translates to an altitude accuracy of about 30 feet.


ATmega328 microcontroller – This 8-bit microcontroller is the same one used in the Arduino. It features SPI and I2C interfaces, 32K program memory, 2K RAM, 1K EEPROM, and operates on a 1.8 to 5.5v supply. At 1 MHz, it draws 300 microamps when active, 40 microamps when idle, and 0.9 microamps in power-save mode.


CR2032 3V coin cell – These tiny batteries hold 220 mAh of juice, and have a maximum discharge current of about 3 mA. 3 mA! That’s not even enough to light a single LED. It’s possible to exceed 3 mA (these batteries are popular in LED throwies), but battery life will be severely degraded.


Power Budget – My goal is for the hike logger to run for at least 4 weeks on a single CR2032. That’s 672 hours of run time, with a 220 mAh battery, so the average current draw can’t exceed about 330 microamps. That looks achievable, assuming the display powers off after a minute of activity, the sample rate is low, and the microcontroller sleeps between samples. In fact, the logger should only draw about 3 microamps when sleeping! Even when it’s active and busy, the current draw should still be less than 1 mA. So the battery life goal looks within reach, as long as I’m careful to avoid parasitic currents in pull-up resistors and similar locations, and I avoid the temptation to add more power-hungry goodies to the design.

Data Retention – The simplest logging mechanism requires 3 bytes per sample – 1 for temperature and 2 for altitude. Using the 1K EEPROM on the microcontroller, that’s space for 341 samples. If I also used the 2K RAM for additional sample storage, that would provide a total space for 1024 samples. That’s 17 hours of data with a 1-minute sampling interval, which is more than enough for this application. With a 5-minute sampling interval, it would provide space for 85 hours or 3.5 days of sample data. Run-length compression or other techniques could probably squeeze more samples into memory. To fit the entire 3 week trip, a small I2C EEPROM could be added.

Physical Design – The whole package will be about 4.5 x 6 x 1 cm, and weigh a few ounces. Designing the device for real-world use presents some challenges. Carrying a naked PCB on a lanyard or inside my backpack doesn’t seem like a good plan. It could get wet, it could short against another object, it could get crushed. A case must protect it from the environment, while not enclosing it so completely that temperature or pressure measurements are affected. The case must also be small and lightweight. I haven’t yet had any great case design ideas, so I may just hang the naked PCB off my backpack and hope for the best.

Jiggle Sensor – One feature-extension idea I may explore is using some kind of jiggle sensor to detect when the device is in motion, and when it’s stationary. This would enable automated recording of the start and end times for each day’s hike, as well as the length of any rest stops along the way. It might be interesting to know that I’d spent only 5 hours actually hiking one day, although it took 8 hours from one camp site to the next. Just how long are those lunch breaks? Graphing this data for a period of several days might be interesting as well. And knowing the distance of each day’s hike from the map, the average speed could be calculated from the travel time. Of course a sensor isn’t really needed for this– I could manually note the start and stop times of the hike, or press a button to mark them, but doing it automatically is much nicer.

I considered a few different ways to detect walking-jiggle motion, but there may be better ones. Any suggestions? One approach would be to use a pair of mechanical tilt switches, mounted at right angles to each other. When at rest, both switches remain in fixed states, open or closed. When moving, the walking motion would probably provide enough vibration to make one of both switches bounce open and closed. This could be detected by the microcontroller and interpreted as a walking signal. The drawback is that tilt sensors aren’t really intended for this, and depending on the orientation of the device in my pocket or bag, the switches might not vibrate reliably open/closed during walking.

An alternative jiggle detector idea is to use a piezo element with a mass on it. These are typically used to detect knocking or tapping motions on a device. This might work, but they seem geared towards detecting hard, high-frequency vibrations, not the slow 1 Hz or so oscillations of walking. Especially if the device is inside a backpack, the motion of walking might not create enough vibration to be detected.

The most promising jiggle detector looks like the SQ-SEN-200 omnidirectional tilt/vibration sensor. It’s a normally closed switch, regardless of its orientation, but when moved the switch chatters open and closed. The drawback with these is that they’re only available directly from the manufacturer, with a $100 minimum order.

Accelerometer – A second feature-extension idea is the addition of an accelerometer. This would immediately solve the jiggle sensor problem, and open up a range of new possibilities. By holding the device to my eye and sighting along the edge to the top of a ridge or peak, it could be used as an inclinometer to calculate the slope angle. With some software analysis of the accelerometer data, it might be possible to detect individual steps when walking, enabling step-counting and stride rate features. I’m not sure it would be possible to extract steps from the acceleration data reliably, but if it is, an Analog Devices accelerometer tech note describes how it can be used to make fairly good estimates of stide length, distance traveled, and movement speed. This opens up all kinds of interesting new feature possibilities.

I’ve been looking at the Analog Devices ADXL345 accelerometer. It has a digital I2C interface, low power requirements (40 to 140 microamps, depending on sample rate), and a sample FIFO that lets the microcontroller read samples less often than the actual sampling rate. Another popular option is the ADXL335, which has an analog interface and draws about 350 microamps. Unfortunately all the accelerometer options are tiny little SMD devices that would be difficult to hand-solder, and break-out boards add more bulk and expense. SparkFun sells a 2.1 x 1.6 cm break-out for the ADXL345 for $28.

Let’s Go! – The basic parts needed are in the mail, and I’m beginning the circuit and software design now. I hope to have a breadboard prototype of the basic features ready for demo in about a week. The results will determine where this project goes next.

Read 7 comments and join the conversation 

Mapping the Tranz 330

The VeriFone Tranz 330 point of sale terminal and its cousins are great platforms for electronics tinkerers. Commonly available from surplus stores or eBay for around $10, these little boxes contain a complete 8-bit computer, with lots of intersting I/O interfaces and a retro-styled vacuum-fluorescent display. The Tranz uses the Z-80 microprocessor, same as the venerable TRS-80 and ZX Spectrum, so software routines from those classic computers can often be reused.

The default program in the terminal’s ROM is a rather dull credit card processing application, but a new program can be substituted by replacing the original ROM chip. With knowledge of the hardware and a Z-80 assembler, you can write a new program to make the Tranz do any crazy thing you can imagine, like Mozart’s Credit Card: turning credit card magnetric stripe data into music. This guide will show you how.

Systems Specs

  • Z-80 CPU at 4 MHz
  • 32K ROM, in a socket for easy replacement
  • 32K RAM
  • 16 character, 16 segment vacuum fluorescent alphanumeric display
  • 16 key 4×4 keypad
  • Piezo speaker
  • Magnetic stripe card reader, ISO 7813 Track 2 format
  • Z-80 PIO, parallel port interface
  • Z-80 DART, serial port interface
  • Z-80 CTC, counter/timer
  • OKI M6242B Real-time calendar/clock chip
  • Micrel MIC10937 display controller
  • RS-232 serial port, 8-pin DIN connector
  • Accessory serial port, 6-pin DIN connector
  • 73K212L-IP single chip 1200 bps modem
  • Internal lithium battery backup for RAM

Related Tranz Terminals

VeriFone also produced several similar Tranz terminal models. I haven’t used these, but most of what’s written here about the Tranz 330 should also apply to them. Software written for the Tranz 330 may need small modifications to account for hardware differences on these other terminals, however.

  • Tranz 380 – Z180 CPU at 6 MHz, 64K ROM, 64K or 128K RAM, track 1/2/3 card reader
  • Tranz 460 – Z80A at 3.579 MHz, track 1/2 card reader, integrated receipt printer

Power Requirements

Used Tranz 330 terminals are often sold cheaply without the AC adapter, but fortunately replacements are inexpensive. The important thing to be aware of is that it requires an AC-to-AC adapter, not AC-to-DC. The stock VeriFone adapter outputs 8.5VAC at 1 amp. I have also successfully used Jameco part number 1586154, which is a 9VAC 1 amp adapter for $7.95. Any other similar adapter with a 2.5mm inner diameter should also work.

Why does the Tranz 330 use an AC-to-AC adapter instead of AC-to-DC? I don’t know, and I didn’t investigate. On-board power regulation is provided by an L387A low dropout 5V DC regulator, and all of the digital electronics use a 5V DC supply. Maybe this was a cost-saving measure of some kind?

I/O Board Interface

Inside the Tranz 330 case, the components are split between two circuit boards, stacked on top of each other and joined only by a single 20-pin 0.1 inch connector. The logic board is on the bottom, and contains the CPU, support chips, power supply, serial ports, and modem. The I/O board is on the top, and contains the keypad, display, MIC10937 display controller, and speaker.

For people using a microcontroller instead of the Z-80, the logic board can be discarded, and the I/O board and case used alone. The connection between the boards is not soldered, so they can be easily separated. This makes a nice self-contained I/O solution for a microcontroller project, with all the necessarily signals available at the 20-pin header.

I/O Board Connector Pinout

  1. keypad top row
  2. keypad second row from top
  3. keypad third row from top
  4. keypad bottom row
  5. keypad right-most column
  6. keypad second column from right
  7. keypad third column from right
  8. keypad left-most column
  9. NC
  10. NC
  11. NC
  12. card reader output
  13. display reset
  14. display data input
  15. display clock
  16. +5V DC
  17. speaker driver input
  18. -5V DC
  19. speaker mute?
  20. GND

Note pin 17 is actually connected to the clock input of a flip-flop in the speaker driver, and not directly to the speaker itself, and so the I/O board is only capable of playing square waves of various frequencies. Pin 19 is connected to the FF’s asynchronous set input. Pin 12 is the signal from the card magnetic read head, and must be decoded as described below in the section about the card reader.

Making a Replacement ROM

If you keep the logic board, you’ll need to replace the original ROM with one containing your new program. The original ROM is a 28-pin, 32K (256 kbit) 27C256 UV-erasable EPROM. Assuming you don’t already own a UV eraser and enjoy spending 15 minutes erasing your EPROM each time you change your program, you’ll want to replace this with a more modern 32K EEPROM or Flash ROM.

You’ll need an EPROM/EEPROM/Flash universal programmer to make your new ROM. If you don’t already have one, MCUmall has a good selection of inexpensive programmers. I use the EasyPro 90B.


Unfortunately no currently-manufactured EEPROM or Flash exactly matches the capacity, physical size, and pinout of the 27C256. The best solution is to use a 27SF512 Flash ROM. This is a 64K ROM, but if you place your program in the upper half (configure your programmer’s software to place the data at $8000), it is pin-for-pin compatible without any further work needed. Sadly, the 27SF512 is no longer manufactured, but is still commonly available from secondary sources and auto parts suppliers, since it’s a favorite of people who re-chip their car’s electronic fuel injection controller.

An alternative solution using all-new parts is to use a 28C256 EEPROM. This is a 32K ROM, but the pinout is slightly different than the 27C256. To make it work, after burning the ROM, pin 27 should be bent up and soldered to pin 28. Make sure that pin 27 does not contact the socket. Of course, this makes it impossible to reprogram the ROM later, so you may want to do this modification to a low-profile ZIF socket instead of the 28C256 itself. As before, you also must place your program in the upper half of the ROM (configure your programmer’s software to place the data at $4000). Since the 28C256 is a 32K device, this limits you to a total ROM image size of 16K. I used this method successfully until I found a supply of 27SF512 chips, but it can be difficult to get a reliable electrical contact between the ZIF and the underlying ROM socket.

Software Tools

Your program will be written in Z-80 assembly language, so you’ll need an assembler. I used sjasm, but any Z-80 assembler will work. You may also want a Z-80 simulator to help test your code before burning it to ROM. I used an evaluation version of Z80 Simulator IDE.

You may find some online references to “programming” the Tranz 330 in a propriety language called TCL. Ignore these. TCL is an ugly run-time interpreted language for making custom point of sale applications, and the interpreter is part of the original ROM. TCL programs run inside the sandbox provided by the interpreter. You’ll be making a native Z-80 program that has direct access to all the hardware.

Tranz 330 Library Routines

While creating the Mozart’s Credit Card demo, I developed a set of reusable library routines for working with the Tranz 330 hardware. These routines simplify common tasks like printing a string to the display, reading the keypad, and playing a tone on the speaker. The file tranz330.asm contains all these library routines, as well as the Mozart demo program to serve as a working example. Use this as a starting point for making your own Tranz 330 programs. Portions of the library routines also appear below in the hardware-specific sections.

You can also download an archive containing the original Tranz 330 program file dump, my commented disassembly of the original program, and two versions of the Mozart demo in source and binary form.

Tranz 330 library routines Tranz 330 + Mozart archive

Memory Map

The Z-80 has a 64K memory address space. ROM is mapped to addresses 0 – $7FFF. At startup, program execution begins at address 0. RAM is mapped to addresses $8000 – $FFFF. Typically the stack will begin at $FFFF, growing downward.

I/O Ports

The Z-80 also has a separate 256 port I/O space. For the Tranz 330, these ports are mapped as follows (all port numbers in hex):

  • Port $00 – PIO parallel port A.
    • Bits 3-0: output, keypad columns. Bit 3 is right-most column.
    • Bit 4: output, display controller reset
    • Bit 5: output, display controller data
    • Bit 6: output, display controller clock
    • Bit 7: input, card reader data
  • Port $01 – PIO control register for port A.
  • Port $02 – PIO parallel port B.
    • Bits 3-0: input, keypad rows. Bit 3 is bottom row.
    • Bits 7-4: not connected
  • Port $03 – PIO control register for port B.
  • Port $10 – CTC channel 0
  • Port $11 – CTC channel 1
  • Port $12 – CTC channel 2. Chan 2 output is connected to speaker driver.
  • Port $13 – CTC channel 3. Chan 3 input trigger is connected to chan 2 output.
  • Port $20 – DART serial port A. 8-pin DIN RS-232 port.
  • Port $21 – DART control register for port A.
  • Port $22 – DART serial port B. 6-pin DIN accessory port.
  • Port $23 – DART control register for port B.
  • Ports $30 – $3F – Real-time clock registers $0-$F
  • Ports $40,$50,$60,$70 – Modem?

Most applications will use the PIO parallel ports. Complete details on using the PIO can be found in the Z-80 PIO user’s manual. For the Tranz 330, this library routine will initialize the ports with the necessary settings:

system_init:
LD A,0xCF           ; control each port bit individually
OUT (0x01),A
LD A,0x80           ; bit 7 is input, others are outputs
OUT (0x01),A
LD A,0x18           ; use interrupt vector 18
OUT (0x01),A
LD A,0x97           ; generate interrupt if any masked bit is low
OUT (0x01),A
LD A,0x7F           ; interrupt mask = bit 7
OUT (0x01),A
LD A,0x3F           ; set the initial output values for port 0
OUT (0x00),A
RET

Display

The 16-character, 16-segment VFD is powered by a Micrel 10937 display controller. It supports upper-case letters, digits 0-9, and a small selection of symbols.


The controller is connected to the CPU through a serial interface, using port $0 bits 6-4. Data bytes must be shifted in to the controller one bit at a time. The serial clock must also be toggled via software for each bit. Data bytes are 8 bits, and are transmitted MSB first. An MSB of 1 indicates a control byte, and an MSB of 0 indicates a data byte to be displayed.

After a reset, the display is turned off by default. Before printing anything, it must first be turned on by setting the display duty cycle to something higher than zero.

display_init:
IN A,(0x00)         ; get current port state
AND 0xEF            ; clear bit 4 (display reset)
OUT (0x00),A
LD B,0x1C           ; wait 0x1C cycles
.L1 DJNZ .L1
OR 0x10             ; set bit 4 (display reset)
OUT (0x00),A
LD C,0xFF           ; set the display duty cycle to 31 (maximum brightness)
CALL display_send_byte
RET

Sending a control or data byte to the display requires shifting it out one bit at a time on port $0 bit 5, while toggling the serial clock on port $0 bit 6:

display_send_byte:
; byte to be sent is in C
; note the display controller does not support lower-case letters!
LD B,0x08           ; 8 bits to send
.L1 IN A,(0x00)         ; get current port state
RLA                 ; rotate the port word until the data bit is in the carry flag
RLA
RLA
RL C                ; shift the next output data bit into the carry flag
RRA                 ; rotate the port word until the data bit is in bit 5
RRA
RRA
OUT (0x00),A        ; setup the data output (bit 5)
OR 0x40             ; set clock high (bit 6)
OUT (0x00),A
AND 0xBF            ; set clock low (bit 6)
OUT (0x00),A
DJNZ .L1            ; continue with the next bit
RET

Keypad

The 16-key keypad is arranged in a 4×4 layout. It contains a 12-key telephone-style section with the digits 0-9, *, and #, as well as the four special function keys CLEAR, BACKSPACE, ALPHA, and ENTER. The keypad is a passive device, requiring work from the CPU to test the keypad state. The keypad has four column lines and four row lines, and the row lines have pull-up resistors. When a key is pressed, an electrical connection is made between the row and column lines that overlap beneath that key.


By driving a 0 to a single column line while driving a 1 to the others, the CPU can activate that column. The current state of the four keys in that column can then be tested by reading the row lines. If a key in an active column is pressed, the corresponding row line value will be 0. If no key is pressed, the pull-up resistor will supply a value of 1 for that row. To detect key presses, the CPU must activate each of the four column lines in turn, and check the row lines for 0’s.

The column lines are connected to the CPU on port $0 bits 3-0, and the row lines on port $2 bits 3-0.

The library function keypad_read returns a key code in the range 1-16 in A, or 0 if no key is pressed.

To enter a multi-character string involving letters, numbers, and symbols, a higher-level input routine is needed. The library function  keypad_get_string reads a complete string from the user, and provides some basic line editing capabilities. Key presses are echoed to the display. BACKSPACE can be used to correct mistakes. Letters and special characters can be entered by first pressing a number key, and then repeatedly pressing ALPHA to cycle through the alternate characters for that key. String entry concludes when ENTER is pressed.

CTC and Speaker

The Z-80 CTC has four independent counter/timer channels, addressable by the CPU on ports $10 – $13. Complete details on using the CTC can be found in the Z-80 CTC User’s Manual. The channels can be configured to trigger an interrupt when timeout occurs. Each channel also has a timeout pin and a trigger pin for direct hardware interfaces. On the Tranz 330, the channel 2 timeout pin is connected to the clock input of a flip-flop in the speaker driver, and also to the channel 3 trigger.

A square wave tone of any desired frequency can be played through the speaker by setting an appropriate time constant on channel 2. Unfortunately, there’s no way to control the volume or create other audio waveforms.

To configure channel 2 for speaker usage, it must be set to timer mode with auto-triggering. The desired frequency can then be played by loading an appropriate time constant into channel 2:


speaker_beep_period:
; beep tone period time constant should be passed in C
LD A,0x07           ; disable interrupts, timer mode, prescale=16, auto-trigger, time constant follows
OUT (0x12),A
LD A,C              ; time const
OUT (0x12),A        ; speaker on
LD BC,0x0096       ; wait 150 ms
CALL wait_ms
LD A,0x03
OUT (0x12),A        ; speaker off
RET

Card Reader

The card reader supports ISO 7813 Track 2 format, which is up to 40 characters, where each character is 4 data bits and one odd parity bit. It will read virtually any credit card, driver’s license, shopping card, ID card, or other card with a magnetic stripe. Typically the data on the magnetic stripe is the same as what’s printed on the card: account number and expiration date. In many cases, depending on the type of card, some additional data may be stored as well. The magnetic stripe does not contain PINs or other private data.

The card reader is connected to the CPU on port $0 bit 7. The port is configured to trigger an interrupt whenever the card reader signal is active. At the CPU interface, the card reader signal is normally 1 when no card is present, and toggles rapidly between 1 and 0 when a card is swiped. If you’re using the I/O board with a microcontroller and directly sensing pin 12 from the 20-pin header, then the polarity is reversed: the signal is normally 0, and toggles rapidly between 1 and 0 when a card is swiped.

Interpreting the card reader signal is complex, because there is no clock signal, the speed at which the data signal changes depends on the speed the card is swiped, and because the signal doesn’t even directly provide the logical binary data from the card.

A track on the magnetic stripe consists a long series of magnetic domains. Each domain is polarized magnetic North or South. When the read head is over a domain of either polarity, the card reader signal is 1. When the read had passes over the boundary between domains of opposing polarities, the card reader signal briefly pulses to 0. The duration of this pulse is approximately 40 microseconds.


Each logical data bit on a track has the same physical length on the magnetic stripe. There will always be a magnetic domain boundary at edge of a bit. A logical 1 bit will also have a magnetic domain boundary within the length of the bit, but a logical 0 will not. Extracting logical 0’s and 1’s is therefore a task of measuring the intervals between pulses of the card reader signal. Two successive pulses each time N since the last represent a logical 1, and a single pulse time 2N since the last represents a logical 0. This encoding format is called F2F or Aiken Biphase.

The data begins with a long string of logical 0’s, enabling the software to calibrate the value of N for that swipe of the card. For better reliability, the card reader library routine also adjusts the value of N during bit transcription, to compensate for an accelerating or decelerating motion during the card swipe.

Using the library routines for the card reader is a multi-step process:

  1. At startup, system_init sets the interrupt handler to be called when the card reader signal pulses low.
  2. The interrupt handler uses a busy loop to measure the duration of each 0 to 1 cycle of the card reader signal, storing the duration data in a buffer. When the handler terminates, control is returned to the routine got_card.
  3. card_parse_bits is called to perform the F2F decoding of the duration measurements, filling the buffer with logical 0’s and 1’s.
  4. card_parse_characters is called to convert the logical bits into ASCII characters, filling the buffer with a null-terminated ASCII string. The ASCII conversion adds $30 (ASCII ‘0’) to each 4-bit datum. If a parity error is detected, the empty string is returned.

The final string returned will begin with a start sentinel (ASCII ‘;’), contain up to 40 characters of content, and end with an end sentinel (ASCII ‘?’). The data content typically begins with the account number (up to 16 characters), followed by a ‘=’ field separator, expiration date in YYMM format, and finally other data specific to the card provider.

Serial Ports

The Tranz 330 has two serial ports: an RS-232 port with 8-pin DIN connector, and an accessory serial port with 6-pin DIN connector. The serial ports are controlled by the Z-80 DART, which is basically a Z-80 SIO/0 without the synchronous clocking capability. Complete details on using the DART can be found in the Z-80 SIO User’s Manual.

The serial ports are connected to the CPU via the DART on ports $20 and $22. Port $21 is the control register for port $20, and port $23 is the control register for port $22.


I didn’t experiment with the serial ports or write any library routines for them, so people wishing to use the serial ports will need to consult the SIO User’s Manual and write the code themselves. Usage is fairly straightforward: write to port $21 to set the serial port direction and speed, then read/write data from/to port $20.

Real-Time Clock/Calendar

The OKI M6242B real-time clock provides a mechanism to query and update the second, minute, hour, day, month, and year. Because the Tranz 330 has an internal backup battery, the date and time is retained even when external power is lost. The M6242B has 16 control registers, which are mapped to CPU I/O ports $30 – $3F. Complete details on using the RTC can be found in the M6242B datasheet.

Internal Modem

The 73K212L-IP single-chip modem provides a 1200 bps interface for communication over dial-up phone lines.
I didn’t experiment with the modem or write any library routines for it. From examination of the original Tranz 330 ROM program, I believe the modem is connected to the CPU on one or more of I/O ports $40, $50, $60, and $70. Complete details on using the modem can be found in the 73K212L datasheet.

Feedback

If you find this technical information useful, and make a cool project based on the Tranz 330, please leave a note in the comments!

Errata

The following information was contributed by John Barthol.

The port addresses are correct in your documentation but the SIOA port is mapped to some pins on the modem chip (not the 8 pin DIN). I found that the 8 pin DIN was mapped to SIOB. The pin pad (6 pin DIN) port is mapped to SYNCB on the SIO and one of the pins on the PIO. The system clock speed is 7159090 so the CTC trigger clock is 1/4 of that or 1789772.5. Baud rate can be calculated by BAUD = 1789772.5 / (CTC_TIME_CONSTANT * DART_SCALE_FACTOR). I ended up using a time constant of 186 and scale factor of 1 to get me to 9600 baud. Actual baud is 9622 but it seems close enough.

Read 15 comments and join the conversation 

Older Posts »