MSCFileSystem memory leak?

27 Jan 2011

Hi everyone.

First a bit of background - I'm writing a program to control a number of stepper motors. Their movement is scripted using a text file where each line is a "frame" that contains each motor's position. The text file is stored on a USB stick which is accessed using the MSCFileSystem library. As each script could potentially last a number of minutes, and there are 25 frames per second this adds up to a lot of lines and a lot of numbers.

I therefore don't want to read the whole text file at once, store all the positions, and then play it through, as the memory this array would use would soon become a limiting factor. To fix this problem I thought I would read the text file line by line (frame by frame) in real time. Do do this, I set up a ticker at 25 Hz, open a text file and the function attached to the ticker reads the text file. Simple right? Here's my code:

#include "mbed.h"
#include "MSCFileSystem.h"

#define FSNAME "stick"
MSCFileSystem msc(FSNAME);

Serial pc(USBTX, USBRX);

Ticker animate;

FILE *fp = fopen("/" FSNAME "/show.txt", "r");

int index = 0;

void test()
{
    int data = fgetc(fp);
    pc.printf("%d %c\n", index, data);
    index++;
}

int main()
{ 
    pc.printf("START\n");
    int data = fgetc(fp);
    animate.attach(&test, 0.04);
}

Now the first thing I'm sure you've noticed is the seemingly stupid code at line 25 (The second line in main). This is the first bug.

Don't ask me why, but unless you start reading the text file from main, it won't read from the test function. As I work mainly with applications the first thing I thought of was a thread issue, but I don't think threading exists with microcontrollers. But that line fixes the issue.

So ignoring that little bug, the code above appears (to me at least) like it should simply print out each character in the text file along with a counter, just so I know which character its got to. Which is exactly what it does do... for the about the first 500 characters... then it stops. The fgetc command freezes.

From what I can gather, this is a memory issue. The program uses all the mbed's memory and crashes. How? I'm not storing anything. Am I? Is there some clean up command I need to be running? Or does the fgetc command have a memory leak?

If anyone can shed some light on this, it would be greatly appreciated.

Many Thanks.

27 Jan 2011

Try moving the fopen() call inside main(). I wonder how that even compiles.

27 Jan 2011

When you do file reads (or writes), the first file function after open is the one creating all the internal buffers. I'm not not completely sure that this will work flawlessly when called from an interrupt (which is what the ticker does). It might be that the memory handling is different in such cases. Also, you ticker might be just too fast (or your method too slow) - printf() takes a while to execute. Can you try lowering your ticker speed?

27 Jan 2011

There's no thread but there is "context". There's your user program main() { ... } and then there are callbacks (like the ticker) that are interrupt.

Two things. First you callback must execute faster than the ticker period, obvious really as I'm sure you'll know. Second, when inside a IRQ callback other IRQs will be blocked until you return.

Try this:-

#include "mbed.h"
#include "MSCFileSystem.h"

#define FSNAME "stick"
MSCFileSystem msc(FSNAME);

DigitalOut P20(p20);

Serial pc(USBTX, USBRX);

Ticker animate;

bool tickerActivated = false;

FILE *fp = fopen("/" FSNAME "/show.txt", "r");

int index = 0;

void test()
{
    tickerActivated = true;
}

int main()
{ 
    P20 = 0;
    pc.baud(115200); // <--- usually good for Serial over USBTX/USBRX
    pc.printf("START\n");
    animate.attach(&test, 0.04);
    while(1) {
        // Keep the actual reading/writing in "user context"
        if(tickerActivated == true) {
            P20 = 1; // used to measure time taken with a scope if you have one.
            tickerActivated = false;
            int data = fgetc(fp);
            pc.printf("%d %c\n", index, data);
            index++;
            P20 = 0;
        }
    }
}

You might also like to increase the Serial baud rate to 115200 from the default 9600. It's over USB so you should be fine.

[Edit: Igor and Hendrik posted while I was typing that up, take their info into account too, try starting with a lower ticker rate as Hendrik suggested and ramp up. And as Igor said, move the fopen into main().]

27 Jan 2011

Thanks for the quick reply Igor. Unfortunately that makes no difference.

Sorry if I make some really basic mistakes, I'm used to writing applications in C# where an initialisation like that would be fine.

27 Jan 2011

Wow thanks for the quick responses!

Thanks for the explanation Hendrink, unfortunately I can't reduce the ticker, I need at least 25 frames per second. But good to know about the interrupts.

Well done Andy, that fixed it! Good idea about using the ticker to trigger main loop code, should have thought of that really, doh. But its interesting that interrupts appear to have a different "context as you put it", will have to keep that in mind in future. I've increased the baud too, though the serial coms are only for debugging and won't be used in the final code.

Will have to see if its quick enough for 25fps, if not I'll have to try a completely different method :os

PROBLEM SOLVED!

27 Jan 2011

Just for completeness this is the working code:

#include "mbed.h"
#include "MSCFileSystem.h"

#define FSNAME "stick"
MSCFileSystem msc(FSNAME);

Serial pc(USBTX, USBRX);

Ticker animate;

FILE *fp;

int index = 0;
bool tick = false;

void test()
{
   tick = true;
}

int main()
{ 
    pc.baud(115200);
    fp = fopen("/" FSNAME "/show.txt", "r");
    pc.printf("START\n");
    animate.attach(&test, 0.04);
    
    while(1)
    {
        if (tick)
        {
            int data = fgetc(fp);
            pc.printf("%d %c\n", index, data);
            index++;
            tick = false;
        }
    }
}

27 Jan 2011

Quote:

if not I'll have to try a completely different method

In the past I've attached a small (physically that is, 8pin device) flash device to an SPI port and copied data from an SD card to it in a properitry data format as reading data natively without the SD library overhead is much faster, especially when you use DMA transfers. I've managed to move data from a flash device at 512bytes per page DMA cycle with an SPI clock of 25MHz. Some go upto 40MHz.

27 Jan 2011

Quote:

In the past I've attached a small (physically that is, 8pin device) flash device to an SPI port and copied data from an SD card to it in a properitry data format as reading data natively without the SD library overhead is much faster, especially when you use DMA transfers. I've managed to move data from a flash device at 512bytes per page DMA cycle with an SPI clock of 25MHz. Some go upto 40MHz.

Awesome thanks for the tip!

27 Jan 2011

Your welcome! Btw, using DMA also has the advantage that you can be "preloading" a 512byte buffer, (ie the next page) under DMA before you need it meaning you keep the "pipe" full at the best speed possible. Just another tip :)

27 Jan 2011

I you want to speed this up: Microchip sells SPI sRAM devices, which are faster esp. when writing data to them (there is also a mbed library available). But the buffer idea can be used without this: just create 2 buffers of, lets say, 256 bytes. Fill them on startup, and then start the ticker. The ticker function then uses the data from the first buffer, and switches later to the second. If it does that, notify your main loop, which can fill up the empty buffer - reads of 256 bytes are not so much slower than reading a single byte, and you have less overhead (and more time to react to the ticker notification).