跳至內容

應用指南:為可程式化擷取卡設計訊號處理 Applet#

所有可程式化影音擷取卡的設計,均允許您使用圖形化 FPGA 設計環境 VisualApplets 來規劃您專屬的個人化影像處理方案(Applets)。

VisualApplets 中的 GPI/GPO Operators#

透過 VisualApplets,您可以設計專屬於您的應用特定觸發系統。例如,您可以決定由哪一個實體 I/O 接腳傳送或接收訊號。

在 VisualApplets 中,有兩個運算元可用於將個別接腳與您的 Applet 設計進行連線:

Operator GPI(用於觸發 IN 訊號)

Operator GPO(用於觸發 OUT 訊號)

VisualApplets 使用 GPI 與 GPO 運算子進行設計

上圖顯示了一個如何實作訊號處理的範例。您想要實作的訊號處理功能取決於您的特定需求。若要為您的應用找到合適的 operator,請造訪 Signal Library。此外,也請根據您的需求選擇 Front GPIO。

上方範例顯示了針對兩個輸入訊號的縮小功能。兩個訊號從 GPI1 和 Front GPI0 輸入至可程式化影音擷取卡。這些訊號首先會進行彈跳消除、選取上升緣或下降緣,然後以 8 或 3 的係數進行 Scaling。系統會為這兩個訊號設定訊號長度,接著將訊號轉發至 FrontGPO0、GPO1、GPO2 以及相機以控制影像擷取。

使用 GPI/GPO Operators#

您想要在 Front GPIO 或 Trigger Extension Board 上使用的每個 Trigger IN / Trigger OUT 接腳,都必須與 GPI/GPO 運算元的執行個體相互連接。

資訊

由於 Operator GPI 與 Operator GPO 具有相同的 funcionality,因此在下文中將這兩個運算元一起說明。

  1. 在設計中的 operator 圖示上按兩下以開啟 Module Properties 視窗。

VisualApplets GPI Operator

Module Properties 視窗隨即開啟:

VisualApplets Module Properties 視窗

如您所見,該運算元具有兩個 Property:

  • Pin_ID:Property Pin_ID 允許您定義要定址所選連接器上的哪一個個別接腳。
  • ConnectorType:Property ConnectorType 允許您定義要定址哪一個連接器(Front GPIO 或透過 GPIO 的 Trigger Extension Board)。

資訊

對於 CXP-12 影音擷取卡, ConnectorType Property 包含在 Pin_ID 參數的值。

Parameter Connector Type#

此 Property 允許您定義要定址哪一組觸發接腳,即 15 針 D-Sub 插座中提供的接腳(Front GPIO),或是 Trigger Extension Board 上提供的接腳(GPIO)。此 Property 的數值範圍為 {Front GPIO, GPIO}。

Value Front GPIO#

如果您選取數值 Front GPIO,即表示您定址可程式化影音擷取卡槽板上的下列連接器(觸發接腳群組):

擷取卡的 Front GPIO

Value GPIO#

如果您選取數值 GPIO,即表示您定址可程式化影音擷取卡上的下列觸發接腳群組:

採集卡的 GPIO

GPIO 用於連接至 Trigger Extension Board:

透過 GPIO 連接 Trigger Board

Property Pin_ID#

此 Property 允許您定義要定址所選連接器(觸發接腳群組)上的哪一個個別接腳。Property 的數值範圍 Pin_ID is {0;7} or {0;3} for connector type GPIO 或 Front GPIO.

資訊

參數的值範圍 Pin_ID for marathon frame grabbers and operator GPO is {0;1}.

Front GPIO 上的接腳組態與 VisualApplets Operator Reference#

前方 GPIO 接腳

Front GPIO 上的接腳組態與 VisualApplets Operator Reference

在差動模式下,引腳 9 可用於在 Pin_ID=3 的 GPI 運算子上接收單端訊號,請參閱「Differential」模式(預設)。

注意

擷取卡觸發系統需要從 Voltage IN 引腳取得供應電壓。如果要連接沒有 PWR 引腳的裝置,則需要從外部來源為 Voltage IN 引腳提供電源。

Trigger INPUT on the Front GPIO#

