Mikromedia PIC24FJ256GB110 VS1053

Writing software for systems that use VLSI Solution's devices as slave codecs to a host microcontroller.
btommo
User
Posts: 14
Joined: Tue 2019-05-21 11:41

Mikromedia PIC24FJ256GB110 VS1053

Post by btommo »

Good morning,

Quite some time ago I attempted to set up code for communication between another PIC24 and VS1053 but was unable to give it the proper focus it required but I am not able to. The Mikromedia PCB has a PIC24FJ256GB110 which shares an SPI port between the VS1053 and an SD card. I am able to read and write to the SD card using FATFS and am now looking at setting us communication between the VS1053 and the PIC using the "VS1053an_playrec" files as well as the notes on setting up the SPI communication and it's functions.

When compiling the code it builds successfully but I am getting the below on terminal making me unsure whether the SPI is set up correctly
There is something wrong with VS10xx SCI registers

Failed initializing VS10xx, exiting
Below Is my main, player1053.c and SPI functions.

Code: Select all

/**
  Generated main.c file from MPLAB Code Configurator

  @Company
    Microchip Technology Inc.

  @File Name
    main.c

  @Summary
    This is the generated main.c using PIC24 / dsPIC33 / PIC32MM MCUs.

  @Description
    This source file provides main entry point for system initialization and application code development.
    Generation Information :
        Product Revision  :  PIC24 / dsPIC33 / PIC32MM MCUs - 1.170.0
        Device            :  PIC24FJ256GB110
    The generated drivers are tested against the following:
        Compiler          :  XC16 v1.61
        MPLAB 	          :  MPLAB X v5.45
 * 
 * code to copy text from one file to another
*/



/**
  Section: Included Files
*/
#include "mcc_generated_files/system.h"
#include "mcc_generated_files/fatfs/fatfs_demo.h"
#include "mcc_generated_files/mcc.h"
#include "mcc_generated_files/fatfs/ff.h"
#include "mcc_generated_files/sd_spi/sd_spi.h"
#include "mcc_generated_files/drivers/spi_master.h"
#include "player.h"
#include <stdlib.h>

#define FCY 32000000UL
#include <libpic30.h>



/*uint8_t*/ TCHAR BYTE_BUFF1[512] = "\0";
static FATFS drive;
static FIL file;
UINT actualLength;
//uint32_t offset = 0;
//uint32_t OLDoffset = 0;

void send_string(const uint8_t *x)
{
    while(*x)
    {
        UART1_Write(*x++);
    }
}

void CustTMR1Int(void)
{
    Tog_Toggle();
    //STAT_Toggle();
}
/*
                         Main application
 */
int main(void)
{
    // initialize the device
    SYSTEM_Initialize();
    INTERRUPT_GlobalEnable();
    TMR1_SetInterruptHandler(CustTMR1Int);
    //spi_master_open(SDSLOW);
    spi_master_open(VS1053SLOW);

    while (1)
    {
        // Add your application code
        //FatFsDemo_Tasks();
        STAT_SetLow();
        __delay_ms(1000);
        printf("Hello!!\r\n");
        send_string("Hello!!\r\n");
        if (VSTestInitHardware() || VSTestInitSoftware()) 
        {
            printf("Failed initializing VS10xx, exiting\r\n");
            exit(EXIT_FAILURE);
        }
        else{
            printf("initialized VS10xx, exiting\r\n");
        }
/* ... */
/* Playback example. You can call these functions many times in a row
because they leave VS1053 in a known state. */
    //VSTestHandleFile("001.mp3", 0);
        /*if (f_mount(&drive,"0:",1) == FR_OK)
        {
            if (f_open(&file, "001.mp3", FA_READ | FA_OPEN_ALWAYS ) == FR_OK)
            {
                f_lseek(&file, offset);
                //spi_master_open(SDFAST);
                f_read(&file, BYTE_BUFF1, sizeof(BYTE_BUFF1)-1, &actualLength);
                //spi_master_open(SDSLOW);
                offset = f_tell(&file);
                //f_gets(BYTE_BUFF1, sizeof(BYTE_BUFF1)-1, &file);
                f_close(&file);
            }
            if(offset != OLDoffset)
            {
                if (f_open(&file, "0011.mp3", FA_OPEN_APPEND | FA_WRITE ) == FR_OK)
                {
                   // spi_master_open(SDFAST);
                    f_write(&file, BYTE_BUFF1, sizeof(BYTE_BUFF1)-1, &actualLength);
                    //spi_master_open(SDSLOW);
                    //f_printf(&file,"%s", BYTE_BUFF1);
                    f_close(&file);
                }
            }
            while(OLDoffset == offset)
            {
                STAT_SetLow();
            }
            
            OLDoffset = offset;
        f_mount(0,"0:",0);
    
        }*/
        while(1)
        {
            ;
            //Tog_SetLow();
            STAT_SetHigh();
        }
    }

    return 1;
}
/**
 End of File
*/

Code: Select all

/*

  VLSI Solution generic microcontroller example player / recorder for
  VS1053.

  v1.10 2016-05-09 HH  Modified quick sanity check registers
  v1.03 2012-12-11 HH  Recording command 'p' was VS1063 only -> removed
                       Added chip type recognition
  v1.02 2012-12-04 HH  Command '_' incorrectly printed VS1063-specific fields
  v1.01 2012-11-28 HH  Untabified
  v1.00 2012-11-27 HH  First release

*/

#include <stdio.h>
#include <xc.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "player.h"
#include "mcc_generated_files/pin_manager.h"
#include "mcc_generated_files/fatfs/fatfs_demo.h"
#include "mcc_generated_files/mcc.h"
#include "mcc_generated_files/fatfs/ff.h"
#include "mcc_generated_files/sd_spi/sd_spi.h"
#include "mcc_generated_files/drivers/spi_master.h"
/* Download the latest VS1053a Patches package and its
   vs1053b-patches-flac.plg. If you want to use the smaller patch set
   which doesn't contain the FLAC decoder, use vs1053b-patches.plg instead.
   The patches package is available at
   http://www.vlsi.fi/en/support/software/vs10xxpatches.html */
#include "vs1053b-patches-flac.plg"
//#include "vs1053b-patches.plg"

/* We also want to have the VS1053b Ogg Vorbis Encoder plugin. To get more
   than one plugin included, we'll have to include it in a slightly more
   tricky way. To get the plugin included below, download the latest version
   of the VS1053 Ogg Vorbis Encoder Application from
   http://www.vlsi.fi/en/support/software/vs10xxapplications.html */
#define SKIP_PLUGIN_VARNAME
const u_int16 encoderPlugin[] = {
#include "venc44k2q05.plg"
};
#undef SKIP_PLUGIN_VARNAME


/* VS1053b IMA ADPCM Encoder Fix, available at
   http://www.vlsi.fi/en/support/software/vs10xxpatches.html */
#define SKIP_PLUGIN_VARNAME
const u_int16 imaFix[] = {
#include "imafix.plg"
};
#undef SKIP_PLUGIN_VARNAME


#define FILE_BUFFER_SIZE 512
#define SDI_MAX_TRANSFER_SIZE 32
#define SDI_END_FILL_BYTES_FLAC 12288
#define SDI_END_FILL_BYTES       2050
#define REC_BUFFER_SIZE 512


/* How many transferred bytes between collecting data.
   A value between 1-8 KiB is typically a good value.
   If REPORT_ON_SCREEN is defined, a report is given on screen each time
   data is collected. */
#define REPORT_INTERVAL 4096
#define REPORT_INTERVAL_MIDI 512
#if 1
#define REPORT_ON_SCREEN
#endif

/* Define PLAYER_USER_INTERFACE if you want to have a user interface in your
   player. */
#if 1
#define PLAYER_USER_INTERFACE
#endif

/* Define RECORDER_USER_INTERFACE if you want to have a user interface in your
   player. */
#if 1
#define RECORDER_USER_INTERFACE
#endif


#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",
};


/*
  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.
*/
u_int32 ReadVS10xxMem32Counter(u_int16 addr) {
  u_int16 msbV1, lsb, msbV2;
  u_int32 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 = ((u_int32)msbV1 << 16) | lsb;
  
  return res;
}


/*
  Read 32-bit non-changing value from addr.
*/
u_int32 ReadVS10xxMem32(u_int16 addr) {
  u_int16 lsb;
  WriteSci(SCI_WRAMADDR, addr);
  lsb = ReadSci(SCI_WRAM);
  return lsb | ((u_int32)ReadSci(SCI_WRAM) << 16);
}


/*
  Read 16-bit value from addr.
*/
u_int16 ReadVS10xxMem(u_int16 addr) {
  WriteSci(SCI_WRAMADDR, addr);
  return ReadSci(SCI_WRAM);
}


/*
  Write 16-bit value to given VS10xx address
*/
void WriteVS10xxMem(u_int16 addr, u_int16 data) {
  WriteSci(SCI_WRAMADDR, addr);
  WriteSci(SCI_WRAM, data);
}

/*
  Write 32-bit value to given VS10xx address
*/
void WriteVS10xxMem32(u_int16 addr, u_int32 data) {
  WriteSci(SCI_WRAMADDR, addr);
  WriteSci(SCI_WRAM, (u_int16)data);
  WriteSci(SCI_WRAM, (u_int16)(data>>16));
}




static const u_int16 linToDBTab[5] = {36781, 41285, 46341, 52016, 58386};

/*
  Converts a linear 16-bit value between 0..65535 to decibels.
    Reference level: 32768 = 96dB (largest VS1053b number is 32767 = 95dB).
  Bugs:
    - For the input of 0, 0 dB is returned, because Minus infinity cannot
      be represented with integers.
    - Assumes a ratio of 2 is 6 dB, when it actually is approx. 6.02 dB.
*/
static u_int16 LinToDB(unsigned short n) {
  int res = 96, i;

  if (!n)               /* No signal should return Minus infinity */
    return 0;

  while (n < 32768U) {  /* Amplify weak signals */
    res -= 6;
    n <<= 1;
  }

  for (i=0; i<5; i++)   /* Find exact scale */
    if (n >= linToDBTab[i])
      res++;

  return res;
}




/*

  Loads a plugin.

  This is a slight modification of the LoadUserCode() example
  provided in many of VLSI Solution's program packages.

*/
void LoadPlugin(const u_int16 *d, u_int16 len) {
  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,
  psUserRequestedCancel,
  psCancelSentToVS10xx,
  psStopped
} playerState;





/*

  This function plays back an audio file.

  It also contains a simple user interface, which requires the following
  funtions that you must provide:
  void SaveUIState(void);
  - saves the user interface state and sets the system up
  - may in many cases be implemented as an empty function
  void RestoreUIState(void);
  - Restores user interface state before exit
  - may in many cases be implemented as an empty function
  int GetUICommand(void);
  - Returns -1 for no operation
  - Returns -2 for cancel playback command
  - Returns any other for user input. For supported commands, see code.

*/
void SaveUIState(void)
{
    ;
}

void RestoreUIState(void)
{
    ;
}

int GetUICommand (void)
{
    ;
}

