Rev 251 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed
/*VLSI Solution generic microcontroller example player / recorder forVS1053.v1.03 2012-12-11 HH Recording command 'p' was VS1063 only -> removedAdded chip type recognitionv1.02 2012-12-04 HH Command '_' incorrectly printed VS1063-specific fieldsv1.01 2012-11-28 HH Untabifiedv1.00 2012-11-27 HH First release*/#include "player.h"#include "hal_SPI.h"#include "hal_hardware_board.h"#include "pff.h"/* Download the latest VS1053a Patches package and itsvs1053b-patches-flac.plg. If you want to use the smaller patch setwhich doesn't contain the FLAC decoder, use vs1053b-patches.plg instead.The patches package is available athttp://www.vlsi.fi/en/support/software/vs10xxpatches.html *///#include "vs1053b-patches.plg"// Extracted plugin contents:#define PLUGIN_SIZE 40const unsigned short plugin[40] = { /* Compressed plugin */0x0007, 0x0001, 0x8010, 0x0006, 0x001c, 0x3e12, 0xb817, 0x3e14, /* 0 */0xf812, 0x3e01, 0xb811, 0x0007, 0x9717, 0x0020, 0xffd2, 0x0030, /* 8 */0x11d1, 0x3111, 0x8024, 0x3704, 0xc024, 0x3b81, 0x8024, 0x3101, /* 10 */0x8024, 0x3b81, 0x8024, 0x3f04, 0xc024, 0x2808, 0x4800, 0x36f1, /* 18 */0x9811, 0x0007, 0x0001, 0x8028, 0x0006, 0x0002, 0x2a00, 0x040e,};/* We also want to have the VS1053b Ogg Vorbis Encoder plugin. To get morethan one plugin included, we'll have to include it in a slightly moretricky way. To get the plugin included below, download the latest versionof the VS1053 Ogg Vorbis Encoder Application fromhttp://www.vlsi.fi/en/support/software/vs10xxapplications.html */// #define SKIP_PLUGIN_VARNAME// const unsigned int encoderPlugin[] = {// #include "venc44k2q05.plg"// };// #undef SKIP_PLUGIN_VARNAME/* VS1053b IMA ADPCM Encoder Fix, available athttp://www.vlsi.fi/en/support/software/vs10xxpatches.html */// #define SKIP_PLUGIN_VARNAME// const unsigned int imaFix[] = {// #include "imafix.plg"// };// #undef SKIP_PLUGIN_VARNAME#define FILE_BUFFER_SIZE 224 // Minimum 32!#define SDI_MAX_TRANSFER_SIZE 32#define SDI_END_FILL_BYTES_FLAC 12288#define SDI_END_FILL_BYTES 2052//#define REC_BUFFER_SIZE 224#define REPORT_INTERVAL 4096#define REPORT_INTERVAL_MIDI 512#define min(a,b) (((a)<(b))?(a):(b))enum AudioFormat {afUnknown,afRiff,afOggVorbis,afMp1,afMp2,afMp3,afAacMp4,afAacAdts,afAacAdif,afFlac,afWma,afMidi} audioFormat = afUnknown;const char *afName[] = {"unknown","RIFF","Ogg","MP1","MP2","MP3","AAC MP4","AAC ADTS","AAC ADIF","FLAC","WMA","MIDI",};/** SCI Write*/void WriteSci(unsigned char addr, unsigned int data) {unsigned char c;do { // Wait until DREQ is highc = VS10XX_DREQ_STAT();} while (c == 0);VS10XX_CS_LOW();spiSendByte(0x02); // Write command codespiSendByte(addr); // Write SCI registerspiSendByte((unsigned char)(data >> 8));spiSendByte((unsigned char)(data & 0xFF));VS10XX_CS_HIGH();}/** SCI Read*/unsigned int ReadSci(unsigned char addr) {unsigned char buffer[2];int ret;unsigned char c;do { // Wait until DREQ is highc = VS10XX_DREQ_STAT();} while (c == 0);VS10XX_CS_LOW();spiSendByte(0x03); // Write command codespiSendByte(addr); // Write SCI registerspiReadFrame(buffer, 2);ret = buffer[0] << 8;ret |= buffer[1];VS10XX_CS_HIGH();return ret;}/** SDI Write*/int WriteSdi(unsigned char *data, unsigned char bytes) {if (bytes > SDI_MAX_TRANSFER_SIZE)return -1;unsigned char c;do { // Wait until DREQ is highc = VS10XX_DREQ_STAT();} while (c == 0);VS10XX_DS_LOW();spiSendFrame(data, (unsigned int)bytes);VS10XX_DS_HIGH();return 0;}/*Read 32-bit increasing counter value from addr.Because the 32-bit value can change while reading it,read MSB's twice and decide which is the correct one.*/unsigned long ReadVS10xxMem32Counter(unsigned int addr) {unsigned int msbV1, lsb, msbV2;unsigned long res;WriteSci(SCI_WRAMADDR, addr+1);msbV1 = ReadSci(SCI_WRAM);WriteSci(SCI_WRAMADDR, addr);lsb = ReadSci(SCI_WRAM);msbV2 = ReadSci(SCI_WRAM);if (lsb < 0x8000U) {msbV1 = msbV2;}res = ((unsigned long)msbV1 << 16) | lsb;return res;}/*Read 32-bit non-changing value from addr.*/unsigned long ReadVS10xxMem32(unsigned int addr) {unsigned int lsb;WriteSci(SCI_WRAMADDR, addr);lsb = ReadSci(SCI_WRAM);return lsb | ((unsigned long)ReadSci(SCI_WRAM) << 16);}/*Read 16-bit value from addr.*/unsigned int ReadVS10xxMem(unsigned int addr) {WriteSci(SCI_WRAMADDR, addr);return ReadSci(SCI_WRAM);}/*Write 16-bit value to given VS10xx address*/void WriteVS10xxMem(unsigned int addr, unsigned int data) {WriteSci(SCI_WRAMADDR, addr);WriteSci(SCI_WRAM, data);}/*Write 32-bit value to given VS10xx address*/void WriteVS10xxMem32(unsigned int addr, unsigned long data) {WriteSci(SCI_WRAMADDR, addr);WriteSci(SCI_WRAM, (unsigned int)data);WriteSci(SCI_WRAM, (unsigned int)(data>>16));}/* Note: code SS_VER=2 is used for both VS1002 and VS1011e */const unsigned int chipNumber[16] = {1001, 1011, 1011, 1003, 1053, 1033, 1063, 1103,0, 0, 0, 0, 0, 0, 0, 0};void Set32(unsigned char *d, unsigned long n) {unsigned int i;for (i=0; i<4; i++) {*d++ = (unsigned char)n;n >>= 8;}}void Set16(unsigned char *d, unsigned int n) {unsigned int i;for (i=0; i<2; i++) {*d++ = (unsigned char)n;n >>= 8;}}/*Loads a plugin.This is a slight modification of the LoadUserCode() exampleprovided in many of VLSI Solution's program packages.*/void LoadPlugin(const unsigned short *d, unsigned short len) {unsigned int i = 0;while (i<len) {unsigned short addr, n, val;addr = d[i++];n = d[i++];if (n & 0x8000U) { /* RLE run, replicate n samples */n &= 0x7FFF;val = d[i++];while (n--) {WriteSci(addr, val);}} else { /* Copy run, copy n samples */while (n--) {val = d[i++];WriteSci(addr, val);}}}}enum PlayerStates {psPlayback = 0,psStopped} playerState;/** Executes a software reset*/void VS1053SoftwareReset(void) {unsigned char c;unsigned short oldMode = ReadSci(SCI_MODE);WriteSci(SCI_MODE, oldMode | SM_RESET);__delay_cycles(100);do { // Wait until DREQ is highc = VS10XX_DREQ_STAT();} while (c == 0);}/*This function plays back an audio file.File playback:1. Send an audio file to VS1053b2. Read extra parameter value endFillByte3. Send at least 2052 bytes of endFillByte4. Set SCI_MODE bit SM_CANCEL5. Send at least 32 bytes of endFillByte6. Read SCI_MODE. If SM_CANCEL is set, goto step 5If still set after 2048 bytes, do a software resetCancelling playback:1. Send a portion of an audio file to VS1053b2. Set SCI_MODE bit SM_CANCEL3. Continue to send audio file, check SM_CANCEL every 32 bytesIf set, goto step 3If still set after 2048 bytes, do a software reset4. Read extra parameter value endFillByte5. Send 2052 bytes of endFillByte*/void VS1053PlayFile(const char *fileName) {unsigned char playBuf[FILE_BUFFER_SIZE];unsigned short bytesRead; // Number of bytes read by pf_read()unsigned short bytesInBuffer; // How many valid bytes in buffer leftunsigned short pos = 0; // File positionint endFillByte = 0; // What byte value to send after fileint playMode = ReadVS10xxMem(PAR_PLAY_MODE);long nextReportPos = 0; // File pointer where to next collect/reportFRESULT res;unsigned int i;playerState = psPlayback; // Set state to normal playbackWriteSci(SCI_DECODE_TIME, 0); // Reset DECODE_TIMEres = pf_open(fileName);if (res != FR_OK)return;/* Main playback loop */// Read the entire file FILE_BUFFER_SIZE (32) bytes at a timedo {unsigned char *bufP = playBuf;// Read FILE_BUFFER_SIZE (32) bytes from the file into the bufferres = pf_read(playBuf, FILE_BUFFER_SIZE, &bytesRead);if (res != FR_OK)break;bytesInBuffer = bytesRead;while (bytesInBuffer && playerState != psStopped) {if (!(playMode & PAR_PLAY_MODE_PAUSE_ENA)) {int bytesToWrite = min(SDI_MAX_TRANSFER_SIZE, bytesInBuffer);// This is the heart of the algorithm: on the following line// actual audio data gets sent to VS10xx.WriteSdi(bufP, bytesToWrite);bufP += bytesToWrite;bytesInBuffer -= bytesToWrite;pos += bytesToWrite;}/* If playback is going on as normal, see if we need to collect andpossibly report */if (playerState == psPlayback && pos >= nextReportPos) {nextReportPos += (audioFormat == afMidi || audioFormat == afUnknown) ?REPORT_INTERVAL_MIDI : REPORT_INTERVAL;/* It is important to collect endFillByte while still in normalplayback. If we need to later cancel playback or run into anytrouble with e.g. a broken file, we need to be able to repeatedlysend this byte until the decoder has been able to exit. */endFillByte = ReadVS10xxMem(PAR_END_FILL_BYTE);}}} while(bytesRead == FILE_BUFFER_SIZE && playerState != psStopped);/* Earlier we collected endFillByte. Now, just in case the file wasbroken, or if a cancel playback command has been given, writeSDI_END_FILL_BYTES (2052) bytes of endFillByte. */for (i = 0; i < FILE_BUFFER_SIZE; i++) {playBuf[i] = endFillByte;}for (i = 0; i < SDI_END_FILL_BYTES; i += SDI_MAX_TRANSFER_SIZE) {WriteSdi(playBuf, SDI_MAX_TRANSFER_SIZE);}/* If the file actually ended, and playback cancellation was notdone earlier, do it now. */if (playerState == psPlayback) {pos = SDI_MAX_TRANSFER_SIZE;unsigned short oldMode = ReadSci(SCI_MODE);WriteSci(SCI_MODE, oldMode | SM_CANCEL);// Write 32 bytes of endFillByteWriteSdi(playBuf, SDI_MAX_TRANSFER_SIZE);i = ReadSci(SCI_MODE);while (i & SM_CANCEL) {// Reset after sending 2048 bytesif (pos > 2048) {VS1053SoftwareReset();break;}WriteSdi(playBuf, SDI_MAX_TRANSFER_SIZE);pos += SDI_MAX_TRANSFER_SIZE;i = ReadSci(SCI_MODE);}}}/** Main initialization function*/int VS1053Init(void) {if (VSInitHardware() != 0)return 1;if (VSInitSoftware() != 0)return 1;return 0;}/*Hardware Initialization for VS1053.*/int VSInitHardware(void) {/* Write here your microcontroller code which puts VS10xx in hardwarereset and back (set xRESET to 0 for at least a few clock cycles,then to 1). */// // Chip Command Select (idle high)// VS10XX_CS_PxOUT |= VS10XX_CS;// VS10XX_CS_PxDIR |= VS10XX_CS;//// // Chip Data Select (idle high)// VS10XX_DC_PxOUT |= VS10XX_DC;// VS10XX_DC_PxDIR |= VS10XX_DC;//// // Reset (idle high)// VS10XX_RESET_PxOUT |= VS10XX_RESET;// VS10XX_RESET_PxDIR |= VS10XX_RESET;//// // DREQ Line// VS10XX_DREQ_PxDIR &= ~VS10XX_DREQ;CHIP_RESET_LOW();__delay_cycles(10000);CHIP_RESET_HIGH();unsigned char c;do { // Wait until DREQ is highc = VS10XX_DREQ_STAT();} while (c == 0);return 0;}/*Software Initialization for VS1053.Note that you need to check whether SM_SDISHARE should be set inyour application or not.*/int VSInitSoftware(void) {unsigned int ssVer;unsigned char c;do { // Wait until DREQ is highc = VS10XX_DREQ_STAT();} while (c == 0);/* Initial SPI bus speed needs to be < 1.5Mhz */halSPISetSpeedLow();/* Start initialization with a dummy read, which makes sure ourmicrocontroller chips selects and everything are where theyare supposed to be and that VS10xx's SCI bus is in a known state. */ReadSci(SCI_MODE);/* First real operation is a software reset. After the softwarereset we know what the status of the IC is. You need, dependingon your application, either set or not set SM_SDISHARE. See thedatasheet for details. */// WriteSci(SCI_MODE, SM_SDINEW|SM_SDISHARE|SM_TESTS|SM_RESET);WriteSci(SCI_MODE, SM_SDINEW|SM_RESET);/* A quick sanity check: write to two registers, then test if weget the same results. Note that if you use a too high SPIspeed, the MSB is the most likely to fail when read again. */WriteSci(SCI_HDAT0, 0xABAD);WriteSci(SCI_HDAT1, 0x1DEA);if (ReadSci(SCI_HDAT0) != 0xABAD || ReadSci(SCI_HDAT1) != 0x1DEA) {// There is something wrong with VS10xxreturn 1;}/* Set the clock. Until this point we need to run SPI slow so thatwe do not exceed the maximum speeds mentioned inChapter SPI Timing Diagram in the datasheet. */WriteSci(SCI_CLOCKF, HZ_TO_SC_FREQ(12288000) | SC_MULT_53_35X | SC_ADD_53_10X);// WriteSci(SCI_CLOCKF, 0x6000);__delay_cycles(10000);/* Now when we have upped the VS10xx clock speed, the microcontrollerSPI bus can run faster. Do that before you start playing orrecording files. */halSPISetSpeedHigh();/* A quick sanity check: write to two registers, then test if weget the same results. Note that if you use a too high SPIspeed, the MSB is the most likely to fail when read again. */WriteSci(SCI_HDAT0, 0xABAD);WriteSci(SCI_HDAT1, 0x1DEA);if (ReadSci(SCI_HDAT0) != 0xABAD || ReadSci(SCI_HDAT1) != 0x1DEA) {// There is something wrong with VS10xxreturn 1;}/* Check VS10xx type */ssVer = ((ReadSci(SCI_STATUS) >> 4) & 15);if (chipNumber[ssVer]) {// Chip is VS%d\n", chipNumber[ssVer]);if (chipNumber[ssVer] != 1053) {// Incorrect chipreturn 1;}} else {// Unknown VS10xx SCI_MODE fieldreturn 1;}/* Set up other parameters. */// WriteVS10xxMem(PAR_CONFIG1, PAR_CONFIG1_AAC_SBR_SELECTIVE_UPSAMPLE);/* Set volume level to max */WriteSci(SCI_VOL, 0x0000);/* Now it's time to load the proper patch set. */LoadPlugin(plugin, sizeof(plugin)/sizeof(plugin[0]));/* We're ready to go. */return 0;}