Netduino home hardware projects downloads community

Jump to content


The Netduino forums have been replaced by new forums at community.wildernesslabs.co. This site has been preserved for archival purposes only and the ability to make new accounts or posts has been turned off.
Photo

So how does the SD card stuff work anyway?


  • Please log in to reply
7 replies to this topic

#1 Chad

Chad

    Member

  • Members
  • PipPip
  • 16 posts

Posted 14 August 2011 - 10:09 PM

I have been trying to get a VB1053 (an MP3 decoder) to play an MP3 file I'm loading off of an SD card on a Netduino (not the Plus). And while it "works" I get an audible clicking sound during playback that I'm guessing is because I'm talking to both the SD card and the VM1053 of the same SPI bus. Even when I try to make everything happen sequentially, (read bytes from SD card, play bytes, read more, play more, etc.) I get the same audio glitch. No matter what I set my buffer size to, the glitch is exactly the same, meaning if I buffer 1k or 20k it's exactly the same. So, I'm just guessing, but it seems to me the StorageDevice's buffer is being filled asynchronously by the Netduino, and it's cutting into the SPI causing the glitch. Could this be true? Any other ideas/suggestions of what to do next? I should note, I'm very very new at working with microcontrollers. Thanks for any help, Chad

#2 Edward

Edward

    Advanced Member

  • Members
  • PipPipPip
  • 38 posts
  • LocationLondon, UK

Posted 16 August 2011 - 11:08 PM

Can you post a link to some info on the VB1053 please. I tried googling it but didn't find any relevant results.

#3 Chad

Chad

    Member

  • Members
  • PipPip
  • 16 posts

Posted 19 August 2011 - 01:46 AM

Of course I typeo'ed the ID. It's a VS1053, not a VB1053. What I acctualy have is this board from sparkfun. http://www.sparkfun.com/products/8954 Thanks again for any help.

#4 Edward

Edward

    Advanced Member

  • Members
  • PipPipPip
  • 38 posts
  • LocationLondon, UK

Posted 21 August 2011 - 01:50 PM

On the Netduino Plus the SD card sits on the SPI0 bus and the SPI1 bus is available to the user. But you say you are not using the Plus so what SD card solution are you using and on which interface? How have you got the chip select lines for the SD card and the VS1053 connected?

#5 Chad

Chad

    Member

  • Members
  • PipPip
  • 16 posts

Posted 26 August 2011 - 02:33 AM

On the Netduino Plus the SD card sits on the SPI0 bus and the SPI1 bus is available to the user. But you say you are not using the Plus so what SD card solution are you using and on which interface? How have you got the chip select lines for the SD card and the VS1053 connected?


I'm using an adafruit wave shield. http://www.adafruit.com/products/94

I just have the DAC wires disconnected, so it's in effect just an SD Card shield.

Pin 10 is the SD Card chip select.

Pin 7 is the VS1053 command chip select (XCS)
Pin 6 is the VS1053 data chip select (XDS)

I also have DREQ and Reset from the VS1053 wired up. (along with the SPI pins 11, 12 & 13)

const Microsoft.SPOT.Hardware.Cpu.Pin XCSPin = Pins.GPIO_PIN_D7;
const Microsoft.SPOT.Hardware.Cpu.Pin XDCSPin = Pins.GPIO_PIN_D6;
const Microsoft.SPOT.Hardware.Cpu.Pin DREQPin = Pins.GPIO_PIN_D5;
const Microsoft.SPOT.Hardware.Cpu.Pin ResetPin = Pins.GPIO_PIN_D4;

I have simplified my code to this:
do
{
bytesRead = fs.Read(bufs[0], 0, bufsize);
spi.Write(bufs[0]);
} while (bytesRead == bufsize);

I know this would write bad stuff out at the end of the file, but I was just trying to get past the glitch in the audio so I was trying a simple test.

No matter the size for bufsize or the buffer size passed into the FileStream constructor, it sounds the same.


Thanks,
Chad

#6 netdruinofreak

netdruinofreak

    New Member

  • Members
  • Pip
  • 2 posts

Posted 26 September 2011 - 04:31 PM

Hi Chad,
I am also trying to create an MP3 player using Netduino with an MP3 shield+SD card.
http://www.sparkfun.com/products/10628

However, I can't seem to get the audio to come out. The shield works with my Arduino. When I port the code over to Netduino It did not work. Can you share your set up for the VS1053 (MODE, CLOCK, VOL, etc...)?

I have spent many long nights and weekends without any luck.

Regards,
Richard


I have been trying to get a VB1053 (an MP3 decoder) to play an MP3 file I'm loading off of an SD card on a Netduino (not the Plus). And while it "works" I get an audible clicking sound during playback that I'm guessing is because I'm talking to both the SD card and the VM1053 of the same SPI bus. Even when I try to make everything happen sequentially, (read bytes from SD card, play bytes, read more, play more, etc.) I get the same audio glitch. No matter what I set my buffer size to, the glitch is exactly the same, meaning if I buffer 1k or 20k it's exactly the same.

