前言
最近負責的產品是一個小型網關,搭載了一塊3.5寸的顯示屏,需要實時顯示一部分數據,還能進行參數設置,為了方便的管理多個窗口和界面的內容展示,所以這裡採用了STemWin,因為使用的主控芯片是STM32F29,所以可以免費使用STemWin,開發過程中遇到了部分問題,這裡記錄一下,方便自己查閱,也希望可以幫助到正在看文章的你。
記錄
1.Keil字符編碼對中文顯示的影響
在開發過程中發現中文字符顯示有問題,但是中文字庫都是已經轉換好的,剛開始不知道是什麼原因,通過與同事探討才發現了問題所在。
原因
文件默認的字符編碼格式與字庫索引的編碼格式不同導致的。
大家應該知道我們平常使用Keil的時候中文註釋會出現亂碼,一箇中文字符會被拆分成兩個字符,因為這是使用的 Encode in ANSI 編碼格式,我們來看一下 Encode in ANSI 編碼格式的百科:
ANSI是一種字符代碼,為使計算機支持更多語言,通常使用 0x00~0x7f 範圍的1 個字節來表示 1 個英文字符。超出此範圍的使用0x80~0xFFFF來編碼,即擴展的ASCII編碼。為使計算機支持更多語言,通常使用 0x80~0xFFFF 範圍的 2 個字節來表示 1 個字符。比如:漢字 ‘中’ 在中文操作系統中,使用 [0xD6,0xD0] 這兩個字節存儲。
可以看出,由於一箇中文符號被拆分成兩個字節來保存,所以就導致假如你用中文註釋的時候,會因為刪除中間的一個字符導致後面的字符全亂碼,大家可以自己嘗試下。正式由於這個原因,導致我想要在顯示屏上顯示中文字符的時候沒法正確索引到正確漢字的位置,從而無法顯示出中文來。
解決辦法
解決辦法也很簡單,我們只需要將字符的編碼格式修改一下即可,如下圖:
將Encode in ANSI修改為Encode in UTF-8 without signature即可,下面是中文顯示效果:
不過還有幾個字沒有製作進字庫當中去,所以沒有顯示,大家能猜出是哪幾個字嗎?
2.DialogBox窗口不支持定時器
因為想在某個子窗口上實時更新一些數據,原本以為直接用STemWin的定時器就可以搞定了,由於子窗口是通過 GUI_CreateDialogBox 函數創建的,也就是我們常說的對話框,但是居然不起作用!但是安富萊的例程也是這樣用的,這就有點讓我摸不到頭腦了。搜索了一下,發現大家也都遇到類似的問題,但是都沒有很好的解決方案,經過跟領導的探討,終於通過另一種方式將這個問題解決掉了,這裡分享出來給大家參考,當然不一定是最好的方案,但是因為項目急,暫且先用著吧。
解決辦法
建立一個子窗口,當然這個子窗口是一個空窗口,我們在這個子窗口的定時器中不斷無效化你要定時刷新的界面,從而達到窗口重繪的效果,下面展示部分代碼:
<code>//主要處理定時器的回調static void _cbTimerWindow(WM_MESSAGE *pMsg){ static WM_HTIMER hTimerTime2; switch(pMsg->MsgId) { case WM_CREATE: /@@* Create timer */ hTimerTime2 = WM_CreateTimer(pMsg->hWin, ID_TIMER_TIME2, 1000, 0); break; case WM_TIMER: /@@* Reset Timer */ //我們通過無效化你要刷新數據界面,從而進入窗口重繪選項,之所以這樣做,是因為直接在Dialog中定時器不起作用 WM_InvalidateWindow(WM_GetClientWindow(TempWin)); WM_RestartTimer(pMsg->Data.v, 1000); break; case WM_DELETE: WM_DeleteTimer(hTimerTime2); break; default: WM_DefaultProc(pMsg); }}//下圖左側的按鈕界面的回調函數static void _cbDeviceInfoMenuDialog(WM_MESSAGE *pMsg){ WM_HWIN hItem; int NCode; int Id; // USER START (Optionally insert additional variables) // USER END switch(pMsg->MsgId) { case WM_INIT_DIALOG: // // Initialization of 'DeviceInfo' // hItem = pMsg->hWin; FRAMEWIN_SetTitleVis(hItem, 0); // USER START (Optionally insert additional code for further widget initialization) BUTTON_SetFont(WM_GetDialogItem(hItem, ID_BUTTON_0), &GUI_FontHeiTi_12); BUTTON_SetFont(WM_GetDialogItem(hItem, ID_BUTTON_1), &GUI_FontHeiTi_12); BUTTON_SetFont(WM_GetDialogItem(hItem, ID_BUTTON_2), &GUI_FontHeiTi_12); BUTTON_SetFont(WM_GetDialogItem(hItem, ID_BUTTON_3), &GUI_FontHeiTi_12); //下面這條語句就是建立的子窗口,其中區域建議大家選在你要更新的界面區域中,我們在_cbTimerWindow這個回調函數中處理定時器 WM_CreateWindowAsChild(80, 45, 200, 40, pMsg->hWin, WM_CF_SHOW | WM_CF_HASTRANS, _cbTimerWindow, 0); // USER END break; case WM_NOTIFY_PARENT: Id = WM_GetId(pMsg->hWinSrc); NCode = pMsg->Data.v; switch(Id) { case ID_BUTTON_0: // Notifications sent by 'DateTime' switch(NCode) { // USER START (Optionally insert additional code for further notification handling) case WM_NOTIFICATION_GOT_FOCUS: //按鈕0獲取到焦點展示界面1 BUTTON_SetTextColor(WM_GetDialogItem(pMsg->hWin, ID_BUTTON_0), BUTTON_CI_UNPRESSED, GUI_RED); TempWin = GUI_CreateDialogBox(_aDatetimeCreate, GUI_COUNTOF(_aDatetimeCreate), _cbDatetimeDialog, WM_GetParent(pMsg->hWin), 0, 0); break; case WM_NOTIFICATION_LOST_FOCUS: //按鈕0失去焦點結束界面1 BUTTON_SetTextColor(WM_GetDialogItem(pMsg->hWin, ID_BUTTON_0), BUTTON_CI_UNPRESSED, GUI_BLACK); GUI_EndDialog(TempWin, 0); break; // USER END } break; } break; // USER START (Optionally insert additional message handling) case WM_KEY: switch(((WM_KEY_INFO *)(pMsg->Data.p))->Key) { case GUI_KEY_ESCAPE: brea; } break; // USER END default: WM_DefaultProc(pMsg); break; }}//主要處理界面的刷新static void _cbDatetimeDialog(WM_MESSAGE *pMsg){ WM_HWIN hItem; // USER START (Optionally insert additional variables) char dateTimeTemp[48] = {0}; // USER END hItem = pMsg->hWin; switch(pMsg->MsgId) { case WM_INIT_DIALOG: // // Initialization of 'Datetime' // // FRAMEWIN_SetTitleVis(hItem, 0); // // Initialization of 'Date' // hItem = WM_GetDialogItem(hItem, ID_TEXT_YEAR); TEXT_SetTextAlign(hItem, GUI_TA_HCENTER | GUI_TA_VCENTER); // USER START (Optionally insert additional code for further widget initialization) // USER END break; // USER START (Optionally insert additional message handling) case WM_KEY: case WM_PAINT: //每次進入更新當前時間 GUI_DrawBitmap(&bmdate, 10, 10); sprintf(dateTimeTemp, "%4d年", pClock_DateTime->TYY); TEXT_SetFont(WM_GetDialogItem(hItem, ID_TEXT_YEAR), &GUI_FontHeiTi_20); TEXT_SetText(WM_GetDialogItem(hItem, ID_TEXT_YEAR), dateTimeTemp); sprintf(dateTimeTemp, "%02d月", pClock_DateTime->TMD); TEXT_SetFont(WM_GetDialogItem(hItem, ID_TEXT_MONTH), &GUI_FontHeiTi_20); TEXT_SetText(WM_GetDialogItem(hItem, ID_TEXT_MONTH), dateTimeTemp); sprintf(dateTimeTemp, "%02d日", pClock_DateTime->TDD); TEXT_SetFont(WM_GetDialogItem(hItem, ID_TEXT_DATE), &GUI_FontHeiTi_20); TEXT_SetText(WM_GetDialogItem(hItem, ID_TEXT_DATE), dateTimeTemp); sprintf(dateTimeTemp, "%02d時", pClock_DateTime->THH); TEXT_SetFont(WM_GetDialogItem(hItem, ID_TEXT_HOURS), &GUI_FontHeiTi_20); TEXT_SetText(WM_GetDialogItem(hItem, ID_TEXT_HOURS), dateTimeTemp); sprintf(dateTimeTemp, "%02d分", pClock_DateTime->TMM); TEXT_SetFont(WM_GetDialogItem(hItem, ID_TEXT_MINUTE), &GUI_FontHeiTi_20); TEXT_SetText(WM_GetDialogItem(hItem, ID_TEXT_MINUTE), dateTimeTemp); sprintf(dateTimeTemp, "%02d秒", pClock_DateTime->TSS); TEXT_SetFont(WM_GetDialogItem(hItem, ID_TEXT_SECOND), &GUI_FontHeiTi_20); TEXT_SetText(WM_GetDialogItem(hItem, ID_TEXT_SECOND), dateTimeTemp); //系統自上電依賴運行的時間,以天數為最小單位 GUI_SetFont(&GUI_FontHeiTi_20); GUI_SetColor(GUI_BLACK); sprintf(dateTimeTemp, "系統已運行%3d天", 48); GUI_DispStringAt(dateTimeTemp, 40, 125); break; // USER END default: WM_DefaultProc(pMsg); break; }}/<code>
經過測試,上面的代碼是可以正常運行並使用的,目前只能通過這種方式先解決了,後面有時間再研究一下,下面是效果演示:
3.批量轉換圖標到代碼腳本
項目用到了不少圖標,這些圖標都是我從iconfont上下載的,這上面有很多不錯的圖標,建議大家有需求也可以去看一下,由於我下載的小圖標有點多,而且一個個的轉換起來有點複雜,所以就用Python寫了個小腳本,批量將圖片轉換成C代碼,當然腳本也很簡單,主要就是調用了一下
BmoCvt.exe這個官方給的工具,下面分享給大家:<code># -*- coding: utf-8 -*-# author: IAMLIUBO# blog: https://blogs.oopswow.comimport os import sys,timefrom PIL import ImageIMAGE_FILTE = ".PNG .png .JPEG .jpeg .jpg .BMP .bmp"CODE_DIR = "C_CODE/"def ConvertImage2Code(path): image_list = os.listdir(path) if not os.path.exists(path + "/../" + CODE_DIR): os.mkdir(path + "/../" + CODE_DIR) for i in image_list: if os.path.splitext(i)[1] in IMAGE_FILTE: code_save_path = CODE_DIR + i.split('.')[0] command = "BmpCvt.exe " + i + " -saveas" + code_save_path + ",1,16 -exit" os.system(command) print("Convert done!")def main(): if len(sys.argv) < 2: print("\\r\\nNo image folder path provided\\r\\n" "Usage:\\r\\n" " python Image2Code4STemWin.py [image folder path]") exit() if not os.path.exists("BmpCvt.exe"): print("\\r\\nPlease put BmpCvt.exe to this folder.\\r\\n") exit() print("\\r\\nImage to C code>
記得使用的時候將BmpCvt.exe這個工具放到當前目錄下,然後指定圖像文件的路徑即可,像下面一樣:
<code>python Image2Code4STemWin.py images/<code>
完成後會在當前目錄下生成一個C_CODE文件夾,裡面就是轉換完成後的代碼文件了。
自動化思想很重要呀,有時候程序要比人更可靠,而且非常適合重複性工作。
隨時更新…
Post author: IAMLIUBOPost date: Wed Mar 25 2020 15:10:52 GMT+0800Post link: https://blogs.oopswow.com/2020/03/25/STemWin-development-issue-record/Copyright Notice: All articles in this blog are licensed under CC BY-NC-ND 4.0 unless stating additionally.Commercial reprint please contact me by email.
閱讀更多 電子芯吧客 的文章