FAT12 readerとIntel HEX parserの実装
こんにちは、リゲル・インテリジェンスです。
RP2040にUSBマスストレージクラス&FAT12ドライブを実装しました。ホストOSからファイルとして書き込まれたデータはmsc_disk変数(RAM)上に展開されていますので、簡単&高速にアクセスすることが出来ます。ただし、msc_disk変数の大きさがそのままPicWriterドライブの容量となり、「.hex」ファイルのサイズ制限にもなります。
RP2040がDual coreであることを利用し、Core0にUSBマスストレージ処理をCore1にPICマイコン書込み処理を行わせますが、msc_disk変数のデータはその両方からアクセスされることになります。本来であれば同時アクセスによる意図せぬデータ破壊などを防ぐために適切な排他処理が必要ですが、Core1からは参照のみであることに加えてPICマイコンへのプログラム書込み中に元ファイルのデータを書き換えることはユースケース上も殆ど無いでしょうから、特に何も処理はせず通常の変数としてアクセスしています。
というわけで、PICマイコンに書込むデータをmsc_disk変数から取り出すには、
- FAT12のreader
- Intel HEXファイルのparser
の2つがあれば良いことになります。
FAT12 reader
FAT12をサポートするファイルシステムソフトウェアは巷に数多くありますが、今回は既にメモリ上にあるデータの参照だけですので(リソース低減の意味からも)外部プログラムなどは使用せず、必要な機能のみを直接実装していくことにします。アクセスの手順としては下記の通りです。
- Block3(Root directory entry)へのWrite10コマンドがあれば、ホストOSからの書込みがあったと判断する
- Block3(Root directory entry)に拡張子が「.HEX」のファイルエントリがあるか確認する
- 該当するファイルエントリがあれば、開始セクタ(Block)、ファイルサイズ、ファイル名を取得する
- 開始セクタの情報からFATチェーンを探索する
ファイル名の取得は動作確認ログに残すために行っています。Long file nameへの対応はPICマイコンへの書込みプログラムの動作上の必要性は無いですが、ログを見たときに不自然感があるので表示に対応しています。Long file nameの仕様では最大20のディレクトリエントリが使用可能です(制御文字含め260字)が、PicWriterドライブではルートディレクトリエントリの最大数が16でありしかもその内最低でも3つはボリュームラベルやファイルのエントリに使用しますので、13エントリx13文字=169字+末尾のNULLを入れて170字を最大数に設定しています。
これらの対応を入れたソースコードは下記になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 |
//****************************************************************************** //* core1_main.c : test program for RP2040 core1 with using Tiny USB on core0. * //* * //* usage: * //* This program is running on CPU core-1 to avoid unexpected wait for USB. * //* * //* * //* history: * //* Sep16,2022 start coding. N.Mato * //* Sep17,2022 add "find_new_hexfile()" funtion. N.Mato * //* * //* Copyright(c)2022 Rigel Intelligence * //****************************************************************************** //*** includes ***************************************************************** #include <stdio.h> #include "pico/stdlib.h" #include "pico/types.h" #include "pico/multicore.h" //*** definitions ************************************************************** #define NEW_FILE_FOUND 100 // flag from Core0 to Core1 #define DISK_BLOCK_NUM 480 // 240KB #define DISK_BLOCK_SIZE 512 #define FILE_NAME_LENGTH 170 // 169 + NULL #define FAT12_TABLE1 1 // Block number of file allocation table #define FAT12_TABLE2 2 // Block number of file allocation table #define ROOT_DIR_ENTRY 3 // Block number of root directory entry // free flag of 1st byte of directory entry #define DIR_ENT_FREE_0 0 #define DIR_ENT_FREE_E5 0xE5 // File attributes #define ATTR_READ_ONLY 0x1 #define ATTR_HIDDEN 0x2 #define ATTR_SYSTEM 0x4 #define ATTR_VOLUME_ID 0x8 #define ATTR_DIRECTORY 0x10 #define ATTR_ARCHIVE 0x20 #define ATTR_LONG_NAME 0xF #define LAST_LONG_ENTRY 0x40 //*** structs/unions *********************************************************** // Directory entry typedef struct { char dir_Name[8]; // Short name char dir_Ext[3]; // Short name (extention) uint8_t dir_Attr; // File attributes uint8_t dir_NTRes; // Reserved for WindowsNT uint8_t dir_CrtTimeTenth; // Millisecond time stamp (0-199) uint16_t dir_CrtTime; // Time files was created uint16_t dir_CrtDate; // Date files was created uint16_t dir_LsrAccDate; // Last access date uint16_t dir_FstClusHi; // High word of first cluster (0 for FAT12) uint16_t dir_WrtTime; // Time of last write uint16_t dir_WrtDate; // Date of last write uint16_t dir_FstClusLo; // Low word of first cluster uint32_t dir_FileSize; // File size in bytes } DirectoryEntry_st; // Directory entry (Long file name) typedef struct { uint8_t ldir_Ord; // Order of long dir entries (1-20, 0x40 last flag) uint8_t ldir_Name1[10]; // Char 1-5 of the long name uint8_t ldir_Attr; // Attributes: ATTR_LONG_NAME (mask with 0xF) uint8_t ldir_Type; // Zero uint8_t ldir_Checksum; uint8_t ldir_Name2[12]; // Char 6-11 of the long name uint8_t ldir_FstClusLO[2]; // Zero uint8_t ldir_Name3[4]; // Char 12-13 of the long name } LongDirectoryEntry_st; typedef union { DirectoryEntry_st sfn; // Short file name LongDirectoryEntry_st lfn; // Long file name } DirEnt_un; // 'HEX' file paramters typedef struct { int start_sector; // 1st sector on storage int file_size; // size of HEX file (Bytes) uint16_t file_name[FILE_NAME_LENGTH]; // Max 128 chars for file name (2bytes code) } HexFileParams_st; //*** prototydes *************************************************************** static int get_FAT12_chain( int currentSector ); static bool find_new_hexfile( void ); //*** constants/variables ****************************************************** extern uint8_t msc_disk[DISK_BLOCK_NUM][DISK_BLOCK_SIZE]; static HexFileParams_st fileParams; //****************************************************************************** //*** functions **************************************************************** //****************************************************************************** // Pickup FAT12 chain value static int get_FAT12_chain( int currentSector ) { int next_sector; uint8_t *fat_sector = &msc_disk[FAT12_TABLE1][0]; if( currentSector %2 ) { // odd number next_sector = (int )((uint )(fat_sector[currentSector/2 *3 +1] >>4) + ((uint )(fat_sector[currentSector/2 *3 +2]) <<4)); } else { // even number next_sector = (int )(((uint )(fat_sector[currentSector/2 *3 +1] & 0xf) <<8) + (uint )(fat_sector[currentSector/2 *3])); } return next_sector; } // find 'HEX' file in storage static bool find_new_hexfile( void ) { int i, j, k, rootEntCnt; DirEnt_un *rootDirEnt; // Number of root directory entry = fixed value '16' rootEntCnt = msc_disk[0][18]; rootEntCnt <<= 8; rootEntCnt += msc_disk[0][17]; // search root directory entry rootDirEnt = (DirEnt_un *)msc_disk[ROOT_DIR_ENTRY]; // Block3 for( i=2; i<rootEntCnt; i++ ) // skip entry0=volume lavel, entry1=README.TXT { // skip free entry if( (rootDirEnt[i].sfn.dir_Name[0] == DIR_ENT_FREE_0)||(rootDirEnt[i].sfn.dir_Name[0] == DIR_ENT_FREE_E5) ) continue; // skip system files or directory if( rootDirEnt[i].sfn.dir_Attr & (ATTR_HIDDEN|ATTR_SYSTEM|ATTR_VOLUME_ID|ATTR_DIRECTORY) ) continue; // find 'HEX' file if( (rootDirEnt[i].sfn.dir_Ext[0] == 'H')&&(rootDirEnt[i].sfn.dir_Ext[1] == 'E')&&(rootDirEnt[i].sfn.dir_Ext[2] == 'X') ) { // start sector (0-4, reserved) if( rootDirEnt[i].sfn.dir_FstClusLo <= 4 ) break; else fileParams.start_sector = rootDirEnt[i].sfn.dir_FstClusLo; // file size if( rootDirEnt[i].sfn.dir_FileSize == 0 ) break; else fileParams.file_size = rootDirEnt[i].sfn.dir_FileSize; // copy short file name uint16_t *fname = fileParams.file_name; for( j=0; j<8; j++ ) *fname++ = (uint16_t )rootDirEnt[i].sfn.dir_Name[j]; *fname++ = '.'; for( j=0; j<3; j++ ) *fname++ = (uint16_t )rootDirEnt[i].sfn.dir_Ext[j]; *fname = 0; // null terminater // search long file name int order = 1; fname = fileParams.file_name; for( j=i-1; j>1; j-- ) { if( (rootDirEnt[j].lfn.ldir_Attr & ATTR_LONG_NAME) != ATTR_LONG_NAME ) break; // no LFN if( (rootDirEnt[j].lfn.ldir_Ord & 0x1F) != order ) break; // illegal order // overwrite with long file name for( k=0; k<10; k+=2 ) *fname++ = ((uint16_t )rootDirEnt[j].lfn.ldir_Name1[k+1] << 8)+((uint16_t )rootDirEnt[j].lfn.ldir_Name1[k]); for( k=0; k<12; k+=2 ) *fname++ = ((uint16_t )rootDirEnt[j].lfn.ldir_Name2[k+1] << 8)+((uint16_t )rootDirEnt[j].lfn.ldir_Name2[k]); for( k=0; k<4; k+=2 ) *fname++ = ((uint16_t )rootDirEnt[j].lfn.ldir_Name3[k+1] << 8)+((uint16_t )rootDirEnt[j].lfn.ldir_Name3[k]); if( rootDirEnt[j].lfn.ldir_Ord & LAST_LONG_ENTRY ) { *fname = 0; // null terminater break; // end of LFN } order++; } // show file info. ==================================== printf("'HEX' file found.\n"); printf("File Name : "); for(j=0; ; j++) { // file name includes 2-byte length char. if( fileParams.file_name[j] == 0 ) break; // null terminate printf("%c", fileParams.file_name[j]); } printf("\n"); printf("File size : %d\n", fileParams.file_size); printf("Start sct.: %d\n", fileParams.start_sector); // search FAT12 chain // read 1st table -> FAT12 table [startSect -1] int cur_table = fileParams.start_sector, next_table; for( k=0; k<DISK_BLOCK_NUM; k++ ) { // search the next table next_table = get_FAT12_chain(cur_table); if( (next_table > 0xf00)||(next_table == 0) ) { printf("\n"); break; } printf("-[%3d]", next_table); if( k%16==15 ) printf("\n"); cur_table = next_table; } printf("\n"); return( true ); } } return( false ); } //*** main() ******************************************************************* void core1_main( void ) { bool result = false; printf("\nCore-1 loaded.\n\n"); while(1) { const uint32_t msc_flag = multicore_fifo_pop_blocking(); printf("\nCore-1 called :%d\n\n", msc_flag); if( msc_flag == NEW_FILE_FOUND ) { result = find_new_hexfile(); } } } |
このプログラムをRP2040にて実行しPicWriterドライブにHEXファイルを書込むと、下記のようにログが得られます。
次ページで「.HEX」ファイルを確認していきます。
コメント