// 476 Final Project (SP04) -- MP3 Radio // David Chai (dtc24) // Jian Gong (jg253) // // Note: the STA013 MP3 decoder initialization and configuration code was borrowed from a // previous group, Nelson Li and Jason Dirner #include #include #include #include #define begin { #define end } // Define STA013 Pins #define RESET PORTC.0 // Reset #define DATA_REQ PINC.1 // DATA_REQ high when STA013 requests a MP3 byte #define CLOCK PORTC.2 // Serial Clk #define DATA PORTC.3 // Serial Data #define I2C_SCL PORTC.4 // I2C Clk #define I2C_SDA_out PORTC.5 // I2C Data to write #define I2C_SDA_in PINC.5 // I2C Data to read #define I2C_SDA_direction DDRC.5 // define commands #define PAUSE 0x03 #define PLAY 0x02 #define NEXT 0x04 #define PREV 0x05 #define STOP 0x07 // Length of configuration file #define max_config_index 4013 #define max_PLL_index 32 #asm .equ PORTC = 0x06 .equ DDRC = 0x17 .equ PINA = 0x16 .equ I2C_SDA_pin = 5 .equ I2C_SCL_pin = 4 .equ DATA_pin = 3 .equ CLOCK_pin = 2 .equ DATA_REQ_pin = 1 .equ RESET = 0 #endasm // Timer 1 Constants #define prescale64 3 #define clear_on_match 8 // States for debouncing the keypad #define release 0 #define debounce_press 1 #define still_pressed 2 #define debounce_release 3 #pragma regalloc- // Subroutines void initialize(void); // Initialize the MCU void sta013_I2C_start(void); // I2C Start Condition void sta013_I2C_stop(void); // I2C Stop Condition void sta013_I2C_read(void); // Read a byte via the I2C interface void sta013_I2C_write(void); // Write a byte via the I2C interface void sta013_read(void); // Read from the given address on the STA013 void sta013_write(void); // Write to the given address on the STA013 unsigned int config_sta013(void); // Configure the STA013 with the data file void sta013_start(void); // Instruct the STA013 to start running void sta013_play(void); // Instruct the STA013 to start playing void sta013_stop(void); // Instruct the STA013 to stop playing void toDecoder(void); void checkSongEnd(void); unsigned char swapbits(unsigned char x); // MP3 variables register unsigned char address @4; // Register address for interaction with the STA013 register unsigned char data @5; // Data read from/written to the STA013 register unsigned char I2C_byte @7; // I2C byte to be written to the STA013 register unsigned char mp3_index_low @8; // Lower 8 bits of the mp3 index register unsigned char mp3_index_high @9; // Upper 8 bits of the mp3 index register unsigned char mp3_max_low @10; // Maximum value for the mp3_index_low value register unsigned char mp3_max_high @11; // Maximum value for the mp3_index_high value register unsigned char errorFlag @12; // Error flag unsigned int config_flag; // Variable to tell what kind of config failure unsigned char start_flag, stop_flag; // MP3 playback flags unsigned char startDemo, volume; unsigned char c, leds, q, u, f, noSound, mode; // Debounce Variables unsigned int debounce_state; // Debounce State variable unsigned char butnum, maybe, pushState; unsigned char testbuffer[1500]; unsigned long int buffIndex; int i, l; // Loop Variables #include // Board specific settings from STA013 data sheet, Table 5 // 14.7456 MHz Crystal flash unsigned char config_PLL[] = {0x54, 0x01, 0x55, 0x21, 0x06, 0x0c, 0x0b, 0x03, 0x50, 0x10, 0x51, 0x00, 0x52, 0x04, 0x61, 0x0f, 0x64, 0x55, 0x65, 0x55, 0x05, 0xa1, 0x18, 0x04, 0x0c, 0x05, 0x13, 0x00, 0x14, 0x00}; // Main task scheduler void main(void) { initialize(); // Initialize the MCU config_flag = config_sta013(); // Configure the STA013 // Configuration succeeds if (config_flag == 0) { sta013_start(); // Start the STA013 start_flag = 1; // Main loop while(1) { // Start playback if (start_flag == 1) { mp3_index_low = 0x00; // Zero both mp3 index registers mp3_index_high = 0x00; sta013_play(); // Start playback start_flag = 0; // Clear start_flag } else if (stop_flag == 1) { sta013_stop(); // Stop playback stop_flag = 0; // Clear stop_flag } if(startDemo == 0) { // Configuration fails putsf("Please reset the system.\r\n"); while(1) ; } } //end while(1) } } //----------------------------------------------------------------- // Configure the sta013 /* * 0 - STA013 pass configuration * 1 - STA013 not present * 2 - STA013 failed configuration of STA013_updatedata[] */ unsigned int config_sta013(void) begin // Read from address 0x01 on the STA013 address = 0x01; // Load the address into the address reg, r4 sta013_read(); // Read data from the STA013 // Test if address 0x01 contains 0xAC 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 putsf("STA013 Present\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]; //printf("%02x(%02x) ", data, address); // 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 // 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 putsf("STA013 Configuration Complete\r\n"); return 0; // Return 0 to indicated pass configation end //---------------------------------------------------------------- // Instruct the STA013 to start running void sta013_start(void) begin // Write 1(0x01) to address 114(0x72) address = 0x72; data = 0x01; sta013_write(); // Check for an error if (errorFlag != 0x00) printf("Error: STA013 Start Failed, %d\r\n", errorFlag); else putsf("Starting mp3 player...\r\n"); end //---------------------------------------------------------------- void sta013_play(void) begin // Write 1(0x01) to address 19(0x13) address = 0x13; data = 0x01; sta013_write(); // Check for an error if (errorFlag != 0x00) { printf("Error: STA013 Play Failed, %d\r\n", errorFlag); startDemo = 0; } else { putsf("Playing MP3...\r\n"); startDemo = 1; } end //---------------------------------------------------------------- void sta013_stop(void) begin // Write 0(0x01) to address 19(0x13) address = 0x13; data = 0x00; sta013_write(); // Check for an error if (errorFlag != 0x00) printf("Error: STA013 Stop Failed, %d\r\n", errorFlag); else putsf("Playback stopped.\r\n"); end //---------------------------------------------------------------- // I2C Start Condition void sta013_I2C_start(void) begin // High to low transition of I2C_SDA while I2C_SCL is high I2C_SDA_direction = 1; I2C_SDA_out = 1; I2C_SCL = 1; I2C_SDA_out = 0; I2C_SCL = 0; end //---------------------------------------------------------------- // I2C Stop Condition void sta013_I2C_stop(void) begin // Low to high transition of I2C_SDA while I2C_SCL is high I2C_SDA_direction = 1; I2C_SDA_out = 0; I2C_SCL = 1; I2C_SDA_out = 1; I2C_SCL = 0; end //---------------------------------------------------------------- // I2C Read Byte void sta013_I2C_read(void) begin data = 0x00; // Clock each bit off of the SDA bus I2C_SDA_direction = 0; I2C_SCL = 0; for(l = 7; l >= 0; l--) begin I2C_SCL = 1; data = data | (I2C_SDA_in << l); // Read the bit while I2C_SCL is high I2C_SCL = 0; end end //---------------------------------------------------------------- // I2C Write Byte void sta013_I2C_write(void) begin // Clock each bit onto the SDA bus (starting with the MSB) I2C_SDA_direction = 1; for(l = 7; l >= 0; l--) begin I2C_SCL = 0; I2C_SDA_out = (I2C_byte >> l) & 0x01; // Write each bit while I2C_SCL is low I2C_SCL = 1; end // Get the ack bit I2C_SCL = 0; I2C_SDA_direction = 0; I2C_SCL = 1; errorFlag = I2C_SDA_in; I2C_SCL = 0; end //---------------------------------------------------------------- // Read a byte from the STA013 void sta013_read(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 putsf("Read Error: Error writing device address (W) to STA013.\r\n"); return; end // Send the read address I2C_byte = address; // printf("%i", address); sta013_I2C_write(); if (errorFlag != 0) begin putsf("Read Error: Error writing read address to STA013.\r\n"); return; end // Start Condition sta013_I2C_start(); // Send the device address with the R/W bit (i.e., the 8th bit) set I2C_byte = 0x87; sta013_I2C_write(); if (errorFlag != 0) begin putsf("Read Error: Error writing device address (R) to STA013.\r\n"); return; end // Read the data from the given address sta013_I2C_read(); // Stop Condition sta013_I2C_stop(); end //---------------------------------------------------------------- // Write a byte from the STA013 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 putsf("Write Error: Error writing device address (W) to STA013.\r\n"); return; end // Send the write address I2C_byte = address; //printf("Address to write: %c\n\r", address); sta013_I2C_write(); if (errorFlag != 0) begin putsf("Write Error: Error writing write address to STA013.\r\n"); return; end // Send the data I2C_byte = data; sta013_I2C_write(); if (errorFlag != 0) begin putsf("Write Error: Error writing data to STA013.\r\n"); return; end // Stop Condition sta013_I2C_stop(); end //---------------------------------------------------------------- // Initialize the MCU void initialize(void) begin // Initialize PORTB to interface with the STA013 MP3 Decoder Chip DDRC = 0b00111101; // set output ports to STA013 //PORTC = 0x00; // clear all pin values I2C_SDA_direction = 1; I2C_SCL = 0; I2C_SDA_in = 0; DATA = 0; CLOCK = 0; noSound = 0; q = 0; u = 0; // Initialize the MP3 index and bound mp3_index_low = 0x00; mp3_index_high = 0x00; mp3_max_low = 0b01110011; mp3_max_high = 0b01111010; // Variable Initialization config_flag = 0; // Initialize set config flag to 0 (NO CONFIG ERROR) start_flag = 0; // Initialize start and stop flags to 0 stop_flag = 0; mode = 0; volume = 0; RESET = 0; delay_ms(1000); RESET = 1; delay_ms(500); putsf("\r\n\r\nMP3 Radio (Alpha)\r\n"); // init USART UCSRA=0x00; UCSRB=0x98; // set to COM1 UCSRC=0x86; UBRRH=0x00; UBRRL = 0x19; // 38400 baud // init TIMER0 compare interrupt TCCR0 = 0b00001011; // %64 prescalar on a 16 MHz clock // 250 kHz clock source ==> 4 us OCR0 = 254; // 4 us * 255 cycles = 1 ms/cycle TIMSK = 0x02; // Turn interrupts on #asm sei #endasm // PORTB <--> LEDS DDRB=0xff; // PORT B is an ouput PORTB=0; leds=0x08 ^ 0xff; // leds: indicate NORMAL mode PORTB = leds; // PORTA <--> PUSHBUTTONS DDRA = 0x00; end interrupt [USART_RXC] void uartr(void) { char outc; c = UDR; // grab data from UDR if(DATA_REQ) { outc = PAUSE; // set command outc printf("%c", outc); // send command to PC } else toDecoder(); // send MP3 data to the decoder } // sends MP3 data to the STA013 MP3 decoder void toDecoder(void) { unsigned char w; // use function swapbits to minimize cycles associated with a for loop w = swapbits(c); CLOCK = 0; DATA = w & 0x01; CLOCK = 1; CLOCK = 0; DATA = w & 0x02; CLOCK = 1; CLOCK = 0; DATA = w & 0x04; CLOCK = 1; CLOCK = 0; DATA = w & 0x08; CLOCK = 1; CLOCK = 0; DATA = w & 0x10; CLOCK = 1; CLOCK = 0; DATA = w & 0x20; CLOCK = 1; CLOCK = 0; DATA = w & 0x40; CLOCK = 1; CLOCK = 0; DATA = w & 0x80; CLOCK = 1; } // swap the bits in paramater x unsigned char swapbits(unsigned char x) { x = ((x & 0xaa) >> 1) | ((x & 0x55) << 1); // 67452301 x = ((x & 0xcc) >> 2) | ((x & 0x33) << 2); // 45670123 return (x >> 4) | (x << 4); } // define debounce states enum {NOPUSH, MAYBEPUSH, PUSHED, MAYBENOPUSH}; // define modes of operation enum {NORMAL, ROCK, JAZZ, CLASSICAL}; void deBouncer(void) { char outc; switch(pushState) { case NOPUSH: if(~PINA == 0x01 || ~PINA == 0x02 || ~PINA == 0x04 || ~PINA == 0x08 || ~PINA == 0x10 || ~PINA == 0x20 || ~PINA == 0x40 || ~PINA == 0x80) pushState = MAYBEPUSH; else pushState = NOPUSH; break; case MAYBEPUSH: if(~PINA == 0x01 || ~PINA == 0x02 || ~PINA == 0x04 || ~PINA == 0x08 || ~PINA == 0x10 || ~PINA == 0x20 || ~PINA == 0x40 || ~PINA == 0x80) pushState = PUSHED; else pushState = NOPUSH; break; case PUSHED: if(~PINA == 0x01 || ~PINA == 0x02 || ~PINA == 0x04 || ~PINA == 0x08 || ~PINA == 0x10 || ~PINA == 0x20 || ~PINA == 0x40 || ~PINA == 0x80) { pushState = NOPUSH; // in the following nested if block, // outc = COMMAND <-- defines command // printf("%", outc) <-- sends command to PC if(~PINA == 0x01) { // PAUSE (and unpause) song outc = PAUSE; printf("%c", outc); } else if(~PINA == 0x02) { // STOP song outc = STOP; printf("%c", outc); } else if(~PINA == 0x04) { // PLAY (and unpause) song outc = PLAY; printf("%c", outc); } else if(~PINA == 0x08) { // NEXT track outc = NEXT; printf("%c", outc); } else if(~PINA == 0x10) { // PREVious track outc = PREV; printf("%c", outc); } else if(~PINA == 0x20) { // toggle mode of operation if(mode++ == CLASSICAL) mode = NORMAL; // set STA013 treble and bass settings to current mode of operation: // // Treble(db) Bass(dB) // NORMAL 0 0 // ROCK 0* 4.5 // JAZZ -4.5 13.5 // CLASSICAL -13.5 -6.5 // // * any positive treble causes extreme amounts of distortion // switch(mode) { case NORMAL: // treble = 0 address = 0x7b; data = 0x00; sta013_write(); // bass = 0 address = 0x7c; data = 0x00; sta013_write(); // light up led3 leds = 0x08 ^ 0xff; PORTB = leds; break; case ROCK: // treble = 0dB address = 0x7b; data = 0x00; sta013_write(); // bass = 4.0dB address = 0x7c; data = 0x03; sta013_write(); // light up led4 leds = 0x10 ^ 0xff; PORTB = leds; break; case JAZZ: // treble = -4.5dB address = 0x7b; data = 0xfd; sta013_write(); // bass = 13.5dB address = 0x7c; data = 0x09; sta013_write(); // light up led5 leds = 0x20 ^ 0xff; PORTB = leds; break; case CLASSICAL: // treble = -13.5dB address = 0x7b; data = 0xf7; sta013_write(); // bass = -6.5dB address = 0x7c; data = 0xfc; sta013_write(); // light up led6 leds = 0x40; PORTB = leds; } } else if(~PINA == 0x40) { // volume up, decrease attenuation on two channels address = 0x48; if(volume == 0) volume = 0; else volume = volume-5; data = volume; sta013_write(); address = 0x46; sta013_write(); } else if(~PINA == 0x80) { // volume down address = 0x48; volume = volume+5; data = volume; sta013_write(); address = 0x46; sta013_write(); } } //end if else pushState = MAYBENOPUSH; break; case MAYBENOPUSH: if(~PINA == 0x01 || ~PINA == 0x02 || ~PINA == 0x04 || ~PINA == 0x08 || ~PINA == 0x10 || ~PINA == 0x20 || ~PINA == 0x40 || ~PINA == 0x80) pushState = PUSHED; else pushState = NOPUSH; } } // TIMER0 ISR interrupt [TIM0_COMP] void clktk1ms(void) { if(q > 60) { // debounce in every 60ms; this seems greater than usual, but prevents a command from being sent twice deBouncer(); q = 0; } // check if a song is done by checking for no sound if(u > 150) { checkSongEnd(); u = 0; } q++; // debouncer() counter u++; // checkSongEnd() counter } // determine whether a song is finished by detecting how long the song has been playing no sound void checkSongEnd(void) { char outc; // when there is no sound, c = 0xff if(c == 0xff) { // detect 0xff leds = leds ^ 0x01; PORTB = leds; // led0 starts to blink when song is done noSound++; } // if there has been no sound for over 3 seconds, assume song is done // move on to the NEXT song; this is a very reasonable assumption if(noSound > 20) { outc = NEXT; printf("%c", outc); // send command to PC noSound = 0; } }