MP3 Player with Hard Drive - Glue Logic

Introduction

This document describes the glue logic to connect a range of peripherals to the 80C188 processor, or to any other processor or microcontroller with an 8-bit Intel bus. The peripherals are:

The glue logic is implemented in an XCR3128XL CPLD from Xilinx. The modules containing the interface logic for the peripherals are VHDL entities, and have a standard bus interface with some or all of the following signals: chip select, read, write, address, data in (8 bits), data out (8 bits), and interrupt request. The top-level entity of the glue logic is described in a schematic, connecting all the components with a module for chip select generation and data multiplexing. Since the CPLD also contains synchronous logic, it is clocked at a low frequency of 15625 Hz (= 4 MHz / 256).

The remainder of this page consists of the following sections

Interfacing IDE Drives

The 80C188 processor has an 8-bit data bus, while IDE has a 16-bit data bus. However, the processor can do 16-bit read/write operations, but they are split into two read/write cycles, the first one accessing "Address", the second one accessing "Address+1". In order to connect the two buses there must be an interface circuit between them. The following paragraphs describe the operation of this circuit.

Read Cycles

A read cycle for an even address is forwarded to the IDE bus and causes a 16-bit read operation there; the interface circuit is transparent in this case. The lower 8 bits are directly handed back to the processor, while the higher 8 bits are stored in a latch within the interface circuit (RD_Latch). A 16-bit read operation causes another read cycle for the next address, which is odd. When reading an odd address from the interface circuit, it returns the content of the latch and the IDE bus remains inactive. Now the processor can reassemble the 16-bit word coming from the IDE bus.
When reading an 8-bit register from the IDE device, this scheme also works when the processor generates only one read cycle for an even address, that means that you can use an 8-bit read operation in the software.

Write Cycles

For a 16-bit write operation, all 16 bits have to be available at the moment when the actual write cycle on the IDE bus occurs. Write operations are also split into two phases: Writing the lower 8 bits to an even address, and writing the higher 8 bits to the consecutive odd address. When the processor writes the lower 8 bits, they are cached in a latch in the interface circuit (WR_Latch), and the IDE bus remains inactive. A write cycle for the upper 8 bits (odd address) is directly forwarded to the IDE bus, now together with the previously stored lower byte.
This means that a write operation, even if it is only to an 8-bit register, must be realized as a 16-bit access in software.

The functionality described above is implemented in idectrl.vhd.

Interfacing the IR-Receiver

Basics

To receive the actual infrared signals from a remote control I used an IR detector/amplifier of the GP1U2xx series from Sharp. This detector outputs either a logical 0 when a signal is present (pulse), or a logical 1 when no signal is present (break). Commands are encoded with a sequence of pulses with different lengths, and breaks with different lengths. This is similar to a barcode, which is a sequence of black and white lines with different widths. When no pulse is detected for a time significantly longer than a usual break, the sequence is over.

Pulses are modulated on a carrier with a frequency which is usually between 33 kHz and 40 kHz. There are different IR detectors available for various modulation frequencies. While they will detect signals with slightly different modulation frequencies, you only get the optimal range and reliability when the frequencies of the detector and the remote control match. Refer to other online sources to find out which frequencies your remote controls use.

Different buttons on the remote control generate different sequences, and different manufacturers use different coding schemes. But all remote controls I tried have in common that there are only two different lengths of pulses and breaks, that is short or long, and you can find a threshold to distinguish them. (Only the first pulse might be longer in some protocols, but you can still classify it as a long pulse without breaking anything.)
Davshomepage has examples of waveforms of different remote controls.

Knowing that the first and the last element in a sequence must be a pulse, and that pulses always alternate with breaks, we can translate a sequence into a binary code:

translating an IR waveform to a binary code
Translating an IR waveform to a binary code

A long element (be it a pulse or a break) is translated to a 1, and a short element is translated to a 0. Now the binary code, in combination with its length, uniquely identifies the button of the remote control. The following paragraphs describe how we can arrive at this binary code.

The Interface

