VS1053 recording example

Writing software for systems that use VLSI Solution's devices as slave codecs to a host microcontroller.
User avatar
Henrik
VLSI Staff
Posts: 1144
Joined: Tue 2010-06-22 14:10

VS1053 recording example

Post by Henrik » Fri 2012-03-16 17:14

Hello!

Here is a generic microcontroller example for generating your VS1053 recording machine. You'll have to create a few VS1053 interfacing functions for it to work (documented in the comments), but otherwise it is pretty standard C. Share and enjoy!

- Henrik

Code: Select all

/*

  Example code how to implement VS1053 Ogg Vorbis recording using
  the VS1053 Ogg Vorbis Recording plugin. The code is generic enough
  that it can be used pretty much unghanged on any microcontroller.

  The code makes the following assumptions:
  - <stdio.h> functions fopen(), fclose(), fgetc() and fputc() are available
  - The following VS1053 interfacing functions must exist:
    - void Write1053Sci(int regNo, u_int16 value): writes value to SCI register
    - u_int16 Read1053Sci(int regNo): reads value from SCI register
    - WaitFor1053Dreq(int timeOut): Waits for 1 us, then until DREQ goes up or
      timeOut is reached (timeOut = 1 is 10 ms).
    - StopKeyPushed(): Returns non-zero if used has pushed Stop button on the
      recording device.
      
  NOTE!
  This code serves as an example of how to create generic C microcontroller
  code for VS1053 recording.

  Author: Henrik Herranen 2011.
  Copyright: Use freely for any project with any VLSI IC.
  Warranty: Absolutely none whatsoever.

*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


typedef unsigned short u_int16;
typedef short s_int16;


/* returns minumum of two given numbers */
#define min(a,b) (((a)<(b))?(a):(b))


/* Loads an image file into VS1053 from a file. */
auto u_int16 SpiLoadImageInto1053(register __i0 FILE *fp);


