Fez Panda 2 SD Card Corruption

Hi,

I use a Panda 2 for a data logging application and see instances where the SD card appears to become corrupt. I can no longer access the card from a pc (Win XP throws a corrupt file dialog) and the Panda no longer creates new files on the SD Card. I am forced to physically remove the card from the Panda, put it in a card reader and reformat the card. After a reformat everything will be ok again for another 10 or 20 logs until the same problem occurs again. I have not been able to track this issue down so any thoughts or ideas would be appreciated.

The hardware and software overview :

A FEZ Panda 2 powered by USB. The MOD pin is connected to ground through a rotary switch. The switch is N/O for logging mode. If the switch is closed, then the USB power cable attached the Panda will go into remote storage mode so that the files on the SD card can be accessed by the PC for offload. If the switch is not closed, then the Panda will start up and log forever until the USB is removed. The SD card is always in the device.

Software Detail :

When the Panda is first powered up it checks the MOD, if it is low then remote storage mode is started, otherwise a new file is created (using FileStream) and a timer is started so that every 5 seconds the file stream will be flushed. This is because I have no idea currently when the power will be pulled. Other threads are started that retrieve data via ADC and CAN etc, these threads all write between 8 and 20 bytes every 50ms or so. There is a lock on the write and flush operations to try to prevent a flush occuring mid write and visa versa. Becasue of the large CAN section in the code I do not have any free bytes to allocate to a large buffer so that I can write the data in a more ordered fashion.

The code :

Parts of the main code are copied below with comments :



using System;
using System.IO;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.IO;
using Microsoft.SPOT.Hardware;
using GHIElectronics.NETMF.FEZ;
using GHIElectronics.NETMF.Hardware;
using GHIElectronics.NETMF.IO;
using GHIElectronics.NETMF.USBClient;

namespace LoggerPanda
{
    public class Program
    {
        // logging vars
        private static Object thisLock = new Object();
        public static FileStream loggerFileStream;
        private static PersistentStorage persistantStorage;
        private static string rootDirectory;

        // timer vars
        static Timer flushTimer;

       // data vars
       private static byte[] speedBytes = new byte[12];

        // main application entry point
        public static void Main()
        {
            // check if we are in remote storage mode
            CheckOperationMode();

            // initialise the persistant storage so we can log data and set the root directory
            persistantStorage = new PersistentStorage("SD");
            persistantStorage.MountFileSystem();
            rootDirectory = VolumeInfo.GetVolumes()[0].RootDirectory;

            // create the next available file name, we can let the gc pickup the var i
            for (int i = 0; i < 1000; i++)
            {
                // if the next number does not exist then initialise the new file stream
                if (File.Exists(rootDirectory + @ "\" + i + ".txt") == false)
                {
                    loggerFileStream = new FileStream(rootDirectory + @ "\" + i + ".txt", FileMode.OpenOrCreate, FileAccess.ReadWrite);
                    break;
                }
            }

            // setup the speed data bytes with header bytes
            speedBytes[0] = 127;
            speedBytes[1] = 127;
            speedBytes[2] = 251;
            speedBytes[3] = 251;

            // setup the timers for the pace calcs, the pcu update and memory card flushing
            flushTimer = new Timer(new TimerCallback(FlushData), null, 5000, 5000);
            speedAndPosnTimer = new Timer(new TimerCallback(UpdateSpeedAndPosition), null, 500, 200); 

            // subscribe to canbus events so we can receive data
            canBus.DataReceivedEvent += new CANDataReceivedEventHandler(can_DataReceivedEvent);
            canBus.ErrorReceivedEvent += new CANErrorReceivedEventHandler(can_ErrorReceivedEvent);

            // sleep forever
            Thread.Sleep(Timeout.Infinite);
        }

        // private method to handle the new can message received event, this is just an example of what I do so you can see how I write the data
        private static void can_DataReceivedEvent(CAN canSender, CANDataReceivedEventArgs args)
        {
            // get the available can messages
            canMessageCount = canSender.GetMessages(msgListReceived, 0, msgListReceived.Length);

            // loop over each can message
            while (canMessageCount > 0)
            {
                // loop over each of the can messages we have just received
                for (canMessageIndex = 0; canMessageIndex < canMessageCount; canMessageIndex++)
                {
                    #region Check for Speed Message

                    // check for the speed message 1
                    if (msgListReceived[canMessageIndex].ArbID == 3)
                    {
                        speedBytes[4] = msgListReceived[canMessageIndex].Data[0];
                        speedBytes[5] = msgListReceived[canMessageIndex].Data[1];
                        speedBytes[6] = msgListReceived[canMessageIndex].Data[2];
                        speedBytes[7] = msgListReceived[canMessageIndex].Data[3];
                        speedBytes[8] = msgListReceived[canMessageIndex].Data[4];
                        speedBytes[9] = msgListReceived[canMessageIndex].Data[5];
                        speedBytes[10] = msgListReceived[canMessageIndex].Data[6];
                        speedBytes[11] = msgListReceived[canMessageIndex].Data[7];

                        // write the data bytes to the memory
                        WriteData(speedBytes);
                    }

                    // etc etc 
             }
        }    

        // private method called during startup to check the operational mode
        private static void CheckOperationMode()
        {
            // if we have the usb connected and the mod pin low we can act as a remote drive
            if (Configuration.DebugInterface.GetCurrent() != Configuration.DebugInterface.Port.USB1)
            {
                // start the mass storage interface
                USBC_MassStorage massStorageInterface = USBClientController.StandardDevices.StartMassStorage();

                // assume that the SD card is connected
                persistantStorage = new PersistentStorage("SD");
                massStorageInterface.AttachLun(0, persistantStorage, " ", " ");

                // enable host access 
                massStorageInterface.EnableLun(0);
            }
        }

        // public method to write the data to the disk
        public static void WriteData(byte[] dataBytesToWriteToMemory)
        {
            // lock this so we cannot flush half way through
            lock (thisLock)
            {
                // get the data bytes and write the data file
                try
                {
                    Program.loggerFileStream.Write(dataBytesToWriteToMemory, 0, dataBytesToWriteToMemory.Length);
                }
                catch { }
            }
        }

        // public method called from a timer to flush the logged data to disk
        public static void FlushData(object o)
        {
            // lock this so we cannot flush whilst we are writing to the card
            lock (thisLock)
            {
                try
                {
                    Program.loggerFileStream.Flush();
                }
                catch { }
            }
        }
}


Your code doesn’t stay in Mass Storage mode does it?

It does seem to because I think the EnableLun(0) is blocking as no code gets executed after this line if the MOD pin is pulled high.

My main concern is how the card is getting corrupted, maybe it is my constant flushing that is causing the issue?