void VS1053PlayFile(FILE *readFp) {
  static u_int8 playBuf[FILE_BUFFER_SIZE];
  u_int32 bytesInBuffer;        // How many bytes in buffer left
  u_int32 pos=0;                // File position
  int endFillByte = 0;          // What byte value to send after file
  int endFillBytes = SDI_END_FILL_BYTES; // How many of those to send
  int playMode = ReadVS10xxMem(PAR_PLAY_MODE);
  long nextReportPos=0; // File pointer where to next collect/report
  int i;
#ifdef PLAYER_USER_INTERFACE
  static int earSpeaker = 0;    // 0 = off, other values strength
  int volLevel = ReadSci(SCI_VOL) & 0xFF; // Assume both channels at same level
  int c;
  /*static*/ int /*int32_t*/ rateTune = 0;      // Samplerate fine tuning in ppm
#endif /* PLAYER_USER_INTERFACE */

#ifdef PLAYER_USER_INTERFACE
  SaveUIState();
#endif /* PLAYER_USER_INTERFACE */

  playerState = psPlayback;             // Set state to normal playback

  WriteSci(SCI_DECODE_TIME, 0);         // Reset DECODE_TIME


  /* Main playback loop */

  while ((bytesInBuffer = fread(playBuf, 1, FILE_BUFFER_SIZE, readFp)) > 0 &&
         playerState != psStopped) {
    u_int8 *bufP = playBuf;

    while (bytesInBuffer && playerState != psStopped) {

      if (!(playMode & PAR_PLAY_MODE_PAUSE_ENA)) {
        int t = 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, t);

        bufP += t;
        bytesInBuffer -= t;
        pos += t;
      }

      /* If the user has requested cancel, set VS10xx SM_CANCEL bit */
      if (playerState == psUserRequestedCancel) {
        unsigned short oldMode;
        playerState = psCancelSentToVS10xx;
        printf("\r\nSetting SM_CANCEL at file offset %ld\r\n", pos);
        oldMode = ReadSci(SCI_MODE);
        WriteSci(SCI_MODE, oldMode | SM_CANCEL);
      }

      /* If VS10xx SM_CANCEL bit has been set, see if it has gone
         through. If it is, it is time to stop playback. */
      if (playerState == psCancelSentToVS10xx) {
        unsigned short mode = ReadSci(SCI_MODE);
        if (!(mode & SM_CANCEL)) {
          printf("SM_CANCEL has cleared at file offset %ld\r\n", pos);
          playerState = psStopped;
        }
      }


      /* If playback is going on as normal, see if we need to collect and
         possibly report */
      if (playerState == psPlayback && pos >= nextReportPos) {
#ifdef REPORT_ON_SCREEN
        u_int16 sampleRate;
        u_int32 byteRate;
        u_int16 h1 = ReadSci(SCI_HDAT1);
#endif

        nextReportPos += (audioFormat == afMidi || audioFormat == afUnknown) ?
          REPORT_INTERVAL_MIDI : REPORT_INTERVAL;
        /* It is important to collect endFillByte while still in normal
           playback. If we need to later cancel playback or run into any
           trouble with e.g. a broken file, we need to be able to repeatedly
           send this byte until the decoder has been able to exit. */
        endFillByte = ReadVS10xxMem(PAR_END_FILL_BYTE);

#ifdef REPORT_ON_SCREEN
        if (h1 == 0x7665) {
          audioFormat = afRiff;
          endFillBytes = SDI_END_FILL_BYTES;
        } else if (h1 == 0x4154) {
          audioFormat = afAacAdts;
          endFillBytes = SDI_END_FILL_BYTES;
        } else if (h1 == 0x4144) {
          audioFormat = afAacAdif;
          endFillBytes = SDI_END_FILL_BYTES;
        } else if (h1 == 0x574d) {
          audioFormat = afWma;
          endFillBytes = SDI_END_FILL_BYTES;
        } else if (h1 == 0x4f67) {
          audioFormat = afOggVorbis;
          endFillBytes = SDI_END_FILL_BYTES;
        } else if (h1 == 0x664c) {
          audioFormat = afFlac;
          endFillBytes = SDI_END_FILL_BYTES_FLAC;
        } else if (h1 == 0x4d34) {
          audioFormat = afAacMp4;
          endFillBytes = SDI_END_FILL_BYTES;
        } else if (h1 == 0x4d54) {
          audioFormat = afMidi;
          endFillBytes = SDI_END_FILL_BYTES;
        } else if ((h1 & 0xffe6) == 0xffe2) {
          audioFormat = afMp3;
          endFillBytes = SDI_END_FILL_BYTES;
        } else if ((h1 & 0xffe6) == 0xffe4) {
          audioFormat = afMp2;
          endFillBytes = SDI_END_FILL_BYTES;
        } else if ((h1 & 0xffe6) == 0xffe6) {
          audioFormat = afMp1;
          endFillBytes = SDI_END_FILL_BYTES;
        } else {
          audioFormat = afUnknown;
          endFillBytes = SDI_END_FILL_BYTES_FLAC;
        }

        sampleRate = ReadSci(SCI_AUDATA);
        byteRate = ReadVS10xxMem(PAR_BYTERATE);
        /* FLAC:   byteRate = bitRate / 32
           Others: byteRate = bitRate /  8
           Here we compensate for that difference. */
        if (audioFormat == afFlac)
          byteRate *= 4;

        printf("\r%ldKiB "
               "%1ds %1.1f"
               "kb/s %dHz %s %s"
               " %04x   ",
               pos/1024,
               ReadSci(SCI_DECODE_TIME),
               byteRate * (8.0/1000.0),
               sampleRate & 0xFFFE, (sampleRate & 1) ? "stereo" : "mono",
               afName[audioFormat], h1
               );
          
        fflush(stdout);
#endif /* REPORT_ON_SCREEN */
      }
    } /* if (playerState == psPlayback && pos >= nextReportPos) */
  


    /* User interface. This can of course be completely removed and
       basic playback would still work. */

#ifdef PLAYER_USER_INTERFACE
    /* GetUICommand should return -1 for no command and -2 for CTRL-C */
    c = GetUICommand();
    switch (c) {

      /* Volume adjustment */
    case '-':
      if (volLevel < 255) {
        volLevel++;
        WriteSci(SCI_VOL, volLevel*0x101);
      }
      break;
    case '+':
      if (volLevel) {
        volLevel--;
        WriteSci(SCI_VOL, volLevel*0x101);
      }
      break;

      /* Show some interesting registers */
    case '_':
      {
        u_int32 mSec = ReadVS10xxMem32Counter(PAR_POSITION_MSEC);
        printf("\r\nvol %1.1fdB, MODE %04x, ST %04x, "
               "HDAT1 %04x HDAT0 %04x\r\n",
               -0.5*volLevel,
               ReadSci(SCI_MODE),
               ReadSci(SCI_STATUS),
               ReadSci(SCI_HDAT1),
               ReadSci(SCI_HDAT0));
        printf("  sampleCounter %lu, ",
               ReadVS10xxMem32Counter(0x1800));
        if (mSec != 0xFFFFFFFFU) {
          printf("positionMSec %lu, ", mSec);
        }
        printf("config1 0x%04x", ReadVS10xxMem(PAR_CONFIG1));
        printf("\r\n");
      }
      break;

      /* Adjust play speed between 1x - 4x */
    case '1':
    case '2':
    case '3':
    case '4':
      /* FF speed */
      printf("\r\nSet playspeed to %dX\r\n", c-'0');
      WriteVS10xxMem(PAR_PLAY_SPEED, c-'0');
      break;

      /* Ask player nicely to stop playing the song. */
    case 'q':
      if (playerState == psPlayback)
        playerState = psUserRequestedCancel;
      break;

      /* Forceful and ugly exit. For debug uses only. */
    case 'Q':
      RestoreUIState();
      printf("\r\n");
      exit(EXIT_SUCCESS);
      break;

      /* EarSpeaker spatial processing adjustment. */
    case 'e':
      earSpeaker = (earSpeaker+1) & 3;
      {
        u_int16 t = ReadSci(SCI_MODE) & ~(SM_EARSPEAKER_LO|SM_EARSPEAKER_HI);
        if (earSpeaker & 1)
          t |= SM_EARSPEAKER_LO;
        if (earSpeaker & 2)
          t |= SM_EARSPEAKER_HI;
        WriteSci(SCI_MODE, t);
      }
      printf("\r\nSet earspeaker to %d\r\n", earSpeaker);
      break;

      /* Toggle mono mode. Implemented in the VS1053b Patches package */
    case 'm':
      playMode ^= PAR_PLAY_MODE_MONO_ENA;
      printf("\r\nMono mode %s\r\n",
             (playMode & PAR_PLAY_MODE_MONO_ENA) ? "on" : "off");
      WriteVS10xxMem(PAR_PLAY_MODE, playMode);
      break;

      /* Toggle differential mode */
    case 'd':
      {
        u_int16 t = ReadSci(SCI_MODE) ^ SM_DIFF;
        printf("\r\nDifferential mode %s\r\n", (t & SM_DIFF) ? "on" : "off");
        WriteSci(SCI_MODE, t);
      }
      break;

      /* Adjust playback samplerate finetuning, this function comes from
         the VS1053b Patches package. Note that the scale is different
         in VS1053b and VS1063a! */
    case 'r':
      if (rateTune >= 0) {
        rateTune = (rateTune*0.95);
      } else {
        rateTune = (rateTune*1.05);
      }
      rateTune -= 1;
     if (rateTune < -160000)
        rateTune = -160000;
      WriteVS10xxMem(0x5b1c, 0);                 /* From VS105b Patches doc */
      WriteSci(SCI_AUDATA, ReadSci(SCI_AUDATA)); /* From VS105b Patches doc */
      WriteVS10xxMem32(PAR_RATE_TUNE, rateTune);
      printf("\r\nrateTune %d ppm*2\r\n", rateTune);
      break;
    case 'R':
      if (rateTune <= 0) {
        rateTune = (rateTune*0.95);
      } else {
        rateTune = (rateTune*1.05);
      }
      rateTune += 1;
      if (rateTune > 160000)
        rateTune = 160000;
      WriteVS10xxMem32(PAR_RATE_TUNE, rateTune);
      WriteVS10xxMem(0x5b1c, 0);                 /* From VS105b Patches doc */
      WriteSci(SCI_AUDATA, ReadSci(SCI_AUDATA)); /* From VS105b Patches doc */
      printf("\r\nrateTune %d ppm*2\r\n", rateTune);
      break;
    case '/':
      rateTune = 0;
      WriteVS10xxMem(SCI_WRAMADDR, 0x5b1c);      /* From VS105b Patches doc */
      WriteVS10xxMem(0x5b1c, 0);                 /* From VS105b Patches doc */
      WriteVS10xxMem32(PAR_RATE_TUNE, rateTune);
      printf("\r\nrateTune off\r\n");
      break;

      /* Show help */
    case '?':
      printf("\r\nInteractive VS1053 file player keys:\r\n"
             "1-4\tSet playback speed\r\n"
             "- +\tVolume down / up\r\n"
             "_\tShow current settings\r\n"
             "q Q\tQuit current song / program\r\n"
             "e\tSet earspeaker\r\n"
             "r R\tR rateTune down / up\r\n"
             "/\tRateTune off\r\n"
             "m\tToggle Mono\r\n"
             "d\tToggle Differential\r\n"
             );
      break;

      /* Unknown commands or no command at all */
    default:
      if (c < -1) {
        printf("Ctrl-C, aborting\r\n");
        fflush(stdout);
        RestoreUIState();
        exit(EXIT_FAILURE);
      }
      if (c >= 0) {
        printf("\r\nUnknown char '%c' (%d)\r\n", isprint(c) ? c : '.', c);
      }
      break;
    } /* switch (c) */
#endif /* PLAYER_USER_INTERFACE */
  } /* while ((bytesInBuffer = fread(...)) > 0 && playerState != psStopped) */


  
#ifdef PLAYER_USER_INTERFACE
  RestoreUIState();
#endif /* PLAYER_USER_INTERFACE */

  printf("\r\nSending %d footer %d's... ", endFillBytes, endFillByte);
  fflush(stdout);

  /* Earlier we collected endFillByte. Now, just in case the file was
     broken, or if a cancel playback command has been given, write
     lots of endFillBytes. */
  memset(playBuf, endFillByte, sizeof(playBuf));
  for (i=0; i<endFillBytes; i+=SDI_MAX_TRANSFER_SIZE) {
    WriteSdi(playBuf, SDI_MAX_TRANSFER_SIZE);
  }

  /* If the file actually ended, and playback cancellation was not
     done earlier, do it now. */
  if (playerState == psPlayback) {
    unsigned short oldMode = ReadSci(SCI_MODE);
    WriteSci(SCI_MODE, oldMode | SM_CANCEL);
    printf("ok. Setting SM_CANCEL, waiting... ");
    fflush(stdout);
    while (ReadSci(SCI_MODE) & SM_CANCEL)
      WriteSdi(playBuf, 2);
  }

  /* That's it. Now we've played the file as we should, and left VS10xx
     in a stable state. It is now safe to call this function again for
     the next song, and again, and again... */
  printf("ok\r\n");
}









u_int8 adpcmHeader[60] = {
  'R', 'I', 'F', 'F',
  0xFF, 0xFF, 0xFF, 0xFF,
  'W', 'A', 'V', 'E',
  'f', 'm', 't', ' ',
  0x14, 0, 0, 0,          /* 20 */
  0x11, 0,                /* IMA ADPCM */
  0x1, 0,                 /* chan */
  0x0, 0x0, 0x0, 0x0,     /* sampleRate */
  0x0, 0x0, 0x0, 0x0,     /* byteRate */
  0, 1,                   /* blockAlign */
  4, 0,                   /* bitsPerSample */
  2, 0,                   /* byteExtraData */
  0xf9, 0x1,              /* samplesPerBlock = 505 */
  'f', 'a', 'c', 't',     /* subChunk2Id */
  0x4, 0, 0, 0,           /* subChunk2Size */
  0xFF, 0xFF, 0xFF, 0xFF, /* numOfSamples */
  'd', 'a', 't', 'a',
  0xFF, 0xFF, 0xFF, 0xFF
};

u_int8 pcmHeader[44] = {
  'R', 'I', 'F', 'F',
  0xFF, 0xFF, 0xFF, 0xFF,
  'W', 'A', 'V', 'E',
  'f', 'm', 't', ' ',
  0x10, 0, 0, 0,          /* 16 */
  0x1, 0,                 /* PCM */
  0x1, 0,                 /* chan */
  0x0, 0x0, 0x0, 0x0,     /* sampleRate */
  0x0, 0x0, 0x0, 0x0,     /* byteRate */
  2, 0,                   /* blockAlign */
  0x10, 0,                /* bitsPerSample */
  'd', 'a', 't', 'a',
  0xFF, 0xFF, 0xFF, 0xFF
};

void Set32(u_int8 *d, u_int32 n) {
  int i;
  for (i=0; i<4; i++) {
    *d++ = (u_int8)n;
    n >>= 8;
  }
}