如果您

  • 正在使用 GPI 運算子(用於定義觸發輸入訊號),
  • 已將參數 Connector Type 設定為 Front GPIO,

參數 Pin_ID 的值對應到下列引腳:

帶有接腳編號的前方 GPIO

Front GPIO 的輸入 (GPI) 引腳是可以設定的。您可以使用六個輸入引腳來接收下列其中一項:

  1. 3 個差動訊號,或
  2. 2 個差動訊號與 1 個單端訊號,或
  3. 4 個單端訊號。

若要針對特定訊號類型(差動或單端)設定 Front GPIO 上的輸入引腳,您有兩種可用模式:

  • 模式 Differential (提供選項 a 和 b)
  • 模式 Single-ended (提供選項 c)

Front GPIO 的預設模式為 Differential。關於如何切換模式,請參閱主題 輸入組態 於採集卡手冊中。

注意

在預設組態中,Front GPIO 的 GPIO 引腳提供:

  • 上拉模式下的 3 個差動輸入訊號,或 2 個差動與 1 個單端輸入訊號
  • 兩個 TTL 輸出訊號

如果您需要其他輸入訊號組態,即:

  • 下拉模式,
  • 4 個單端訊號(且無差動訊號),

您可以根據需求輕鬆設定 GPI。若要進行設定,請使用隨 Framegrabber SDK 一起安裝在您系統上的命令列工具。

模式 Differential (預設)#

在模式 Differential (預設)下,Front GPIO 的所有六個輸入接腳均設定為接收差動訊號:

  • 接腳 8/9 -> 1 個差動訊號
  • 接腳 11/12 -> 1 個差動訊號
  • 接腳 13/14 -> 1 個差動訊號

或者,您可以使用接腳對 8/9(在模式 Differential中)來接收一個單端訊號:

  • 接腳 9 -> 1 個單端訊號
  • 接腳 11/12 -> 1 個差動訊號
  • 接腳 13/14 -> 1 個差動訊號

模式中的個別接腳 Differential:

「差動」(Differential) 模式下的個別接腳

接腳 11 與 12:

  • 接腳 11 與 12 上的輸入訊號會被解讀為一個差動訊號(接腳 11 為正 [+] 值,接腳 12 為負 [-] 值)。
  • 若要在 VisualApplets 中接收此差動訊號,請使用 Pin_ID = 0 的 GPI 運算子執行個體。

接腳 13 與 14:

  • 接腳 13 與 14 上的輸入訊號會被解讀為一個差動訊號(接腳 13 為正 [+] 值,接腳 14 為負 [-] 值)。
  • 若要在 VisualApplets 中接收此差動訊號,請使用 Pin_ID = 1 的 GPI 運算子執行個體。

接腳 8 與 9:

  • 接腳 8 與 9 上的輸入訊號會被解讀為一個差動訊號(接腳 8 為正 [+] 值,接腳 9 為負 [-] 值)。
  • 若要在 VisualApplets 中接收此差動訊號,請使用 Pin_ID = 2 的 GPI 運算子執行個體。

或者,您可以使用接腳對 8/9 來接收一個單端訊號(儘管在模式 Differential中):在此情況下,

  • 您可以將單端輸入訊號連接至實體接腳 9。
  • 若要在 VisualApplets 中接收此單端信號,請使用 Pin_ID = 3 的 GPI operator 實例。

模式 Single-Ended#

在模式 Single-ended (可設定),Front GPIO 的六個輸入引腳經設定可接收 4 個單端信號:

  • Pin 8 -> 1 個單端信號
  • 接腳 9 -> 1 個單端訊號
  • Pin 11 -> 1 個單端信號
  • Pin 13 -> 1 個單端信號

模式中的個別接腳 Single-ended:

「單端」(Single-ended) 模式下的個別接腳

Pin 11 和 12 上的信號:

  • Pin 11 上的輸入信號會被解讀為單端信號。
  • 若要在 VisualApplets 中接收此單端信號,請使用 Pin_ID = 0 的 GPI operator 實例。
  • 在此模式下不使用 Pin 12 Single-ended.

