前言
- 本博文基于emWin V5.50和MDK環境下編寫和調試;
- 本部落格内容介紹STM32利用STemWIn庫從搭載了檔案系統(FATFS V13c)的SD卡上擷取到一個位圖流,并顯示在LCD的指定位置上的小實驗;看本博文需要有一些GUI的基礎和位圖基礎;
- 如有不足,還請多多指教;
擷取流程
對于一個大的GUI項目來說,如果所有的圖示資料都儲存在單片機内部ROM中,所占用的記憶體空間是非常大的,尤其是像單片機這種小容量CPU,記憶體尤為珍貴。如果把這些圖示都儲存在外部函數中,雖說在一定程度上增加了響應時間,但是很省記憶體,在一定程度上降低了成本。為此emWin專門提供了功能函數可以将位于外部儲存設備上的流位圖檔案顯示在LCD上,其資料轉移流程如下:

在我的這個實驗中這并不是一個簡單的從存儲器讀取資料顯示在LCD上,他要經過幾個結構層,如下圖:
是以這個實驗需要:
- 硬體:SD卡,LCD顯示器;
- 軟體:FATFS,SD卡驅動,STemWin,LCD底層驅動(描點函數);
但是 本實驗并不介紹上面這些驅動和移植過程,而是着重介紹STemWin提供的相應繪圖函數GUI_DrawStreamedBitmapExAuto()
外部存儲器流位圖擷取函數
int GUI_DrawStreamedBitmapExAuto(GUI_GET_DATA_FUNC * pfGetData, const void * p, int x, int y);
描述: 自動識别從外部擷取到的不同格式的位圖流資料,進而将其顯示在以(x,y)為起點的LCD螢幕上;
參數:
pfGetData:外部資料擷取函數。GUI_DrawStreamedBitmapExAuto()本身并不能從外部存儲器擷取位圖資料,而是通過一個使用者自定義的GetData函數将要顯示的位圖資料從外部存儲器讀出來,指針就指向這個GetData函數;GUI_DrawStreamedBitmapExAuto()會多次調用這個函數;
p:使用者自定義指針。這個指針很重要,從外部存儲器擷取資料的方式有很多種。比如不需要FATFS的是直接通路,那麼這裡的P所指向的變量或結構體,用來将需要讀取資料的起始位址,讀取資料的大小等這些重要資訊傳遞給pfGetData所指向的函數;同理,外部存儲器如果搭載了FATFS,那麼這裡可以傳遞檔案類型結構體變量,進而擷取相應資料;
x,y:這兩個參數很好了解,就是LCD螢幕點坐标;
傳回值: 0 on success, 1 on error.
注意:這裡有一個問題,在資料擷取的過程中是要占用單片機的RAM的,如果一個位圖的大小大于了單片機本身的RAM大小,這很棘手。是以GUI_DrawStreamedBitmapExAuto()提供了分批擷取資料的方法。但是每一批擷取的資料量必須大于或等于LCD螢幕上一行資料的像素點資料所占用的記憶體空間; 這一點需要怎麼自己設定;
指定位圖格式的擷取函數: 功能上和Auto沒有太大差別,如下
資料擷取函數GetData (兩種)
在官方手冊的204頁,給出了擷取函數的要求:
下面這是我自己編寫的帶FATFS的SD卡資料擷取函數:
第一種: 分兩批擷取資料函數
int APP_GetData_Stream(void * p,const U8 ** ppData,unsigned NumBytesReq,U32 offset )
{
FRESULT res_SD = FR_OK;
FIL * phFile = (FIL *)p;
U8 * pData = (U8 *)*ppData;
DWORD NumBytesRead;
//将資料讀入緩沖區(至少一行像素點資料大小的緩沖區)
res_SD = f_mount(&FATFS_DEV_SDHC,"1:",1);
if(res_SD == FR_OK)
{
//1.打開檔案;
res_SD = f_open(phFile,"1:AliPay.dta",FA_READ|FA_OPEN_EXISTING);
if( res_SD == FR_OK )
{
printf( "檔案打開成功!\r\n");
//2.根據偏移量設定檔案讀寫指針位置
f_lseek(phFile,offset);
//3.擷取檔案頭資訊/單行位元組資料;
res_SD = f_read(phFile,pData,NumBytesReq,&NumBytesRead);
if((res_SD == FR_OK) && (NumBytesReq == NumBytesRead))
{
printf("資料擷取成功!\r\n");
}
else
printf("資料擷取失敗!錯誤代碼:%d\r\n",res_SD);
}
else
printf("檔案打開失敗!錯誤代碼:%d\r\n",res_SD);
f_close(phFile);
}
else
printf("檔案系統挂載失敗!錯誤代碼:%d\r\n",res_SD);
f_mount(NULL,"1:",0);
return NumBytesRead;
}
效果圖:
第二種: 多批擷取資料函數 (這個函數的功能我并沒有實作,測試了很多遍,以後再修補把,這裡先把代碼寫下來)
int APP_GetData(void * p,const U8 ** ppData,unsigned NumBytes,U32 offset )
{
FRESULT res_SD = FR_OK;
static U8 acBuffer[480];
FIL * phFile = NULL;
DWORD NumBytesRead;
phFile = (FIL *)p;
if( NumBytes > sizeof(acBuffer) ) NumBytes = sizeof( acBuffer );
res_SD = f_mount(&FATFS_DEV_SDHC,"1:",1);
if(res_SD == FR_OK)
{
//1.打開檔案;
res_SD = f_open(phFile,"1:AliPay.dta",FA_READ|FA_OPEN_EXISTING);
if( res_SD == FR_OK )
{
printf( "檔案打開成功!\r\n");
//2.根據偏移量設定檔案讀寫指針位置
f_lseek(phFile,offset);
//3.擷取檔案頭資訊/單行位元組資料;
res_SD = f_read(phFile,acBuffer,NumBytes,&NumBytesRead);
if((res_SD == FR_OK) && (NumBytes == NumBytesRead))
*ppData = acBuffer; //傳回緩存區的起始位址;
else
printf("資料擷取失敗!錯誤代碼:%d\r\n",res_SD);
}
else
printf("檔案打開失敗!錯誤代碼:%d\r\n",res_SD);
f_close(phFile);
}
else
printf("檔案系統挂載失敗!錯誤代碼:%d\r\n",res_SD);
f_mount(NULL,"1:",0);
return NumBytesRead;
}