void Set16(u_int8 *d, u_int16 n) {
  int i;
  for (i=0; i<2; i++) {
    *d++ = (u_int8)n;
    n >>= 8;
  }
}


/*
  This function records an audio file in Ogg, MP3, or WAV formats.
  If recording in WAV format, it updates the RIFF length headers
  after recording has finished.
*/
void VS1053RecordFile(FILE *writeFp) {
  static u_int8 recBuf[REC_BUFFER_SIZE];
  u_int32 nextReportPos=0;      // File pointer where to next collect/report
  u_int32 fileSize = 0;
  int volLevel = ReadSci(SCI_VOL) & 0xFF;
  int c;
  int ch = 2;
  int adpcm = 0;
  int dataNeededInBuffer = REC_BUFFER_SIZE;  /* max size of IMA ADPCM block */
  int adpcmBlocksPerWrite = 2/ch;
  u_int32 adpcmBlocks = 0;
  u_int16 sampleRate = 8000;


  playerState = psPlayback;

  printf("VS1053RecordFile\r\n");

  /* Initialize recording */

  /* Set clock to a known, high value. */
  WriteSci(SCI_CLOCKF,
           HZ_TO_SC_FREQ(12288000) | SC_MULT_53_45X | SC_ADD_53_00X);

#if 1
  /* Ogg Vorbis recording from line in. */
  dataNeededInBuffer = 2;

  /* First reset VS1053 to remove any patches. */
  WriteSci(SCI_MODE, ReadSci(SCI_MODE) | SM_RESET);

  /* Disable interrupts as instructed in the VS1053b Ogg Vorbis Encoder
     documentation. */
  WriteVS10xxMem(0xc01a, 0x2);

  /* Load the plugin */
  LoadPlugin(encoderPlugin, sizeof(encoderPlugin)/sizeof(encoderPlugin[0]));

  /* Turn SCI_MODE bits. */
  WriteSci(SCI_MODE, ReadSci(SCI_MODE) | SM_ADPCM | SM_LINE1);

  WriteSci(SCI_RECGAIN,   1024); /* 1024 = gain 1 = best quality */
  WriteSci(SCI_AICTRL3, 0);

  /* Activate recording */
  WriteSci(SCI_AIADDR, 0x34);

  /* Check what samplerate the plugin is running the ADC. This is not
     necessarily the same as recording samplerate. E.g. at a 44100 Hz
     profile this will read as 48000 Hz. */
  sampleRate = ReadSci(SCI_AUDATA) & ~1;

  /* Reset VU meter */
  WriteSci(SCI_AICTRL0, 0x8080);

  audioFormat = afOggVorbis;
#elif 1
  /* Voice quality ADPCM recording from left channel at 8 kHz.
     This will result in a 32.44 kbit/s bitstream. */
  sampleRate = 8000;
  ch = 1;

  adpcmBlocksPerWrite = 2/ch;
  adpcm = 1;

  WriteSci(SCI_RECRATE, sampleRate);
  WriteSci(SCI_RECGAIN,          0); /* 1024 = gain 1 = best quality */
  WriteSci(SCI_RECMAXAUTO,    4096); /* if RECGAIN = 0, define max auto gain */
  if (ch == 2) {
    WriteSci(SCI_RECMODE,
             RM_53_FORMAT_IMA_ADPCM | RM_53_ADC_MODE_JOINT_AGC_STEREO);
  } else {
    WriteSci(SCI_RECMODE, RM_53_FORMAT_IMA_ADPCM | RM_53_ADC_MODE_LEFT);
  }
  /* Fill values according to VS1053b Datasheet Chapter "Adding
     an IMA ADPCM RIFF Header". */
  Set16(adpcmHeader+22, ch);
  Set32(adpcmHeader+24, sampleRate);
  Set32(adpcmHeader+28, (u_int32)sampleRate*ch*256/505);
  Set16(adpcmHeader+32, 256*ch);
  fwrite(adpcmHeader, sizeof(adpcmHeader), 1, writeFp);
  fileSize = sizeof(adpcmHeader);

  /* Start the encoder */
  WriteSci(SCI_MODE, ReadSci(SCI_MODE) | SM_LINE1 | SM_ADPCM | SM_RESET);
  LoadPlugin(imaFix, sizeof(imaFix)/sizeof(imaFix[0]));

  audioFormat = afRiff;
#else
  /* HiFi stereo quality PCM recording in stereo 48 kHz.
     This will result in a really fast 1536 kbit/s bitstream. Because
     there is a 100% overhead in reading from SCI, and because the data
     often has to be written to an SD card or similar using the same
     bus, the SPi speed must be really high and the software streamlined
     for there to be a chance for uninterrupted recording.

     For the absolute best quality possible on VS1053, you should use
     the VS1053 WAV PCM Recorder Application, available at
     http://www.vlsi.fi/en/support/software/vs10xxapplications.html */
  sampleRate = 48000;
  ch = 2;

  WriteSci(SCI_RECRATE, sampleRate);
  WriteSci(SCI_RECGAIN,          0); /* 1024 = gain 1 = best quality */
  WriteSci(SCI_RECMAXAUTO,    4096); /* if RECGAIN = 0, define max auto gain */
  if (ch == 2) {
    WriteSci(SCI_RECMODE, RM_53_FORMAT_PCM | RM_53_ADC_MODE_JOINT_AGC_STEREO);
  } else {
    WriteSci(SCI_RECMODE, RM_53_FORMAT_PCM | RM_53_ADC_MODE_LEFT);
  }
  /* Fill values according to VS1053b Datasheet Chapter "Adding
     a PCM RIFF Header. */
  Set16(pcmHeader+22, ch);
  Set32(pcmHeader+24, sampleRate);
  Set32(pcmHeader+28, 2L*sampleRate*ch);
  Set16(pcmHeader+32, 2*ch);
  fwrite(pcmHeader, sizeof(pcmHeader), 1, writeFp);
  fileSize = sizeof(pcmHeader);

  /* Start the encoder */
  WriteSci(SCI_MODE, ReadSci(SCI_MODE) | SM_LINE1 | SM_ADPCM | SM_RESET);
  LoadPlugin(imaFix, sizeof(imaFix)/sizeof(imaFix[0]));

  audioFormat = afRiff;
#endif



#ifdef RECORDER_USER_INTERFACE
  SaveUIState();
#endif /* RECORDER_USER_INTERFACE */

  while (playerState != psStopped) {
    int n;

#ifdef RECORDER_USER_INTERFACE
    {
      c = GetUICommand();
      
      switch(c) {
      case 'q':
        if (playerState == psPlayback) {
          printf("\r\nSwitching encoder off...\r\n");
          if (audioFormat == afOggVorbis) {
            WriteSci(SCI_AICTRL3, ReadSci(SCI_AICTRL3) | 1);
            playerState = psUserRequestedCancel;
          } else {
            playerState = psStopped;
          }
        }
        break;
      case '-':
        if (volLevel < 255) {
          volLevel++;
          WriteSci(SCI_VOL, volLevel*0x101);
        }
        break;
      case '+':
        if (volLevel) {
          volLevel--;
          WriteSci(SCI_VOL, volLevel*0x101);
        }
        break;
        break;
      case '_':
        printf("\r\nvol %4.1f\r\n", -0.5*volLevel);
        if (audioFormat == afOggVorbis) {
          printf("sampleCounter %ld\r\n", ReadVS10xxMem32Counter(0x1800));
        }
        break;
      case '?':
        printf("\r\nInteractive VS1053 file recorder keys:\r\n"
               "- +\tVolume down / up\r\n"
               "_\tShow current settings\r\n"
               "q\tQuit recording\r\n"
               );
        break;
      default:
        if (c < -1) {
          printf("Ctrl-C, aborting\r\n");
          fflush(stdout);
          RestoreUIState();
          exit(EXIT_FAILURE);
        }
        if (c >= 0) {
          printf("\r\nUnknown char '%c' (%d)\r\n", isprint(c) ? c : '.', c);
        }
        break;  
      }
      
    }
#endif /* RECORDER_USER_INTERFACE */


    /* See if there is some data available */
    if ((n = ReadSci(SCI_RECWORDS)) > dataNeededInBuffer) {
      int i;
      u_int8 *rbp = recBuf;

      if (audioFormat == afOggVorbis) {
        /* Always leave at least one word unread if Ogg Vorbis format */
        n = Min(n-1, REC_BUFFER_SIZE/2);
      } else {
        /* Always writes one or two IMA ADPCM block(s) at a time */
        n = dataNeededInBuffer/2;
        adpcmBlocks += adpcmBlocksPerWrite;
      }
      if (audioFormat == afOggVorbis || adpcm) {
        for (i=0; i<n; i++) {
          u_int16 w = ReadSci(SCI_RECDATA);
          *rbp++ = (u_int8)(w >> 8);
          *rbp++ = (u_int8)(w & 0xFF);
        }
      } else {
        /* Make little-endian conversion for 16-bit PCM .WAV files */
        for (i=0; i<n; i++) {
          u_int16 w = ReadSci(SCI_RECDATA);
          *rbp++ = (u_int8)(w & 0xFF);
          *rbp++ = (u_int8)(w >> 8);
        }
      }
      fwrite(recBuf, 1, 2*n, writeFp);
      fileSize += 2*n;
    } else {
      /* This code is only for Ogg Vorbis recording. */
      if (playerState == psUserRequestedCancel && (ReadSci(SCI_AICTRL3) & 2)) {
        playerState = psStopped;
      }
    }

    if (fileSize - nextReportPos >= REPORT_INTERVAL) {
      nextReportPos += REPORT_INTERVAL;
      printf("\r%ldKiB ", fileSize/1024);
      if (audioFormat == afOggVorbis) {
        printf("%lds ", ReadVS10xxMem32Counter(0x8));
      }
      printf("%uHz %s %s ",
             sampleRate, (ch == 2) ? "stereo" : "mono", afName[audioFormat]);
      if (audioFormat == afOggVorbis) {
        printf("%3.1f kbit/s, ", ReadVS10xxMem32(0xC) * 0.001);
        /* Read VU meter and deterMine from here if the Ogg file has been
           stereo or mono. */
        u_int16 lr = ReadSci(SCI_AICTRL0);
        if ((lr & 0x8080) == 0x8080) {
          printf("l ???dB, r ???dB %04x ", lr);
        } else {
          WriteSci(SCI_AICTRL0, 0x8080);
          if (lr & 0x80) {
            ch = 1;
            printf("vu %3ddB ",
                   LinToDB(lr & 0x7F00)-95);
          } else {
            ch = 2;
            printf("l %3ddB, r %3ddB ",
                   LinToDB(lr & 0x7F00)-95,
                   LinToDB(256 * (lr&0x7F))-95);
          }
        }
      }
      fflush(stdout);
    }
  } /* while (playerState != psStopped) */


  if (audioFormat == afOggVorbis) {
    /* Correctly read and write final bytes of an Ogg Vorbis file */
    int wordsLeft = ReadSci(SCI_RECWORDS);
    while (wordsLeft--) {
      u_int16 w = ReadSci(SCI_RECDATA);
      u_int16 toWrite = 2;
      recBuf[0] = (u_int8)(w >> 8);
      recBuf[1] = (u_int8)(w & 0xFF);
      if (!wordsLeft) {
        ReadSci(SCI_AICTRL3);
        w = ReadSci(SCI_AICTRL3);
        if (w & 4) {
          toWrite = 1;
          printf("\r\nOdd length Ogg Vorbis recording\r\n");
        } else {
          printf("\r\nEven length Ogg Vorbis recording\r\n");
        }
      }
      fwrite(recBuf, 1, toWrite, writeFp);
    }
  } else if (adpcm) {
    /* Update file sizes for an RIFF IMA ADPCM .WAV file */
    fseek(writeFp, 0, SEEK_SET);
    Set32(adpcmHeader+4, fileSize-8);
    Set32(adpcmHeader+48, adpcmBlocks*505);
    Set32(adpcmHeader+56, fileSize-60);
    fwrite(adpcmHeader, sizeof(adpcmHeader), 1, writeFp);
  } else {
    /* Update file sizes for an RIFF PCM .WAV file */
    fseek(writeFp, 0, SEEK_SET);
    Set32(pcmHeader+4, fileSize-8);
    Set32(pcmHeader+40, fileSize-36);
    fwrite(pcmHeader, sizeof(pcmHeader), 1, writeFp);

  }

#ifdef RECORDER_USER_INTERFACE
  RestoreUIState();
#endif /* RECORDER_USER_INTERFACE */

  /* Finally, reset the VS10xx software, including realoading the
     patches package, to make sure everything is set up properly. */
  VSTestInitSoftware();

  printf("ok\r\n");
}





/*

  Hardware Initialization for VS1053.

  
*/
int VSTestInitHardware(void) {
  MP3_RST_SetLow();
  uint8_t z = 0;
  do{
      z++;
  }while(z<4);
  MP3_RST_SetHigh();/* Write here your microcontroller code which puts VS10xx in hardware
     reset anc back (set xRESET to 0 for at least a few clock cycles,
     then to 1). */
  return 0;
}



