微处理器简单 DOS






4.38/5 (9投票s)
适用于非常小型系统的“磁盘操作系统”

引言
本项目有两个目标:学习如何使用 Microsoft 编译器为微处理器编写高效代码,以及为微处理器编写一个简单有效的 DOS。
我一直在使用 MSP430 微型计算机,最近发现(大多数?)微型 SD 卡支持 SPI 模式。这很令人鼓舞。我一直希望使用廉价的、任何 MSP430 项目都能轻松使用的可移动内存。这意味着使用卡的文件系统,FAT 系统,因此在 Windows 平台上不需要专用软件。这段代码应该适用于任何微型计算机,除了底层的东西,我都尽可能保持简单。
我开始直接将 FatFs 编译到微处理器中。但它工作不正常,并且占用了大量的 ROM。调试它看起来像一团乱麻。
所以我想,接下来应该从 Windows 机器开始。我使用了一个 MFC 应用程序来启动,这样我就可以在运行时看到我的代码在做什么。比微型计算机开发系统提供的调试工具更高级的调试工具确实很好。接下来,我在 VS 中查看了 Lewin A.R.W. Edwards 的 DOSFS Embedded FAT。好多了,代码中有合理的文档。我遇到了一个问题,它卡在一个循环中,无法识别 FAT16 文件结束标记。它正在寻找 FAT32 签名。但我能看出它的工作原理。
它又大又笨重,我想要一个小的、简单的。我没有用 MSPGCC 编译它,但它声称在没有底层代码的情况下大约能生成 ~4k。对于 MSP430F149 来说,这并不过分。我发现,无论我在 VS 中根据 C 规则编写了什么代码,都可以通过复制粘贴移植到微处理器中,并且大部分都能工作。到目前为止,我只发现了一个地方,在使用 MSPGCC 时我必须进行类型转换,而使用 Microsoft 编译器时则不需要,以免丢失 DWORD 的高位部分。那是一个强制 `mult16_32`,我计划写出来。我还看到上面两个示例中不必要的另一个是“Pack Data”。MSP430 是小端序,就像 x86 一样,可以直接读写 FAT 数据。我这里的代码,包含底层和主程序,编译后大约是 3k。从转储来看,DOS 代码大约是 1.5k。
背景
目前,我将目标定为 MSP430F1612。它有 DMA,F. Foust(密歇根州立大学电气与计算机工程系)编写的代码编译并运行完美。
这是对 SD 卡的底层访问,`GetSectorData(...)` 和 `SetSectorData(...)` 对其进行了封装。未来将包含一个不带 DMA 的更简单版本。
我的硬件开发平台
使用代码 (MFC)
MFC 代码中的额外功能是调用以枚举计算机上的驱动器。
void EnumDrives( HEDriveInfoSet& inSet );
您可以在 "HE_Drives.h" 中找到支持此功能的声明。代码在 "HE_Drives.cpp" 中。
bool GetSectorData( pHEDriveInfo pDrvInfo, long sector, BYTE* buf );
bool SetSectorData( pHEDriveInfo pDrvInfo, long sector, BYTE* buf );
这些代码可以读取和写入任何已挂载驱动器的原始扇区,因此请小心不要损坏您的 'C' 驱动器。我正在使用一个廉价的 USB 适配器来处理 SD 卡。MFC 小工具是一个扇区查看器,很有趣,但最终用处不大。
使用代码 (DOS)
在 "mfc_diskioView.cpp" 中大约从第 200 行开始有测试代码。在这里,我只是开始测试 DOS 代码,并确保它在集群之间正确读写以及追加文件。
一旦进入微处理器代码,底层访问看起来是这样的:
bool GetSectorData( DWORD sector );
bool SetSectorData( DWORD sector );
缓冲区 RAM 是共享的,微处理器上只有一个驱动器。移植到微处理器中的代码位于 "HE_dos.cpp" 和 "HE_dos.h" 中。但是,在移植的代码中没有任何 C++ 代码。MSPGCC 在处理 C 方面做得很好,但我还没有尝试用 C++ 来压榨它。话说回来,我认为抽象对于 PC 软件来说非常棒,但对于微处理器来说,你会非常清楚机器级别的操作。对于几 KB 的代码来说,C++ 可能是过度的。而且,面向对象存在于思维中……
DOS 接口的函数调用尽可能直观。只有两个结构,一个用于驱动器卷信息,另一个用于“打开”的文件。对于第一个版本,没有“关闭文件”,因为无论在“关闭卷”时打开了哪些文件,最后一个缓冲区都会被刷新。之所以需要刷新,是因为底层确实进行了缓存。缓存有两个原因。首先是 DOS 代码不必检查不必要的读写。其次是节省电池寿命。在单个缓冲区级别,缓存非常简单,PC 示例代码位于 "mfc_diskioView.cpp" 顶部附近。读取扇区零,保证不会被“文件”缓存,并且即使所有文件仍然真的打开,您也已经“关闭”了所有文件!
此代码的一个特点是没有乘除法运算。(已注意到一个例外,将进行清理。)所有乘除法都通过移位完成。编译器足够智能,可以将 `val << 8` 转换为丢弃低字节并继续。而 `val << 9` 或 `>> 9` 只需要一次移位。
代码是基础的,刚好够完成任务。很少或没有边界检查,因为调用代码应该知道。即使将其扩展到支持 FAT32,我也绝不会考虑支持 FAT12,我认为没有意义。
DOS 声明
以下所有函数都返回 `true` 表示成功。
在微处理器复位(RAM 上电)后调用一次此函数,以初始化卷信息。
bool HEDsk_LoadVolumeInfo( );
这是文件的“打开”操作。它初始化现有文件的文件信息。文件名是规范的,即形式为“TEST TXT”。没有标志,它以读写方式打开,并且必须存在。文件名只有 11 个字节,不需要尾随的 null 字符。文件指针设置为零。
bool HEDsk_GetFileInfo( pHEDskFileInfo pInfo, char* lpsCanonicalName );
从文件中读取 'count
' 个字节到 buf
。将文件指针向前移动 count
。读取超过文件末尾的行为不确定。
bool HEDsk_ReadFile( pHEDskFileInfo pInfo, BYTE* buf, WORD count );
将 buf
中的 'count
' 个字节写入文件,从当前文件指针开始。如果需要,将扩展文件大小。
bool HEDsk_WriteFile( pHEDskFileInfo pInfo, BYTE* buf, WORD count );
将文件指针设置为 pos
。不检查 pos < fileSize
。文件大小可在 pInfo->size
中获取。
bool HEDsk_SeekFile( pHEDskFileInfo pInfo, DWORD pos );
将来可能会添加可选代码,使用 `ifdef` 来按需创建和清零文件。目前没有处理文件数据的修改。对于将卡从微处理器移到计算机上来说,这不是一个真正的问题。但是,在计算机上单独工作时,操作系统将看不到文件已被修改,缓存可能会保持陈旧。我不得不拔出卡并重新插入卡以获取文件的最新副本。我尝试了一个不起作用的技巧。如果您知道一个,请告诉我。
此代码在 MSP430 上运行
HEDsk_LoadVolumeInfo( );
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 );
}
项目有一个名为 MspCode 的文件夹,其中包含所有微处理器文件。
关注点
历史
- 2009年10月28日:初始版本
谢谢,Dan。