So, I'm just guessing, but it seems to me the StorageDevice's buffer is being filled asynchronously by the Netduino, and it's cutting into the SPI causing the glitch. Could this be true? Any other ideas/suggestions of what to do next?

I should note, I'm very very new at working with microcontrollers.

Thanks for any help,

Chad



#7 NicholasSTG

NicholasSTG

    Member

  • Members
  • PipPip
  • 16 posts

Posted 16 December 2011 - 10:00 PM

Hi Chad,
I am also trying to create an MP3 player using Netduino with an MP3 shield+SD card.
http://www.sparkfun.com/products/10628

However, I can't seem to get the audio to come out. The shield works with my Arduino. When I port the code over to Netduino It did not work. Can you share your set up for the VS1053 (MODE, CLOCK, VOL, etc...)?

I have spent many long nights and weekends without any luck.

Regards,
Richard


Hi, I am having the same issues. I was curious if you had managed to make yours work.

#8 Serkan Polat

Serkan Polat

    Member

  • Members
  • PipPip
  • 17 posts

Posted 20 December 2011 - 09:47 AM

Hi, I am having the same issues. I was curious if you had managed to make yours work.



if you still looking for an answer, here is the code i used:


vs1053 class (sparkfun's)
using System;
using System.IO;
using Microsoft.SPOT.Hardware;
using System.Threading;
using SecretLabs.NETMF.Hardware.NetduinoPlus;
using System.Runtime.CompilerServices;

namespace smg.Drivers.Music
{
    public static class Vs1053
    {
        // GPIO ports:
        static private OutputPort _reset;
        static private InterruptPort _dreq;

        const Cpu.Pin PinBsync = Pins.GPIO_PIN_D2;
        const Cpu.Pin PinDreq = Pins.GPIO_PIN_D3;
        const Cpu.Pin PinReset = Pins.GPIO_PIN_D6;  // NOTE: This doesn't map to an actual pin
        const Cpu.Pin PinCs = Pins.GPIO_PIN_D9;

        // Define SPI Configuration for VS1053 MP3 decoder:
        static private readonly SPI.Configuration DataConfig = new SPI.Configuration(PinBsync, false, 0, 0, false, true, 3000, SPI.SPI_module.SPI1);
        static private readonly SPI.Configuration CmdConfig = new SPI.Configuration(PinCs, false, 0, 0, false, true, 3000, SPI.SPI_module.SPI1);
        static private SPI _spi;

        // Registers:
        const int RegisterSciMode = 0x00;
        const int RegisterSciVol = 0xFF;
        const int RegisterSciClockf = 0x03;

        public static readonly int BufferSize = 256;
        private const ushort SciMode = 0x880;  // SM_SDINEW (default) + SM_EARSPEAKER_HI

        static private bool _isInitialized;
        static private readonly byte[] ReadBuffer = new byte[BufferSize];
        static private readonly byte[] CmdBuffer = new byte[4];

        static private readonly AutoResetEvent AutoResetEvent = new AutoResetEvent(false);


        private static bool _initialized;       // Pins created succesfully when true
        private static int _duration = -1;      // Duration of file playing (in seconds)
        private static int _position = -1;      // Current Play Position (in seconds)
        private static object _source;        // Filename or Resource Reference
        private static bool _busy = false;      // Playing a file when true
        private static bool _pause = false;     // Paused when true
        private static long _rawSize = -1;
        private static long _readSoFar = -1;
        private static int _bitrate = 0;
        private static int _mpegLayer = 0;
        private static bool _stop = false;
        private static int _lastPos = -1;
        static private byte[] block = new byte[32];
        static private byte[] cmdBuffer = new byte[4];
        private static bool _cancelMusic = false;

        public static event musicFinished onMusicFinished;
        public delegate void musicFinished();


        public static bool CancelMusic { set { _cancelMusic = value; } get { return _cancelMusic; } }

        public static void Initialize()
        {
            if (_isInitialized)
                Shutdown();

            _spi = new SPI(CmdConfig);
            _reset = new OutputPort(PinReset, true);
            _dreq = new InterruptPort(PinDreq, false, Port.ResistorMode.PullUp, Port.InterruptMode.InterruptEdgeBoth);
            _dreq.OnInterrupt += _dreq_OnInterrupt;

            _isInitialized = true;

            Reset();

            CommandWrite(RegisterSciMode, SciMode | (1 << 2));
            CommandWrite(RegisterSciClockf, 7 << 13);
            CommandWrite(RegisterSciVol, 0xFEFE);

            _spi.Config = DataConfig;
        }

        private static void _dreq_OnInterrupt(uint port, uint state, DateTime time)
        {
            if (state == 0)
            {
                AutoResetEvent.Set();
            }
            else
            {
                AutoResetEvent.WaitOne();
            }
            _dreq.ClearInterrupt();
        }

        private static void Reset()
        {
            _reset.Write(false);
            Thread.Sleep(1);
            _reset.Write(true);
            Thread.Sleep(1);
        }

        static private void CommandWrite(byte address, ushort data)
        {
            CmdBuffer[0] = 0x02;
            CmdBuffer[1] = address;
            CmdBuffer[2] = (byte)(data >> 8);
            CmdBuffer[3] = (byte)data;

            _spi.Write(CmdBuffer);
        }

        static private ushort CommandRead(byte address)
        {
            CmdBuffer[0] = 0x03;
            CmdBuffer[1] = address;
            CmdBuffer[2] = 0;
            CmdBuffer[3] = 0;

            _spi.WriteRead(CmdBuffer, CmdBuffer, 2);

            ushort command = CmdBuffer[0];
            if (command < 100)
            {
                throw new ArgumentOutOfRangeException("volume");
            }
            //SetVolumePercent(volume, volume);
            return command;
        }

        public static void SetVolumePercent(int leftChannel, int rightChannel)
        {
            if (leftChannel < 0 || leftChannel > 100)
                throw new ArgumentOutOfRangeException("leftChannel");
            if (rightChannel < 0 || rightChannel > 100)
                throw new ArgumentOutOfRangeException("rightChannel");

            // TODO: Invert decibel value, divide by percent, call SetVolume(ushort leftChannel, ushort rightChannel)
        }

        public static void SetVolume(ushort bothChannels)
        {
            CommandWrite(RegisterSciVol, bothChannels);  // TODO: This doesn't work outside the Initialize() method
        }

        public static void SetVolume(ushort leftChannel, ushort rightChannel)
        {
            SetVolume((ushort)(leftChannel * 256 + rightChannel)); // TODO: Verify no loss of fidelity
        }

        public static void SendData(FileStream fileStream)
        {
            var size = fileStream.Length - fileStream.Length % BufferSize;
            for (var i = 0; i < size; i += BufferSize)
            {
                fileStream.Read(ReadBuffer, 0, BufferSize);

                _spi.Write(ReadBuffer);
                while (_dreq.Read() == false) { Thread.Sleep(1); }

                if (_cancelMusic) { break; }
            }
        }

        public static void Shutdown()
        {
            if (!_isInitialized) return;

            Reset();

            _spi.Dispose();
            _reset.Dispose();
            _dreq.Dispose();

            _isInitialized = false;
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public static void PlayLoop()
        {
            byte[] b;

            _cancelMusic = false;
            _busy = true;
            _stop = false;
            _readSoFar = 0;
            _lastPos = -1;

            if (_source is string)
            {
                FileStream iFile = new FileStream((string)_source, FileMode.Open, FileAccess.Read);
                _rawSize = iFile.Length;
                b = new byte[2 * 1024];
                int count = iFile.Read(b, 0, b.Length);
                while (count > 0)
                {
                    SendData(B);
                    count = iFile.Read(b, 0, b.Length);
                    if (_cancelMusic) { break; }
                }
                iFile.Close();
            }
            else if (_source is byte[])
            {
                b = (byte[])_source;
                _rawSize = b.Length;
                SendData(B);
            }

            _busy = false;
            _readSoFar = -1;
            _position = _duration;
            _bitrate = 0;
            _mpegLayer = 0;
            if (onMusicFinished!=null)
            {
                onMusicFinished();
            }

        }
        [MethodImpl(MethodImplOptions.Synchronized)]
        public static void SendData(byte[] data)
        {
            int size = data.Length - data.Length % 32;

            //_spi.Config = dataConfig;
            for (int i = 0; i < size; i += 32)
            {

                while (_dreq.Read() == false)
                    Thread.Sleep(1);    // wait till done


                Array.Copy(data, i, block, 0, 32);

                _spi.Write(block);
                _readSoFar += 32;       // We need to know how many bytes we've read to calculate header size to get duration

                while (_pause)
                    Thread.Sleep(1);    // Wait while paused
            }
        }

        public static string Filename
        {
            get
            {
                if (_source is string)
                    return (string)_source;
                else
                    return "Embedded Resource";
            }
            set { _source = value; }
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public static void Play()
        {
            new Thread(PlayLoop).Start();
        }
    }
}


and here how i use it

Vs1053.Initialize();
            Vs1053.SetVolume(0xFEFE);

Vs1053.Filename = "\\SD\test.mp3";
                  
                    Vs1053.PlayLoop();


also i implemented two new thing
1-
Vs1053.onMusicFinished += new Vs1053.musicFinished(Vs1053_onMusicFinished);
fires event when music finishes..(useful when threading music playback.. normally it is not async)

2-
Vs1053.CancelMusic = true;

this cancels music immediately , useful when you want to cancel and play next file..




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users

home    hardware    projects    downloads    community    where to buy    contact Copyright © 2016 Wilderness Labs Inc.  |  Legal   |   CC BY-SA
This webpage is licensed under a Creative Commons Attribution-ShareAlike License.