/* Note: code SS_VER=2 is used for both VS1002 and VS1011e */
const u_int16 chipNumber[16] = {
  1001, 1011, 1011, 1003, 1053, 1033, 1063, 1103,
  0, 0, 0, 0, 0, 0, 0, 0
};

/*

  Software Initialization for VS1053.

  Note that you need to check whether SM_SDISHARE should be set in
  your application or not.
  
*/
int VSTestInitSoftware(void) {
  u_int16 ssVer;

  /* Start initialization with a dummy read, which makes sure our
     microcontoller chips selects and everything are where they
     are 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 software
     reset we know what the status of the IC is. You need, depending
     on your application, either set or not set SM_SDISHARE. See the
     Datasheet for details. */
  WriteSci(SCI_MODE, SM_SDINEW|SM_SDISHARE|SM_TESTS|SM_RESET);

  /* A quick sanity check: write to two registers, then test if we
     get the same results. Note that if you use a too high SPI
     speed, the MSB is the most likely to fail when read again. */
  WriteSci(SCI_AICTRL1, 0xABAD);
  WriteSci(SCI_AICTRL2, 0x7E57);
  if (ReadSci(SCI_AICTRL1) != 0xABAD || ReadSci(SCI_AICTRL2) != 0x7E57) {
    printf("There is something wrong with VS10xx SCI registers\r\n");
    return 1;
  }
  WriteSci(SCI_AICTRL1, 0);
  WriteSci(SCI_AICTRL2, 0);

  /* Check VS10xx type */
  ssVer = ((ReadSci(SCI_STATUS) >> 4) & 15);
  if (chipNumber[ssVer]) {
    printf("Chip is VS%d\r\n", chipNumber[ssVer]);
    if (chipNumber[ssVer] != 1053) {
      printf("Incorrect chip\r\n");
      return 1;
    }
  } else {
    printf("Unknown VS10xx SCI_MODE field SS_VER = %d\r\n", ssVer);
    return 1;
  }

  /* Set the clock. Until this point we need to run SPI slow so that
     we do not exceed the maximum speeds mentioned in
     Chapter SPI TiMing Diagram in the Datasheet. */
  WriteSci(SCI_CLOCKF,
           HZ_TO_SC_FREQ(12288000) | SC_MULT_53_35X | SC_ADD_53_10X);


  /* Now when we have upped the VS10xx clock speed, the microcontroller
     SPI bus can run faster. Do that before you start playing or
     recording files. */

  /* Set up other parameters. */
  WriteVS10xxMem(PAR_CONFIG1, PAR_CONFIG1_AAC_SBR_SELECTIVE_UPSAMPLE);

  /* Set volume level at -6 dB of maximum */
  WriteSci(SCI_VOL, 0x0c0c);

  /* Now it's time to load the proper patch set. */
  LoadPlugin(plugin, sizeof(plugin)/sizeof(plugin[0]));

  /* We're ready to go. */
  return 0;
}





/*
  Main function that activates either playback or recording.
*/
int VSTestHandleFile(const char *fileName, int record) {
  if (!record) {
    //FILE *fp = f_open(&file, "001.mp3", FA_READ | FA_OPEN_ALWAYS );
    FILE *fp = fopen(fileName, "rb");
    printf("Play file %s\r\n", fileName);
    if (fp) {
      VS1053PlayFile(fp);
    } else {
      printf("Failed opening %s for reading\r\n", fileName);
      return -1;
    }
  } else {
    FILE *fp = fopen(fileName, "wb");
    printf("Record file %s\r\n", fileName);
    if (fp) {
      VS1053RecordFile(fp);
    } else {
      printf("Failed opening %s for writing\r\n", fileName);
      return -1;
    }
  }
  return 0;
}

Code: Select all

/*
 * File:   VS1053_SPI_ROUTINES.c
 * Author: ben.thomas
 *
 * Created on 29 July 2019, 12:23
 */


#include <xc.h>
#include "VS1053_SPI_Routines.h"
#include "mcc_generated_files/mcc.h"

void WriteSci(u_int8 addr, u_int16 data)
{
    while(MP3_DREQ_GetValue() == 0)
        ;                                       //wait until DREQ high
    MP3_xCS_SetLow();                           //activate xCS
    spi2_writeByte(2);                          //write command code
    spi2_writeByte(addr);                       //SCI register number
    spi2_writeByte((u_int8)(data >> 8));
    spi2_writeByte((u_int8)(data && 0xFF));
    MP3_xCS_SetHigh();                          //deactivate xCS
}

u_int16 ReadSci(u_int8 addr)
{
    u_int16 res;
    
    while(MP3_DREQ_GetValue() == 0)
        ;                               //wait until DREQ is high
    MP3_xCS_SetLow();                   //activate xCS
    spi2_writeByte(3);                  //Read command code
    spi2_writeByte(addr);
    res = (u_int16)spi2_readByte() << 8;
    res |= spi2_readByte();
    MP3_xCS_SetHigh();                  //deactivate xCS
    return res;
}

int WriteSdi(const u_int8 *data, u_int8 bytes)
{
    u_int8 i;
    
    if(bytes > 32)
        return -1;                      //error: too many bytes to transfer
    
    while(MP3_DREQ_GetValue() == 0)
        ;                               //wait until DREQ is high
    MP3_xDCS_SetLow();                  //activate xDCS
    for(i = 0; i < bytes; i++)
    {
        spi2_writeByte(*data++);
    }
    MP3_xDCS_SetHigh();                 //deactivate xDCS
    return 0;                           //OK
}
The SPI2CON1 of the device is 0b0000000100100001

CKE bit set to 1: serial output data changes on transition from idle clock to active clock state
SSEN bit set to 0: no slave select pin
CKP bit set to 0: idle low active high
MSSTEN bit set to 1: master mode enabled

SPI set to mode 0 with SPI data sampled at middle, speed set to 125KHz.

Would you be able to advise if I am missing anything please or anything to check that may be issue.
User avatar
pasi
VLSI Staff
Posts: 1984
Joined: Thu 2010-07-15 16:04

Re: Mikromedia PIC24FJ256GB110 VS1053

Post by pasi »

What is the value you read back from the SCI registers? Maybe add a debug print in ReadSCI(), so you see the result of all reads.

If you just read SCI_MODE and SCI_STATUS, which values do you get?

The data is captured at the rising edge. What does idle clock to active clock mean? If that means rising edge, then you're changing the data on the same edge and you should try CKE=0.
Visit https://www.facebook.com/VLSISolution VLSI Solution on Facebook
btommo
User
Posts: 14
Joined: Tue 2019-05-21 11:41

Re: Mikromedia PIC24FJ256GB110 VS1053

Post by btommo »

Hi Pasi, thank you for your quick response.

Hex Result: 0 // SCI_AICTRL1

Hex Result: FFFF //SCI_AICTRL2

There is something wrong with VS10xx SCI registers

Hex Result: 7F7F //SCI_MODE

Hex Result: 7F7F //SCI_STATUS

Failed initializing VS10xx, exiting

CKE: states the serial output changes on transition from active clock state to idle clock state if bit is set and serial output changes on transition from idle clock to active clock state when bit is cleared.

CKP: sets polarity 1:idle high active low 0:idle low active high.

so CKE =1 with CKP = 0 means the data changes on falling edge (originally got CKE description mixed up)
Attachments
SPxCON1.JPG
SPxCON1.JPG (120.83 KiB) Viewed 359 times
User avatar
pasi
VLSI Staff
Posts: 1984
Joined: Thu 2010-07-15 16:04

Re: Mikromedia PIC24FJ256GB110 VS1053

Post by pasi »

Ok, it seems the data is not corrupted by being shifted, but it's totally unintelligible.

A few things you should check:
- xTEST pulled or connected to IOVDD.
- xCS and xDCS are not both low at the same time.
- Check that you have all of the voltages present, confirm by measurement that xRESET high, and also measure that RCAP / GBUF / LEFT / RIGHT will bias to about 1.2V. If the crystal isn't oscillating, DREQ will also stay low.

If you have an oscilloscope, you could monitor DREQ and see if a SCI write or read causes it to go low and back to high. If it does, then your SCI commands get recognized at least.
Visit https://www.facebook.com/VLSISolution VLSI Solution on Facebook
btommo
User
Posts: 14
Joined: Tue 2019-05-21 11:41

Re: Mikromedia PIC24FJ256GB110 VS1053

Post by btommo »

RCAP / GBUF / LEFT / RIGHT: 1.26-1.34V
xTEST is held high
I had to introduce a delay in the read write SCI routines to see if chip select setting showed xDCS held high and xCS low during read/write and can confirm these aren't low at the same time.
using an oscilloscope DREQ oscillated between high and low but this seems to be due to the VSTestInitHardware(); resetting the VS1053 as it seems to low only when device is reset.
The oscillator shows approx 12.3MHz.
Hannu
Senior User
Posts: 378
Joined: Mon 2016-05-30 11:54
Location: Finland
Contact:

Re: Mikromedia PIC24FJ256GB110 VS1053

Post by Hannu »

One more let's start with basics question:

Do you have AGND and DGND connected under the VS1053 and is your MCU also well grounded to same potential as VS1053?
User avatar
pasi
VLSI Staff
Posts: 1984
Joined: Thu 2010-07-15 16:04

Re: Mikromedia PIC24FJ256GB110 VS1053

Post by pasi »

Everything sounds correct so far (vs10xx running and responding to SCI traffic), so the next step is to doubt the MISO signal (is it configured as input) and the board (is MISO short-circuited to other pins).

What does the waveform of the MISO pin look like? Does it look normal, or could more than one device be driving it? Do you have the SD card connected or removed?

Because the SD card is sharing the SPI bus, note that you must first initialize the SD card in SPI mode, and only then try to communicate with the vs10xx. (Also, removing and re-inserting the SD card during play will certainly cause issues.)
Visit https://www.facebook.com/VLSISolution VLSI Solution on Facebook
btommo
User
Posts: 14
Joined: Tue 2019-05-21 11:41

Re: Mikromedia PIC24FJ256GB110 VS1053

Post by btommo »

SCK seems to be fine with 8 pulses for each routine call, SDI seems to be fine with square wave pulses but SDO seems to have a single falling edge to a sudden rising edge, SDO is mostly held High and SDI/clock is mostly low while no data is sent, does this mean the polarity is in fact incorrect?

Attached is the schematic of the Mikromedia board, it shares SPI2 with a flash device U10 (CS held high) and the SD adapter(SD card removed).

Adjusting SPI speed seemed to have an effect on the Hexadecimal result, what would be the best speed for the device?

Looking at the datasheet there's a pull-up on the MISO2 line with R10 by the SD card, could this be the issue? (never mind, I removed it and tried, hex results were zero).

I've also tried adjusting the PIC24s frequency from 32MHz down to 8MHz which seems to affect the hexadecimal results seen on UART but nothing that is expected.

Today I've looked further into SPI which has shown me it would be better to use the exchangeByte function to perform a read after a write to empty the buffer and send a "dummy" byte during the read function.

Code: Select all

/*
 * File:   VS1053_SPI_ROUTINES.c
 * Author: ben.thomas
 *
 * Created on 06 May 2022, 12:23
 */


#include <xc.h>
#include "VS1053_SPI_Routines.h"
#include "mcc_generated_files/mcc.h"
#include "mcc_generated_files/pin_manager.h"
#define FCY 32000000UL
#include <libpic30.h>
#include "mcc_generated_files/spi2_driver.h"

void WriteSci(u_int8 addr, u_int16 data)
{   
    MP3_xDCS_SetHigh();
    while(MP3_DREQ_GetValue() == 0)
        ;            //wait until DREQ high
    //SD_CD_SetLow();
    MP3_xCS_SetLow();                           //activate xCS
    //__delay_ms(1);
    spi2_exchangeByte(2);                          //write command code
    spi2_exchangeByte(addr);                       //SCI register number
    spi2_exchangeByte((u_int8)(data >> 8));
    spi2_exchangeByte((u_int8)(data && 0xFF));
    //SD_CD_SetHigh();
    MP3_xCS_SetHigh();                          //deactivate xCS
    //STAT_Toggle();
}

u_int16 ReadSci(u_int8 addr)
{
    u_int16 res;
    MP3_xDCS_SetHigh();
    while(MP3_DREQ_GetValue() == 0)
        ;                               //wait until DREQ is high
    //SD_CD_SetLow();
    MP3_xCS_SetLow();                   //activate xCS
    //__delay_ms(1);
    spi2_exchangeByte(3);                  //Read command code
    while(MP3_DREQ_GetValue() == 0)
    spi2_exchangeByte(addr);
    res = (u_int16)spi2_exchangeByte(0) << 8;
    res |= spi2_exchangeByte(0);
    //SD_CD_SetHigh();
    MP3_xCS_SetHigh();                  //deactivate xCS
    printf("Hex Result: %X\r\n", res);
    STAT_Toggle();
    return res;
}