The IR receiver logic detects changes in the logic level coming from the IR detector and measures the time a certain level persists with a counter. When the logic level changes, it generates an interrupt for the processor. Now the processor can read the status register of the IR receiver which contains the last logic level of its input and the duration that level persisted.

When the counter measuring the duration of a level reaches its maximum value, the receiver logic also generates an interrupt. This way the processor is informed that the currently received sequence has ended and can interpret the sequence.

interrupt generation scheme of the IR receiver logic
Interrupts generated by the IR receiver logic, the resulting content of the status register, and the binary code it would be translated to. (The threshold in this example is between 16 and 30.)

On every interrupt the processor reads the status register of the IR receiver logic, compares the duration of the last level with the threshold and arrives at the next bit of the binary code. When the end of a sequence is detected, the software compares the received code with all known codes to find out which button has been pressed.

What is the right threshold?

The answer is: That depends on your remote control. I am working with a constant value that corresponds to a signal length of 960 μs. This works with my remote controls from Onkyo, Daewoo, Sony and Samsung. When you want something more generic, you can write a piece of code that separates the durations you measured into two classes, in order to dynamically compute a threshold between them.

I also tried an approach where I recorded the durations themselves without reducing them to a binary code. Then I would see if the recorded durations are similar to the programmed patterns. In my tests the approach using the threshold showed to have a lower failure rate. Also it is faster to compare the received code with the known codes.

VHDL Code

The follwing VHDL files describe the IR receiver circuit.

Knowing How Long a Button is Being Pressed

Take for example the button to increase the volume on a TV remote control. You would expect the volume of your TV to increase as long as you hold that button down, so there must be a way for the remote control to communicate how long a button is pressed. With different remote controls I came across the following two behaviors:

A generic way of recognizing both cases in software is the following: Part of the program's state is the information about the button that is currently pressed on the remote control. When receiving a valid code, record that the button associated with it is currently pressed. When a certain time 'T' elapses, record that the button has been released. But, in case we receive something from the remote control that does not correspond to a different known code before time 'T' elapses, extend the time. This way we know that the button is still pressed, no matter which of the two protocols the remote control uses.

Note that for some buttons it simply doesn't make any difference how long they are pressed. An example is the selection of the signal source of an amplifier, where the remote control may send the code only once. Therefore detecting how long a button is pressed is not possible for all buttons of all remote controls.

Software Note

The IR receiver generates an interrupt every time when the IR signal changes. In the worst case this happens approximately every 600 μs. This is the highest latency the interrupt may have, and also the code in the interrupt service routine has to be very fast, which comes close to a realtime system... It is possible to meet these constraints when you assign the highest priority to the IR receiver interrupt and take care that nowhere in the whole program are too long section where interrupts are disabled.

A possible improvement is to integrate a FIFO buffer in the receiver hardware. The problem here is that you only have a very limited number of registers in a CPLD, and a FIFO would simply not fit. Another possible improvement is to decode the IR signal in the CPLD, meaning to do the comparison with the threshold and to store one single bit per level change. Then the interrupt would be generated when eight bits are collected, increasing the acceptable interrupt latency by a factor of eight.

Interfacing the Rotary Encoder