Pin 13 和 14 上的信号:

  • Pin 13 上的輸入信號會被解讀為單端信號。
  • 若要在 VisualApplets 中接收此單端信號,請使用 Pin_ID = 1 的 GPI operator 實例。
  • 在此模式下不使用 Pin 14 Single-ended.

接腳 8 與 9:

  • Pin 8 和 9 上的輸入信號會被解讀為兩個單端信號。
  • 若要在 VisualApplets 中接收這些單端信號,請使用

    • 針對 Pin 8 使用 Pin_ID = 2 的 GPI operator 實例,以及
    • 針對 Pin 9 使用 Pin_ID = 3 的 GPI operator 實例。

觸發 OUTPUT TTL 位於 Front GPIO 上#

如果您

  • 使用 GPO operator 來定義觸發輸出信號,
  • 您已將 Connector Type 參數設定為 Front GPIO,

Pin_ID 參數的值對應至下列引腳:

前方 GPIO TTL 輸出接腳編號

這兩個引腳會發送 TTL 信號(5 V)。

Front GPIO 上的輸出 TTL 信號(5 V):

前方 GPIO 上的輸出 TTL 信號 (5 V)

定址 Trigger Extension Boards 的觸發訊號 (透過 GPIO)#

GPIO 連接器是可程式化擷取卡的實體觸發介面,專為連接至 Trigger Extension Board 而設計:

採集卡與 Trigger Board 的 GPIO

GPIO 連接器,也就是每個 Trigger Extension Board,提供:

  • 8 個數位輸入(IN 0 - 7)(8 個單端信號或 4 個差動信號)
  • 8 個數位輸出(OUT 0 - 7)

這八個輸入的索引為 0 到 7。這八個輸出的索引也為 0 到 7。

有關接腳配置的更多資訊,請參閱 Pin Layout on SUB-D15 - TTL Trigger Board 或 PIN Layout Opto-Coupled Trigger 5 - Opto-Coupled Trigger 5 Board 擴充板。

Pin_ID TTL Trigger Extension Boards 上的分配#

Port A#

接腳編號 Pin_ID 於 VisualApplets 中
1 0
2 1
3 2
4 3
5
6
7
8
9
10
11 0
12 1
13 2
14 3
15 0

Port B#

接腳編號 Pin_ID 於 VisualApplets 中
(單端信號)
1 4
2 5
3 6
4 7
5
6
7
8
9
10
11 4
12 5
13 6
14 7
15

Pin_ID OPTO Trigger 5 Extension Boards 上的分配#

Port A#

接腳編號 Pin_ID 於 VisualApplets 中
(單端信號)
Pin_ID 於 VisualApplets 中
(差動信號)
1 0
2 1
3 2
4 3
5
6
7
8
9
10
11 0 0
12 1 0
13 2 2
14 3 2
15

Port B#

接腳編號 Pin_ID 於 VisualApplets 中
(單端信號)
Pin_ID 於 VisualApplets 中
(差動信號)
1 4
2 5
3 6
4 7
5
6
7
8
9
10
11 4 4
12 5 4
13 6 6
14 7 6
15

設定觸發系統#

您可以透過兩種方式存取觸發組態:

  • microDisplay X:您可以在此透過 GUI 輸入觸發參數值,以檢查特定組態的結果。
  • SDK:用於將擷取卡程式介面實作到特定的影像處理軟體應用程式中。

對觸發系統的要求會因您的應用而異:

  • 對於面陣相機(Area Scan),只需要一種觸發類型(影格觸發)。
  • 對於線陣相機(Line Scan),需要兩種觸發類型:線觸發(為每一行發送一個觸發脈衝)和影格觸發。在線陣相機應用中,影格觸發控制影像中附加了多少行。

您可以在此處找到有關如何觸發面陣和線陣相機的更多資訊。

資訊

有關採集卡觸發系統的進一步資訊,請參閱主題 GPIO Connectors。

資訊

有關哪些參數可用(以及特定設定可能產生的影響)的資訊,請參閱您正在使用的 Applet 的說明文件。

在硬體上執行您的 Applet#

先決條件:

在您的可程式化擷取卡上執行 Applet 之前,必須先將您的 *.hap 檔案(Applet)複製到主機 PC 上的 Basler Framegrabber SDK 安裝目錄中,並将其燒錄至您的可程式化擷取卡。