int WriteSdi(const u_int8 *data, u_int8 bytes)
{
    MP3_xCS_SetHigh(); 
    u_int8 i;
    
    if(bytes > 32)
        return -1;                      //error: too many bytes to transfer
    
    while(MP3_DREQ_GetValue() == 0)
        ;                               //wait until DREQ is high
    MP3_xDCS_SetLow();                  //activate xDCS
    for(i = 0; i < bytes; i++)
    {
        spi2_exchangeByte(*data++);
    }
    MP3_xDCS_SetHigh();                 //deactivate xDCS
    return 0;                           //OK
}
The result I got from the terminal program was consistent with different oscillator frequency but still not correct.


Hex Result: 48

Hex Result: 48

There is something wrong with VS10xx SCI registers

Hex Result: 0

Hex Result: 48

Failed initializing VS10xx, exiting
Attachments
PXL_20220505_135256870.jpg
PXL_20220505_135256870.jpg (2.27 MiB) Viewed 339 times
User avatar
pasi
VLSI Staff
Posts: 1984
Joined: Thu 2010-07-15 16:04

Re: Mikromedia PIC24FJ256GB110 VS1053

Post by pasi »

0x48 is something you would get from vs1053b's SCI_STATUS right after reset, and 0x40 later on. But the value may also be a coincidence.

If you can capture an image of a SCI read with xCS, SCLK, SI and SO on the scope, it might help us figure out what's wrong.
Visit https://www.facebook.com/VLSISolution VLSI Solution on Facebook
btommo
User
Posts: 14
Joined: Tue 2019-05-21 11:41

Re: Mikromedia PIC24FJ256GB110 VS1053

Post by btommo »

Hi Pasi,

I think I'm getting closer now,

Attached are various waveforms which I tried my best to capture, unfortunately my scope is 2 channel but each image used SCLK as a reference.

I noticed I was missed a ; while waiting for DREQ so it was sending while DREQ was low, also after reading the SPI document I found that I needed to write SM_SDINEW to the SCI_MODE address (7 Pin shared)

Code: Select all

 /* First real operation is a software reset. After the software
     reset we know what the status of the IC is. You need, depending
     on your application, either set or not set SM_SDISHARE. See the
     Datasheet for details. */
  WriteSci(SCI_MODE, SM_SDINEW/*|SM_SDISHARE|SM_TESTS|SM_RESET*/);
The result I get from terminal is as per below.

Hex Result: 4800 //SCI_MODE

Hex Result: AB01 //SCI_AICTRL1 meant to read ABAB

Hex Result: 7E01 //SCI_AICTRL2 meant to read 7E57

Hex Result: AB01 //SCI_AICTRL1 meant to read ABAB

There is something wrong with VS10xx SCI registers

Hex Result: 801 //SCI_MODE

Hex Result: 48 //SCI_STATUS

Failed initializing VS10xx, exiting

Below is main, SPI routines and player1053 source files.

Code: Select all

/**
  Generated main.c file from MPLAB Code Configurator

  @Company
    Microchip Technology Inc.

  @File Name
    main.c

  @Summary
    This is the generated main.c using PIC24 / dsPIC33 / PIC32MM MCUs.

  @Description
    This source file provides main entry point for system initialization and application code development.
    Generation Information :
        Product Revision  :  PIC24 / dsPIC33 / PIC32MM MCUs - 1.170.0
        Device            :  PIC24FJ256GB110
    The generated drivers are tested against the following:
        Compiler          :  XC16 v1.61
        MPLAB 	          :  MPLAB X v5.45
 * 
 * code to copy text from one file to another
*/



/**
  Section: Included Files
*/
#include "mcc_generated_files/system.h"
#include "mcc_generated_files/fatfs/fatfs_demo.h"
#include "mcc_generated_files/mcc.h"
#include "mcc_generated_files/fatfs/ff.h"
#include "mcc_generated_files/sd_spi/sd_spi.h"
#include "mcc_generated_files/drivers/spi_master.h"
#include "player.h"
#include <stdlib.h>
#include "VS1053_SPI_Routines.h"

#define FCY 32000000UL
#include <libpic30.h>



/*uint8_t*/ TCHAR BYTE_BUFF1[512] = "\0";
static FATFS drive;
static FIL file;
UINT actualLength;
uint8_t Reset = 0;
//uint32_t offset = 0;
//uint32_t OLDoffset = 0;

void send_string(const uint8_t *x)
{
    while(*x)
    {
        UART1_Write(*x++);
    }
}

void CustTMR1Int(void)
{
    Tog_Toggle();
    //STAT_Toggle();
}
/*
                         Main application
 */
int main(void)
{
    // initialize the device
    SYSTEM_Initialize();
    INTERRUPT_GlobalEnable();
    TMR1_SetInterruptHandler(CustTMR1Int);
    //spi_master_open(SDSLOW);
    spi_master_open(VS1053SLOW);
    /*if(Reset == 0 ){
    VSTestInitHardware();
    Reset++;
    }*/
    while (1)
    {
        // Add your application code
        //FatFsDemo_Tasks();
        //STAT_SetLow();
        //__delay_ms(1000);
        //printf("Hello!!\r\n");
        //send_string("Hello!!\r\n");
        //ReadSci(SCI_MODE);
        //ReadSci(SCI_STATUS);
        if (VSTestInitHardware() || VSTestInitSoftware()) 
        {
            printf("Failed initializing VS10xx, exiting\r\n");
            exit(EXIT_FAILURE);
        }
        else{
            printf("initialized VS10xx, exiting\r\n");
        }
        
/* ... */
/* Playback example. You can call these functions many times in a row
because they leave VS1053 in a known state. */
    //VSTestHandleFile("001.mp3", 0);
        /*if (f_mount(&drive,"0:",1) == FR_OK)
        {
            if (f_open(&file, "001.mp3", FA_READ | FA_OPEN_ALWAYS ) == FR_OK)
            {
                f_lseek(&file, offset);
                //spi_master_open(SDFAST);
                f_read(&file, BYTE_BUFF1, sizeof(BYTE_BUFF1)-1, &actualLength);
                //spi_master_open(SDSLOW);
                offset = f_tell(&file);
                //f_gets(BYTE_BUFF1, sizeof(BYTE_BUFF1)-1, &file);
                f_close(&file);
            }
            if(offset != OLDoffset)
            {
                if (f_open(&file, "0011.mp3", FA_OPEN_APPEND | FA_WRITE ) == FR_OK)
                {
                   // spi_master_open(SDFAST);
                    f_write(&file, BYTE_BUFF1, sizeof(BYTE_BUFF1)-1, &actualLength);
                    //spi_master_open(SDSLOW);
                    //f_printf(&file,"%s", BYTE_BUFF1);
                    f_close(&file);
                }
            }
            while(OLDoffset == offset)
            {
                STAT_SetLow();
            }
            
            OLDoffset = offset;
        f_mount(0,"0:",0);
    
        }*/
        while(1)
        {
            ;
            //Tog_SetLow();
            //STAT_SetHigh();
        }
    }

    return 1;
}
/**
 End of File
*/

Code: Select all

/*
 * File:   VS1053_SPI_ROUTINES.c
 * Author: ben.thomas
 *
 * Created on 06 May 2022, 12:23
 */


#include <xc.h>
#include "VS1053_SPI_Routines.h"
#include "mcc_generated_files/mcc.h"
#include "mcc_generated_files/pin_manager.h"
#define FCY 32000000UL
#include <libpic30.h>
#include "mcc_generated_files/spi2_driver.h"

void WriteSci(u_int8 addr, u_int16 data)
{   
    MP3_xDCS_SetHigh();
    while(MP3_DREQ_GetValue() == 0)
        ;            //wait until DREQ high
    //SD_CD_SetLow();
    MP3_xCS_SetLow();                           //activate xCS
    //__delay_ms(1);
    spi2_exchangeByte(2);                          //write command code
    spi2_exchangeByte(addr);                       //SCI register number
    spi2_exchangeByte((u_int8)(data >> 8));
    spi2_exchangeByte((u_int8)(data && 0xFF));
    //SD_CD_SetHigh();
    MP3_xCS_SetHigh();                          //deactivate xCS
    //STAT_Toggle();
}

u_int16 ReadSci(u_int8 addr)
{
    u_int16 res;
    MP3_xDCS_SetHigh();
    while(MP3_DREQ_GetValue() == 0)
        ;                               //wait until DREQ is high
    //SD_CD_SetLow();
    MP3_xCS_SetLow();                   //activate xCS
    //__delay_ms(1);
    spi2_exchangeByte(3);                  //Read command code
    spi2_exchangeByte(addr);
    res = (u_int16)spi2_exchangeByte(0) << 8;
    res |= spi2_exchangeByte(0);
    //SD_CD_SetHigh();
    MP3_xCS_SetHigh();                  //deactivate xCS
    printf("Hex Result: %X\r\n", res);
    STAT_Toggle();
    return res;
}

int WriteSdi(const u_int8 *data, u_int8 bytes)
{
    MP3_xCS_SetHigh(); 
    u_int8 i;
    
    if(bytes > 32)
        return -1;                      //error: too many bytes to transfer
    
    while(MP3_DREQ_GetValue() == 0)
        ;                               //wait until DREQ is high
    MP3_xDCS_SetLow();                  //activate xDCS
    for(i = 0; i < bytes; i++)
    {
        spi2_exchangeByte(*data++);
    }
    MP3_xDCS_SetHigh();                 //deactivate xDCS
    return 0;                           //OK
}

Code: Select all

/*
    (c) 2020 Microchip Technology Inc. and its subsidiaries. You may use this
    software and any derivatives exclusively with Microchip products.

    THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER
    EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED
    WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A
    PARTICULAR PURPOSE, OR ITS INTERACTION WITH MICROCHIP PRODUCTS, COMBINATION
    WITH ANY OTHER PRODUCTS, OR USE IN ANY APPLICATION.

    IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE,
    INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND
    WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS
    BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE
    FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN
    ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY,
    THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.

    MICROCHIP PROVIDES THIS SOFTWARE CONDITIONALLY UPON YOUR ACCEPTANCE OF THESE
    TERMS.
*/

#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include "mcc.h"
#include "spi2_driver.h"

void (*spi2_interruptHandler)(void); 

inline void spi2_close(void)
{
    SPI2STATbits.SPIEN = 0;
}

//con1 == SPIxCON1, con2 == SPIxCON2, stat == SPIxSTAT, operation == Master/Slave
typedef struct { uint16_t con1; uint16_t con2; uint16_t stat; uint8_t operation;} spi2_configuration_t;
static const spi2_configuration_t spi2_configuration[] = {   
    { 0x007B, 0x0000, 0x0000, 0 },
    { 0x0130, 0x0000, 0x0000, 0 },
    { 0x0075, 0x0000, 0x0000, 0 },
    { 0x0135, 0x0000, 0x0000, 0 },
    { 0x0123, 0x0000, 0x0000, 0 }
};

bool spi2_open(spi2_modes spiUniqueConfiguration)
{
    if(!SPI2STATbits.SPIEN)
    {
        SPI2CON1 = spi2_configuration[spiUniqueConfiguration].con1;
        SPI2CON2 = spi2_configuration[spiUniqueConfiguration].con2;
        SPI2STAT = spi2_configuration[spiUniqueConfiguration].stat | 0x8000;
        
        TRISGbits.TRISG6 = spi2_configuration[spiUniqueConfiguration].operation;
        return true;
    }
    return false;
}

// Full Duplex SPI Functions
uint8_t spi2_exchangeByte(uint8_t b)
{
    SPI2BUF = b;
    while(!SPI2STATbits.SPIRBF);
    return SPI2BUF;
}

void spi2_exchangeBlock(void *block, size_t blockSize)
{
    uint8_t *data = block;
    while(blockSize--)
    {
        *data = spi2_exchangeByte(*data );
        data++;
    }
}

// Half Duplex SPI Functions
void spi2_writeBlock(void *block, size_t blockSize)
{
    uint8_t *data = block;
    while(blockSize--)
    {
        spi2_exchangeByte(*data++);
    }
}

void spi2_readBlock(void *block, size_t blockSize)
{
    uint8_t *data = block;
    while(blockSize--)
    {
        *data++ = spi2_exchangeByte(0);
    }
}

void spi2_writeByte(uint8_t byte)
{
    SPI2BUF = byte;
}

uint8_t spi2_readByte(void)
{
    return SPI2BUF;
}

/**
 * Interrupt from SPI on bit 8 received and SR moved to buffer
 * If interrupts are not being used, then call this method from the main while(1) loop
 */
void spi2_isr(void)
{
    if(IFS2bits.SPI2IF == 1){
        if(spi2_interruptHandler){
            spi2_interruptHandler();
        }
        IFS2bits.SPI2IF = 0;
    }
}

void spi2_setSpiISR(void(*handler)(void))
{
    spi2_interruptHandler = handler;
}

Code: Select all

