Page 1 of 1

Problems with ogg recording on VS1053B

Posted: Wed 2014-09-17 13:54
by arranholmes

I am trying to design a simple compact system that will record to SD when power is applied and stop when power is removed, with high quality audio but long recording times.

I am having problems recording ogg using a vs1053b to an SD card using a PIC24. Im using the FSFAT library. The VS1053b is mounted on a sparkfun VS10XX breakout board and I'm using encoder version 1.70c.

It appears that I have successfully loaded the plugin from SD into the VS1053b, however when I activate recording I SCI_HDAT1 contains the value 0x02bc (or there about depending on which plugin I use).
When I read data 256 Words(512 bytes) from SCI_HDAT1 all appears to be ok and I can see I receive an ogg header.
I check SCI_HDAT1:0x01bd AICTRL3:0x0000 and 0x180F:0x0002 which all appear to be correct.
Then try to read another 256 words from SCI_HDAT1 however after reading 240 words DREQ goes low and stays low.
Sequential reads of SCI_HDAT1 do not show any increase before or after reading from SCI_HDAT0.

I have tried messing with the VOX setting and using profiles that do not support VOX but all produce the same results? I cannot understand why the encoder always stops after producing a small amount of data.

Any help would be gratefully received.

Arran Holmes

Here is the output from the application code:

Code: Select all

OGG Recorder debug console

LFN Enabled, Code page: 437
Mounting file system: OK
Init VS1053B....OK
Opening VS1053b ogg encoder plugin: OK
Uploading plugin to VS1053b
Type:1  Len:0x0020  Addr:0x1800
Type:0  Len:0x3edc  Addr:0x0034
Type:0  Len:0x003c  Addr:0x0010
Type:1  Len:0x0004  Addr:0x0000
Type:1  Len:0x2640  Addr:0x0100
Type:2  Len:0x158c  Addr:0x0110
Type:1  Len:0x0028  Addr:0x0004
Type:1  Len:0x0020  Addr:0x0019
Type:2  Len:0x0434  Addr:0x0bd8
Type:1  Len:0x2000  Addr:0x3000
Type:2  Len:0x0880  Addr:0x1000
Type:3  Len:0x0000  Addr:0x0034

Plugin address:0x0034
Closing plugin file...OK
Opening output file "00000001.OGG" OK
Starting plugin...OK




Code: Select all

 * File:   main.c
 * Author: Arran Holmes
 * Created on 10 September 2014, 23:10
// must be at the top!
#define USE_AND_OR

#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
#include <p24FJ64GA002.h>
#include "spi.h"
#include "PPS.h"
#include "ports.h"
#include "vs10xx_uc.h"
#include "pic24f.h"

#include "xprintf.h"
#include "uart_pic24f.h"

#include "ffconf.h"
#include "ff.h"
#include "diskio.h"

#include <libpic30.h> // __delay_ms() etc

#pragma config POSCMOD = NONE           // Primary Oscillator Select (Primary oscillator disabled)
#pragma config I2C1SEL = PRI            // I2C1 Pin Location Select (Use default SCL1/SDA1 pins)
#pragma config IOL1WAY = OFF            // IOLOCK Protection (Once IOLOCK is set, cannot be changed)
#pragma config OSCIOFNC = ON           // Primary Oscillator Output Function (OSC2/CLKO/RC15 functions as CLKO (FOSC/2))
#pragma config FCKSM = CSDCMD           // Clock Switching and Monitor (Clock switching and Fail-Safe Clock Monitor are disabled)
#pragma config FNOSC = FRCPLL           // Oscillator Select (Fast RC Oscillator with PLL module (FRCPLL))
#pragma config SOSCSEL = SOSC           // Sec Oscillator Select (Default Secondary Oscillator (SOSC))
#pragma config WUTSEL = LEG             // Wake-up timer Select (Legacy Wake-up Timer)
#pragma config IESO = ON                // Internal External Switch Over Mode (IESO mode (Two-Speed Start-up) enabled)