資訊

只有 marathon 擷取卡才需要進行燒錄。

您可以在 Hardware Applet: From Idea to Application 下找到關於如何將 VisualApplets 設計轉換為硬體 applet 的詳細資訊。

  1. 將您在 VisualApplets 中建立的 *.hap 檔案複製到目錄 [Framegrabber SDK installation directory]/Hardware Applets/[frame grabber model]中。如果沒有對應可程式化擷取卡型號名稱的子目錄,請自行建立。如果您在 VisualApplets 中使用了預設設定,您將在 VisualApplets 安裝目錄的子目錄 *.hap 中找到您的 Designs.

  2. 依照 Flashing 中的說明將您的 applet 安裝到可程式化採集卡上。

完成此操作後,您可以:

  • 使用作為 Framegrabber SDK 一部分的 Basler 工具 microDisplay X 來測試您的 applet,並直接檢視 applet 的影像處理結果。請參閱主題 Configuring the Applet (microDisplay X)。

範例:當您將訊號處理範例設計的硬體 Applet 載入 microDisplay X 時,您可以在參數樹中設定動態參數,例如降階縮放比例與訊號寬度,如下圖所示。

信號處理設計範例

在您自己的軟體中啟動 Applet#

若要開始在您自己的軟體中使用該 Applet:

  1. 在 Basler 應用程式介面上,使用呼叫 Fg_Init 來啟動 Applet。請指定您使用 VisualApplets 建立的 *.hap 檔案所在的儲存路徑。

有關所有可用函式呼叫的詳細說明與參考,請參閱 Framegrabber API Manual 與 Framegrabber API Function Reference。

作為 C++ SDK 的基礎,您可以使用 VisualApplets 內部 SDK 程式碼產生器,該產生器位於圖示選單中,或在 VisualApplets 的 Build 與 Generate SDK example 下找到。此功能會自動產生對應的 CMake 檔案與 Visual Studio 專案檔案 (另請參閱 Hardware Applet: From Idea to Application)。

自動產生的 SDK 程式碼包含下列影像擷取與參數設定所需的功用區塊:

  • 擷取卡初始化
  • 為緩衝區配置 Memory
  • 定義 Get 和 Set 參數
  • 建立顯示畫面
  • 為每個現有的連接埠啟動 Applet 與相機的擷取
  • 擷取影像
  • 停止擷取
  • 關閉顯示
  • 釋放緩衝區的 Memory
  • 擷取卡取消初始化

以下是適用於訊號處理範例的 C++ Framegrabber API 程式碼範例。

  • 擷取卡初始化並配置緩衝區的 Memory:
