
再次感謝沁恒官方寄送的這塊 CH32V103 開發板,集成了仿真下載器,集成了USB轉串口,還兼容了Arduino接口,使用起來很方便。
FAT 文件配置表經過多年發展(FAT、FAT12、FAT16、FAT32、exFAT),目前已經廣泛應用在 內存卡、U盤 等小容量存儲介質中。
FAT~FAT16 由于最大容量限制等原因,目前已經逐步退出歷史舞臺,FAT32目前使用還比較廣泛也是我正在使用的。
本文使用 FATFS,實現對文件目錄的檢索和長文件名的讀取轉換,最終將結果打印到串口調試助手。
先看一下U盤里都存了什么(一不小心暴露了年齡
):通過FATFS,我們能輕而易舉的讀取到這些文件的 8.3 短文件名,8字節文件名(中文則為4字),3字節后綴。規規整整,但總覺得少了點什么,只有歌手名沒有歌曲名?小寫的后綴mp3變成大寫的?然后~1又是什么?想了解短文件名的可以搜一下論壇了解,這里就不重復,我們目的是長文件名,繼續。
在 FATFS 配置中開啟中文長文件名支持:#define _LFN_UNICODE 1,開啟中文支持:#define _CODE_PAGE 936

成功了一半,由于長文件名是 Unicode 編碼的,直接讀出來,再通過串口打印顯示,全是亂碼??這里就涉及編碼轉換問題,串口助手一般都是顯示GBK編碼的字符,編碼不同就導致亂碼,需要用到 FATFS 里的編碼轉換函數進行轉換,但由于 GBK 和 Unicode 轉換是無規律的,只能純粹的查表,老美給中國人設置的坑???為什么要為難中文和非英文??這個表還非常大,要占用 170K 以上的存儲空間。。。
我這是一個 64K Flash 的單片機???
有難度才有動力啊,放入外置Flash?沒有Flash,那只好放入內存卡了,剛好這些歌曲也是放在內存卡的,就建立一個目錄,SYS,里面放入 Unicode 和 GBK 互轉的對照表。在內存卡插入的時候,掛載文件系統,讀取 Unicode 和 GBK 轉換表。 if(SD_Detect()) { if(sd_status == 0) { sd_status = 1; printf("Insert\r\n"); } } else { sd_status = 0; }
if(sd_status == 1) { path[0] = '0'; path[1] = ':'; path[2] = 0; res_sd = f_mount(&fs,path,1); if(res_sd == FR_OK) { sd_status = 2; printf("Mount Ok\r\n");
path[0] = '0'; path[1] = ':'; path[2] = '/'; path[3] = 'S'; path[4] = 'Y'; path[5] = 'S'; path[6] = '/'; path[7] = 'U'; path[8] = 'N'; path[9] = 'I'; path[10] = 'G'; path[11] = 'B'; path[12] = 'K'; path[13] = '.'; path[14] = 'B'; path[15] = 'I'; path[16] = 'N'; path[17] = 0;
res_sd = f_open(&gd_FileUNItoGBK,path,FA_READ); if(res_sd == FR_OK) printf("Load UNIGBK.BIN Ok\r\n"); else printf("Load UNIGBK.BIN Fail\r\n");
path[2] = '/'; path[3] = 0; scan_files(path); } else if(res_sd == FR_NO_FILESYSTEM) { printf("No File System\r\n"); } else { printf("Mount Fail\r\n"); } }
改寫 cc936.c 文件里的 ff_convert 函數,改為從內存卡指定偏移位置讀取對照表信息。
WCHAR ff_convert ( WCHAR src, UINT dir ){ WCHAR t[2]; WCHAR c; DWORD i, li, hi; WCHAR n; UINT cout; DWORD offset;
if(src < 0x80) return src; else { if(dir) { offset = gd_FileUNItoGBK.fsize >> 1; } else { offset = 0; } if(gd_FileUNItoGBK.fsize != 0) { hi = gd_FileUNItoGBK.fsize >> 1; hi = (hi >> 2) - 1; li = 0; for(n=16; n; n--) { i = li + ((hi - li) >> 1); f_lseek(&gd_FileUNItoGBK, (i << 2) + offset); f_read(&gd_FileUNItoGBK, &t, 4, &cout); if(src == t[0]) break; if(src > t[0]) li = i; else hi = i; } c = n ? t[1] : 0; } else c = 0; } return c;}
核心部分基本就完成了,接下來讀取長文件名,轉換為GBK編碼并打印。
p = pt; pth = *fno.lfname ? fno.lfname : fno.fname; while(*pth != 0) { ct = ff_convert(*pth, 0);
if(ct > 255) { *p = (u8)(ct>>8); p++; *p = (u8)ct; p++; } else { *p = (u8)ct; p++; } pth++; } *p = 0;
printf("%s\r\n", pt);
由于是 SPI 接口讀取內存卡,中文編碼轉換速度有點慢,在沒有 SDIO 接口的情況下,只能調高 SPI 速率,再借助 DMA 來提高讀寫速度。顯示到串口助手顯然不太方便,我看目錄還要帶個電腦?再說有電腦我還需要這樣看目錄?
說干就干。
從雜物堆里找來了一個12864顯示屏,非常mini的那種,0.96寸,某寶上也有很多哦,10米左右即可擁有。

怎么點亮屏幕這篇文章就不說了,買屏送的資料就夠亮屏了。
由于內存卡存放的文件,文件名可能是各種各樣的,如果要顯示任意中文字符,就需要對所有 漢字 進行取模,生成字庫。
1、漢字有上萬個,常見的也有幾千個,一一取模再轉換未免太費神。
2、這么多漢字,如何一一對照,例如我想在這一堆字模中,快速找到“我”這個字的字模。
什么?忘記取模的時候“我”的字模排在第幾位了。
對于第一個問題,感謝一些前輩大佬,已經有很多生成字庫的軟件了。第二個問題,漢字編碼有很多種,例如 GB2312(收錄6763漢字)、GBK(收錄21886漢字)、Unicode(收錄6萬多字)、UTF-32則收錄了12萬字。
已經有以上多種編碼了,編碼就代表著文字的排序,直接對照就好。
這里取GBK和Unicode為例。GBK中出現的第一個漢字為“丂”,編碼為16進制的8140,一個漢字用2個字節表示。
如何獲取漢字GBK編碼?這里提供一種土辦法,串口助手!
將RX和TX短接,發送設置為字符GBK編碼,接收設置為16進制,發送后接收到的就是字符編碼:
獲取 Unicode 同理,發送切換到 Unicode 編碼,接收為16進制:取模不管對照GBK編碼,還是Unicode編碼,都是能實現我們這個功能的。直接選個編碼開始取模:
再放入內存卡即可,然后使用 FATFS 讀取字庫文件信息:
f_open(&gd_FileFontLib16, path, FA_READ);
偏移文件讀取指針:
f_lseek(&gd_FileFontLib16, foffset);
讀取字模信息:
f_read (&gd_FileFontLib16, Buff, 32, &cout);
使用 Unicode 編碼的字庫時,foffset偏移就是漢字編碼,使用 GBK 編碼的字庫時,foffset偏移按照以下計算方法:
qh = *Str; ql = *(++Str);
if(qh<0x81 || ql<0x40 || ql==0xff || qh==0xff) { for(i=0; i<(Size<<1) ;i++) *Buff++ = 0x00; return 0; }
if(ql < 0x7f) ql-=0x40; else ql-=0x41;
qh -= 0x81; foffset = ((u32)190 * qh + ql) * (Size&0x07 ? (Size>>3)+1 : Size>>3) * Size;