#pragma config WDTPS = PS32768          // Watchdog Timer Postscaler (1:32,768)
#pragma config FWPSA = PR128            // WDT Prescaler (Prescaler ratio of 1:128)
#pragma config WINDIS = ON              // Watchdog Timer Window (Standard Watchdog Timer enabled,(Windowed-mode is disabled))
#pragma config FWDTEN = ON              // Watchdog Timer Enable (Watchdog Timer is enabled)
#pragma config ICS = PGx1               // Comm Channel Select (Emulator EMUC1/EMUD1 pins are shared with PGC1/PGD1)
#pragma config GWRP = OFF               // General Code Segment Write Protect (Writes to program memory are allowed)
#pragma config GCP = OFF                // General Code Segment Code Protect (Code protection is disabled)
#pragma config JTAGEN = OFF              // JTAG Port Enable (JTAG port is enabled)

#define OUTPUT 0
#define INPUT 1
#define DREQ _RB14
#define DCS _LATA0
#define CS _LATB15
#define RESET _LATA1

#define noSectors 1
#define sectorSize 512
unsigned char buffer[noSectors][sectorSize];

// fsfat stuff
#if _USE_LFN
TCHAR Lfname[256];
FATFS FatFs;			// File system object

//BYTE Buff[4096];		// Working buffer
volatile UINT Timer;	// 1kHz increment timer
volatile WORD rtcYear = 2014;
volatile BYTE rtcMon = 4, rtcMday = 6, rtcHour, rtcMin, rtcSec;

