There were two objectives with this project. To learn how to write
effective code for a micro possessor with the Microsoft compiler and
to write a simple, effective, DOS for a micro possessor.
I have been working with the MSP430 micro computer and recently
found out that the SPI mode is supported on (most?) micro SD cards.
This is
encouraging. I have wanted to use inexpensive removable memory
that could easily be utilized by anyone from an MSP430 project. That
means using the file system of the card, the FAT system, so specialized
software is not
needed on a Windows platform. This
code should
work on any micro computer, I have kept all but the low level stuff as
plain as possible.
I started by compiling FatFs directly to the micro.
But it didn't work properly and occupied a large amount of ROM.
Debugging it started to look like a can of worms.
So I figured that the next place to start would be on a Windows box.
I used
an MFC application to get going so I could see what my code was doing
at run time. It was just nice to have a higher class of debugging
tools than appear with a micro computer development system. Next I took
a
look at
DOSFS Embedded FAT by Lewin A.R.W. Edwards in VS. Much better, it has
reasonable documentation in the code. I
got to the point where it hung in a loop not recognizing the FAT16 end
of file table marker. It was looking for the FAT32 signature. But I
could see how it was intended to work.
It was big and clunky and I wanted something small and simple. I
didn't compile it with MSPGCC but it was advertised to make at about
~4k without the low level code. Not unreasonable for the likes of the
MSP430F149. What I found was that what ever code I wrote in VS
according to C rules
could be ported
to the micro with a copy and paste and it would work for the most part.
So far I have
found only one place where I had to cast with MSPGCC, where I didn't
with the Microsoft compiler, so as not to
loose
the higher part of a DWORD .
That was to force a mult16_32, and I plan to write that out. Another
thing
I saw that was unnecessary in both examples above was a
'Pack Data'. The MSP430 is little endian, just like the x86, so can
read
and write the FAT data directly. The code I have here, with low level
and the main, compiled to ~3k. From the dump, it looks like ~1.5k for
the DOS code.
For now I'm targeting the MSP430F1612. It has DMA and the code written by F. Foust, Dept. of Electrical and Computer Engineering, Michigan State University, compiled and ran perfectly.
Application
Note
- Secure Digital Card Interface for the MSP430
This is low level access to the SD card that GetSectorData(...) and
SetSectorData(...) wrap. A simpler version without DMA will be included
in the future.
My hardware development platform
The extras in the MFC code is a call to enumerate drives on the box.
void EnumDrives( HEDriveInfoSet&inSet
);
You will find the decelerations that support this in "HE_Drives.h"
The code is in "HE_Drives.cpp"
bool GetSectorData( pHEDriveInfopDrvInfo
, longsector
, BYTE*buf
);
bool SetSectorData( pHEDriveInfopDrvInfo
, longsector
, BYTE*buf
);
These are to read and write raw sectors on any mounted drive so be
careful you don't smash your 'C' drive. I'm working on the SD card with
a cheap USB adapter. The MFC gadget is a sector viewer, fun, but not
that useful after all.
bool GetSectorData( DWORDsector
);
bool SetSectorData( DWORDsector
);
The buffer RAM is shared and there is only one drive on the micro.
The code that ports to the micro is in "HE_dos.cpp" and "HE_dos.h". But
there is no C++ in any
of the code that ports. MSPGCC does an excellent job with C but I
haven't tried to beat it up with C++. At that, I think abstraction is
great for PC software, but you will tend to be very aware of what
happens at the machine level with a micro. C++ is probably over kill
for a few K of code. Besides, OO is in the mind...
The function calls for the DOS interface are about as straight
forward as it gets. There are only two structures. One for the drive
volume information. Its data is persistent and lives for the life of
the application. One for an 'open' file and that data remains valid
once a file is 'opened'. There is no 'Close File', instead there is a
flush file. After a flush you can keep using the file or discard the
file info data as if the file were closed. The 'get-put' sector level
does caching. There are two reasons for
caching. First is that the DOS code does not have to check for
unnecessary reads and writes. Second is to save on battery life.
Caching is pretty simple at the level of a single buffer. The PC
example is in code starting near the top of "mfc_diskioView.cpp".
One feature of this code is there is no mult or div math. (One
exception noted and will be cleaned up.) All mults and divs are done
with shifts. And the compiler is smart enough to translate a val
<< 8
into dropping the low byte and and moving over the
bytes. Likewise, a val
<<
9 or >> 9
is just one shift.
The code is bare bones, just enough to get the job done. I looked to
keep the proper information on the volume and files so as to hold down
the math and indirection. After I sleep on this I may find I can do
more to make it size and speed efficient.
All of the below return true if successful.
Call this once after a micro reset, (RAM power up), to initialize the
volume information.
bool HEDsk_LoadVolumeInfo( );
This is the 'Open' for a file. It initializes the file info for an
existing file. The name is canonical, i.e., in the form of
"TEST TXT". No flags, it is open for read and write
and must exist. Just 11 BYTES for the name, a trailing null is not
necessary. The file pointer is set to zero.
bool HEDsk_GetFileInfo( pHEDskFileInfopInfo
, char*lpsCanonicalName
);
Reads 'count' of BYTES into buf from the file. Moves the file
pointer up by count. Reading past the end of file is undetermined.
bool HEDsk_ReadFile( pHEDskFileInfopInfo
, BYTE*buf
, WORDcount
);
Writes 'count' in BYTES from buf to the file from the current file
pointer. Will extend the size of the file if needed.
bool HEDsk_WriteFile( pHEDskFileInfopInfo
, BYTE*buf
, WORDcount
);
Sets the file pointer to pos. No check is made to make sure pos <
fileSize. The file size is available in pInfo->size.
bool HEDsk_SeekFile( pHEDskFileInfopInfo
, DWORDpos
);
This will write off the file infromation to the drive. The file is
still open and pointers are still in place as they were before the call.
bool HEDsk_FlushFile( pHEDskFileInfopInfo
);
This code was run on the MSP430 and wrote the card fine:
HEDsk_LoadVolumeInfo( );There is a folder in the project called MspCode with all the files for the micro.
if( HEDsk_GetFileInfo( &heFileInfo, "TEST TXT" ) )
{
for( j= 0; j < 100; ++j )
{
P1OUT ^= BIT4; // Toggle LED
HEDsk_WriteFile( &heFileInfo, "this is a test\r\n", 16 );
}
HEDsk_FlushFile( &heFileInfo );
}
Things that may happen in the future is to add optional code with ifdefs so files can be created and zeroed if needed. There is no handling the 'date modified' of the file. For a card that will be moved from the micro to a box this is not a real issue. But while working on the box alone the OS will not see that the file is modified and the cache may stay stale. I have had to pull the card and reinsert it to get a fresh copy of files. I tried one hack that didn't work in 'HEDsk_FlushFile', If you know one, let me know. Even if this is extended to handle FAT32, I would never look to support FAT12, I don't see the point.