
By:
Chris Dolen (cd247) and Matt Watkins (maw72)
This project was designed to be an mp3 player that you can use in your car.
In this project we used an Atmel microcontroller, an LCD display, some pushbuttons, an mp3 decoder, and a digital to analog converter. We also used an SD (Secure Digital) card for storage to hold the MP3’s. An SD card can have MP3 files written to it using a PC. The card is formatted using the FAT file system that Microsoft Windows uses. The microcontroller is then used to read this SD card to get the mp3 data. Song titles are displayed on the LCD screen. Pushbuttons on the STK500 are used for functions such as skip to the next song, stop, and play. A mini-RCA jack (3.5mm audio jack) is used as the output of the mp3 player, which can be connected to a car-cassette adapter or a wireless FM transmitter (not included in the project). A 12V DC plug is the power source so that it can be plugged into the cigarette lighter of a car for power. The volume will be controlled by the car’s audio system. As the design only needs a 12V DC power source, no batteries are required. The mp3 player will never skip on bumpy surfaces as its just reading flash memory and there’s no disk drive or CD spinning that’s being read. Since the SD card is removable, the storage size can increase as new, larger SD cards are created, as opposed to being a fixed size like most mp3 players today.
The main reason for choosing an MP3 player as a project idea is that I’ve always wanted to build one. I’ve always been curious how it all works and I’m into digital circuitry in general, and I thought this would be a good culmination of a lot of things I’ve learned over the years. It involves C coding, building a complete circuit that involves digital and analog pieces, accessing external flash memory and a FAT file system, and the end result is something that you can actually use.
There weren’t really any calculations that needed to be done to begin the design. The most important calculation that could be done is to determine the maximum bit rate of the mp3 that we could support, for example, 128 kbps. There are two transfers for every bit of data: we must first get the mp3 data from the SD card, and then send it to the mp3 decoder. The fastest we can clock the transfer of data from the SD card is 4 MHz, and the fastest we can send a bit of data to the mp3 decoder is 8 MHz. But, these values don’t take into account all of the setup of the calls and the checking of loop variables that also need to be done. So therefore it really depends on how you implement the required functions. Also, our project was all done in C, which means it also depends on how the assembler translates our C code. You might have a function that you think only takes 1 cycle, but it could take more, and this would affect your calculation. Therefore we decided to just start testing with small bit rate files and determine the maximum bit rate later on. We did know, however, that it was possible to achieve some relatively decent bit rate (24 kbps or so) because others had done it with similar hardware and clock frequencies. Had we not had these other designs as references, we would have had to estimate our maximum bit rate based on what we thought the code would look like, but as you can imagine, this could be quite difficult and potentially not very accurate.
There were a few different stages to the creation of the design. First we needed to choose a microcontroller. We wanted to stick as close to the Atmel Mega32, as that’s what we were used to. But after researching other various designs, we found that a very popular choice for an mp3 decoder and DAC (digital to analog converter) were the STA013 and the CS4334. These two were proven to work well together, and there were at least a few other designs that had used these two. This gave us a lot of background information on how to use them, which made creating an mp3 player in a months time seem more feasible. But, the STA013 was a 3V chip and required 3V communication lines with the microcontroller. Since the Mega32 was a 5V chip, we would have needed to build level converter circuitry between these two chips. To avoid this, we used the Mega32L, which is very similar to the Mega32, but can be run at 3V. The downside to this is that it can only be clocked at a maximum of 8 MHz instead of 16 MHz. As the bottleneck of our design is currently the speed of the microcontroller, we realize we could have achieved higher bitrates by using the Mega32 and creating the level conversion circuitry, but the Mega32L did keep our hardware design simpler.
The next step was to choose what type of flash memory to use to store the mp3 files. We knew we wanted flash memory instead of a hard drive or cd-rom, as those are larger and require more energy to power them. We chose the SD card because it was one of the popular flash memory standards, it used the SPI interface so we only needed 4 I/O lines from our microcontroller, and it ran at 3V. The only other piece of hardware we needed was an LCD display. We chose a 16X2 LCD display that is very similar to the ones we had been using in lab all year. A schematic of the hardware is shown below (modified from http://instruct1.cit.cornell.edu/courses/ee476/FinalProjects/s2002/jmd48/nll4 jmd48 - EE 476 Final Project Web Page/476_final_project_schematic.htm and http://www.pjrc.com/mp3/sta013.html )

As I mentioned above, the majority of the schematic was used from http://www.pjrc.com/mp3/sta013.html. The SD card which used the SPI interface needed to be on port B as that’s what the SPI functions use. So the I2C code for the STA013 needed to be modified to used port A. Port C was connected to the LCD display, and port D was used for pushbuttons. D.0 and D.1 weren’t used, so that we could continually use HyperTerminal for debugging with printf’s. The CS4334 DAC didn’t require a connection to the microcontroller, as it was completely configured through the STA013. As shown in the schematic, there are 3 pushbuttons. Button 4 will play the song that is currently displayed on the LCD display. Button 5 will stop playing the current song. Button 2 will skip to the next song on the SD card and display its title on the LCD display. There is one last tricky part to point out here: the 1K ohm resistors running to the LCD display. The LCD display was a 5V display, while all the signals from the Mega32L were 3V. So we connected a 5V supply to Vdd on the LCD. But the LCD sends data signals back to the Mega32L, which we needed to limit to 3V. This is the job of the resistors. Also, the inputs of the Mega32L have protection diodes which will protect it from high voltages, so the resistors were all we needed.
MP3, or MPEG-1 Audio Layer 3, is
digital audio encoding. It is a lossy compression format, meaning that if you compress the
data and then uncompress it, the new uncompressed version will not exactly
match the original version. According to
wikipedia.org, it uses psychoacoustic models to discard components less audible
to human hearing. This means that
frequencies and noises that people can’t hear anyway are removed, making the
amount of data that needs to be stored smaller.
There are various bit rates available: 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256 and 320 kbit/s. There are also various sampling frequencies:
32, 44.1 and 48 KHz. 44.1
KHz and 128 or 192 kbits/s are said to be the
most popular right now. The mp3 file has
a standard format of which is a frame consisting of 384, 576, or 1152
samples. This value depends on the MPEG
version. Also, all the frames have
header information consisting of 32 bits and side information consisting of 9,
17, or 32 bytes which help the decoder to decode the encoded data
correctly. An mp3 frame consists of an
mp3 header and mp3 data. Multiple frames
create an mp3 file. The details for the
mp3 header are shown below, as taken from http://en.wikipedia.org/wiki/MP3.

Our mp3 decoder is smart enough that if it doesn’t see mp3 data, it doesn’t output anything to the DAC. It can also determine the correct bit rate and frequency of an mp3 by reading the header itself. Therefore, all you need to do is stream it the mp3 file, and it will do the rest for you.
According to wikipedia.org, many different organizations have claimed ownership of patents related to decoding and/or encoding of mp3 files. There are open source encoders and decoders that patent holders allow to be distributed freely, but there has been legal action against certain companies selling mp3 devices without paying any licensing fees. Regardless, mp3 was patented in 1991, which means that by 2011, no patent could apply to mp3 any longer.
The SD card is also covered by patents, according to http://en.wikipedia.org/wiki/Secure_digital. Using the SD interface requires expensive royalties to be paid to the SDCA, but Wikipedia states that a common workaround for this is to use the MMC/SPI interface, which this project uses. While in this mode, you can not access the proprietary encryption features of the SD card, but this project only requires storing data, and encryption of the mp3’s would not really be useful anyway. MMC is an open standard that anyone can use, but the specification must be purchased from the MMC Association if it is needed.
We programmed the design in different pieces, and put it all together at the end. Step 1 was to access the SD card and be able to read and write data. Step 2 was to interface with the mp3 decoder, configure it, and stream some sample mp3 data to it. In step 3 we added code to support reading data from an SD card formatted with the FAT file system.
Step 1: The SD card
The information used to understand the SD card was taken from a few different sources: http://www.cs.ucr.edu/~amitra/sdcard/Additional/sdcard_appnote_foust.pdf, http://www.maxim-ic.com/appnotes.cfm/an_pk/3969, http://www.captain.at/electronic-atmega-mmc.php, and http://elm-chan.org/docs/mmc/mmc_e.html. The SD card interfaces with the microcontroller via SPI. We used some of information from http://instruct1.cit.cornell.edu/courses/ee476/SPI/index.html to understand an implement SPI. The base code that we began with came from http://www.captain.at/electronic-atmega-mmc.php. The description below refers to code in a file called mp3_withsd48test.c. This file is not part of the final project code, but was an important stepping stone on our way to the final result.
To start with, the code must include the spi.h file. This file allows us to use the Atmel SPI functions. We also make a note of which data lines are connected to which I/O pins: MOSI (master out slave in) is connected to B.5, MISO (master in slave out) is connected to B.6, SCLK (slave clock) is connected to B.7, and chip select is connected to B.4. In the initialize function we must set these I/O pins to the correct data direction. We must also correctly set the SPI configuration registers. This is shown below:
//set up SPI
//bit 7 SPIE=0 no ISR
//bit 6 SPE=1 enable spi
//bit 5 DORD=0 msb
first
//bit 4 MSTR=1 Mega32 is spi master
//bit 3 CPLO=1 clock polarity
//bit 2 CPHA=1 clock phase
//bit 1,0 rate sel=10 along with SPSR=1 sets clk
to f/32 = 250 kHz
SPCR = 0b01011110 ; //clock must not exceed 400 kHz for
initialization
SPSR = 1; //turn on SPI2x clock
//set up i/o data
direction for SPI
//connect MOSI B.5 to DI
//connect MISO B.6 to DO
//connect SCLK B.7 to CLK
//connect B.4 to chip select
DDRB.4 = 1; //output chip select
DDRB.5 = 1; //output MOSI
DDRB.6 = 0; //input MISO
DDRB.7 = 1; //output SCLK
It is important to set the SPI clock to be under 400 kHz for the initialization of SD card. After initialization is complete, we can increase the clock to 4 MHz, the maximum allowed when the MCU clock is 8 MHz (SPI clock max of f/2). After initialization we called the testSD() function. This function tests the SD card to make sure that it’s connected properly. First it calls MMC_Init(). MMC_Init follows an initialization flowchart from http://www.cs.ucr.edu/~amitra/sdcard/Additional/sdcard_appnote_foust.pdf:

Our initialization differs slightly from this chart. For example, in the first step we set the clock to 250 KHz, not 400 KHz, due to the available SPI clock dividers and our 8 MHz base clock. The next step is to assert the CS line by setting PORTB.4 to 1. We then send 80 clock pulses, which just means writing 80 bits of data. This is done with the simple loop:
for(i=0; i < 10; i++) spi(0xFF); // send 10*8=80 clock pulses
By calling spi(0xFF), the microcontroller takes care of setting all the appropriate signals in order to send the 8 bits of data specified in the parameter. Since it’s a 1 bit serial line, each bit takes 1 clock pulse, so 80 bits of data will create 80 clock pulses. Next, the CS line is deasserted by setting PORTB.4 to 0. Sixteen more clock pulses are sent, similar to the code above except the loop is only performed twice. Now we can start sending commands. The first command is command 0, which is the reset command:
Command(0x40,0,0,0x95)
The Command function is pretty simple. Every command that goes to the SD card has 6 bytes. The format can be seen in this picture, from http://www.maxim-ic.com/appnotes.cfm/an_pk/3969:

The first byte always starts out with a 0 then a 1 in the upper bits. Then the command you want to send is entered in the remain 6 bits of this first byte. For command 0, these bits are just 0. Then you pass arguments, depending on which command you send. If the command doesn’t take any arguments, or if it takes fewer that 4 bytes of arguments, then those bytes are just ignored. Lastly you send a 7 bit CRC, with a 1 in the least significant bit. The Command function takes 4 parameters: the command byte, two 16 bit arguments, and a CRC byte. The CRC byte is 0x95 or 0xFF for these commands, and for later commands the value of CRC is irrelevant. The Command function then just sends all of these values, bit by bit, to the SD card, as shown below:
char Command(char command, unsigned int AdrH, unsigned int AdrL, char CRCbits )
{ // sends a command to the MMC
spi(0xFF);
spi(command);
spi((unsigned char)(AdrH >>
8));
spi((unsigned char)AdrH);
spi((unsigned char)(AdrL >>
8));
spi((unsigned char)AdrL);
spi(CRCbits);
spi(0xFF);
return spi(0xFF); // return
the last received character
}
The return value of the function is the response from the SD card. There are different formats of the response, depending on the command issued. For all the commands we use, it responds in the R1 format, which is:

What we want to see is a return value of 1, which means the command completed and it’s in the idle state now. Now are ready to send command 41 and command 55, which are the initialization commands. These commands must be called until command 55 returns a 1 in the idle bit of the return value. Once this completes, the initialization is complete. If you want there are other checks that can be done, for example by issuing command 58 to read the operating conditions register, but it’s not necessary to do so.
Now that the SD card is initialized, we can speed up the SPI clock. The maximum speed you can run this clock at is the frequency of the clock of the microcontroller / 2, which is 4 MHz in our case. To set this up, you need to set the two SPI registers as follows:
SPCR
= 0b01011100;
SPSR
= 1; //turn on SPI2x clock
The default block size for a data transfer on an SD card should be 512 bytes. To guarantee this we can use command 16 to set the block length to 512, as follows:
Command(0x50,0,512,0xFF)
Now the SD card is properly configured. In order to test it, we need something to write to it. In this stage of our testing a function called fillram() is called, which simply writes a string over and over again into a block of ram (called sector[ ]) which is 512 bytes long. The function looks like this:
void fillram(void) { // fill RAM sector with ASCII characters
int
i,c;
c = 0;
for (i=0;i<=512;i++) {
sector[i] = mystring[c];
c++;
if (c >
17) { c = 0; }
}
}
The string used here (called mystring) is simply:
char *mystring = "Chris was here!
";
So now we have 512 bytes of RAM filled with multiple copies of this string. To write this data to the SD card, we call writeramtommc(). This function copies a string that is in RAM on the microcontroller to the SD card. The function looks like this:
int
writeramtommc(void) { // write RAM sector to MMC
int
i;
unsigned char c;
// 512 byte-write-mode (single block)
response = Command(0x58,0,512,0xFF);
if (response !=0)
{ //CMD24, which is a single block
write, addr is 512
printf("MMC: write error 1 \n\r");
printf("response = %d\n\r", response);
return 1;
}
printf("response = %d\n\r", response);
spi(0xFF);
spi(0xFF);
spi(0xFE); //start token
// write ram sectors to MMC
for (i=0;i<512;i++) {
spi(sector[i]);
}
// at
the end, send 2 dummy bytes (unused CRC bytes)
spi(0xFF);
spi(0xFF);
c = spi(0xFF);
printf("c1 = %d\n\r", c);
c &= 0x1F; // 0x1F = 0b.0001.1111;
printf("c2 = %d\n\r", c);
if (c != 0x05) {
// 0x05 = 0b.0000.0101
//if c=0x05, data is accepted
printf("MMC: write error 2 \n\r");
return 1;
}
// wait until MMC is not busy anymore
while(spi(0xFF) != (char)0xFF);
return 0;
}
First, command 24 is used to tell the SD card we are going to perform a write. This command has 1 argument: the address to which we would like to write to. In this case you can see that we are writing to address 512. The address has to be a multiple of 512, since the block size is 512 bytes. Once the command is accepted, we need to send the start token, which is 0xFE. Then we can write 512 bytes to the address we specified. So we call spi() for each byte of sector[] which transfers the desired 512 bytes to the SD card. We then send two dummy bytes at the end, and check to see what the response is. If the response is 0x05, then the data was accepted. Now we wait until the idle bit goes high by repeatedly sending 0xFF and looking at the idle bit. Once it goes high, we have completed the write, and we can return.
To really know if the write succeeded, we need to read from the SD card. To do this, we call sendmmc(). This function reads the data we just wrote to the SD card, and writes it to HyperTerminal. It looks like this:
int sendmmc(void) { //
send 512 bytes from the MMC
int
i;
// 512 byte-read-mode
response =
Command(0x51,0,512,0xFF);
if (response != 0)
{ //CMD17 is read single block, addr is 512
printf("MMC: read error 1 \n\r");
printf("response = %d\n\r", response);
}
// wait for 0xFE - start of any
transmission
startToken
= 0;
while(startToken == 0)
begin
response = spi(0xFF);
printf("response2
= %d\n\r", response);
if (response == 0xFE) startToken =
1;
end
for(i=0; i < 512; i++) {
recvd
= spi(0xFF);
// get character
printf("%c", recvd);
}
// at the end, send 2 dummy bytes
spi(0xFF); // actually this returns the CRC/checksum byte
spi(0xFF);
return 0;
}
Here we use command 17 to tell the SD card that we are going to perform a read, and we send 512 as a parameter to indicate the address we want to read from. Then we wait for the SD card to return the start token, which is 0xFE. To do this we keep sending 0xFF until we get 0xFE returned to us. At this point the read data is ready. To get it, we loop 512 times calling spi(0xFF) each time and print the return value which contains the data returned from the card to HyperTerminal. If the entire process is successful, we see “Chris was here! Chris was here! Chris was here!” printed over and over again on the screen.
Step 2: The mp3
decoder (STA013) and the DAC (CS4334):
To begin with, we define the mp3 decoder I/O names to the pins they correspond to:
// Define STA013 Pins
#define
I2C_SDA_direction DDRA.0
#define
I2C_SDA_in PINA.0
#define
I2C_SDA_out PORTA.0
#define
I2C_SCL PORTA.1
#define
DATA PORTA.2
#define
CLOCK PORTA.3
#define
DATA_REQ PINA.4
#define
RESET PORTA.5
As you can see, there are 4 outputs and 1 input. SDA and SCL are the data and clock pins used for the I2C connection. This is used for configuration purposes, such as to set up the mp3 decoder. DATA, CLOCK, DATA_REQ, and RESET are used to actually send mp3 data to the mp3 decoder. Note that there is an I2C library for the Atmel microcontrollers, but we didn’t use it here.
To configure the mp3 decoder, we call config_sta013(). This function is displayed below:
unsigned int config_sta013(void) begin
// Read from address 0x01 on the STA013
printf("Starting config_sta013\r\n");
address =
0x01; // Load the address into the address reg, r4
sta013_read(); //
Read data from the STA013
// Test if address 0x01 contains 0xAC
printf("data = %x, errorFlag =
%d\r\n", data, errorFlag);
if((data != 0xac)
|| (errorFlag != 0x00)) begin
printf("Error: STA013 Not Present, %d, \r\n", errorFlag); //
Print out the error flag
return 1; //
Return 1 to indicate that the STA013 is not present
end
else
printf("STA013 Present\r\n");
printf("beginning to load STA013 config
file\r\n");
// Send STA013_updatedata information to
STA013 (i.e., STA013 config file)
for (i = 0 ; i < max_config_index ;) begin
address =
STA013_UpdateData[i]; //
Load the address into the address reg, r4
data =
STA013_UpdateData[i+1]; // Load
the data into the data reg, r5
sta013_write(); // Write data to
STA013
// Increment i
i += 2;
// Check for an error in the write
if (errorFlag != 0x00) begin
printf("Error: STA013 Configuration Failed, %d\r\n", errorFlag); // Print
out the error flag
return 2; // Return 2
to indicate sta013 failed configuration
end
// Delay initialization for soft reboot
if (address ==
0x10) begin
delay_ms(1000);
end
end
printf("finished loading STA013 config
file\r\n");
printf("beginning to load STA013 board data\r\n");
// Send STA013 board specific data
for (i = 0; i < max_PLL_index;
) begin
address = config_PLL[i]; //
Load the address into the address reg, r4
data = config_PLL[i+1]; // Load the data into
the data reg, r5
sta013_write(); // Write data to STA013
// Increment i
i += 2;
if (errorFlag != 0x00) begin
printf("Error: STA013 PLL Configuration Failed, %d\r\n",
errorFlag); //
Print out the error flag
return 2; // Return 2
to indicate sta013 failed configuration
end
end
printf("finished loading STA013 board data\r\n");
printf("STA013 Configuration Complete\r\n");
return 0; //
Return 0 to indicate pass configuration
end
This function begins by making sure that the STA013 chip is present and wired properly. In order to do this, it performs a read from address 0x01 on the chip. This address contains 0xAC, so we need to make sure we can read this value from that address. To perform a read, the sta013_read() function is called:
void sta013_read(void) begin
printf("inside sta013_read\r\n");
// Start Condition
sta013_I2C_start();
//i2c_start();
printf("called I2C_start\r\n");
// Send the device address with the R/W bit
(i.e., the 8th bit) cleared
printf("about to try sta013_I2C_write\r\n");
I2C_byte = 0x86;
sta013_I2C_write();
if (errorFlag != 0) begin
printf("Read Error: Error writing device address (W) to
STA013.\r\n");
return;
end
printf("finished calling sta013_I2C_write, errorFlag = %d\r\n", errorFlag);
// Send the read address
printf("about to try sta013_I2C_write again\r\n");
I2C_byte = address;
sta013_I2C_write();
if (errorFlag != 0) begin
printf("Read Error: Error writing read address to
STA013.\r\n");
return;
end
printf("finished calling sta013_I2C_write again, errorFlag = %d\r\n", errorFlag);
// Start Condition
sta013_I2C_start();
printf("called I2C_start again\r\n");
// Send the device address with the R/W bit
(i.e., the 8th bit) set
printf("about to try sta013_I2C_write 3rd time\r\n");
I2C_byte = 0x87;
sta013_I2C_write();
if (errorFlag != 0) begin
printf("Read Error: Error writing device address (R) to
STA013.\r\n");
return;
end
printf("finished calling sta013_I2C_write 3rd time, errorFlag = %d\r\n", errorFlag);
// Read the data from the given address
printf("about to try sta013_I2C_read\r\n");
sta013_I2C_read();
printf("finished calling sta013_I2C_read, data =
%x\r\n", data);
// Stop Condition
sta013_I2C_stop();
end
To do any sort of I2C command, you first need to call the sta013_I2C_start() function:
void sta013_I2C_start(void) begin
// High to low transition of I2C_SDA while
I2C_SCL is high
I2C_SDA_direction = 1;
delay_us(5);
I2C_SDA_out = 1;
delay_us(5);
I2C_SCL = 1;
delay_us(5);
I2C_SDA_out = 0;
delay_us(5);
I2C_SCL = 0;
delay_us(5);
end
This function prepares the chip for a command. First we set the direction to 1 to make it an output. Data is sampled on the rising edge of the clock, so we first set the SDA line to 1 and then bring the clock line high. Next we set the SDA line to 0 and drop the clock line. This signifies a start condition. There are 5 microsecond delays in between all of the I2C changes. This is because I2C can’t be clocked very fast, and since this is just a 1 time configuration anyway, we chose a high delay between all I2C transactions to ensure correct operation.
Now that the start condition is done, we send the value 0x86 to signify that we would like to perform a read. We store 0x86 in I2C_byte, and call sta013_I2C_write():
void sta013_I2C_write(void) begin
// Clock each bit onto the SDA bus (starting
with the MSB)
I2C_SDA_direction = 1;
for(j = 7; j >=
0; j--) begin
delay_us(5);
I2C_SCL = 0; //I2C_SCL is the clock (what
is the min/max clock speed?)
delay_us(5);
I2C_SDA_out = (I2C_byte >> j) &
0x01; // Write each bit while I2C_SCL is
low
delay_us(5);
I2C_SCL = 1;
delay_us(5);
end
// Get the ack bit
I2C_SCL = 0;
delay_us(5);
I2C_SDA_direction = 0;
delay_us(5);
I2C_SCL = 1;
delay_us(5);
errorFlag
= I2C_SDA_in; //should be a 0
delay_us(5);
I2C_SCL = 0;
end
To write data over the I2C bus, you set the SDA direction to output, lower the clock, write the data bit, and raise the clock. You repeat this lowering of the clock, sending a bit, and raising the clock until you have sent your data byte. To receive the ack bit, you set the SDA direction to input, lower and raise the clock, and then read SDA. The value should be a 0 if everything went ok. Finally, lower the clock again and exit the function.
Now we need to perform another write to send the address that we want to read from. The void sta013_I2C_write() function is called again, except this time the address to read from is stored in the I2C_byte variable. Now the STA013 knows which address we want to read from, and we just need tell it that we’re going to do a read next. To do that, we do another write with value 0x87, saying we’re ready to read. Once this is complete, we can call sta013_I2C_read():
void sta013_I2C_read(void) begin
data = 0x00;
// Clock each bit off of the SDA bus
I2C_SDA_direction = 0;
delay_us(5);
I2C_SCL = 0;
delay_us(5);
for(j = 7; j >=
0; j--) begin
I2C_SCL = 1;
delay_us(5);
data = data |
(I2C_SDA_in << j); // Read the
bit while I2C_SCL is high
delay_us(5);
I2C_SCL = 0;
delay_us(5);
end
end
This function just reads bit by bit while controlling the clock. The data changes on a low to high transition, so after the clock is high a new data bit can be read. We now have the data at address 0x01, and we just need to check to make sure it’s 0xAC. If it is we can move on. We also need to call the sta013_I2C_stop() function, which tells the STA013 that we are done with it for now:
void sta013_I2C_stop(void) begin
// Low to high transition of I2C_SDA while I2C_SCL
is high
I2C_SDA_direction = 1;
delay_us(5);
I2C_SDA_out = 0;
delay_us(5);
I2C_SCL = 1;
delay_us(5);
I2C_SDA_out = 1;
delay_us(5);
I2C_SCL = 0;
delay_us(5);
end
This function simply changes the SDA line from low to high which the clock line is high, which is the stop condition for the STA013.
The next
step is very important. The STA013
requires some configuration data in order to function properly. This data is available in a file from
STMicroelectronics, here: http://www.st.com/stonline/products/families/audio/audiodecoders/mp3/sta013.htm. The file is called p02_0609.bin, and contains many pairs of addresses and data
that need to be written to the STA013.
This data is stored in an array called STA013_UpdateData. These address/data pairs are loaded into the
address and data variables, and sta013_write() is
called:
void sta013_write(void) begin
// Start Condition
sta013_I2C_start();
// Send the device
address with the R/W bit (i.e., the 8th bit) cleared
I2C_byte = 0x86;
sta013_I2C_write();
if
(errorFlag != 0) begin
printf("Write Error:
Error writing device address (W) to STA013.\r\n");
return;
end
// Send the write address
I2C_byte = address;
sta013_I2C_write();
if
(errorFlag != 0) begin
printf("Write Error:
Error writing write address to STA013.\r\n");
return;
end
// Send the data
I2C_byte = data;
sta013_I2C_write();