2012年1月12日 星期四

[Embedded] OSD 表單設計


OSD 設計

第一個任務是在Cortex M3上,實作一個程式,使其能夠控制攝影機,並且讓攝影機輸出對應的控制表單。

預期工作:
1.      設計 OSD 表單
2.      定義 OSD 表單對應的資料結構
3.      實作 OSD 控制邏輯



設計如下

1.      OSD表單:
假設OSD有三層表單,其表單結構如下:
l   LEVEL-1
n   LEVEL -11
u  LEVEL -111
u  LEVEL -112
n   LEVEL -12
l   LEVEL -2
n   LEVEL -21
l   LEVEL -3
n   LEVEL -31


2.      定義資料結構
C語言而言 “Level-1” 會分別對應到 ASCII 編碼的七個字元,但是OSD內部顯示 “Level-1” 字串時,並不一定是採用 ASCII編碼,就我拿到的版子而言,其OSD只支援64個字元。因此表單所顯示的字元編碼語ASCII不同,此部份需要轉換。

由於此表單只需要轉換一次,因此不需要將轉換的動作寫入在Cortex M5內,為了簡單起見,我直接使用JAVASript來實作一個簡單的轉換函數,產生需要使用的表格,當需要控制OSD時,直接使用轉換後的表格即可,程式碼如下:




<html> <head> <script type="text/javascript"> function Menu2Bytes() { var i, vLen, vTableSize, vIndex=0, vCh, bQuote; var vpTempIn = "", vpTempOut = "" ; var vpOutput="<p>The translation table (for C) is</p> " var pLookUp = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ!?(),.:;&;~*%+-X/=\"'_@" var vpInput = document.getElementById("InputArea").value; vTableSize = pLookUp.length; vLen = vpInput.length; bQuote = 0; for(i=0;i<vLen;i++) { vCh = vpInput.charAt(i) ; // Recored the menu string vpTempIn += vCh; // Generate the output string vIndex = pLookUp.indexOf(vCh); if(vIndex >= 0) { if(bQuote==0) { bQuote = 1; vpTempOut = "{" } else { vpTempOut += "," } if(vIndex<10) vpTempOut += "0x0" + vIndex; else vpTempOut += "0x" + vIndex; } else { vpOutput = vpOutput + vpTempOut + "}, //" + vpTempIn + "<p></P>" bQuote = 0; vpTempIn = "" vpTempOut = "" } } document.getElementById("demo").innerHTML=vpOutput; } </script> </head> <body> <h1>Translate the menu to bytes for C language </h1> <p>Paste the menu design here.</p> <textarea id = "InputArea" rows=10 cols=50 > LEVEL-1 LEVEL-11 LEVEL-111 LEVEL-112 LEVEL-12 LEVEL-2 LEVEL-21 LEVEL-3 LEVEL-31</textarea> <p id="demo">This is a paragraph.</p> <button type="demo" onclick="Menu2Bytes()">Translate Menu</button> </body> </html>
: 可以透過此網址,線上練習JavaScript


確定了要顯示的字串之後,需要定義一個資料結構,此資料結構需要有以下特性:
l   顯示字串
l   找到目前位於哪一階層
l   進入下一階層
l   回到上一階層
l   列出同階層內的所有表單項目
l   找到對應需要設定的函數

根據以上需求,我將資料結構定義如下:
      typedef struct
      {
         char                        Level;        // Level 1, 2, 3
         eOSDCmd         Cmd;        // up, down, right, left, enter, menu
         char                           *pCaption;    // menu item string
         tpCmdHandler       pCmdHandler; // function to send command to device
}_tOSDCmdTable;

產生的表格如下,共有8個欄位
      static _tOSDCmdTable _gpxCodeTable[OSD_TABLE_SIZE] =
      {
        { 1, _eLevel_1,  STR_LEVEL_1, NULL },
                { 2, _eLevel_1,  STR_LEVEL_11, NULL },
                        { 3, _eLevel_1,  STR_LEVEL_111, NULL },
                        { 3, _eLevel_1,  STR_LEVEL_111, NULL },
        { 1, _eLevel_2,  STR_LEVEL_1, NULL },
                { 2, _eLevel_2,  STR_LEVEL_1, NULL },
        { 1, _eLevel_3,  STR_LEVEL_1, NULL },
                { 2, _eLevel_3,  STR_LEVEL_1, NULL },      
};               


3. 實作 OSD 控制邏輯

    對於OSD的控制,一般會有兩種做法。四個控制鍵或是六個控制鍵。
    這裡我假設產品會使用六個控制鍵來控制OSD,分別是Up, Down, Right, Left, Menu, Enter,使用者可以利用這些鍵來執行OSD所提供的各項功能。

    其操作方式如下:
l   初始化 menu
      找出gpxCodeTable內屬於 Level 1 的所有項目,製造表單項目,並且將目前的表單選項設定為第一筆
for(i=0; i<Table_Size; i++)
{
     if(gpxCodeTable[i].Level == 1)
    {
       memcpy(pOutput, gpxCodeTable[i].pCaption, strlen(gpxCodeTable[i].pCaption));
       pOutput += 28;  // change to next line
    }
}

l   找到目前位於哪一階層
CurrentLevel = gpxCodeTable[i].Level;

l   進入下一階層 (即列舉出下一階層的所有表單)
for(i= CurrentLevel; i<Table_Size; i++)
{
     if( (gpxCodeTable[i].Level > CurrentLevel) &&
        (gpxCodeTable[i].Cmd == gpxCodeTable[CurrentLevel].Cmd) )
      {
         memcpy(pOutput, gpxCodeTable[i].pCaption, strlen(gpxCodeTable[i].pCaption));
         pOutput += 28;  // change to next line
      }
}

l   回到上一階層 (即列舉出上一階層的所有表單)
for(i= CurrentLevel; i>0; i--)
{
      if(gpxCodeTable[i].Level < CurrentLevel)
     {
         UpperLevel  = i+1;
     }
}

for(i= UpperLevel ; i<Table_Size; i++)
{
      if( (gpxCodeTable[i].Level > CurrentLevel) &&
          (gpxCodeTable[i].Cmd == gpxCodeTable[CurrentLevel].Cmd) )
     {
         memcpy(pOutput, gpxCodeTable[i].pCaption, strlen(gpxCodeTable[i].pCaption));
         pOutput += 28;  // change to next line
     }
}

l   列出同階層內的所有表單項目
for(i= 0 ; i<Table_Size; i++)
{
   if( (gpxCodeTable[i].Level == CurrentLevel) &&
       (gpxCodeTable[i].Cmd == gpxCodeTable[CurrentLevel].Cmd) )
   {
       memcpy(pOutput, gpxCodeTable[i].pCaption, strlen(gpxCodeTable[i].pCaption));
       pOutput += 28;  // change to next line
   }
}

l   找到對應需要設定的函數
    pCBFunction = gpxCodeTable[i]. pCmdHandler;


上述設計便可以達到以下功能
l   顯示 OSD menu
l   利用 up, down, left, right瀏覽OSD menu
l   離開 OSD menu