Library for VS1053 chip
VLSIcodec.c
- Committer:
- clemente
- Date:
- 2012-05-24
- Revision:
- 1:399afe8151de
- Parent:
- 0:e97876f96d4b
File content as of revision 1:399afe8151de:
/** mbed VLSIcodec library. To access VS1053 MPEG3 codec. Copyright (c) 2010 NXP 3790 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "mbed.h" #include "VLSIcodec.h" #include "VLSIcodec_patch.h" #ifdef __ENCODE_OGG #include "VLSIcodec_OGG.h" #endif #ifdef __PLUGINGA #include "VLSIcodec_pluginGA.h" #define cBASE 0x1800 /* Base address of X-RAM for a VS1053 */ #endif //****************************************************************************** //****************************************************************************** // Section: Constants //****************************************************************************** //****************************************************************************** #define VLSI_MODE 0x00 #define VLSI_STATUS 0x01 #define VLSI_BASS 0x02 #define VLSI_CLOCKF 0x03 #define VLSI_DECODE_TIME 0x04 #define VLSI_AUDATA 0x05 #define VLSI_WRAM 0x06 #define VLSI_WRAMADDR 0x07 #define VLSI_HDAT0 0x08 #define VLSI_HDAT1 0x09 #define VLSI_AIADDR 0x0A #define VLSI_VOL 0x0B #define VLSI_AICTRL0 0x0C #define VLSI_AICTRL1 0x0D #define VLSI_AICTRL2 0x0E #define VLSI_AICTRL3 0x0F #define VLSI_STATUS_VER 0x00F0u #define VER_VS1001 (0u<<4) #define VER_VS1011 (1u<<4) #define VER_VS1002 (2u<<4) #define VER_VS1011E (2u<<4) #define VER_VS1003 (3u<<4) #define VER_VS1053 (4u<<4) #define VER_VS1033 (5u<<4) #define VER_VS1103 (7u<<4) #define cWAIT_DREQ_1MS wait_ms( 1); while (!_dreq); #define cWAIT_DREQ while (!_dreq); /* VLSI 1053, MODE Register bit configuration. */ #define SM_DIFF 0x01 #define SM_LAYER12 0x02 #define SM_RESET 0x04 #define SM_CANCEL 0x08 #define SM_EARSPEAKER_LO 0x10 #define SM_TESTS 0x20 #define SM_STREAM 0x40 #define SM_EARSPEAKER_HI 0x80 #define SM_DACT 0x100 #define SM_SDIORD 0x200 #define SM_SDISHARE 0x400 #define SM_SDINEW 0x800 #define SM_ADPCM 0x1000 #define SM_NOTUSE 0x2000 #define SM_LINE1 0x4000 #define SM_CLK_RANGE 0x8000 /* SC MULT MASK CLKI */ #define SC_MULT_0 0x0000 /* XTALI */ #define SC_MULT_2 0x2000 /* XTALI×2.0 */ #define SC_MULT_2_5 0x4000 /* XTALI×2.5 */ #define SC_MULT_3 0x6000 /* XTALI×3.0 */ #define SC_MULT_3_5 0x8000 /* XTALI×3.5 */ #define SC_MULT_4 0xA000 /* XTALI×4.0 */ #define SC_MULT_4_5 0xC000 /* XTALI×4.5 */ #define SC_MULT_5 0xE000 /* XTALI×5.0 */ /* SC ADD tells how much the decoder firmware is allowed to add to the multiplier specified by SC MULT if more cycles are temporarily needed to decode a WMA or AAC stream. The values are: SC ADD MASK Multiplier addition */ #define SC_ADD_NOMOD 0x0000 /* No modification is allowed */ #define SC_ADD_X1 0x0800 /* 1.0× */ #define SC_ADD_X1_5 0x1000 /* 1.5× */ #define SC_ADD_X2 0x1800 /* 2.0× */ #define SC_FREQ 0x0000 /* 12.288 MHz clock. */ /* */ #define cMODEREG_SET ( SM_SDINEW ) #define cCLOCK_SET ( SC_MULT_5 | SC_ADD_X1 | SC_FREQ) #define VS1053_CS_ENABLE _cs=0; #define VS1053_CS_DISABLE _cs=1; #define VS1053_XDCS_ENABLE _xdcs=0; #define VS1053_XDCS_DISABLE _xdcs=1; #define VS1053_RST_ENABLE _rst=0; #define VS1053_RST_DISABLE _rst=1; /* */ #define cVS1053_CS_DELAY 50 /** VS1053Codec */ VS1053Codec::VS1053Codec( PinName mosi, PinName miso, PinName sclk, PinName cs, PinName dreq, PinName rst, PinName xdcs) : _spi( mosi, miso, sclk), _cs( cs), _dreq( dreq), _rst( rst), _xdcs( xdcs) { // defaults params VS1053_CS_DISABLE; VS1053_XDCS_DISABLE; } /** Wait untile the dreq is released * @param none * @retval none */ void VS1053Codec::testdreq( void){ while( !_dreq); } /** Put the VS1053 in low power mode (RESET) * * @param none * @retval none */ void VS1053Codec::lowpower( void) { VS1053_RST_ENABLE; wait_ms( 10); } /** Initialize the VS1053 */ void VS1053Codec::init(void) { // VS1053_RST_DISABLE; VS1053_CS_DISABLE; VS1053_XDCS_DISABLE; // _spi.format( 8, 0); _spi.frequency( MP3_INIT_SPEED); // Assert RESET VS1053_RST_ENABLE; wait_ms( 100); // Deassert RESET (active low) VS1053_RST_DISABLE; VS1053_CS_DISABLE; VS1053_XDCS_DISABLE; wait_ms( 100); // Wait while(!_dreq); // Reset the vs1053 writereg( VLSI_MODE, ( cMODEREG_SET | SM_RESET) ); wait_ms( 10); // Wait... while(!_dreq); /* * Write configuration MODE register in a loop to verify that the chip is * connected and running correctly */ do { writereg( VLSI_MODE, ( cMODEREG_SET )); wait_ms( 1); }while( readreg( VLSI_MODE) != ( cMODEREG_SET )); // if((readreg( VLSI_STATUS) & VLSI_STATUS_VER) == VER_VS1053) { // Set the clock to maximum speed (VS1053 only) to allow all audio formats // to be decoded without glitching. Note that this increases power // consumption. // SC_MULT = XTALI*4.5, SC_ADD = noMod, SC_FREQ = 0 (12.288MHz) // VLSIWriteReg(VLSI_CLOCKF, SC_MULT_4_5 | SC_ADD_X1 | SC_FREQ); writereg(VLSI_CLOCKF, cCLOCK_SET); /* */ wait_ms( 2); while(!_dreq); } else if ((readreg( VLSI_STATUS) & VLSI_STATUS_VER) == VER_VS1011E) { // Nothing special to do ; } else { // VLSI Chip version not tested, not supported, halt program execution. // This trap should be caught during the design phase. while(1); } _spi.frequency( MP3_RUN_SPEED); } void VS1053Codec::loadpatch(void) /* Questa funzione carica una patch al firmware del codec */ { int i = 0, plgin_len; unsigned int addr, n, val; plgin_len=sizeof( plugin)/sizeof(plugin[0]); while (i<plgin_len) { addr = plugin[i++]; n = plugin[i++]; if (n & 0x8000U) { /* RLE run, replicate n samples */ n &= 0x7FFF; val = plugin[i++]; while (n--) { writeregnowait(addr, val); while(!_dreq); } } else { /* Copy run, copy n samples */ while (n--) { val = plugin[i++]; writeregnowait(addr, val); while(!_dreq); } } } // wait_ms( 1); while(!_dreq); } #ifdef __PLUGINGA void VS1053Codec::loadgapatch(void) /* Questa funzione carica un plugin nel codec */ { int i = 0, plgin_len; unsigned int addr, n, val; plgin_len=sizeof( gaplugin)/sizeof(gaplugin[0]); while (i<plgin_len) { addr = gaplugin[i++]; n = gaplugin[i++]; if (n & 0x8000U) { /* RLE run, replicate n samples */ n &= 0x7FFF; val = gaplugin[i++]; while (n--) { writeregnowait(addr, val); while(!_dreq); } } else { /* Copy run, copy n samples */ while (n--) { val = gaplugin[i++]; writeregnowait(addr, val); while(!_dreq); } } } // wait_ms( 1); while(!_dreq); } #endif #ifdef __ENCODE_OGG void VS1053Codec::loadoggpatch(void) /* Questa funzione carica una patch per gestire l'encoder OGG */ { int i = 0, plgin_len; unsigned int addr, n, val; plgin_len=sizeof( ogg_enc)/sizeof(ogg_enc[0]); while (i<plgin_len) { addr = ogg_enc[i++]; n = ogg_enc[i++]; if (n & 0x8000U) { /* RLE run, replicate n samples */ n &= 0x7FFF; val = ogg_enc[i++]; while (n--) { writeregnowait(addr, val); while(!_dreq); } } else { /* Copy run, copy n samples */ while (n--) { val = ogg_enc[i++]; writeregnowait(addr, val); while(!_dreq); } } } // wait_ms( 1); while(!_dreq); } #endif #ifdef __PLUGINGA void VS1053Codec::readgavalue( unsigned char *currval, unsigned char *peak) { writereg( VLSI_WRAMADDR, cBASE+2); unsigned short bands = (unsigned short)readreg( VLSI_WRAM); // writereg( VLSI_WRAMADDR, cBASE+4); // for (int i=0;i<bands;i++) { short pv = readreg( VLSI_WRAM); /* current value in bits 5..0, normally 0..31 peak value in bits 11..6, normally 0..31 */ currval[i]=(unsigned char)(pv&0x003F); peak[i]=(unsigned char)( (pv>>6)&0x003F); } } unsigned short VS1053Codec::readgabands( void) { writereg( VLSI_WRAMADDR, cBASE+2); unsigned short bands = (unsigned short)readreg( VLSI_WRAM); /* */ return bands; } #endif void VS1053Codec::setvmeter( void) { unsigned short tmp; tmp=(unsigned short)readreg( VLSI_STATUS); tmp|=(1<<9); writereg( VLSI_STATUS, tmp); } void VS1053Codec::getvmeterval( unsigned char* left, unsigned char* right) { unsigned short tmp; tmp=(unsigned short)readreg( VLSI_AICTRL3); *right=(unsigned char)tmp&0x00FF; *left=(unsigned char)(tmp>>8); } /**************************************************************************** Function: void VS1053Codec::setbassboost(BYTE bass, BYTE gfreq) Description: This function sets the bass boost. Precondition: None Parameters: BYTE bass - Bass gain in dB, range from 0 to 15 (MSB nibble) BYTE gfreq - Limit frequency for bass boost, 10 Hz steps (range from 20 to 150) (LSB nibble) Returns: None Remarks: None ***************************************************************************/ void VS1053Codec::setbassboost( unsigned char bass, unsigned char gfreq) { unsigned char templ; unsigned short tmp; // if(bass > 15u) bass = 15; if(gfreq > 150u) gfreq = 150; if ( gfreq < 20) gfreq = 20; // templ = (unsigned char)gfreq/10; // put bass boost value into the upper 4 bit templ |= (bass << 4); // tmp=(unsigned short)readreg( VLSI_BASS); tmp &= 0xFF00; tmp |= templ; // writereg( VLSI_BASS, tmp); } /**************************************************************************** Function: void VS1053Codec::settrebleboost(BYTE bass, WORD gfreq) Description: This function sets the bass boost. Precondition: None Parameters: BYTE treble - Bass gain in dB, range from -8 to 7 (MSB nibble) WORD gfreq - Limit frequency for bass boost, 1000 Hz steps (range from 1000 to 15000) (LSB nibble) Returns: None Remarks: None ***************************************************************************/ void VS1053Codec::settrebleboost( char treble, unsigned int gfreq) { unsigned char templ; unsigned short tmp; // if(treble > 7) treble = 7; if ( treble < (char)-8) treble = (char)-8; if(gfreq > 15000u) gfreq = 15000; if(gfreq < 1000) gfreq = 1000; // templ = (unsigned char)gfreq/1000; // put treble boost value into the upper 4 bit templ |= (treble << 4); // tmp=(unsigned short)readreg( VLSI_BASS); tmp &= 0x00FF; tmp |= (templ << 8); // writereg( VLSI_BASS, tmp); } void VS1053Codec::getplaytime(char *playtime) { unsigned short playTime; unsigned char minutes, seconds; playTime = (unsigned short)readreg(VLSI_DECODE_TIME); minutes = playTime/60; seconds = playTime%60; *playtime++=('0'+minutes/10); *playtime++=('0'+minutes%10); *playtime++=(':'); *playtime++=('0'+seconds/10); *playtime++=('0'+seconds%10); } /**************************************************************************** Function: void setvolume(unsigned char vRight, unsigned char vLeft) Description: This function set volume for analog outputs on the VLSI codec. Precondition: None Parameters: unsigned char vRight - right channel attenuation from maximum volume, 0.5dB steps (0x00 = full volume, 0xFF = muted) unsigned char vLeft - left channel attenuation from maximum volume, 0.5dB steps (0x00 = full volume, 0xFF = muted) Returns: None Remarks: None ***************************************************************************/ void VS1053Codec::setvolume(unsigned char vRight, unsigned char vLeft) { writereg(VLSI_VOL, ((unsigned int)vLeft)<<8 | vRight); } void VS1053Codec::sinetest( unsigned char pitch) { unsigned int i; while(!_dreq); i=(unsigned short)readreg( VLSI_MODE); writereg( VLSI_MODE, ( i | SM_TESTS )); VS1053_XDCS_ENABLE; if ( pitch) { // START Sine Test _spi.write(0x53); _spi.write(0xEF); _spi.write(0x6E); _spi.write( pitch); _spi.write(0x00); _spi.write(0x00); _spi.write(0x00); _spi.write(0x00); } else { // STOP Sine Test _spi.write(0x45); _spi.write(0x78); _spi.write(0x69); _spi.write(0x74); _spi.write(0x00); _spi.write(0x00); _spi.write(0x00); _spi.write(0x00); } VS1053_XDCS_DISABLE; } void VS1053Codec::writedata( unsigned char data) /* * Write data to SDI. */ { int i=0; VS1053_XDCS_ENABLE; for( i=0; i<cVS1053_CS_DELAY; i++); _spi.write( data); for( i=0; i<cVS1053_CS_DELAY; i++); VS1053_XDCS_DISABLE; } //****************************************************************************** //****************************************************************************** //****************************************************************************** // Section: Internal Functions //****************************************************************************** //****************************************************************************** //****************************************************************************** unsigned short VS1053Codec::readreg(unsigned char vAddress) { unsigned short wValue; unsigned int i; VS1053_CS_ENABLE; for ( i=0; i<cVS1053_CS_DELAY;i++); _spi.write(0x03); // Read _spi.write(vAddress); // Register address ((unsigned char*)&wValue)[1] = _spi.write(0xFF); // 8 bit value high byte ((unsigned char*)&wValue)[0] = _spi.write(0xFF); // 8 bit value low byte for ( i=0; i<cVS1053_CS_DELAY;i++); VS1053_CS_DISABLE; return wValue; } void VS1053Codec::writereg(unsigned char vAddress, unsigned int wValue) { int i; while(!_dreq); VS1053_CS_ENABLE; for ( i=0; i<cVS1053_CS_DELAY;i++); _spi.write(0x02); // Write _spi.write(vAddress); // Register address _spi.write(((unsigned char*)&wValue)[1]); // 8 bit value to write high byte _spi.write(((unsigned char*)&wValue)[0]); // 8 bit value to write low byte for ( i=0; i<cVS1053_CS_DELAY;i++); VS1053_CS_DISABLE; } /* */ void VS1053Codec::reset( void) { // Reset the vs1053 writereg(VLSI_MODE, ( cMODEREG_SET | SM_RESET) ); wait_ms( 5); // Wait... while(!_dreq); // Configure CLOCK register. writereg(VLSI_CLOCKF, cCLOCK_SET); wait_ms( 5); // Wait... while(!_dreq); } void VS1053Codec::writeregnowait(unsigned char vAddress, unsigned int wValue) /* Write to SCI interface without waiting for the DREQ pin */ { int i; VS1053_CS_ENABLE; for ( i=0; i<cVS1053_CS_DELAY;i++); _spi.write(0x02); // Write _spi.write(vAddress); // Register address _spi.write(((unsigned char*)&wValue)[1]); // 8 bit value to write high byte _spi.write(((unsigned char*)&wValue)[0]); // 8 bit value to write low byte for ( i=0; i<cVS1053_CS_DELAY;i++); VS1053_CS_DISABLE; } #ifdef __ENCODE_OGG void VS1053Codec::VoggEncoding_Start( void) /* VorbisEncoder160c pag. 9 */ { unsigned short temp; /* 2. vs1053b at 55.3MHz */ writeregnowait( VLSI_CLOCKF, 0xC000); cWAIT_DREQ_1MS; /* 3. SCI_BASS to 0 */ writeregnowait( VLSI_BASS, 0x0000); cWAIT_DREQ_1MS; /* 4. Disable user application (no wait fot DREQ pin and no delay, as from the VLSI source file example */ /* record.c from playercode1053-Aug2009.zip */ writeregnowait( VLSI_AIADDR, 0x0000); /* 5. Disable all IRQ except the SCI IRQ */ writeregnowait( VLSI_WRAMADDR, 0xC01A); writeregnowait( VLSI_WRAM, 0x0002); /* 6. Load the plugin code... */ loadoggpatch(); /* 7. Set bit SM_ADPCM to 1. */ temp = (unsigned short)readreg( VLSI_MODE); cWAIT_DREQ_1MS; temp |= SM_ADPCM; writeregnowait( VLSI_MODE, temp); cWAIT_DREQ_1MS; writeregnowait( VLSI_AICTRL0, 0x0000); cWAIT_DREQ_1MS; /* 8. Set recording level. Value are for conservative AGC. */ writeregnowait( VLSI_AICTRL1, 0x0000); cWAIT_DREQ_1MS; writeregnowait( VLSI_AICTRL2, 4096); cWAIT_DREQ_1MS; /* 10. Set 0 to SCI_AICTRL3. */ writeregnowait( VLSI_AICTRL3, 0x0000); cWAIT_DREQ_1MS; /* 11. Active the encoder... */ wait_ms( 5); writeregnowait( VLSI_AIADDR, 0x0034); wait_ms( 1); /* 12. wait DREQ high. */ cWAIT_DREQ; /* Loop forever. */ } #if 0 void VS1053Codec::VoggEncoding_ReadStatus( RUNTIME_VOGG_VALUE *status) /* VorbisEncoder160c par 2.3.4, pag. 11 */ /* see VLSICodec.h for RUNTIME_VOGG_VALUE struct data. */ { unsigned short temp, temph; /* Recording Time */ writereg( VLSI_WRAMADDR, 0x0008); cWAIT_DREQ_1MS; temp = (unsigned short)readreg( VLSI_WRAM); /* LSB recording Time */ temph= (unsigned short)readreg( VLSI_WRAM); /* MSB recording Time */ status->rec_time = ((unsigned long)temph << 16) | temp; /* Average bitrate */ writereg( VLSI_WRAMADDR, 0x000C); cWAIT_DREQ_1MS; temp = (unsigned short)readreg( VLSI_WRAM); /* LSB Average bitrate */ temph= (unsigned short)readreg( VLSI_WRAM); /* MSB Average bitrate */ status->average_bitrate = ((unsigned long)temph << 16) | temp; /* Sample Counter */ writereg( VLSI_WRAMADDR, 0x1800); cWAIT_DREQ_1MS; temp = (unsigned short)readreg( VLSI_WRAM); /* LSB Sample Counter */ temph= (unsigned short)readreg( VLSI_WRAM); /* MSB Average bitrate */ status->sample_counter = ((unsigned long)temph << 16) | temp; } #endif /* * Questa funzione legge il registro SCI_HDAT1 per verificare la presenza di * sample disponibili. In caso ci siano li legge dal reg. SCI_HDAT0 * salvandoli nel puntatore passatogli. Ritorna il numero di byte copiati. */ unsigned int VS1053Codec::VoggEncoding_ReadBuffer( unsigned char *voggSample) /* VorbisEncoder160c pag. 10 */ { unsigned int i; unsigned short temp, sample; temp=0; i=0; sample=0; // printf("readbuffer\r\n"); do { /* reading the sample counter... */ temp = (unsigned short)readreg( VLSI_HDAT1); if ( temp ) { // printf("sample %d\r\n", temp); /* there are samples... */ i=0; while( i < temp) { /* read the sample as word */ sample=(unsigned short)readreg( VLSI_HDAT0); /* save it into the array as single byte. */ *voggSample++ = (unsigned char)(sample >> 8); /* MSB */ *voggSample++ = (unsigned char)(sample & 0x00FF); /* LSB */ i++; wait_ms( 1); } /* Exit the loop. */ break; } else { // printf("no sample\r\n"); /* Exit the loop. */ break; } } while( 1); /* just relax... */ wait_ms( 1); /* Return the number of byte saved. */ return i*2; } /* * Questa funzione esegue la procedura di fine encoding memorizzando i rimanenti sample * nel puntatore passatogli. Ritorna il numero di sample letti. */ unsigned int VS1053Codec::VoggEncoding_Stop( unsigned char *voggSample) /* VorbisEncoder160c pag. 11 */ { unsigned int res, i, ii; unsigned short temp, sample; res=0; temp=0; sample=0; /* 1. set bit 0 to 1 */ temp = (unsigned short)readreg( VLSI_AICTRL3); temp |= 0x0001; writereg( VLSI_AICTRL3, temp); /* 2. continue reading data but checking the bit 1 of VLSI_AICTRL3 reg. */ res=0; do { /* reading the sample counter... */ temp = (unsigned short)readreg( VLSI_HDAT1); if ( temp) { // printf("EncStop: %d\r\n", temp); i=0; while( i < temp ) { /* */ sample = (unsigned short)readreg( VLSI_HDAT0); *voggSample++ = (unsigned char)(sample >> 8); *voggSample++ = (unsigned char)sample & 0x00FF; i++; wait_ms( 1); } res+=i; } /* just relax... */ wait_ms( 1); temp = (unsigned short)readreg( VLSI_AICTRL3); /* verify the bit 1 of VLSI_AICTRL3 reg. */ if ( temp & 0x0002) { // printf("Encoding STOP\r\n"); /* encoding has finished. */ break; } } while( 1); /* Loop forever... */ /* 3. write the remaining word... */ ii=0; do { temp = (unsigned short)readreg( VLSI_HDAT1); if ( temp) { // printf("Flush %d\r\n", temp); /* there are samples... */ i=0; while( i < temp ) { /* con il numero precedente. */ /* */ sample = (unsigned short)readreg( VLSI_HDAT0); *voggSample++ = (unsigned char)(sample >> 8); *voggSample++ = (unsigned char)sample & 0x00FF; i++; wait_ms( 1); } res+=i; } else { wait_ms( 1); ii++; } } while( ii<5); /* Loops ... */ /* 4. read twise AICTRL3 */ temp = (unsigned short)readreg( VLSI_AICTRL3); temp = (unsigned short)readreg( VLSI_AICTRL3); // printf("last read %d\r\n", temp); if ( temp & 0x0004 ) { /* remove last byte ( bits 7:0) from the last word. */ voggSample--; *voggSample=0; res=(res*2)-1; } else { res=res*2; } /* 5. Reset the codec. */ /* Ritorno il numero di sample, in byte, letti. */ return res; } #endif