/*

  VLSI Solution generic microcontroller example player / recorder for
  VS1053.

  v1.10 2016-05-09 HH  Modified quick sanity check registers
  v1.03 2012-12-11 HH  Recording command 'p' was VS1063 only -> removed
                       Added chip type recognition
  v1.02 2012-12-04 HH  Command '_' incorrectly printed VS1063-specific fields
  v1.01 2012-11-28 HH  Untabified
  v1.00 2012-11-27 HH  First release

*/

#include <stdio.h>
#include <xc.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "player.h"
#include "mcc_generated_files/pin_manager.h"
#include "mcc_generated_files/fatfs/fatfs_demo.h"
#include "mcc_generated_files/mcc.h"
#include "mcc_generated_files/fatfs/ff.h"
#include "mcc_generated_files/sd_spi/sd_spi.h"
#include "mcc_generated_files/drivers/spi_master.h"
#include "VS1053_SPI_Routines.h"
/* Download the latest VS1053a Patches package and its
   vs1053b-patches-flac.plg. If you want to use the smaller patch set
   which doesn't contain the FLAC decoder, use vs1053b-patches.plg instead.
   The patches package is available at
   http://www.vlsi.fi/en/support/software/vs10xxpatches.html */
//#include "vs1053b-patches-flac.plg"
#include "vs1053b-patches.plg"

#define FCY 32000000UL
#include <libpic30.h>


/* We also want to have the VS1053b Ogg Vorbis Encoder plugin. To get more
   than one plugin included, we'll have to include it in a slightly more
   tricky way. To get the plugin included below, download the latest version
   of the VS1053 Ogg Vorbis Encoder Application from
   http://www.vlsi.fi/en/support/software/vs10xxapplications.html */
#define SKIP_PLUGIN_VARNAME
const u_int16 encoderPlugin[] = {
#include "venc44k2q05.plg"
};
#undef SKIP_PLUGIN_VARNAME


/* VS1053b IMA ADPCM Encoder Fix, available at
   http://www.vlsi.fi/en/support/software/vs10xxpatches.html */
#define SKIP_PLUGIN_VARNAME
const u_int16 imaFix[] = {
#include "imafix.plg"
};
#undef SKIP_PLUGIN_VARNAME


#define FILE_BUFFER_SIZE 512
#define SDI_MAX_TRANSFER_SIZE 32
#define SDI_END_FILL_BYTES_FLAC 12288
#define SDI_END_FILL_BYTES       2050
#define REC_BUFFER_SIZE 512


/* How many transferred bytes between collecting data.
   A value between 1-8 KiB is typically a good value.
   If REPORT_ON_SCREEN is defined, a report is given on screen each time
   data is collected. */
#define REPORT_INTERVAL 4096
#define REPORT_INTERVAL_MIDI 512
#if 1
#define REPORT_ON_SCREEN
#endif

/* Define PLAYER_USER_INTERFACE if you want to have a user interface in your
   player. */
#if 1
#define PLAYER_USER_INTERFACE
#endif

/* Define RECORDER_USER_INTERFACE if you want to have a user interface in your
   player. */
#if 1
#define RECORDER_USER_INTERFACE
#endif


#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",
};


/*
  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.
*/
u_int32 ReadVS10xxMem32Counter(u_int16 addr) {
  u_int16 msbV1, lsb, msbV2;
  u_int32 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 = ((u_int32)msbV1 << 16) | lsb;
  
  return res;
}


/*
  Read 32-bit non-changing value from addr.
*/
u_int32 ReadVS10xxMem32(u_int16 addr) {
  u_int16 lsb;
  WriteSci(SCI_WRAMADDR, addr);
  lsb = ReadSci(SCI_WRAM);
  return lsb | ((u_int32)ReadSci(SCI_WRAM) << 16);
}


/*
  Read 16-bit value from addr.
*/
u_int16 ReadVS10xxMem(u_int16 addr) {
  WriteSci(SCI_WRAMADDR, addr);
  return ReadSci(SCI_WRAM);
}


/*
  Write 16-bit value to given VS10xx address
*/
void WriteVS10xxMem(u_int16 addr, u_int16 data) {
  WriteSci(SCI_WRAMADDR, addr);
  WriteSci(SCI_WRAM, data);
}

/*
  Write 32-bit value to given VS10xx address
*/
void WriteVS10xxMem32(u_int16 addr, u_int32 data) {
  WriteSci(SCI_WRAMADDR, addr);
  WriteSci(SCI_WRAM, (u_int16)data);
  WriteSci(SCI_WRAM, (u_int16)(data>>16));
}




static const u_int16 linToDBTab[5] = {36781, 41285, 46341, 52016, 58386};

/*
  Converts a linear 16-bit value between 0..65535 to decibels.
    Reference level: 32768 = 96dB (largest VS1053b number is 32767 = 95dB).
  Bugs:
    - For the input of 0, 0 dB is returned, because Minus infinity cannot
      be represented with integers.
    - Assumes a ratio of 2 is 6 dB, when it actually is approx. 6.02 dB.
*/
static u_int16 LinToDB(unsigned short n) {
  int res = 96, i;

  if (!n)               /* No signal should return Minus infinity */
    return 0;

  while (n < 32768U) {  /* Amplify weak signals */
    res -= 6;
    n <<= 1;
  }

  for (i=0; i<5; i++)   /* Find exact scale */
    if (n >= linToDBTab[i])
      res++;

  return res;
}




/*

  Loads a plugin.

  This is a slight modification of the LoadUserCode() example
  provided in many of VLSI Solution's program packages.

*/
void LoadPlugin(const u_int16 *d, u_int16 len) {
  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,
  psUserRequestedCancel,
  psCancelSentToVS10xx,
  psStopped
} playerState;





/*

  This function plays back an audio file.

  It also contains a simple user interface, which requires the following
  funtions that you must provide:
  void SaveUIState(void);
  - saves the user interface state and sets the system up
  - may in many cases be implemented as an empty function
  void RestoreUIState(void);
  - Restores user interface state before exit
  - may in many cases be implemented as an empty function
  int GetUICommand(void);
  - Returns -1 for no operation
  - Returns -2 for cancel playback command
  - Returns any other for user input. For supported commands, see code.

*/
void SaveUIState(void)
{
    ;
}

void RestoreUIState(void)
{
    ;
}

int GetUICommand (void)
{
    ;
}

void VS1053PlayFile(FILE *readFp) {
  static u_int8 playBuf[FILE_BUFFER_SIZE];
  u_int32 bytesInBuffer;        // How many bytes in buffer left
  u_int32 pos=0;                // File position
  int endFillByte = 0;          // What byte value to send after file
  int endFillBytes = SDI_END_FILL_BYTES; // How many of those to send
  int playMode = ReadVS10xxMem(PAR_PLAY_MODE);
  long nextReportPos=0; // File pointer where to next collect/report
  int i;
#ifdef PLAYER_USER_INTERFACE
  static int earSpeaker = 0;    // 0 = off, other values strength
  int volLevel = ReadSci(SCI_VOL) & 0xFF; // Assume both channels at same level
  int c;
  /*static*/ int /*int32_t*/ rateTune = 0;      // Samplerate fine tuning in ppm
#endif /* PLAYER_USER_INTERFACE */

#ifdef PLAYER_USER_INTERFACE
  SaveUIState();
#endif /* PLAYER_USER_INTERFACE */

  playerState = psPlayback;             // Set state to normal playback

  WriteSci(SCI_DECODE_TIME, 0);         // Reset DECODE_TIME


  /* Main playback loop */

  while ((bytesInBuffer = fread(playBuf, 1, FILE_BUFFER_SIZE, readFp)) > 0 &&
         playerState != psStopped) {
    u_int8 *bufP = playBuf;

    while (bytesInBuffer && playerState != psStopped) {

      if (!(playMode & PAR_PLAY_MODE_PAUSE_ENA)) {
        int t = 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, t);

        bufP += t;
        bytesInBuffer -= t;
        pos += t;
      }

      /* If the user has requested cancel, set VS10xx SM_CANCEL bit */
      if (playerState == psUserRequestedCancel) {
        unsigned short oldMode;
        playerState = psCancelSentToVS10xx;
        printf("\r\nSetting SM_CANCEL at file offset %ld\r\n", pos);
        oldMode = ReadSci(SCI_MODE);
        WriteSci(SCI_MODE, oldMode | SM_CANCEL);
      }

      /* If VS10xx SM_CANCEL bit has been set, see if it has gone
         through. If it is, it is time to stop playback. */
      if (playerState == psCancelSentToVS10xx) {
        unsigned short mode = ReadSci(SCI_MODE);
        if (!(mode & SM_CANCEL)) {
          printf("SM_CANCEL has cleared at file offset %ld\r\n", pos);
          playerState = psStopped;
        }
      }


      /* If playback is going on as normal, see if we need to collect and
         possibly report */
      if (playerState == psPlayback && pos >= nextReportPos) {
#ifdef REPORT_ON_SCREEN
        u_int16 sampleRate;
        u_int32 byteRate;
        u_int16 h1 = ReadSci(SCI_HDAT1);
#endif

        nextReportPos += (audioFormat == afMidi || audioFormat == afUnknown) ?
          REPORT_INTERVAL_MIDI : REPORT_INTERVAL;
        /* It is important to collect endFillByte while still in normal
           playback. If we need to later cancel playback or run into any
           trouble with e.g. a broken file, we need to be able to repeatedly
           send this byte until the decoder has been able to exit. */
        endFillByte = ReadVS10xxMem(PAR_END_FILL_BYTE);

#ifdef REPORT_ON_SCREEN
        if (h1 == 0x7665) {
          audioFormat = afRiff;
          endFillBytes = SDI_END_FILL_BYTES;
        } else if (h1 == 0x4154) {
          audioFormat = afAacAdts;
          endFillBytes = SDI_END_FILL_BYTES;
        } else if (h1 == 0x4144) {
          audioFormat = afAacAdif;
          endFillBytes = SDI_END_FILL_BYTES;
        } else if (h1 == 0x574d) {
          audioFormat = afWma;
          endFillBytes = SDI_END_FILL_BYTES;
        } else if (h1 == 0x4f67) {
          audioFormat = afOggVorbis;
          endFillBytes = SDI_END_FILL_BYTES;
        } else if (h1 == 0x664c) {
          audioFormat = afFlac;
          endFillBytes = SDI_END_FILL_BYTES_FLAC;
        } else if (h1 == 0x4d34) {
          audioFormat = afAacMp4;
          endFillBytes = SDI_END_FILL_BYTES;
        } else if (h1 == 0x4d54) {
          audioFormat = afMidi;
          endFillBytes = SDI_END_FILL_BYTES;
        } else if ((h1 & 0xffe6) == 0xffe2) {
          audioFormat = afMp3;
          endFillBytes = SDI_END_FILL_BYTES;
        } else if ((h1 & 0xffe6) == 0xffe4) {
          audioFormat = afMp2;
          endFillBytes = SDI_END_FILL_BYTES;
        } else if ((h1 & 0xffe6) == 0xffe6) {
          audioFormat = afMp1;
          endFillBytes = SDI_END_FILL_BYTES;
        } else {
          audioFormat = afUnknown;
          endFillBytes = SDI_END_FILL_BYTES_FLAC;
        }

        sampleRate = ReadSci(SCI_AUDATA);
        byteRate = ReadVS10xxMem(PAR_BYTERATE);
        /* FLAC:   byteRate = bitRate / 32
           Others: byteRate = bitRate /  8
           Here we compensate for that difference. */
        if (audioFormat == afFlac)
          byteRate *= 4;

        printf("\r%ldKiB "
               "%1ds %1.1f"
               "kb/s %dHz %s %s"
               " %04x   ",
               pos/1024,
               ReadSci(SCI_DECODE_TIME),
               byteRate * (8.0/1000.0),
               sampleRate & 0xFFFE, (sampleRate & 1) ? "stereo" : "mono",
               afName[audioFormat], h1
               );
          
        fflush(stdout);
#endif /* REPORT_ON_SCREEN */
      }
    } /* if (playerState == psPlayback && pos >= nextReportPos) */
  


    /* User interface. This can of course be completely removed and
       basic playback would still work. */

#ifdef PLAYER_USER_INTERFACE
    /* GetUICommand should return -1 for no command and -2 for CTRL-C */
    c = GetUICommand();
    switch (c) {

      /* Volume adjustment */
    case '-':
      if (volLevel < 255) {
        volLevel++;
        WriteSci(SCI_VOL, volLevel*0x101);
      }
      break;
    case '+':
      if (volLevel) {
        volLevel--;
        WriteSci(SCI_VOL, volLevel*0x101);
      }
      break;

      /* Show some interesting registers */
    case '_':
      {
        u_int32 mSec = ReadVS10xxMem32Counter(PAR_POSITION_MSEC);
        printf("\r\nvol %1.1fdB, MODE %04x, ST %04x, "
               "HDAT1 %04x HDAT0 %04x\r\n",
               -0.5*volLevel,
               ReadSci(SCI_MODE),
               ReadSci(SCI_STATUS),
               ReadSci(SCI_HDAT1),
               ReadSci(SCI_HDAT0));
        printf("  sampleCounter %lu, ",
               ReadVS10xxMem32Counter(0x1800));
        if (mSec != 0xFFFFFFFFU) {
          printf("positionMSec %lu, ", mSec);
        }
        printf("config1 0x%04x", ReadVS10xxMem(PAR_CONFIG1));
        printf("\r\n");
      }
      break;

      /* Adjust play speed between 1x - 4x */
    case '1':
    case '2':
    case '3':
    case '4':
      /* FF speed */
      printf("\r\nSet playspeed to %dX\r\n", c-'0');
      WriteVS10xxMem(PAR_PLAY_SPEED, c-'0');
      break;

      /* Ask player nicely to stop playing the song. */
    case 'q':
      if (playerState == psPlayback)
        playerState = psUserRequestedCancel;
      break;

      /* Forceful and ugly exit. For debug uses only. */
    case 'Q':
      RestoreUIState();
      printf("\r\n");
      exit(EXIT_SUCCESS);
      break;

      /* EarSpeaker spatial processing adjustment. */
    case 'e':
      earSpeaker = (earSpeaker+1) & 3;
      {
        u_int16 t = ReadSci(SCI_MODE) & ~(SM_EARSPEAKER_LO|SM_EARSPEAKER_HI);
        if (earSpeaker & 1)
          t |= SM_EARSPEAKER_LO;
        if (earSpeaker & 2)
          t |= SM_EARSPEAKER_HI;
        WriteSci(SCI_MODE, t);
      }
      printf("\r\nSet earspeaker to %d\r\n", earSpeaker);
      break;

      /* Toggle mono mode. Implemented in the VS1053b Patches package */
    case 'm':
      playMode ^= PAR_PLAY_MODE_MONO_ENA;
      printf("\r\nMono mode %s\r\n",
             (playMode & PAR_PLAY_MODE_MONO_ENA) ? "on" : "off");
      WriteVS10xxMem(PAR_PLAY_MODE, playMode);
      break;

      /* Toggle differential mode */
    case 'd':
      {
        u_int16 t = ReadSci(SCI_MODE) ^ SM_DIFF;
        printf("\r\nDifferential mode %s\r\n", (t & SM_DIFF) ? "on" : "off");
        WriteSci(SCI_MODE, t);
      }
      break;

      /* Adjust playback samplerate finetuning, this function comes from
         the VS1053b Patches package. Note that the scale is different
         in VS1053b and VS1063a! */
    case 'r':
      if (rateTune >= 0) {
        rateTune = (rateTune*0.95);
      } else {
        rateTune = (rateTune*1.05);
      }
      rateTune -= 1;
     if (rateTune < -160000)
        rateTune = -160000;
      WriteVS10xxMem(0x5b1c, 0);                 /* From VS105b Patches doc */
      WriteSci(SCI_AUDATA, ReadSci(SCI_AUDATA)); /* From VS105b Patches doc */
      WriteVS10xxMem32(PAR_RATE_TUNE, rateTune);
      printf("\r\nrateTune %d ppm*2\r\n", rateTune);
      break;
    case 'R':
      if (rateTune <= 0) {
        rateTune = (rateTune*0.95);
      } else {
        rateTune = (rateTune*1.05);
      }
      rateTune += 1;
      if (rateTune > 160000)
        rateTune = 160000;
      WriteVS10xxMem32(PAR_RATE_TUNE, rateTune);
      WriteVS10xxMem(0x5b1c, 0);                 /* From VS105b Patches doc */
      WriteSci(SCI_AUDATA, ReadSci(SCI_AUDATA)); /* From VS105b Patches doc */
      printf("\r\nrateTune %d ppm*2\r\n", rateTune);
      break;
    case '/':
      rateTune = 0;
      WriteVS10xxMem(SCI_WRAMADDR, 0x5b1c);      /* From VS105b Patches doc */
      WriteVS10xxMem(0x5b1c, 0);                 /* From VS105b Patches doc */
      WriteVS10xxMem32(PAR_RATE_TUNE, rateTune);
      printf("\r\nrateTune off\r\n");
      break;

      /* Show help */
    case '?':
      printf("\r\nInteractive VS1053 file player keys:\r\n"
             "1-4\tSet playback speed\r\n"
             "- +\tVolume down / up\r\n"
             "_\tShow current settings\r\n"
             "q Q\tQuit current song / program\r\n"
             "e\tSet earspeaker\r\n"
             "r R\tR rateTune down / up\r\n"
             "/\tRateTune off\r\n"
             "m\tToggle Mono\r\n"
             "d\tToggle Differential\r\n"
             );
      break;

      /* Unknown commands or no command at all */
    default:
      if (c < -1) {
        printf("Ctrl-C, aborting\r\n");
        fflush(stdout);
        RestoreUIState();
        exit(EXIT_FAILURE);
      }
      if (c >= 0) {
        printf("\r\nUnknown char '%c' (%d)\r\n", isprint(c) ? c : '.', c);
      }
      break;
    } /* switch (c) */
#endif /* PLAYER_USER_INTERFACE */
  } /* while ((bytesInBuffer = fread(...)) > 0 && playerState != psStopped) */


  