/* Main recording function */
int RecordVS1053(void) {
  FILE *outFP = NULL, *inFP = NULL;
  u_int16 pluginStartAddr;
  u_int16 state = 0;
  u_int16 i;
  u_int16 wordsToRead;
  u_int16 wordsWaiting;


  /* Following VS1053 operations are according to the
     VS1053b Ogg Vorbis Encoder application manual. For
     details read Chapter Loading and Starting the Code. */

  /* Set VS1053 clock to 4.5x = 55.3 MHz */
  Write1053Sci(SCI_CLOCKF, 0xC000);
  WaitFor1053Dreq(TIMER_TICKS/10);

  /* Clear SCI_BASS */
  Write1053Sci(SCI_BASS, 0);

  /* Reset VS1053 */
  Write1053Sci(SCI_MODE, SMF_SDINEW | SMF_RESET);
  WaitFor1053Dreq(TIMER_TICKS/10);        // Wait until DREQ is high or 100 ms

  // Disable all interrupts except SCI
  Write1053Sci(SCI_WRAMADDR, VS1053_INT_ENABLE);
  Write1053Sci(SCI_WRAM, 0x2);

  /* Load the recorder application to VS1053. This is a selected image file
     available from the "VS1053 Off Vorbis Encoder Application" package,
     downloadable at
     http://www.vlsi.fi/en/support/software/vs10xxapplications.html */
  inFP = fopen("rcplugin.img", "rb");
  if (!inFP)
    return 1;
  pluginStartAddr = SpiLoadImageInto1053(inFP);
  fclose(inFP);
  inFP = NULL;

  /* If loading failed, give up. */
  if (pluginStartAddr == 0xFFFF)
    return 1;

  /*  Now open output file. It's better to do this before activating
      recording so that even if opening the output file is slow,
      you will not lose data. */
  outFP = fopen("voice01.ogg", "wb");
  if (!outFP)
    return 1;

  /* Set VS1053 mode bits as instructed in the VS1053b Ogg Vorbis Encoder
     manual. Note: if using microphone input, leave SMF_LINE1 unset! */
  Write1053Sci(SCI_MODE, SMF_LINE1 | SMF_ADPCM | SMF_SDINEW);

  /* Rec level: 1024 = 1. If 0, use AGC */
  Write1053Sci(SCI_AICTRL1, 1024);
  /* Maximum AGC level: 1024 = 1. Only used if SCI_AICTRL1 is set to 0. */
  Write1053Sci(SCI_AICTRL2, 0);
  /* Miscellaneous bits that also must be set before recording. */
  Write1053Sci(SCI_AICTRL3, 0);

  /* Activate recording from the address we got. (In the case of the Ogg
     Vorbis Encoder application, pluginStartAddr = 0x34.) */
  Write1053Sci(SCI_AIADDR, pluginStartAddr);
  WaitFor1053Dreq(TIMER_TICKS/10);


  /*
    By now we have:
    - Initialized the VS1053
    - Loaded the VS1053 recording plugin
    - Opened the output file
    - Activated the VS1053 recording plugin

    So, now it's time to record. Below is the recording loop.

    The variable "state" controls the progression of recording as follows:
    state = 0: normal recording.
    state = 1: user has requested end of recording, microcontroller has
        also requested this from the VS1053 recording plugin.
    state = 2: the VS1053 plugin has stopped recording, but the
        microcontroller is still collecting data from the VS1053 buffers.
    state = 3: recording finished.
  */


  /* Main loop */
  while (state < 3) {

    /* Example microcontroller Stop key = end recording */
    /* Change following line to suit your own microcontroller. */
    if (StopKeyPushed() && !state) {
      state = 1;
      Write1053Sci(SCI_AICTRL3, 1); //Request to stop recording
    }

    /* See how many 16-bit words there are waiting in the VS1053 buffer */
    wordsWaiting = Read1053Sci(SCI_HDAT1);

    /* If user has requested stopping recording, and VS1053 has
       stopped recording, proceed to the next state. */
    if (state == 1 && Read1053Sci(SCI_AICTRL3) & (1<<1)) {
      state = 2;
      /* It is important to reread the HDAT1 register once after
         VS1053 has stopped. Otherwise there is the chance that
         a few more words have just arrived although we just
         read this register. So, do NOT optimize the following
         line away! */
      wordsWaiting = Read1053Sci(SCI_HDAT1);
    }

    /* Read and transfer whole 512-byte (256-word) disc blocks at
       the time. The only exception is when recording ends: then
       allow for a non-full block. */
    while (wordsWaiting >= ((state < 2) ? 256 : 1)) {
      wordsToRead = min(wordsWaiting, 256);
      wordsWaiting -= wordsToRead;

      /* If this is the very last block, read one 16-bit word less,
         because it will be handled later separately. */
      if (state == 2 && !wordsWaiting)
        wordsToRead--;

      /* Transfer one full data block, or if this is the very last
         block, all data that's left except for the last word. */
      {
        u_int16 t;
        u_int16 i;
        for (i=0; i<wordsToRead; i++) {
          t = Read1053Sci(SCI_HDAT0);
          fputc(t >> 8  , outFP);
          fputc(t & 0xFF, outFP);
        }
      }

      /* If this is the last data block... */
      if (wordsToRead < 256) {
        u_int16 lastWord;
        state = 3;

        /* ... read the very last word of the file */
        lastWord = Read1053Sci(SCI_HDAT0);

        /* Always write first half of the last word. */
        fputc(lastWord >> 8  , outFP);

        /* Read twice SCI_AICTRL3, then check bit 2 of latter read. */
        Read1053Sci(SCI_AICTRL3);
        if (!(Read1053Sci(SCI_AICTRL3) & (1<<2))) {
          /* Write last half of the last word only if bit 2 is clear. */
          fputc(lastWord & 0xFF, outFP);
        }
      } /* if (wordsToRead < 256) */
    } /* while (wordsWaiting >= ((state < 2) ? 256 : 1)) */

  } /* while (state < 3), end of main loop */


  /* That's it! We've perfectly recorded an Ogg Vorbis file, so now
     we only need to close the file and be happy about it. */
  if (outFP)
    fclose(outFP);

  /* Finally, reset VS1053 so that we will hear no more monitor audio */
  Write1053Sci(SCI_MODE, SMF_SDINEW | SMF_RESET);

  /* Success! */
  return 0;
}





/*
  SpiLoadImageInto1053() loads an image from a file into the memory
  of VS1053(), then returns the start address to the caller. If it
  fails, it will return 0xFFFFU.

  The file format is as follows:

  The file starts with three characters, "P&H".

  The rest of the file are followed by records that are in the
  following format:
  Tp  L1 L0  A1 A0  D0 D1 D2 ....

  Tp: The type of the record. Values 0..3 are valid:
  - 0 = Code (Instructions)
  - 1 = X memory
  - 2 = Y memory
  - 3 = Execute (start address)

  (L1<<8) + L0: Length of the record in bytes. Always an even number.

  (A1<<8) + A0: Start address of the record. If the record is of type
    Execute, then this is the execution start address. It is also the
    end of the file.

  Dx: Data. Read this two bytes at a time and send to VS1053 in a
    big-endian fashion, as shown in the code below.
*/
auto u_int16 SpiLoadImageInto1053(register __i0 FILE *fp) {
  s_int16 type;

  if (fgetc(fp) != 'P' || fgetc(fp) != '&' || fgetc(fp) != 'H') {
    return 0xFFFF;
  }

  while ((type = fgetc(fp)) >= 0) {
    /*
      offsets are: Code (I), X Mem, Y Mem when written through SCI_WRAM.
      See VS1053 datasheet's documentation for SCI registers
      SCI_WRAM and SCI_WRAMADDR for details.
    */
    static u_int16 offsets[3] = {0x8000U, 0x0U, 0x4000U};
    u_int16 len, addr;

    if (type >= 4) {
      /* Error condition */
      return 0xFFFF;
    }

    len = (u_int16)fgetc(fp) << 8;
    len |= fgetc(fp) & ~1;
    addr = (u_int16)fgetc(fp) << 8;
    addr |= fgetc(fp);
    if (type == 3) {
      /* Execute record: we can now return with the execute address */
      return addr;
    }

    /* Set address */
    Write1053Sci(SCI_WRAMADDR, addr + offsets[type]);

    /* Write data */
    do {
      u_int16 data;
      data = (u_int16)fgetc(fp) << 8;
      data |= fgetc(fp);
      Write1053Sci(SCI_WRAM, data);
    } while ((len -= 2));
  }
  return 0xFFFF;
}
Good signatures never die. They just fade away.