I used an optical relative rotary encoder (from Greyhill's 62A series), also called incremental encoder, or position encoder, as an additional user input device. This rotary encoder is a knob that can be turned in steps of a couple degrees, for example it might have 32 steps in a full turn. Its outputs allow an attached circuit to detect the direction of the turn and to count the number of steps, but it is not possible to identify the absolute position.

states of the rotary encoder's outputs
The rotary encoder's output has four possible states. The states are traversed circularly in a direction depending on the direction the knob is turned.

To make this encoder useable for a microprocessor system some interface logic is required that determines the direction it is turned and counts the steps. Detecting steps is straightforward: Whenever the encoder is turned by one step, there is a change in one of the outputs, or, equivalently, there is an edge in the signal "A xor B".

how to determine the direction of the last step
Determining the direction of the last step

To determine the direction of the last step, we need the following information: the current level of output B, and the last level of output A (before the step). When the last level of A is equal to the current level of B, the last step was a turn to the right (see blue arrows in the figure). Otherwise, when the last level of A is different from the current level of B, the last step was a turn to the left (see red arrows in the figure).

Have a look at the schematic which implements the described behavior with discrete logic. The block in the north generates a short pulse whenever one of the inputs changes. On the positive edge of the pulse, the registers in IC4 are loaded, so that the direction is available to the counter IC3. Shortly after, on the negative edge of the pulse (notice the inverter IC1C), the counter is incremented or decremented.

The VHDL description in posenc.vhd is an almost direct translation of this schematic.

The counter is connected to the bus in a way so that it is reset to zero when a read operation takes place. Reading the value will therefore always return the number of steps the encoder has been turned since the last read access, and the sign of the returned number corresponds to the direction of the turn.

Software Note

During power-up, while the voltage is rising, one or both outputs of the decoder might transition from low to high, depending on the current position of the encoder. The circuit (and the VHDL model) might falsely detect up to two steps, leaving the counter value undefined.

Since a read access resets the counter, one dummy read access in the initialization phase of the application is necessary to arrive at a defined state.

Interfacing the LCD and the Keys

LCD

I used a ready-made LCD module based on Toshiba's T6963C LCD controller. The controller has two registers (command and data) accessible with a standard Intel bus interface. Therefore I did not even write an extra VHDL module, and the interface is included in the top-level schematic (in the east).

Keys

The MP3 player can use up to 16 keys, which are not arranged in a matrix but read directly from 16 input pins. The only thing the interface logic has to do is to map the 16 bits to two addresses, so that they can be accessed from an 8-bit bus. This is done with a simple multiplexor, controlled by an address line, in the top-level schematic (m2_8, slightly south from the center).

Debouncing of Keys

When pressing a key, the key "bounces" like a ball against its contacts several times before it settles into firm contact. When released, it bounces some more until it reverts to the uncontacted state. This behavior produces spikes in the signal that is used to detect the key's state while it is pressed or released. These spikes can be erroneously interpreted as multiple press or release events. Debouncing is a method to distinguish between bouncing of the key and valid key presses and releases.

However, explicit debouncing of keys is not necessary in my application. This is because my program polls for changes in the states of the keys in regular intervals in the timer interrupt service routine; in my case every 20 ms. When the polling interval is longer than the maximum period during which spikes can occur, there is no danger that additional press or release events are detected.

waveform of bouncing key signal and its sampling
As long as it is guaranteed that not more than one sample is taken during the bouncing period, only one key event is identified.

In the figure, the first sample is 0 and the third sample is 1. No matter what the second sample is (it can be either 0 or 1), only one transition from 0 to 1 is detected, and therefore only one key press event is identified. The worst case bouncing time depends on the type of key used and has to be determined empirically. With the tactile buttons I used I didn't experience any problems with a sample period of 20 ms, which means that their worst-case bouncing time is shorter than that.

When key events should generate interrupts, bouncing is a more serious problem, which can be solved pretty easily (as long as the keys are not arranged in a matrix) by adding a small capacitor, like in this example schematic, and by feeding the signal into a Schmitt-trigger input. This scheme has the additional benefit that it is more resistant against electromagnetic disturbances.

Reset Signal Generator

Additionally a 3-bit latch is mapped to the same address as the keys, but can only be accessed in write-mode. The outputs of the latch are connected to the reset inputs of three peripherals (LCD, IDE drive, USB controller). The latch is located in the south-east of the top-level schematic.

Putting Everything Together

The only thing left to do for a fully functional interface logic chip is to put all these modules together. A module has one or more of the following ports: A data output bus (8 bits), chip select, read enable, and write enable. To put everything together we just need another module that generates the appropriate chip select signals for a given address and passes on the selected data during read cycles. This functionality is implemented in decodedemux.vhd.

© 2002-2007, Michael Wurm. – Go back to the "MP3 Player with Hard Drive" project page.