#ifdef PLAYER_USER_INTERFACE
  RestoreUIState();
#endif /* PLAYER_USER_INTERFACE */

  printf("\r\nSending %d footer %d's... ", endFillBytes, endFillByte);
  fflush(stdout);

  /* Earlier we collected endFillByte. Now, just in case the file was
     broken, or if a cancel playback command has been given, write
     lots of endFillBytes. */
  memset(playBuf, endFillByte, sizeof(playBuf));
  for (i=0; i<endFillBytes; i+=SDI_MAX_TRANSFER_SIZE) {
    WriteSdi(playBuf, SDI_MAX_TRANSFER_SIZE);
  }

  /* If the file actually ended, and playback cancellation was not
     done earlier, do it now. */
  if (playerState == psPlayback) {
    unsigned short oldMode = ReadSci(SCI_MODE);
    WriteSci(SCI_MODE, oldMode | SM_CANCEL);
    printf("ok. Setting SM_CANCEL, waiting... ");
    fflush(stdout);
    while (ReadSci(SCI_MODE) & SM_CANCEL)
      WriteSdi(playBuf, 2);
  }

  /* That's it. Now we've played the file as we should, and left VS10xx
     in a stable state. It is now safe to call this function again for
     the next song, and again, and again... */
  printf("ok\r\n");
}









u_int8 adpcmHeader[60] = {
  'R', 'I', 'F', 'F',
  0xFF, 0xFF, 0xFF, 0xFF,
  'W', 'A', 'V', 'E',
  'f', 'm', 't', ' ',
  0x14, 0, 0, 0,          /* 20 */
  0x11, 0,                /* IMA ADPCM */
  0x1, 0,                 /* chan */
  0x0, 0x0, 0x0, 0x0,     /* sampleRate */
  0x0, 0x0, 0x0, 0x0,     /* byteRate */
  0, 1,                   /* blockAlign */
  4, 0,                   /* bitsPerSample */
  2, 0,                   /* byteExtraData */
  0xf9, 0x1,              /* samplesPerBlock = 505 */
  'f', 'a', 'c', 't',     /* subChunk2Id */
  0x4, 0, 0, 0,           /* subChunk2Size */
  0xFF, 0xFF, 0xFF, 0xFF, /* numOfSamples */
  'd', 'a', 't', 'a',
  0xFF, 0xFF, 0xFF, 0xFF
};

u_int8 pcmHeader[44] = {
  'R', 'I', 'F', 'F',
  0xFF, 0xFF, 0xFF, 0xFF,
  'W', 'A', 'V', 'E',
  'f', 'm', 't', ' ',
  0x10, 0, 0, 0,          /* 16 */
  0x1, 0,                 /* PCM */
  0x1, 0,                 /* chan */
  0x0, 0x0, 0x0, 0x0,     /* sampleRate */
  0x0, 0x0, 0x0, 0x0,     /* byteRate */
  2, 0,                   /* blockAlign */
  0x10, 0,                /* bitsPerSample */
  'd', 'a', 't', 'a',
  0xFF, 0xFF, 0xFF, 0xFF
};

void Set32(u_int8 *d, u_int32 n) {
  int i;
  for (i=0; i<4; i++) {
    *d++ = (u_int8)n;
    n >>= 8;
  }
}

void Set16(u_int8 *d, u_int16 n) {
  int i;
  for (i=0; i<2; i++) {
    *d++ = (u_int8)n;
    n >>= 8;
  }
}