int main ()
{

    // ---------------------------------------------------------------------------
    // Frame Grabber: Initialization
    // ---------------------------------------------------------------------------


    int nrOfCycles = 5000; // Acquire 5000 frames, TODO: insert the number according to your needs
    unsigned int boardIndex = 0; // TODO: Use the correct board index at multi frame grabber systems
    int rc = 0; // return code

    Fg_Struct* fg = Fg_Init("SignalProcessingDesign.hap", boardIndex);
    if(fg == NULL){
        fprintf(stderr, "Error in Fg_Init(): %s\n", Fg_getLastErrorDescription(NULL));
        fprintf(stderr, "Press <Return> to continue...\n");
        getchar();
        return -1;
    }

    int nrOfParameter = Fg_getNrOfParameter(fg);
    fprintf(stderr, "No. of Parameter: %d\n", nrOfParameter);

    int i;
    for (i = 0; i < nrOfParameter; i++) {
        const char *name = Fg_getParameterName(fg,i);
        fprintf(stderr," Param %d: %s,%x\n",i,name,Fg_getParameterId(fg,i));
    }


    / ---------------------------------------------------------------------------
    // Frame Grabber: Allocate memory for buffer(s)
    // ---------------------------------------------------------------------------


    // Feature Cnt 0,0
    int width0 = 1024;
    int height0 = 1024;
    int byteWidth0 = 1;
    size_t bufSize0 = width0 * height0 * (size_t)byteWidth0;
    const unsigned int mem0_bufs = 4;
    dma_mem *pmem0 = Fg_AllocMemEx(fg, bufSize0 * mem0_bufs, mem0_bufs);
    if(pmem0 == NULL){
        fprintf(stderr, "Error in Fg_AllocMemEx(): %s\n", Fg_getLastErrorDescription(fg));
        Fg_FreeGrabber(fg);
        fg = NULL;
        fprintf(stderr, "Press <Return> to continue...\n");
        getchar();
        return -1;
    }
  • 以下摘錄自 Get 和 Set 參數區段。在此您可以看到如何設定動態參數。以下程式碼展示了如何設定縮小比例(downscale factor),以及如何選擇特定脈衝作為 operator 的參數 Device1_Process0_ByFactor8:
// ---------------------------------------------------------------------------
// Frame Grabber: Get* and Set* parameters for Process0
// ---------------------------------------------------------------------------


/*============ Downscale : ByFactor8 ============== */


int Device1_Process0_ByFactor8_Downscale_Id = Fg_getParameterIdByName(fg, "Device1_Process0_ByFactor8_Downscale");
if(Device1_Process0_ByFactor8_Downscale_Id <= 0) {
    fprintf(stderr, "\n");
    fprintf(stderr, "Error in Fg_getParameterIdByName(Device1_Process0_ByFactor8_Downscale): %s (%d)\n", Fg_getLastErrorDescription(fg), Device1_Process0_ByFactor8_Downscale_Id);
    fprintf(stderr, "Press <Return> to continue...\n");
    getchar();
}

unsigned int Device1_Process0_ByFactor8_Downscale;
Device1_Process0_ByFactor8_Downscale = 8;
rc = Fg_setParameterWithType(fg, Device1_Process0_ByFactor8_Downscale_Id, Device1_Process0_ByFactor8_Downscale, 0);
if(rc < 0) {
    fprintf(stderr, "\n");
    fprintf(stderr, "Error in Fg_setParameterWithType(Device1_Process0_ByFactor8_Downscale_Id): %s (%d)\n", Fg_getLastErrorDescription(fg), rc);
    fprintf(stderr, "Press <Return> to continue...\n");
    getchar();
}

int Device1_Process0_ByFactor8_SelectedPulse_Id = Fg_getParameterIdByName(fg, "Device1_Process0_ByFactor8_SelectedPulse");
if(Device1_Process0_ByFactor8_SelectedPulse_Id <= 0) {
    fprintf(stderr, "\n");
    fprintf(stderr, "Error in Fg_getParameterIdByName(Device1_Process0_ByFactor8_SelectedPulse): %s (%d)\n", Fg_getLastErrorDescription(fg), Device1_Process0_ByFactor8_SelectedPulse_Id);
    fprintf(stderr, "Press <Return> to continue...\n");
    getchar();
}

unsigned int Device1_Process0_ByFactor8_SelectedPulse;
Device1_Process0_ByFactor8_SelectedPulse = 1;
rc = Fg_setParameterWithType(fg, Device1_Process0_ByFactor8_SelectedPulse_Id, Device1_Process0_ByFactor8_SelectedPulse, 0);
if(rc < 0) {
    fprintf(stderr, "\n");
    fprintf(stderr, "Error in Fg_setParameterWithType(Device1_Process0_ByFactor8_SelectedPulse_Id): %s (%d)\n", Fg_getLastErrorDescription(fg), rc);
    fprintf(stderr, "Press <Return> to continue...\n");
    getchar();
}

資訊

您可以從自動產生的 C++ Framegrabber API 程式碼中刪除所有在擷取期間進行參數設定時無關的參數。只需保留在擷取期間需要設定的參數即可。

  • 建立顯示畫面、開始擷取並擷取影像:
// ---------------------------------------------------------------------------
// Frame Grabber: Create the display(s)
// ---------------------------------------------------------------------------

int id0 = CreateDisplay(8 * byteWidth0, width0, height0);
if(id0 < 0) {
    fprintf(stderr, "Error in CreateDisplay(): %s (%d)\n", Fg_getLastErrorDescription(fg), id0);
    Fg_FreeMemEx(fg, pmem0);
    pmem0 = NULL;
    Fg_FreeGrabber(fg);
    fg = NULL;
    fprintf(stderr, "Press <Return> to continue...\n");
    getchar();
    return id0;
}
SetBufferWidth(id0, width0, height0);


// ---------------------------------------------------------------------------
// Frame Grabber: Start acquisition at applet and camera for each present port
// ---------------------------------------------------------------------------

rc = Fg_AcquireEx(fg, 0, nrOfCycles, ACQ_STANDARD, pmem0);
if(rc != 0) {
    fprintf(stderr, "Error in Fg_AcquireEx() for channel 0: %s\n", Fg_getErrorDescription(fg, rc));
    CloseDisplay(id0);
    Fg_FreeMemEx(fg, pmem0);
    pmem0 = NULL;
    Fg_FreeGrabber(fg);
    fg = NULL;
    fprintf(stderr, "Press <Return> to continue...\n");
    getchar();
    return rc;
}

// ---------------------------------------------------------------------------
// Frame Grabber: Grab images
// ---------------------------------------------------------------------------

frameindex_t frameNo0 = 0;
for(i = 1; i <= nrOfCycles; ++i){
frameNo0 = Fg_getLastPicNumberBlockingEx(fg, frameNo0 + 1, 0, 5, pmem0);
    if(frameNo0 < 0) {
        fprintf(stderr, "Error in Fg_getLastPicNumberBlockingEx(): %s (%d)\n", Fg_getLastErrorDescription(fg), frameNo0);
        fprintf(stderr, "Press <Return> to continue...\n");
        getchar();
        break;
    } else {
        fprintf(stderr, "grabbed image %d\n", frameNo0 );
    }

    void *iPtr0 = Fg_getImagePtrEx(fg, frameNo0, 0, pmem0);
    if(iPtr0 == NULL) {
        fprintf(stderr, "Error in Fg_getImagePtrEx(): %s\n", Fg_getLastErrorDescription(fg));
        Fg_stopAcquireEx(fg, 0, pmem0, 0);
        CloseDisplay(id0);
        Fg_FreeMemEx(fg, pmem0);
        pmem0 = NULL;
        Fg_FreeGrabber(fg);
        fg = NULL;
        fprintf(stderr, "Press <Return> to continue...\n");
        getchar();
        return -1;
    }
    DrawBuffer(id0, iPtr0, frameNo0, "");
}
  • 停止擷取、關閉顯示、釋放緩衝區的 Memory:
// ---------------------------------------------------------------------------
// Frame Grabber: Stop acquisition
// ---------------------------------------------------------------------------

rc = Fg_stopAcquireEx(fg, 0, pmem0, 0);
if(rc < 0) {
    fprintf(stderr, "Error in Fg_stopAcquireEx(): %s (%d)\n", Fg_getLastErrorDescription(fg), rc);
    fprintf(stderr, "Press <Return> to continue...\n");
    getchar();
}


// ---------------------------------------------------------------------------
// Frame Grabber: Close the display
// ---------------------------------------------------------------------------

CloseDisplay(id0);


// ---------------------------------------------------------------------------
// Frame Grabber: Release the memory for buffer(s)
// ---------------------------------------------------------------------------

rc = Fg_FreeMemEx(fg, pmem0);
if(rc < 0) {
    fprintf(stderr, "Error in Fg_FreeMemEx(): %s (%d)\n", Fg_getLastErrorDescription(fg), rc);
    fprintf(stderr, "Press <Return> to continue...\n");
    getchar();
}
pmem0 = NULL;


// ---------------------------------------------------------------------------
// Frame Grabber: Uninitialization
// ---------------------------------------------------------------------------

rc = Fg_FreeGrabber(fg);
if(rc < 0) {
    fprintf(stderr, "Error in Fg_FreeGrabber(): %s\n", Fg_getLastErrorDescription(NULL));
    fprintf(stderr, "Press <Return> to continue...\n");
    getchar();
}
fg = NULL;


fprintf(stderr, "Press <Return> to exit...\n");
getchar();

return (0);
}

您可以將此範例程式碼整合至更大的 SDK 環境中。