The CD-ROM interface consisted of four layers of interfacing, which I will now describe starting from lowest (closest to hardware) to highest (closer to software):


ATA is an interface used by internal storage devices within personal computers such as hard drives, CD-ROM drives, ZIP drives and several other ones. IDE and EIDE are names denoting the same thing as ATA. It is both a hardware and a software standard. Writing an ATA interface was something I had done before and was somewhat familiar. My previous experience however was on a PC where the ATA interface is already built into hardware in most systems. The major advantages of the ATA interface and one of the prime reasons it has remained the dominant standard in personal computers for 15+ years is the fact that it integrates the drive controller into each storage device, leaving a simple hardware interface to the drives. This fact was helpful in our project because of the ease with which we were able to connect it to the Atmel microcontrollers. The ATA interface is based around manipulating 14 basic registers. All registers are one byte in size and perform various functions; the data register is 16-bits but is more of a transfer bus than a register. Within my ATA.C file I defined functions to set and get values from all registers. Since I used the ATAPI interface I did not use most of the ATA registers in their normal fashions. For example ATA is used for hard drives so there are many registers devoted to allowing you to select which sector on the hard disk you would like to read or write; however ATAPI bypasses use of these registers and instead transfers information about requested sectors within the packet data. I initialized the ATA interface in two phases, first by toggling the reset pin on the bus momentarily and secondly by toggling the software reset bit in the device control register. My other use of the ATA interface was limited to simply sending the command to initiate ATAPI packet command transfers. To learn more about the ATA/IDE interface please reference the standards documents at


Coming into this project I was unfamiliar with ATAPI and had always avoided it because it seemed like a complicated hack on ATA. ATAPI is based around using the ATA interface with the ATAPI packet command to transfer twelve-byte commands to devices. These command packets describe particular commands for the peripheral storage device to execute such as read and write. There are around 50 different ATA commands and a similar number of ATAPI commands. Both are powerful interfaces with enough features to support a wide range of devices and transfer modes. After issuing any ATAPI commands you must wait and the drive will cause and interrupt indicating that it has either failed the command or had success and has a buffer of data ready to return. I used the following ATAPI commands to access our CD-ROM drive:

ATAPI identification - This is actually an ATA command but is specific to ATAPI, issuing it makes ATAPI devices return a 256 byte structure identifying all aspects of the device including manufacturer, model, media supported and identifies the different transfer rates and modes supported. Since ATAPI is a modern standard, many ATAPI devices remain dormant and do not respond until they are pinged with this command; the reason being that on older computer whose BIOS does not support ATAPI devices but supports the ATA bus it causes problems if the computer attempts communication with a ATAPI device.

ATAPI software reset - This is another command that causes ATAPI drives to become active and non-dormant. Issuing it resets the ATAPI devices' controller and drive mechanisms.

ATAPI sense - Whenever an ATAPI device reports an error has occurred this command must be issued. This command causes the drive to return a 20 byte structure of error information. After detection of an error (indicated by the setting of bit 0 in the ATA status register) this command should be repeated until the 11th and 12th bytes are zero, indicating no more error information to return. Figuring out I needed to issue this command was a very hard process because it is not clearly documented as required to restore a drive to normal non-error mode after an error occurs.

ATAPI read table of contents - This command returns a 48 byte structure describing the sessions present on a CD-ROM inserted in the drive. Before accessing a CD-ROM for the first time this command should be repeated until it return successfully, this normally takes a few calls. Success of this command indicates that a CD-ROM was detected, spun-up and successfully read. Issuing this command is a good way to tell if a disc is inserted in the drive.

ATAPI read sector 10h - This is obviously the most important command. It allows you to specify which sector to start reading from and how many sectors to read. I never transferred more than one sector, as sectors are 2048 bytes a piece and I was using an 8515 or Atmega103 microcontroller with only 512 or 4096 total bytes of SRAM. On the 8515 I would throw out all pieces of a sector I didn't need and store only part of it in SRAM. On the Atmega103 I devoted half of the internal SRAM to a sector buffer, this made parsing the file system tables much easier.

Because of the small buffer in the STA013 and the high rate of data transmission feeding it, it was necessary to keep all ATA/ATAPI calls extremely low latency. This was accomplished by not having any spin loop waiting and breaking the issuing of commands like read sector into several small parts.


The ISO-9660 is an interesting file system format I had not previously dealt with. I had dealt with Microsoft's FAT16 and FAT32 systems before but they differed from ISO-9660 in that they allowed writing and were fairly space inefficient by comparison. The ISO-9660 file system is composed of different descriptors, which are basically fixed or variable length structures. The following is a explanatory list of the descriptors I used, read and parsed:

Volume Descriptors - These lie one-per-sector starting at sector 16 of the CD-ROM. Each one describes a file system that is present on the disc. It seems odd that CD-ROMs have multiple file systems but it is common for them to have 3 or more totally different systems present on a single media. One may be for older DOS compatible computers with 8.3 file name formats, another may be for Windows compatible computers with long file name support and another may be for Macintosh compatible computers. Volume descriptors describe such things as where other descriptors for this file system exist on the disc, how big the disc, and what the name of the volume is and who made it.