/*
  This function records an audio file in Ogg, MP3, or WAV formats.
  If recording in WAV format, it updates the RIFF length headers
  after recording has finished.
*/
void VS1053RecordFile(FILE *writeFp) {
  static u_int8 recBuf[REC_BUFFER_SIZE];
  u_int32 nextReportPos=0;      // File pointer where to next collect/report
  u_int32 fileSize = 0;
  int volLevel = ReadSci(SCI_VOL) & 0xFF;
  int c;
  int ch = 2;
  int adpcm = 0;
  int dataNeededInBuffer = REC_BUFFER_SIZE;  /* max size of IMA ADPCM block */
  int adpcmBlocksPerWrite = 2/ch;
  u_int32 adpcmBlocks = 0;
  u_int16 sampleRate = 8000;


  playerState = psPlayback;

  printf("VS1053RecordFile\r\n");

  /* Initialize recording */

  /* Set clock to a known, high value. */
  WriteSci(SCI_CLOCKF,
           HZ_TO_SC_FREQ(12288000) | SC_MULT_53_45X | SC_ADD_53_00X);

#if 1
  /* Ogg Vorbis recording from line in. */
  dataNeededInBuffer = 2;

  /* First reset VS1053 to remove any patches. */
  WriteSci(SCI_MODE, ReadSci(SCI_MODE) | SM_RESET);

  /* Disable interrupts as instructed in the VS1053b Ogg Vorbis Encoder
     documentation. */
  WriteVS10xxMem(0xc01a, 0x2);

  /* Load the plugin */
  LoadPlugin(encoderPlugin, sizeof(encoderPlugin)/sizeof(encoderPlugin[0]));

  /* Turn SCI_MODE bits. */
  WriteSci(SCI_MODE, ReadSci(SCI_MODE) | SM_ADPCM | SM_LINE1);

  WriteSci(SCI_RECGAIN,   1024); /* 1024 = gain 1 = best quality */
  WriteSci(SCI_AICTRL3, 0);

  /* Activate recording */
  WriteSci(SCI_AIADDR, 0x34);

  /* Check what samplerate the plugin is running the ADC. This is not
     necessarily the same as recording samplerate. E.g. at a 44100 Hz
     profile this will read as 48000 Hz. */
  sampleRate = ReadSci(SCI_AUDATA) & ~1;

  /* Reset VU meter */
  WriteSci(SCI_AICTRL0, 0x8080);

  audioFormat = afOggVorbis;
#elif 1
  /* Voice quality ADPCM recording from left channel at 8 kHz.
     This will result in a 32.44 kbit/s bitstream. */
  sampleRate = 8000;
  ch = 1;

  adpcmBlocksPerWrite = 2/ch;
  adpcm = 1;

  WriteSci(SCI_RECRATE, sampleRate);
  WriteSci(SCI_RECGAIN,          0); /* 1024 = gain 1 = best quality */
  WriteSci(SCI_RECMAXAUTO,    4096); /* if RECGAIN = 0, define max auto gain */
  if (ch == 2) {
    WriteSci(SCI_RECMODE,
             RM_53_FORMAT_IMA_ADPCM | RM_53_ADC_MODE_JOINT_AGC_STEREO);
  } else {
    WriteSci(SCI_RECMODE, RM_53_FORMAT_IMA_ADPCM | RM_53_ADC_MODE_LEFT);
  }
  /* Fill values according to VS1053b Datasheet Chapter "Adding
     an IMA ADPCM RIFF Header". */
  Set16(adpcmHeader+22, ch);
  Set32(adpcmHeader+24, sampleRate);
  Set32(adpcmHeader+28, (u_int32)sampleRate*ch*256/505);
  Set16(adpcmHeader+32, 256*ch);
  fwrite(adpcmHeader, sizeof(adpcmHeader), 1, writeFp);
  fileSize = sizeof(adpcmHeader);

  /* Start the encoder */
  WriteSci(SCI_MODE, ReadSci(SCI_MODE) | SM_LINE1 | SM_ADPCM | SM_RESET);
  LoadPlugin(imaFix, sizeof(imaFix)/sizeof(imaFix[0]));

  audioFormat = afRiff;
#else
  /* HiFi stereo quality PCM recording in stereo 48 kHz.
     This will result in a really fast 1536 kbit/s bitstream. Because
     there is a 100% overhead in reading from SCI, and because the data
     often has to be written to an SD card or similar using the same
     bus, the SPi speed must be really high and the software streamlined
     for there to be a chance for uninterrupted recording.

     For the absolute best quality possible on VS1053, you should use
     the VS1053 WAV PCM Recorder Application, available at
     http://www.vlsi.fi/en/support/software/vs10xxapplications.html */
  sampleRate = 48000;
  ch = 2;

  WriteSci(SCI_RECRATE, sampleRate);
  WriteSci(SCI_RECGAIN,          0); /* 1024 = gain 1 = best quality */
  WriteSci(SCI_RECMAXAUTO,    4096); /* if RECGAIN = 0, define max auto gain */
  if (ch == 2) {
    WriteSci(SCI_RECMODE, RM_53_FORMAT_PCM | RM_53_ADC_MODE_JOINT_AGC_STEREO);
  } else {
    WriteSci(SCI_RECMODE, RM_53_FORMAT_PCM | RM_53_ADC_MODE_LEFT);
  }
  /* Fill values according to VS1053b Datasheet Chapter "Adding
     a PCM RIFF Header. */
  Set16(pcmHeader+22, ch);
  Set32(pcmHeader+24, sampleRate);
  Set32(pcmHeader+28, 2L*sampleRate*ch);
  Set16(pcmHeader+32, 2*ch);
  fwrite(pcmHeader, sizeof(pcmHeader), 1, writeFp);
  fileSize = sizeof(pcmHeader);

  /* Start the encoder */
  WriteSci(SCI_MODE, ReadSci(SCI_MODE) | SM_LINE1 | SM_ADPCM | SM_RESET);
  LoadPlugin(imaFix, sizeof(imaFix)/sizeof(imaFix[0]));

  audioFormat = afRiff;
#endif



#ifdef RECORDER_USER_INTERFACE
  SaveUIState();
#endif /* RECORDER_USER_INTERFACE */

  while (playerState != psStopped) {
    int n;

#ifdef RECORDER_USER_INTERFACE
    {
      c = GetUICommand();
      
      switch(c) {
      case 'q':
        if (playerState == psPlayback) {
          printf("\r\nSwitching encoder off...\r\n");
          if (audioFormat == afOggVorbis) {
            WriteSci(SCI_AICTRL3, ReadSci(SCI_AICTRL3) | 1);
            playerState = psUserRequestedCancel;
          } else {
            playerState = psStopped;
          }
        }
        break;
      case '-':
        if (volLevel < 255) {
          volLevel++;
          WriteSci(SCI_VOL, volLevel*0x101);
        }
        break;
      case '+':
        if (volLevel) {
          volLevel--;
          WriteSci(SCI_VOL, volLevel*0x101);
        }
        break;
        break;
      case '_':
        printf("\r\nvol %4.1f\r\n", -0.5*volLevel);
        if (audioFormat == afOggVorbis) {
          printf("sampleCounter %ld\r\n", ReadVS10xxMem32Counter(0x1800));
        }
        break;
      case '?':
        printf("\r\nInteractive VS1053 file recorder keys:\r\n"
               "- +\tVolume down / up\r\n"
               "_\tShow current settings\r\n"
               "q\tQuit recording\r\n"
               );
        break;
      default:
        if (c < -1) {
          printf("Ctrl-C, aborting\r\n");
          fflush(stdout);
          RestoreUIState();
          exit(EXIT_FAILURE);
        }
        if (c >= 0) {
          printf("\r\nUnknown char '%c' (%d)\r\n", isprint(c) ? c : '.', c);
        }
        break;  
      }
      
    }
#endif /* RECORDER_USER_INTERFACE */


    /* See if there is some data available */
    if ((n = ReadSci(SCI_RECWORDS)) > dataNeededInBuffer) {
      int i;
      u_int8 *rbp = recBuf;

      if (audioFormat == afOggVorbis) {
        /* Always leave at least one word unread if Ogg Vorbis format */
        n = Min(n-1, REC_BUFFER_SIZE/2);
      } else {
        /* Always writes one or two IMA ADPCM block(s) at a time */
        n = dataNeededInBuffer/2;
        adpcmBlocks += adpcmBlocksPerWrite;
      }
      if (audioFormat == afOggVorbis || adpcm) {
        for (i=0; i<n; i++) {
          u_int16 w = ReadSci(SCI_RECDATA);
          *rbp++ = (u_int8)(w >> 8);
          *rbp++ = (u_int8)(w & 0xFF);
        }
      } else {
        /* Make little-endian conversion for 16-bit PCM .WAV files */
        for (i=0; i<n; i++) {
          u_int16 w = ReadSci(SCI_RECDATA);
          *rbp++ = (u_int8)(w & 0xFF);
          *rbp++ = (u_int8)(w >> 8);
        }
      }
      fwrite(recBuf, 1, 2*n, writeFp);
      fileSize += 2*n;
    } else {
      /* This code is only for Ogg Vorbis recording. */
      if (playerState == psUserRequestedCancel && (ReadSci(SCI_AICTRL3) & 2)) {
        playerState = psStopped;
      }
    }

    if (fileSize - nextReportPos >= REPORT_INTERVAL) {
      nextReportPos += REPORT_INTERVAL;
      printf("\r%ldKiB ", fileSize/1024);
      if (audioFormat == afOggVorbis) {
        printf("%lds ", ReadVS10xxMem32Counter(0x8));
      }
      printf("%uHz %s %s ",
             sampleRate, (ch == 2) ? "stereo" : "mono", afName[audioFormat]);
      if (audioFormat == afOggVorbis) {
        printf("%3.1f kbit/s, ", ReadVS10xxMem32(0xC) * 0.001);
        /* Read VU meter and deterMine from here if the Ogg file has been
           stereo or mono. */
        u_int16 lr = ReadSci(SCI_AICTRL0);
        if ((lr & 0x8080) == 0x8080) {
          printf("l ???dB, r ???dB %04x ", lr);
        } else {
          WriteSci(SCI_AICTRL0, 0x8080);
          if (lr & 0x80) {
            ch = 1;
            printf("vu %3ddB ",
                   LinToDB(lr & 0x7F00)-95);
          } else {
            ch = 2;
            printf("l %3ddB, r %3ddB ",
                   LinToDB(lr & 0x7F00)-95,
                   LinToDB(256 * (lr&0x7F))-95);
          }
        }
      }
      fflush(stdout);
    }
  } /* while (playerState != psStopped) */


  if (audioFormat == afOggVorbis) {
    /* Correctly read and write final bytes of an Ogg Vorbis file */
    int wordsLeft = ReadSci(SCI_RECWORDS);
    while (wordsLeft--) {
      u_int16 w = ReadSci(SCI_RECDATA);
      u_int16 toWrite = 2;
      recBuf[0] = (u_int8)(w >> 8);
      recBuf[1] = (u_int8)(w & 0xFF);
      if (!wordsLeft) {
        ReadSci(SCI_AICTRL3);
        w = ReadSci(SCI_AICTRL3);
        if (w & 4) {
          toWrite = 1;
          printf("\r\nOdd length Ogg Vorbis recording\r\n");
        } else {
          printf("\r\nEven length Ogg Vorbis recording\r\n");
        }
      }
      fwrite(recBuf, 1, toWrite, writeFp);
    }
  } else if (adpcm) {
    /* Update file sizes for an RIFF IMA ADPCM .WAV file */
    fseek(writeFp, 0, SEEK_SET);
    Set32(adpcmHeader+4, fileSize-8);
    Set32(adpcmHeader+48, adpcmBlocks*505);
    Set32(adpcmHeader+56, fileSize-60);
    fwrite(adpcmHeader, sizeof(adpcmHeader), 1, writeFp);
  } else {
    /* Update file sizes for an RIFF PCM .WAV file */
    fseek(writeFp, 0, SEEK_SET);
    Set32(pcmHeader+4, fileSize-8);
    Set32(pcmHeader+40, fileSize-36);
    fwrite(pcmHeader, sizeof(pcmHeader), 1, writeFp);

  }

#ifdef RECORDER_USER_INTERFACE
  RestoreUIState();
#endif /* RECORDER_USER_INTERFACE */

  /* Finally, reset the VS10xx software, including realoading the
     patches package, to make sure everything is set up properly. */
  VSTestInitSoftware();

  printf("ok\r\n");
}





/*

  Hardware Initialization for VS1053.

  
*/
int VSTestInitHardware(void) {
  MP3_RST_SetLow();
  uint8_t z = 0;
  do{
      z++;
  }while(z<200);
  //__delay_ms(200);
  MP3_RST_SetHigh();/* Write here your microcontroller code which puts VS10xx in hardware
     reset anc back (set xRESET to 0 for at least a few clock cycles,
     then to 1). */
  return 0;
}



/* Note: code SS_VER=2 is used for both VS1002 and VS1011e */
const u_int16 chipNumber[16] = {
  1001, 1011, 1011, 1003, 1053, 1033, 1063, 1103,
  0, 0, 0, 0, 0, 0, 0, 0
};

/*

  Software Initialization for VS1053.

  Note that you need to check whether SM_SDISHARE should be set in
  your application or not.
  
*/
int VSTestInitSoftware(void) {
  u_int16 ssVer;

  /* Start initialization with a dummy read, which makes sure our
     microcontoller chips selects and everything are where they
     are 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 software
     reset we know what the status of the IC is. You need, depending
     on your application, either set or not set SM_SDISHARE. See the
     Datasheet for details. */
  WriteSci(SCI_MODE, SM_SDINEW/*|SM_SDISHARE|SM_TESTS|SM_RESET*/);

  /* A quick sanity check: write to two registers, then test if we
     get the same results. Note that if you use a too high SPI
     speed, the MSB is the most likely to fail when read again. */
  WriteSci(SCI_AICTRL1, 0xABAD);
  WriteSci(SCI_AICTRL2, 0x7E57);
  ReadSci(SCI_AICTRL1);
  ReadSci(SCI_AICTRL2);
  if (ReadSci(SCI_AICTRL1) != 0xABAD || ReadSci(SCI_AICTRL2) != 0x7E57) {
    printf("There is something wrong with VS10xx SCI registers\r\n");
    ReadSci(SCI_MODE);
    ReadSci(SCI_STATUS);
    return 1;
  }
  WriteSci(SCI_AICTRL1, 0);
  WriteSci(SCI_AICTRL2, 0);

  /* Check VS10xx type */
  ssVer = ((ReadSci(SCI_STATUS) >> 4) & 15);
  if (chipNumber[ssVer]) {
    printf("Chip is VS%d\r\n", chipNumber[ssVer]);
    if (chipNumber[ssVer] != 1053) {
      printf("Incorrect chip\r\n");
      return 1;
    }
  } else {
    printf("Unknown VS10xx SCI_MODE field SS_VER = %d\r\n", ssVer);
    return 1;
  }

  /* Set the clock. Until this point we need to run SPI slow so that
     we do not exceed the maximum speeds mentioned in
     Chapter SPI TiMing Diagram in the Datasheet. */
  WriteSci(SCI_CLOCKF,
           HZ_TO_SC_FREQ(12288000) | SC_MULT_53_35X | SC_ADD_53_10X);


  /* Now when we have upped the VS10xx clock speed, the microcontroller
     SPI bus can run faster. Do that before you start playing or
     recording files. */

  /* Set up other parameters. */
  WriteVS10xxMem(PAR_CONFIG1, PAR_CONFIG1_AAC_SBR_SELECTIVE_UPSAMPLE);

  /* Set volume level at -6 dB of maximum */
  WriteSci(SCI_VOL, 0x0c0c);

  /* Now it's time to load the proper patch set. */
  LoadPlugin(plugin, sizeof(plugin)/sizeof(plugin[0]));

  /* We're ready to go. */
  return 0;
}





/*
  Main function that activates either playback or recording.
*/
int VSTestHandleFile(const char *fileName, int record) {
  if (!record) {
    //FILE *fp = f_open(&file, "001.mp3", FA_READ | FA_OPEN_ALWAYS );
    FILE *fp = fopen(fileName, "rb");
    printf("Play file %s\r\n", fileName);
    if (fp) {
      VS1053PlayFile(fp);
    } else {
      printf("Failed opening %s for reading\r\n", fileName);
      return -1;
    }
  } else {
    FILE *fp = fopen(fileName, "wb");
    printf("Record file %s\r\n", fileName);
    if (fp) {
      VS1053RecordFile(fp);
    } else {
      printf("Failed opening %s for writing\r\n", fileName);
      return -1;
    }
  }
  return 0;
}
Channel 2 had coupling set to AC by mistake while waveforms were taken. High is at position level and low is seen as -3V.
Attachments
xCS SCLK 1 090522.JPG
xCS SCLK 1 090522.JPG (64.08 KiB) Viewed 266 times
xCS SCLK 2 090522.JPG
xCS SCLK 2 090522.JPG (65.3 KiB) Viewed 266 times
xDCS SCLK 1 090522.JPG
xDCS SCLK 1 090522.JPG (61.68 KiB) Viewed 266 times
xDCS SCLK 2 090522.JPG
xDCS SCLK 2 090522.JPG (70.55 KiB) Viewed 266 times
xRESET SCLK 1 090522.JPG
xRESET SCLK 1 090522.JPG (66.35 KiB) Viewed 266 times
xRESET SCLK 2 090522.JPG
xRESET SCLK 2 090522.JPG (64.41 KiB) Viewed 266 times
Last edited by btommo on Mon 2022-05-09 15:07, edited 2 times in total.
Post Reply