在嵌入式裝置缺乏 HDMI/VGA 輸出、且無法直接連接顯示器的情境下,透過自訂通訊通道將裝置顯示記憶體 (Frame Buffer) 傳回 PC 端重構畫面,是除錯與監控的關鍵技術。本文詳細記錄如何在 .NET Micro Framework (NETMF) 裝置端擷取 16-bit RGB565/RGB555 顯存資料,並在 PC 端透過 Bitmap 掩碼機制修正 RGB/BGR 色序偏差,實現即時遠端截圖。
嵌入式 LCD 常見 16-bit 色深,主要有三種編排:
NETMF Bitmap 建構子支援 PixelFormat.Format16bppRgb565 與 Format16bppRgb555,但無法直接指定 RGB/BGR 順序。解決方案利用 BMP 檔頭 BITMAPINFOHEADER 的 biCompression = BI_BITFIELDS 機制,在檔頭後附加 12 bytes 掩碼(R/G/B Mask),強制指定各色通道在 16-bit 中的位置。
#define Custom_Command_Screenshots 0x02
LCD_GetFrameBuffer() 取得顯存指標,memcpy 回傳資料。
case Custom_Command_Screenshots:
UINT32 offset = (inData[4]<<24) | (inData[3]<<16) | (inData[2]<<8) | inData[1];
UINT16 size = (inData[6]<<8) | inData[5];
if (offset == 0xFFFFFFFF) // 要求掩碼
{
*outLength = 6;
// 範例:裝置為 BGR555 (B:7C00, G:03E0, R:001F)
UINT16 R_Mask = 0x001F, G_Mask = 0x03E0, B_Mask = 0x7C00;
outData[0] = (UINT8)(R_Mask & 0xFF); outData[1] = (UINT8)(R_Mask >> 8);
outData[2] = (UINT8)(G_Mask & 0xFF); outData[3] = (UINT8)(G_Mask >> 8);
outData[4] = (UINT8)(B_Mask & 0xFF); outData[5] = (UINT8)(B_Mask >> 8);
}
else // 讀取顯存區塊
{
UINT8* pScreen = (UINT8*)LCD_GetFrameBuffer();
*outLength = size;
memcpy(outData, pScreen + offset, size);
}
break;
private void palScreen_Paint(object sender, PaintEventArgs e)
{
if (LCD_BitsPerPixel == 16)
{
// 1. 建立 565 格式空白 Bitmap(預設 RGB 掩碼 0xF800/0x07E0/0x001F)
LCD_bmp = new Bitmap(LCD_Width, LCD_Height, PixelFormat.Format16bppRgb565);
// 2. 存成 BMP 記憶體流,修改檔頭掩碼區
MemoryStream ms = new MemoryStream();
LCD_bmp.Save(ms, ImageFormat.Bmp);
ms.Position = 54; // BITMAPINFOHEADER 結束位置 (14+40)
// 3. 寫入裝置回傳的 R/G/B Mask (各 4 bytes,Little-Endian)
ms.Write(BitConverter.GetBytes(R_Mask), 0, 4);
ms.Write(BitConverter.GetBytes(G_Mask), 0, 4);
ms.Write(BitConverter.GetBytes(B_Mask), 0, 4);
// 4. 附加實際顯存資料
ms.Write(LCD_Buffer, 0, LCD_Buffer.Length);
ms.Flush();
// 5. 從修改後的流重建 Bitmap,GDI+ 會依掩碼正確解讀色彩
LCD_bmp = new Bitmap(ms);
// 6. 裝置座標系可能上下翻轉,需 RotateFlip 修正
LCD_bmp.RotateFlip(RotateFlipType.Rotate180FlipX);
e.Graphics.DrawImage(LCD_bmp, 0, 0);
}
else // 不支援色深顯示紅叉
{
// ... 繪製錯誤指示 ...
}
}
| 環節 | 關鍵點 |
|---|---|
| 顯存取得 | LCD_GetFrameBuffer() 為 HAL 層函式,需依平台實作(如 SAM9261 映射 LCDC 基址)。 |
| 分塊傳輸 | WireProtocol 封包有大小限制,PC 端需迴圈分批請求 (Offset/Size) 組合完整畫面。 |
| 掩碼來源 | 由裝置端開發者提供(最清楚 LCD 控制器配置),避免 PC 端猜測。 |
| BMP 掩碼機制 | 僅 PixelFormat.Format16bppRgb565 對應 BI_BITFIELDS 支援自訂掩碼;555 格式固定 BI_RGB 無法修改,故統一按 565 容器載入再由掩碼修正。 |
| 座標翻轉 | 嵌入式顯存常以左上為原點、逐行掃描;GDI+ 同樣左上原點,但部分驅動程式會垂直翻轉寫入,故需 RotateFlip 實測調整。 |
此技術展示了 NETMF 透過 WireProtocol 擴充自訂通道,結合 PC 端 GDI+ 進階影像處理,解決跨平台色彩格式不一致的經典案例。同樣原理可擴充至遠端觸控注入、鍵盤模擬、GPIO 狀態視覺化等互動式診斷工具開發。