fgomes
User
Posts: 9
Joined: Mon 2016-01-11 15:53

Re: VS1053 recording example

Post by fgomes » Tue 2016-05-17 17:13

Hi, I was trying to record the mic signal using a VS1053 board and the ogg encoder, and I have used the code from this post and also the Adafruit sample code. After activating recording (writing 0x34 to SCI_AIADDR) I keep getting that the number of words waiting is zero (reading SCI_HDAT1 register). Is there any idea of what could be the reason and how to debug this issue?

Thanks in advance

Fernando

fgomes
User
Posts: 9
Joined: Mon 2016-01-11 15:53

Re: VS1053 recording example

Post by fgomes » Wed 2016-05-18 12:54

I'm posting here the code that I'm currently testing and getting no data as I described in my previous post. In order to be easier to understand I removed the use of include files and libraries, putting all the code in a single file. I'm using Arduino and the ESP8266 MCU. I have the same hardware setup using the VS1053 as a player (decoder) and it is running flawlessly, so the problem seems not to be hardware (unless there are some hardware requirements specific for using it as an encoder). The specific modules that I'm using are the NodeMCU board for the ESP8266 and the LC Technology board for the VS1053. As it has been discussed in this forum and in this blog(http://www.bajdi.com/lcsoft-vs1053-mp3-module/), I've made the hardware patch to the VS1053 board, soldering together the pins 33 and 34 (otherwise the board did't work as an audio decoder, it booted as a MIDI player). So this is the current code (it doesn't process the encoded audio, it just checks if there is encoded audio ready):

#include <SPI.h>
#include <FS.h>

/* SCI registers */

#define SCI_MODE 0x00
#define SCI_STATUS 0x01
#define SCI_BASS 0x02
#define SCI_CLOCKF 0x03
#define SCI_DECODE_TIME 0x04
#define SCI_AUDATA 0x05
#define SCI_WRAM 0x06
#define SCI_WRAMADDR 0x07
#define SCI_HDAT0 0x08 /* VS1063, VS1053, VS1033, VS1003, VS1011 */
#define SCI_IN0 0x08 /* VS1103 */
#define SCI_HDAT1 0x09 /* VS1063, VS1053, VS1033, VS1003, VS1011 */
#define SCI_IN1 0x09 /* VS1103 */
#define SCI_AIADDR 0x0A
#define SCI_VOL 0x0B
#define SCI_AICTRL0 0x0C /* VS1063, VS1053, VS1033, VS1003, VS1011 */
#define SCI_MIXERVOL 0x0C /* VS1103 */
#define SCI_AICTRL1 0x0D /* VS1063, VS1053, VS1033, VS1003, VS1011 */
#define SCI_ADPCMRECCTL 0x0D /* VS1103 */
#define SCI_AICTRL2 0x0E
#define SCI_AICTRL3 0x0F

#define SM_DIFF (1<< 0)
#define SM_LAYER12 (1<< 1) /* VS1063, VS1053, VS1033, VS1011 */
#define SM_RECORD_PATH (1<< 1) /* VS1103 */
#define SM_RESET (1<< 2)
#define SM_CANCEL (1<< 3) /* VS1063, VS1053 */
#define SM_OUTOFWAV (1<< 3) /* VS1033, VS1003, VS1011 */
#define SM_OUTOFMIDI (1<< 3) /* VS1103 */
#define SM_EARSPEAKER_LO (1<< 4) /* VS1053, VS1033 */
#define SM_PDOWN (1<< 4) /* VS1003, VS1103 */
#define SM_TESTS (1<< 5)
#define SM_STREAM (1<< 6) /* VS1053, VS1033, VS1003, VS1011 */
#define SM_ICONF (1<< 6) /* VS1103 */
#define SM_EARSPEAKER_HI (1<< 7) /* VS1053, VS1033 */
#define SM_DACT (1<< 8)
#define SM_SDIORD (1<< 9)
#define SM_SDISHARE (1<<10)
#define SM_SDINEW (1<<11)
#define SM_ENCODE (1<<12) /* VS1063 */
#define SM_ADPCM (1<<12) /* VS1053, VS1033, VS1003 */
#define SM_EARSPEAKER1103 (1<<12) /* VS1103 */
#define SM_ADPCM_HP (1<<13) /* VS1033, VS1003 */
#define SM_LINE1 (1<<14) /* VS1063, VS1053 */
#define SM_LINE_IN (1<<14) /* VS1033, VS1003, VS1103 */
#define SM_CLK_RANGE (1<<15) /* VS1063, VS1053, VS1033 */
#define SM_ADPCM_1103 (1<<15) /* VS1103 */

#define VS1053_INT_ENABLE 0xc01a


uint8_t _CS;
uint8_t _DCS;
uint8_t _DREQ;
uint8_t _RST;
uint16_t wordsWaiting;
uint16_t pluginStartAddr;

uint16_t LoadPlugin(char *plugname) {

File plugin = SPIFFS.open(plugname, "r");
if (!plugin) {
Serial.println("Couldn't open the plugin file");
Serial.println(plugin);
return 0xFFFF;
}

if ((plugin.read() != 'P') ||
(plugin.read() != '&') ||
(plugin.read() != 'H')) {
Serial.println("Plugin not good");
return 0xFFFF;
}

uint16_t type;

// Serial.print("Patch size: "); Serial.println(patchsize);
while ((type = plugin.read()) >= 0) {
uint16_t offsets[] = {0x8000UL, 0x0, 0x4000UL};
uint16_t addr, len;

Serial.print("type: "); Serial.println(type, HEX);

if (type >= 4) {
plugin.close();
return 0xFFFF;
}

len = plugin.read();
len <<= 8;
len |= plugin.read() & ~1;
addr = plugin.read();
addr <<= 8;
addr |= plugin.read();
Serial.print("len: "); Serial.print(len);
Serial.print(" addr: $"); Serial.println(addr, HEX);

if (type == 3) {
// execute rec!
plugin.close();
return addr;
}

// set address
WriteRegister(SCI_WRAMADDR, addr + offsets[type]);
// write data
do {
uint16_t data;
data = plugin.read(); data <<= 8;
data |= plugin.read();
WriteRegister(SCI_WRAMADDR, data);
} while ((len -=2));
}

plugin.close();
return 0xFFFF;
}


void WaitForDREQ(void)
{
while (!digitalRead(_DREQ)) {
yield();
}
}

void WaitForDREQt(uint8_t waitTime)
{
unsigned long startTime = millis();

while (!digitalRead(_DREQ)) {
yield();
if (millis() - startTime > waitTime*10)
break;
}
}

void ControlModeOn(void)
{
if(_DCS!=0xFF)
digitalWrite(_DCS, HIGH);
digitalWrite(_CS, LOW);
}
void ControlModeOff(void)
{
digitalWrite(_CS, HIGH);
}

void WriteRegister(uint8_t _reg, uint16_t _value)
{
ControlModeOn();
delayMicroseconds(1); // tXCSS
SPI.transfer(0x02); // Write operation
SPI.transfer(_reg); // Which register
SPI.transfer(_value >> 8); // Send hi byte
SPI.transfer(_value & 0xff); // Send lo byte
delayMicroseconds(1); // tXCSH
WaitForDREQ();
ControlModeOff();
}

uint16_t ReadRegister(uint8_t _reg)
{
uint16_t result;

ControlModeOn();
delayMicroseconds(1); // tXCSS
SPI.transfer(0x03); // Read operation
SPI.transfer(_reg); // Which register
result = SPI.transfer(0xff) << 8; // read high byte
result |= SPI.transfer(0xff); // read low byte
delayMicroseconds(1); // tXCSH
WaitForDREQ();
ControlModeOff();
return result;
}

bool Init()
{

_CS = 16;
_DCS = 15;
_DREQ = 4;
_RST = 5;

pinMode(12, FUNCTION_2); //MISO
pinMode(13, FUNCTION_2); //MOSI
pinMode(14, FUNCTION_2); //SCLK
SPI.begin(); // Start SPI

pinMode(_RST,OUTPUT);
digitalWrite(_RST,LOW);
pinMode(_CS,OUTPUT);
digitalWrite(_CS,HIGH);
pinMode(_DCS,OUTPUT);
digitalWrite(_DCS,HIGH);
pinMode(_DREQ,INPUT); // DREQ is an input
delay(100);
digitalWrite(_RST,HIGH); //Mp3ReleaseFromReset();

SPI.setClockDivider(SPI_CLOCK_DIV64); // Slow SPI!

delay(100);

/* Set VS1053 clock to 4.5x = 55.3 MHz */
WriteRegister(SCI_CLOCKF, 0xC000);
WaitForDREQt(100);

/* Clear SCI_BASS */
WriteRegister(SCI_BASS, 0);

/* Reset VS1053 */
WriteRegister(SCI_MODE, SM_SDINEW | SM_RESET);
WaitForDREQt(100); // Wait until DREQ is high or 100 ms

/* Disable all interrupts except SCI */
WriteRegister(SCI_WRAMADDR, VS1053_INT_ENABLE);
WriteRegister(SCI_WRAM, 0x2);

/* Load Plugin */
pluginStartAddr = LoadPlugin("/venc08k1q00.img");

/* Set VS1053 mode bits as instructed in the VS1053b Ogg Vorbis Encoder
manual. Note: if using microphone input, leave SMF_LINE1 unset! */
WriteRegister(SCI_MODE, SM_LINE1 | SM_ADPCM | SM_SDINEW);

/* Rec level: 1024 = 1. If 0, use AGC */
WriteRegister(SCI_AICTRL1, 1024);
/* Maximum AGC level: 1024 = 1. Only used if SCI_AICTRL1 is set to 0. */
WriteRegister(SCI_AICTRL2, 0);
/* Miscellaneous bits that also must be set before recording. */
WriteRegister(SCI_AICTRL3, 0);

/* Activate recording from the address we got. (In the case of the Ogg
Vorbis Encoder application, pluginStartAddr = 0x34.) */
WriteRegister(SCI_AIADDR, pluginStartAddr);
WaitForDREQt(100);

while(true) {
wordsWaiting = ReadRegister(SCI_HDAT1);
Serial.println(wordsWaiting);
yield();
}
}


void setup() {
Serial.begin(115200); // Start Serial
Serial.setTimeout(5);
pinMode(0, INPUT); // 'Flash' button

// Initialize file system.
if (!SPIFFS.begin()) {
Serial.println("Failed to mount file system");
return;
}
Init();
}

void loop() {
}



The code output is the following:

type: 1
len: 32 addr: $1800
type: 0
len: 14780 addr: $34
type: 1
len: 4 addr: $0
type: 1
len: 9758 addr: $100
type: 2
len: 5516 addr: $110
type: 1
len: 40 addr: $4
type: 0
len: 60 addr: $10
type: 1
len: 32 addr: $19
type: 2
len: 1076 addr: $BD8
type: 1
len: 8192 addr: $3000
type: 2
len: 2176 addr: $1000
type: 3
len: 0 addr: $34
0
0
0
0
0
0
0
0

... it keeps always printing zeros, so there are no words available when calling ReadRegister(SCI_HDAT1). Any idea of why this could happen? Is there any hardware design in the VS1053 board that could cause this wrong behavior?

I've also seen this post - viewtopic.php?f=7&t=595&start=10 - where it rfers that to be able to work with the board I'm using it would be necessary to add this code:

wram_write ( 0xC017, 3 ) ; // GPIO DDR = 3
wram_write ( 0xC019, 0 ) ; // GPIO ODATA = 0

I've tried it but it didn't work also (same behavior as before):

WriteRegister(SCI_WRAMADDR, 0xC017);
WriteRegister(SCI_WRAM, 0x03);
WriteRegister(SCI_WRAMADDR, 0xC019);
WriteRegister(SCI_WRAM, 0x00);

Any tip on how to debug this problem will be appreciated!

Best regards

Fernando

User avatar
Henrik
VLSI Staff
Posts: 1144
Joined: Tue 2010-06-22 14:10

Re: VS1053 recording example

Post by Henrik » Wed 2016-05-18 14:47

Hello Fernando!

I read through your code and cannot find any glaring errors. All your VS1053 register writes look sensible, as does the output of LoadPlugin().

To me it looks you must have 99% of everything right; and taking into consideration that you have been able to master playback earlier, there must be just that tiny bit missing that we can't quite put our fingers on!

Some things to check:
- Does DREQ get high after you have disabled the interrupts but before you run LoadPlugin()?
- Does DREQ get high after you have run LoadPlugin()?
- Does DREQ get high after you write written pluginStartAddr to SCI_AIADDR?

The reason I'm asking is that your DREQ waiting routine has a timeout (as it should normally be); it would be interesting to know whether, in these cases, that function has returned because of the timeout or because DREQ actually went high.

Then, although seemingly stupid questions, but still worth a check:
- After you've started the encoder, do you still get sensible values if you read e.g. SCI_AICTRL0 (0) and SCI_AICTRL1 (1024)?

Kind regards,
- Henrik
Good signatures never die. They just fade away.

fgomes
User
Posts: 9
Joined: Mon 2016-01-11 15:53

Re: VS1053 recording example

Post by fgomes » Wed 2016-05-18 16:17

Hi Henrik

Thanks you very much for your reply. My DREQ test function initially didn't had a timeout and the code still run to the end, so the DREQ seems to appear normally, I just introduced the timeout in this last version. I will try to read the registers you mentioned (SCI_AICTRL0 (0) and SCI_AICTRL1 (1024)) in order to check if they still have the expected values and post here the result.

Best regards

Fernando

fgomes
User
Posts: 9
Joined: Mon 2016-01-11 15:53

Re: VS1053 recording example

Post by fgomes » Wed 2016-05-18 17:02

Hi Henrik

I was checking the VorbisEncoder170v.pdf and noticed that it is mandatory to initialize the SCI_AICTRL0 register, but I am not doing it in my code (it isn't done also in the vs1053oggrec.c example code). Is it necessary to write to SCI_AICTRL0 only if I want to use the VU functionality? If I don't write to it, the expected read value is 0?

Best regards

Fernando

User avatar
Henrik
VLSI Staff
Posts: 1144
Joined: Tue 2010-06-22 14:10

Re: VS1053 recording example

Post by Henrik » Thu 2016-05-19 11:02

Hello Fernando,

I noticed that you don't initialize SCI_AICTRL0. I didn't mention it because, as you correctly assumed, it is zero after boot, and because it doesn't matter if you don't use VU meter functionality.

Kind regards,
- Henrik
Good signatures never die. They just fade away.

pradeepsysargus
User
Posts: 3
Joined: Tue 2016-12-27 6:31

Re: VS1053 recording example

Post by pradeepsysargus » Tue 2016-12-27 6:57

Hello Henrik,

We are using VS1053 for recording functionality via Micro-phone input. The micro-controller is STM32F4xx.

I am using the code that is given on top of this topic.. I implemented the Write1053Sci(), Read1053Sci() functions for STM32F4 interfacing with VS1053 via SPI lines.

Does your code accept the plugin image venc44k2q05.img ?
We are using SD card to store the venc44k2q05.img and also store the recorded output ogg file "MyRecord.ogg".
We used Chan's FATFS library for it.
The recording stops when a button is pressed, and StopRecording variable goes to 1( handled by Interrupt function).

Please find the code below:

Code: Select all

typedef unsigned short u_int16;
typedef short s_int16;

volatile uint8_t StopRecording = 0;

/* returns minumum of two given numbers */
#define min(a,b) (((a)<(b))?(a):(b))

/* Loads an image file into VS1053 from a file. */
u_int16 SpiLoadImageInto1053(FIL fp);

int RecordVS1053(void) {
  //FILE *outFP = NULL, *inFP = NULL;
  FIL outFP,inFP;
	u_int16 pluginStartAddr;
  u_int16 state = 0;
  u_int16 i;
  u_int16 wordsToRead;
  u_int16 wordsWaiting;

      
     FATFS playFs;
		  FRESULT fr;
     FILINFO filinfo; 
		 uint16_t file_size;
	   uint16_t br; 
		 uint8_t res; //, rval = 0;
	
	
	 /* Following VS1053 operations are according to the
     VS1053b Ogg Vorbis Encoder application manual. For
     details read Chapter Loading and Starting the Code. */

  /* Set VS1053 clock to 4.5x = 55.3 MHz */
  vs1053_WriteRegisterW(SCI_CLOCKF, 0xC000);
	
	while(!(GPIOC->IDR & 0x0008)); //Wait for DREQ to go high ..
	
	/* Clear SCI_BASS */
  vs1053_WriteRegisterW(SCI_BASS, 0);

	  /* Reset VS1053 */
  vs1053_WriteRegisterW(SCI_MODE, vs1053_ReadRegister(SCI_MODE) | SMF_SDINEW | SMF_RESET);
  while(!(GPIOC->IDR & 0x0008)); //Wait for DREQ to go high ..

	  // Disable all interrupts except SCI
  vs1053_WriteRegisterW(SCI_WRAMADDR, VS1053_INT_ENABLE); // Write 0xC01A to SCI_WRAMADDR
  vs1053_WriteRegisterW(SCI_WRAM, 0x2);
	
	/* Load the recorder application to VS1053. This is a selected image file
     available from the "VS1053 Off Vorbis Encoder Application" package,
     downloadable at
     http://www.vlsi.fi/en/support/software/vs10xxapplications.html */
		 
		if(f_mount(&playFs, "SD:", 1) != FR_OK) 
					return 1;
		if (f_open(&inFP, "venc44k2q05.img", FA_READ ) != FR_OK)
				{
						f_mount(0, "", 1); 
					  return 1;
				}
			  pluginStartAddr = SpiLoadImageInto1053(inFP);
				f_close(&inFP);

							
		/* If loading failed, give up. */
		if (pluginStartAddr == 0xFFFF)
		{
			printf("PlugInStarAddr is 0xFFFF ..Failed\r\n");
    return 1;
		}
	/*  Now open output file. It's better to do this before activating
      recording so that even if opening the output file is slow,
      you will not lose data. */
			
		 if (f_open(&outFP, "MyRecord.ogg", FA_WRITE | FA_READ | FA_CREATE_ALWAYS) != FR_OK)
		 {
			 f_mount(0, "", 1);
			 printf("File Open 'MyRecord.ogg' has failed\r\n");
			 return 1;
		 }
		 
		 /* Set VS1053 mode bits as instructed in the VS1053b Ogg Vorbis Encoder
     manual. Note: if using microphone input, leave SMF_LINE1 unset! */
		// Write1053Sci(SCI_MODE, SMF_LINE1 | SMF_ADPCM | SMF_SDINEW);
			 vs1053_WriteRegisterW(SCI_MODE, vs1053_ReadRegister(SCI_MODE) | SMF_ADPCM | SMF_SDINEW);

		/* Rec level: 1024 = 1. If 0, use AGC */
			vs1053_WriteRegisterW(SCI_AICTRL1, 1024);
		/* Maximum AGC level: 1024 = 1. Only used if SCI_AICTRL1 is set to 0. */
			vs1053_WriteRegisterW(SCI_AICTRL2, 0);
		/* Miscellaneous bits that also must be set before recording. */
			vs1053_WriteRegisterW(SCI_AICTRL3, 0);

			/* Activate recording from the address we got. (In the case of the Ogg
     Vorbis Encoder application, pluginStartAddr = 0x34.) */
			vs1053_WriteRegisterW(SCI_AIADDR, pluginStartAddr); // Need to check.
		  while(!(GPIOC->IDR & 0x0008)); //Wait for DREQ to go high ..

			/*
    By now we have:
    - Initialized the VS1053
    - Loaded the VS1053 recording plugin
    - Opened the output file
    - Activated the VS1053 recording plugin

    So, now it's time to record. Below is the recording loop.

    The variable "state" controls the progression of recording as follows:
    state = 0: normal recording.
    state = 1: user has requested end of recording, microcontroller has
        also requested this from the VS1053 recording plugin.
    state = 2: the VS1053 plugin has stopped recording, but the
        microcontroller is still collecting data from the VS1053 buffers.
    state = 3: recording finished.
    */

		/* Main loop */
  while (state < 3) {

	 /* Example microcontroller Stop key = end recording */
    /* Change following line to suit your own microcontroller. */
    if (StopRecording == 1 && !state) {
      state = 1;
      vs1053_WriteRegisterW(SCI_AICTRL3, 1); //Request to stop recording
    }

		/* See how many 16-bit words there are waiting in the VS1053 buffer */
    wordsWaiting = vs1053_ReadRegister(SCI_HDAT1);

		
		/* If user has requested stopping recording, and VS1053 has
       stopped recording, proceed to the next state. */
    if (state == 1 && vs1053_ReadRegister(SCI_AICTRL3) & (1<<1)) {
      state = 2;
      /* It is important to reread the HDAT1 register once after
         VS1053 has stopped. Otherwise there is the chance that
         a few more words have just arrived although we just
         read this register. So, do NOT optimize the following
         line away! */
      wordsWaiting = vs1053_ReadRegister(SCI_HDAT1);
    }

		  /* Read and transfer whole 512-byte (256-word) disc blocks at
       the time. The only exception is when recording ends: then
       allow for a non-full block. */
    while (wordsWaiting >= ((state < 2) ? 256 : 1)) {
      wordsToRead = min(wordsWaiting, 256);
      wordsWaiting -= wordsToRead;

      /* If this is the very last block, read one 16-bit word less,
         because it will be handled later separately. */
      if (state == 2 && !wordsWaiting)
        wordsToRead--;

      /* Transfer one full data block, or if this is the very last
         block, all data that's left except for the last word. */
      {
        u_int16 t;
        u_int16 i;
        for (i=0; i<wordsToRead; i++) {
          t = vs1053_ReadRegister(SCI_HDAT0);
          //fputc(t >> 8  , outFP);
          //fputc(t & 0xFF, outFP);
          f_putc(t>>8, &outFP);
					f_putc(t & 0xFF, &outFP);
				
				}
      }

      /* If this is the last data block... */
      if (wordsToRead < 256) {
        u_int16 lastWord;
        state = 3;

        /* ... read the very last word of the file */
        lastWord = vs1053_ReadRegister(SCI_HDAT0);

        /* Always write first half of the last word. */
        //fputc(lastWord >> 8  , outFP);
					f_putc(lastWord >> 8, &outFP);
			
        /* Read twice SCI_AICTRL3, then check bit 2 of latter read. */
        vs1053_ReadRegister(SCI_AICTRL3);
        if (!(vs1053_ReadRegister(SCI_AICTRL3) & (1<<2))) {
          /* Write last half of the last word only if bit 2 is clear. */
          //fputc(lastWord & 0xFF, outFP);
					f_putc(lastWord & 0xFF, &outFP);
				}
      } /* if (wordsToRead < 256) */
    } /* while (wordsWaiting >= ((state < 2) ? 256 : 1)) */

  } /* while (state < 3), end of main loop */

	  /* That's it! We've perfectly recorded an Ogg Vorbis file, so now
     we only need to close the file and be happy about it. */
 //  if (outFP)
 //   fclose(outFP);
		
		f_close(&outFP);
		f_mount(0, "", 1);
	
	 /* Finally, reset VS1053 so that we will hear no more monitor audio */
  vs1053_WriteRegisterW(SCI_MODE, vs1053_ReadRegister(SCI_MODE) | SMF_SDINEW | SMF_RESET);

	/* Success! */
  return 0;

}

/*
  SpiLoadImageInto1053() loads an image from a file into the memory
  of VS1053(), then returns the start address to the caller. If it
  fails, it will return 0xFFFFU.

  The file format is as follows:

  The file starts with three characters, "P&H".

  The rest of the file are followed by records that are in the
  following format:
  Tp  L1 L0  A1 A0  D0 D1 D2 ....

  Tp: The type of the record. Values 0..3 are valid:
  - 0 = Code (Instructions)
  - 1 = X memory
  - 2 = Y memory
  - 3 = Execute (start address)

  (L1<<8) + L0: Length of the record in bytes. Always an even number.

  (A1<<8) + A0: Start address of the record. If the record is of type
    Execute, then this is the execution start address. It is also the
    end of the file.

  Dx: Data. Read this two bytes at a time and send to VS1053 in a
    big-endian fashion, as shown in the code below.
*/

u_int16 SpiLoadImageInto1053(FIL fp) {
	s_int16 type;
	
//	 if (fgetc(fp) != 'P' || fgetc(fp) != '&' || fgetc(fp) != 'H') {
//    return 0xFFFF;
//  }
    char test1[2],test2[2],test3[2],test4[2],test5[2];
		
    f_gets(test1,2,&fp);
		f_gets(test2,2,&fp);
		f_gets(test3,2,&fp);
		
		if(test1[0] != 'P' || test2[0] != '&' || test3[0] != 'H')
		{
			printf("P&H are not Found\r\n");
			return 0xFFFF;
		}
		printf("P&H are Found\r\n");

	//while ((type = fgetc(fp)) >= 0) {
		
		f_gets(test4,2,&fp);
		type = test4[0];
		
	while ( type  >= 0) {
				
	 /*
      offsets are: Code (I), X Mem, Y Mem when written through SCI_WRAM.
      See VS1053 datasheet's documentation for SCI registers
      SCI_WRAM and SCI_WRAMADDR for details.
    */
    static u_int16 offsets[3] = {0x8000U, 0x0U, 0x4000U};
    u_int16 len, addr;

    if (type >= 4) {
      /* Error condition */
      return 0xFFFF;
    }

    //len = (u_int16)fgetc(fp) << 8;
    //len |= fgetc(fp) & ~1;
    //addr = (u_int16)fgetc(fp) << 8;
    //addr |= fgetc(fp);
		  f_gets(test5,2,&fp);
			len = (u_int16)test5[0] << 8;
			f_gets(test5,2,&fp);
			len |= test5[0] & ~1;
			f_gets(test5,2,&fp);
			addr = (u_int16)test5[0] << 8;
			f_gets(test5,2,&fp);
			addr |= test5[0];
			
		
		if (type == 3) {
      /* Execute record: we can now return with the execute address */
      return addr;
    }

    /* Set address */
    vs1053_WriteRegisterW(SCI_WRAMADDR, addr + offsets[type]);

    /* Write data */
    do {
      u_int16 data;
      //data = (u_int16)fgetc(fp) << 8;
      //data |= fgetc(fp);
       f_gets(test5,2,&fp);
			 data = (u_int16)test5[0] << 8;
			 f_gets(test5,2,&fp);
			 data |= test5[0];
			
			vs1053_WriteRegisterW(SCI_WRAM, data);
    } while ((len -= 2));
  
	  f_gets(test4,2,&fp);
		type = test4[0];

	}
	
	  return 0xFFFF;
}


void main()
{
printf("Start Recording now...\r\n");
RecordVS1053();
printf("Recording ended..\r\n");
			
		 while (1)
			{
			
	               }
}

pradeepsysargus
User
Posts: 3
Joined: Tue 2016-12-27 6:31

Re: VS1053 recording example

Post by pradeepsysargus » Tue 2017-01-03 6:49

In the function: SpiLoadImageInto1053()

We were using f_gets() ... we replaced it with f_read().. we are now able to get 0x34 at pluginStartAddr...

The data that we are getting at the line wordsWaiting = vs1053_ReadRegister(SCI_HDAT1) is like 64KBytes, it is taking lot of time to put that data to "MyRecord.ogg" file. It writes in chunks of 256 words (512 bytes) as defined by the for loop:

for (i=0; i<wordsToRead; i++) {
t = vs1053_ReadRegister(SCI_HDAT0);
f_putc(t>>8, &outFP);
f_putc(t & 0xFF, &outFP);
}

I am getting an output ogg file containing mostly with the characters ÿ or ý.. It is equivalent to 0xff or 0xfd

The ogg headers are not added in the output file, although it is returning 0x34 at pluginStarAddr.

Please suggest ..

Thanks,

User avatar
pasi
VLSI Staff
Posts: 1575
Joined: Thu 2010-07-15 16:04

Re: VS1053 recording example

Post by pasi » Thu 2017-01-05 20:02

You should get much smaller values from SCI_HDAT1. I think the maximum value should be about 4 kilobytes, and the amount right after starting the app is only the Ogg Vorbis header, which is much smaller.

(Henrik will be back from holiday next week and can comment then.)

You could try adding a small wait after the software reset before loading the application. It is also possible to verify the successful loading by going over the image again, just instead of writing to SCI_WRAM you read from it and compare the values. (Write to SCI_WRAMADDR as before.)

Or simply after loading the image, write 0x8034 to SCI_WRAMADDR, then read SCI_WRAM maybe 32 times and print the results. That should give us an idea if there is an issue with the upload.
Visit https://www.facebook.com/VLSISolution VLSI Solution on Facebook

Post Reply