void __attribute__((interrupt, auto_psv)) _T1Interrupt (void)
	static const BYTE samurai[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
	static UINT div1k;
	BYTE n;

	_T1IF = 0;			// Clear irq flag
	Timer++;			// Performance counter for this module
	disk_timerproc();	// Drive timer procedure of low level disk I/O module

	// Real Time Clock
	if (++div1k >= 1000) {
		div1k = 0;
		if (++rtcSec >= 60) {
			rtcSec = 0;
			if (++rtcMin >= 60) {
				rtcMin = 0;
				if (++rtcHour >= 24) {
					rtcHour = 0;
					n = samurai[rtcMon - 1];
					if ((n == 28) && !(rtcYear & 3)) n++;
					if (++rtcMday > n) {
						rtcMday = 1;
						if (++rtcMon > 12) {
							rtcMon = 1;

void WaitForDreq(int timeOut){
    int i =0;
    while(DREQ == 0){
        if (i == timeOut){
            //xputs("DREQ - Timeout");

    // need to add timeout

u_int16 ReadSci(u_int8 addr) {
    unsigned int i;
    u_int16 res;
    WaitForDreq(10); // Wait until DREQ is high
    CS = 0; // Activate xCS

    WriteSPI2(3); // Read command code

    WriteSPI2(addr); // SCI register number

    WriteSPI2(0); //
    res = ReadSPI2() << 8;

    res |= ReadSPI2();

    CS = 1; // Deactivate xCS
    return res;
void WriteSci(u_int8 addr, u_int16 data) {
    int i;
    WaitForDreq(10); // Wait until DREQ is high
    CS = 0; // Activate xCS

    WriteSPI2(2); // Write command code

    WriteSPI2(addr); // SCI register number

    WriteSPI2((u_int8)(data >> 8));

    WriteSPI2((u_int8)(data & 0xFF));
    CS = 1; // Deactivate xCS

unsigned char f_getc(FIL * fp){
  unsigned char buf;
  unsigned int bytesRead;
  FRESULT stat;

  stat = f_read(fp,&buf,1,&bytesRead);
  return buf;
  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 (f_getc(fp) != 'P' || f_getc(fp) != '&' || f_getc(fp) != 'H') {
    xputs("incorrect header\n");
    return 0xFFFF;

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

    if (type >= 4) {
      // Error condition
      xputs("Error invalied type record\n");
      return 0xFFFF;

    len = (u_int16)f_getc(fp) << 8;
    len |= f_getc(fp);// & ~1;
    addr = (u_int16)f_getc(fp) << 8;
    addr |= f_getc(fp);

    xprintf("Type:%d  Len:0x%04x  Addr:0x%04x\n",type,len,addr);
    if (type == 3) {
      // Execute record: we can now return with the execute address
      return addr;

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

    // Write data
    do {
      u_int16 data;
      data = (u_int16)f_getc(fp) << 8;
      data |= f_getc(fp);
      WriteSci(SCI_WRAM, data);
    } while ((len -= 2));
  xputs("Error invalied type record 0\n");
  return 0xFFFF;

DWORD get_fattime (void)
	DWORD tmr;

	// Pack date and time into a DWORD variable
	tmr =	  (((DWORD)rtcYear - 1980) << 25)
			| ((DWORD)rtcMon << 21)
			| ((DWORD)rtcMday << 16)
			| (WORD)(rtcHour << 11)
			| (WORD)(rtcMin << 5)
			| (WORD)(rtcSec >> 1);

	return tmr;

void put_rc (FRESULT rc)
	const char *str =
		"OK\0" "DISK_ERR\0" "INT_ERR\0" "NOT_READY\0" "NO_FILE\0" "NO_PATH\0"

	for (i = 0; i != rc && *str; i++) {
		while (*str++) ;
	xprintf("%s\n", str);

int main(int argc, char** argv) {
    //configure clock
    CLKDIV = 0x0100; //32Mhz

    //configure IO ports

    AD1PCFG = 0xFFFF; // all digital ports
    CS = 1;
    DCS = 1;
    RESET = 0;
    _RA3 = 1; // SD Card CS

    _TRISA0 = OUTPUT; //    #define DCS _LATB10
    _TRISB15 = OUTPUT; //    #define CS _LATB15
    _TRISB14 = INPUT; //     #define DREQ _RB14
    _TRISA2 = INPUT; //  SD_CD Card Detect
    _TRISA3 = OUTPUT; // SD_CS Chip Select

    _CN30PUE = 1; // SD_CD
    //Configure PPS
    //SPI2 - VS1053B

    //UART1 - Debug onsole

    //SPI1 - SDCARD


    // SPI2 Configuration VS1053B
    unsigned int SPI2CON1Value,	SPI2CON2Value,SPI2STATValue,test;
    OpenSPI2(SPI2CON1Value,SPI2CON2Value,SPI2STATValue );

    // SPI1 Configuration SDCARD
    unsigned int SPI1CON1Value,SPI1CON2Value,SPI1STATValue;

    SPI1CON2Value = 0x0000;

    // Start Timer1 in interval time of 1ms
    PR1 = FCY / 8 / 1000;
    _TCKPS0 = 1;	// Select prescaler Fcy/8
    _TON = 1;		// Start Timer1
    _T1IE = 1;		// Enable Timer1 interrupt
    // UART Configuration
    xdev_in(uart_getc);		// Join UART and console

    xputs("\nOGG Recorder debug console\n");

    xputs("\nInit FSFAT\n");
    xputs(_USE_LFN ? "LFN Enabled" : "LFN Disabled");
    xprintf(", Code page: %u\n", _CODE_PAGE);
    #if _USE_LFN	// Initialize file info structure if in LFN cfg
            Finfo.lfname = Lfname;
            Finfo.lfsize = sizeof Lfname;
    DSTATUS stat;
    stat = disk_initialize(0);
    if (stat != 0){
        xprintf("SD Card Error: %u\n", stat);
    xputs("SD CARD init OK\n");
    xputs("Mounting file system: ");
    put_rc(f_mount(&FatFs, "", (BYTE)0));

    xputs("Init VS1053B");
    RESET = 1;
    // Set VS1053 clock to 4.5x = 55.3 MHz
    WriteSci(SCI_CLOCKF, 0xC000);

    // Clear SCI_BASS
    WriteSci(SCI_BASS, 0);

    // Reset VS1053
    WaitForDreq(10);      // Wait until DREQ is high or 100 ms

    // Disable all interrupts except SCI
    WriteSci(SCI_WRAMADDR, 0xC01A);
    WriteSci(SCI_WRAM, 0x2);


    u_int16 startAddress;
    FIL file;
    xputs("\nOpening VS1053b ogg encoder plugin: ");

    xputs("Uploading plugin to VS1053b\n");
    startAddress = SpiLoadImageInto1053(&file);
    xprintf("\nPlugin address:0x%04x\n",startAddress);
    xputs("Closing plugin file...");
    // Set VS1053 mode bits as instructed in the VS1053b Ogg Vorbis Encoder
    // manual. Note: for microphone input, leave SMF_LINE1 unset!

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

    // Activate recording from the address we got. (In the case of the Ogg
    // Vorbis Encoder application, pluginStartAddr = 0x34.)

    xputs("Opening output file \"00000001.OGG\" ");

    //WriteSci(SCI_WRAMADDR, 0x180F); // VOX off
    //WriteSci(SCI_WRAM, 0x2);

    xputs("Starting plugin...");
    WriteSci(SCI_AIADDR, startAddress);

    u_int16 state = 0;
    u_int16 wordsToRead;
    u_int16 wordsWaiting;
    #define EndRecording() 0

    while (1) {

    /* See how many 16-bit words there are waiting in the VS1053 buffer */
    wordsWaiting = ReadSci(SCI_HDAT1);
    u_int16 t;
    u_int16 i;
    if (wordsWaiting >= 256){
        for (i=0; i!=512;) { // read 256 words or 512 bytes
          t = ReadSci(SCI_HDAT0);
          buffer[0][i++] = t >> 8;
          buffer[0][i++]= t & 0xFF;
          //f_putc((TCHAR)(t >> 8)  , &file);
          //f_putc((TCHAR)(t & 0xFF), &file);
    } else if (wordsWaiting >0) {
        for (i=0; i!=(wordsWaiting *2);) {
          t = ReadSci(SCI_HDAT0);
          buffer[0][i++] = t >> 8;
          buffer[0][i++]= t & 0xFF;
          //f_putc((TCHAR)(t >> 8)  , &file);
          //f_putc((TCHAR)(t & 0xFF), &file);

    t = ReadSci(SCI_AICTRL3);
    xprintf("\nAICTRL3:0x%04x ",t);
    return (EXIT_SUCCESS);

Re: Problems with ogg recording on VS1053B

Posted: Sat 2014-09-20 20:48
by Panu
We discussed this for a bit, and need to take another look. It's like the encoder stops running. Maybe you have a loading problem.

What happens if you read one word less?


Re: Problems with ogg recording on VS1053B

Posted: Sun 2014-09-21 0:36
by arranholmes

Thank you for your time, I've just managed to get it working it was my mistake! It appears that XDCS was not being driven properly by the MCU. Since it's not required for the design I've pulled it up with 100k and everything is now fine.

Thank you
Arran Holmes

Re: Problems with ogg recording on VS1053B

Posted: Sun 2014-09-21 20:50
by Panu

That's great! Well, chip select problems are the most common problems with VS10xx.

So, the above code is a working recorder code, then?


Re: Problems with ogg recording on VS1053B

Posted: Sun 2014-09-21 22:39
by arranholmes

It works as far as it's written. Power it up and it will write the ogg data to an SD card but that's about it. It's just a mix of your generic microcontroller code with the fsfat PIC24 example ( It's not nearly complete yet, it still needs lots of work. I will happily let you have a copy of the completed code/schematics etc.

The finished project will be a standalone recorder. It will start recording when switched on and continue until the battery is flat or it's turned off.
It will split files every X MB - this needs some work to split the ogg stream and create valid files.
Files will be time/date stamped (PIC24 RTCC)
Low ish off current draw.
Low operating current for long battery life.
Low voltage sensing for shutdown to produce valid ogg files and protect battery.
Fault tolerant.
UART for configuration (setting quality, date/time etc) and debugging.
Low cost.

The hardware will just be a VS1053b/VS1063 with a PIC24FJXXXXX, LiPo battery, SD card, on/off switch, good quality mic, maybe mic preamp (possibly MAX9814), associated passives xtals etc on the smallest footprint I can produce.

I need to try to reduce the noise as much as possible for crystal clear quiet voice or voice in a noisy environment. This will be my biggest challenge as my analogue design is not great to non-existant!


Re: Problems with ogg recording on VS1053B

Posted: Mon 2014-09-22 8:18
by Panu

Sounds great! For the analog part, be careful with the ground. Place the RCAP capacitor first and design the PCB so that you place the analog signal traces first and place a continuous ground plane below the analog traces. Avoid any cuts in the ground plane below the signal traces at all costs! The return current "reflection" of your analog signals follows the signal traces quite closely in the ground plane. If the return current can't flow freely below the signal trace, it must go around any obstacles. The different physical location of the signal trace and the flow of return current in the ground plane creates an area of problems which receives all kinds of noise.

Use a single ground plane. But for good ground layout, you can use a trick when designing the PCB: start with an idea of a separate ground plane for the analog and the digital. "Place" the analog ground plane below the top row of analog signals in the VS1053, extending over any other analog ICs, to below the analog connectors. Then "place" the digital ground plane over the rest of the PCB. Now, when you design the PCB, don't make any digital signals over the gap between the planes. Route the PCB to the end. And now comes the clever part: you can now remove the gap between the ground planes and the PCB will function as if it had separate analog and digital ground planes, even though there is just one physical ground plane. Furthermore, unlike with separate planes, "both planes" will have the same DC voltage, so your ground reference is always the same voltage.

In theory, anyway. If you can't route the PCB so that you can be sure that digital ground currents won't enter the analog ground area, then you're probably better off using separate analog and digital grounds with a gap between them, except below the top analog row of pins in the VS1053, where the analog and digital grounds in all cases must be together across the whole width of the IC.

Other than that, use separate (low-drop) linear regulators for analog, digital and the core voltage. Use sufficiently large capacitors before and after the linear regulator. Before the linear regulator (at the regulator's input) there must be more total capacitance than after. And for each AVDD, IOVDD and CVDD pin, place a 100nF capacitor.

Good luck with your project!


Re: Problems with ogg recording on VS1053B

Posted: Tue 2014-09-23 11:20
by Henrik
Hello Arran!

Great to hear you have solved your initial problems!

I'd like to have a word about splitting the Ogg stream into valid files, though. I don't know if you are aware of the fact that Ogg Vorbis files always need to have a specific header which tells the exact decoding parameters, and which can be upto 4 kilobytes long (our encoder creates headers between approximately 1000-1500 bytes)? Without this header the whole stream will be undecodable. So, if you want to make your life easy, just stop the encoder when you reach the end of a file, then restart it for the next file. If you need uninterrupted audio, then you need to store the header in a separate place so that you can apply it in front of any subsequent piece of the whole stream.

Good cutting points for separate files are just before the character string "OggS" which marks the beginning of the next Ogg frame. There are no guarantees that that string could not exist in the compressed random data by accident, but that is so unlikely that, quite honestly, I wouldn't worry about it.

Another thing:
I'd like to ask is how wide bitrates you are going to use. The reason I am asking is that current SD cards are fast on average, but they may be notoriously slow when you cross an internal erase page boundary, or when starting recording (these lock-ups seem to occur randomly to the user, but they have to do with the SD cards internal memory management / wear levelling). This is made even worse if your microcontroller updates the FAT tables while writing to a file, or whenever you start a new file. You might easily end up with an overflow of VS1053's internal 8 KiByte bitstream buffer in such a case. So, if your average bitrate is 64 kbit/s or more, you may need to create your own ring data buffer in your microcontroller if you want to guarantee absolute uninterrupted sound. Also, be sure that your code uses the continuous WRITE_MULTIPLE_BLOCKS method instead of writing single 512-byte blocks. With very old SD/MMC cards single block writes were adequately fast, but new SD cards are all optimized for high average speed when writing large amounts of data, so the single block write method can be slow beyond all belief.

Kind regards,
- Henrik

Re: Problems with ogg recording on VS1053B

Posted: Fri 2019-07-12 16:45
by davideferrario
Hello Henrik.

I am interested in your latest answer.

I am working on an ESP32 + VS1053 based Ogg audio record that will send audio files to a web server. I'd like split continuos audio into sequential files (of about 1 minute time lenght).

I am able to start VL1053 chip, start Ogg recording and save data to SD card.

I see your latest answer "If you need uninterrupted audio, then you need to store the header in a separate place so that you can apply it in front of any subsequent piece of the whole stream".

Ho can I identify the Ogg header I have to apply to every file?

Thank you