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 { }
}
}
}