Directory Descriptors - The volume descriptor points to the root directory descriptor which is just an ordinary directory descriptor. Directory descriptors are sequential groups of sectors containing tightly packed variable length file descriptors.

File Descriptors - File descriptors describe the name, size, attributes and location of a file on the disc. They are of variable length depending on the length of the file name they contain. Parsing these was useful to find files with .MP3 extensions to play and extracting the track title from the filename. For example an CD of MP3s generally has file names like "Blink 182 - All the small things.MP3", in our project I parsed off the last four characters ".MP3" and used the rest of the name as the track title when playing that file.


JOLIET is an extension to the ISO-9660 format that allows a few variations from the normal ISO-9660 standard including long filename support. I wanted to use JOLIET because normally track names cannot be included with an eight character filename. After reading the documentation I figured out that the JOLIET volume descriptor can be found by checking that the 88th, 89th and 40th bytes were 025h, 02Fh, and 000h. All text within JOLIET descriptors including volume names and file names is written using double-byte UNICODE characters so I just skipped the first byte of every character.


The MP3 decoding is handled entirely by the STA013. Once it is properly configured, the STA013 accepts raw MP3 files, automatically ignoring the file header and adjusting the bitrate and DAC. Thus, the primary job of the play loop is to keep the decoder fed with data while making sure that the CD-ROM continues to stream data into the buffer.

The control software uses an aggressively-timed polled approach to most of its I/O functions. The timing requirements for the CD MP3 player are very stringent because the decoder chip maintains only a 256-byte buffer. The audio will skip if the decoder's buffer underruns. An additional complication is that the MP3 decoder has a serial interface that requires the explicit clocking of individual bits, which requires significantly more clock cycles than sending bytes over a parallel connection.


The i2c bus is used to manipulate the STA013's I/O registers. The microcontroller acts as the bus master and drives the bus clock. Eight bits of data are sent sequentially, after which the receiver of the data provides an acknowledgement signal by pulling the data bus low. The bits are transferred MSB-first. The bi2c_xxx functions in sta013.c implement the i2c protocol. The bi2c_get() and bi2c_put() implement the proper protocol sequences for reading and writing individual registers. See the code for further details.


The MP3 data bus is used to stream MP3 data to the STA013. The bits must be sent MSB first on SDI and clocked with SCKR. These transfers represent the most timing-critical portion of the microcontroller code and had to be hand-coded in assembly with the bit-transfer loop unrolled to improve performance. Since DATA_REQ is asserted when only 4 bytes are free in the STA013's buffer, only 4 bytes can be safely sent before polling again. This bus may be compatible with SPI, which would further increase throughput. However, we already exceed our design goal of 128kbps with the optimized assembly.


Due to the potentially long latencies involved in a CD-ROM access, data must be cached in the external SRAM. Originally, we had planned to use the full 32KB of external SRAM as a buffer. The AVR datasheet made it misleadingly appears as though the external SRAM were mapped into the address space at an address starting immediately above that of the internal RAM. We eventually discovered that the external SRAM is mapped starting at address 0, and that the portion of SRAM that overlaps with the internal RAM lacks a distinct address space. As a result, we were forced to reduce the size of our buffer. While we could have had a 28KB buffer, we chose a 16KB buffer because it proved to be sufficient for all applications and its use has a small speed advantage over the conventional buffer in allowing the use of an AND-mask for modulus in the buffer size. The buffer is implemented as a circular queue of 2KB blocks. This size was chosen because it matches the sector size of the CD-ROM. Access to the buffer follows a simple single-producer single-consumer algorithm to minimize overhead. Choosing to implement queuing at the block level provides a significant performance gain over an equivalent implementation at the byte level because it reduces the number of queue status checks by a factor of over one-thousand. Also, the queue status functions were implemented as macros to achieve an absolute minimum of overhead.


1. Initialize LCD
2. validate SRAM
3. Initialize STA013
    a. provide RESET signal
    b. check device-present
    c. send initialization code from EEPROM
    d. initialize volume, enable decoding
4. Initialize ATAPI
    a. provide RESET signal
    b. set SRST for software reset
    c. send ATAPI reset packet
    d. send ATAPI identify device command
    e. read table of contents until successful
5. Initialize ISO-9660
    a. read volume descriptors until JOLIET signature found
    b. parse volume descriptor to find root directory
    c. read and parse root directory to find MP3 files
    d. prepare first MP3 file for play
6. Initialize buttons
7. Initialize Timer 0 ISR


while (no consumable data) {
  poll CD
  poll buttons
for all consumable data, 4 bytes per iteration {
  while (DATA_REQ not asserted) {
    poll CD
    if (DATA_REQ asserted)
    poll buttons
  send 4 bytes of data



The timing for the button debounce logic is derived from timer 0 at an interval of 30ms. The ISR only sets a flag to indicate that button-checking is required, to prevent interference with timing-critical operation.


The standard LCD library was used to write to the LCD. On top of the standard LCD library, we built a few functions to assist us in displaying formatting values. Also, the timer 0 ISR is utilized to update the location of the scrolling text on the LCD.