ROS CAN總線設備接入(一)Linux動態庫的顯式調用

前提:

(1),如果在libpcan安裝正常的話,那麼可以用以下命令查找到libpcan.so

ls /usr/lib/libpcan*

查找到方可進行api載入。

(2),確保CMakeList.txt 當前目標文件已加入add_executable...;target_link_libraries...;

此外加入 target_link_libraries(源碼名 ${CMAKE_DL_LIBS})

【正文】

1,添加載入需要的頭文件及文件中自己使用的頭文件。載入相關的頭文件爲 dlfcn.h, fcntl.h,由於目標使用libpcan函數,所以libpcan.h也要加入。

dlfcn.h : Linux動態庫的顯式調用庫,包括dlopen(),dlclose()等。

fcntl.h :fcntl.h定義了很多宏和open,fcntl函數原型,包括close open等關閉文件的系列操作。本文中用到了其打開文件的宏定義,即打開文件的控制模式。

     int open(const char *pathname, int oflag, ... /* mode_t mode */);
  返回值:成功則返回文件描述符,否則返回 -1
  對於 open 函數來說,第三個參數(...)僅當創建新文件時(即使用了O_CREAT 時)才使用,用於指定文件的訪問權限位(access permission bits)。pathname 是待打開/創建文件的路徑名(如 C:/cpp/a.cpp);oflag 用於指定文件的打開/創建模式,這個參數可由以下常量(定義於 fcntl.h)通過邏輯或構成。
  O_RDONLY 只讀模式
  O_WRONLY 只寫模式
  O_RDWR 讀寫模式
  打開/創建文件時,至少得使用上述三個常量中的一個。以下常量是選用的:
  O_APPEND 每次寫操作都寫入文件的末尾
  O_CREAT 如果指定文件不存在,則創建這個文件
  O_EXCL 如果要創建的文件已存在,則返回 -1,並且修改 errno 的值
  O_TRUNC 如果文件存在,並且以只寫/讀寫方式打開,則清空文件全部內容(即將其長度截短爲0)
  O_NOCTTY 如果路徑名指向終端設備,不要把這個設備用作控制終端。
  O_NONBLOCK 如果路徑名指向 FIFO/塊文件/字符文件,則把文件的打開和後繼 I/O設置爲非阻塞模式(nonblocking mode)
  以下三個常量同樣是選用的,它們用於同步輸入輸出
  O_DSYNC 等待物理 I/O 結束後再 write。在不影響讀取新寫入的數據的前提下,不等待文件屬性更新。
  O_RSYNC read 等待所有寫入同一區域的寫操作完成後再進行
  O_SYNC 等待物理 I/O 結束後再 write,包括更新文件屬性的 I/O open 返回的文件描述符一定是最小的未被使用的描述符。
2,根據目標使用的函數及其參數形式定義目標指針。
此處暫時使用兩個函數,一個CAN_Init,一個LINUX_CAN_Open函數。
下面爲libpcan.h頭文件中的定義。
DWORD CAN_Init(HANDLE hHandle, WORD wBTR0BTR1, int nCANMsgType);
HANDLE LINUX_CAN_Open(const char *szDeviceName, int nFlag);
根據上面定義我們聲明兩個函數指針的類型,方便後文使用
//define mapping function according to target function in libpcan.h
typedef DWORD (*funCAN_Init_TYPE)(HANDLE hHandle, WORD wBTR0BTR1, int nCANMsgType);
typedef HANDLE (*funLINUX_CAN_Open_TYPE)(const char *szDeviceName, int nFlag);
然後用聲明的類型定義映射函數名
    //define function pointer,there is a one-to-one mapping between target function and your defined function
    funCAN_Init_TYPE fun_CAN_Init;
    funLINUX_CAN_Open_TYPE funLINUX_CAN_Open;
3,定義文件訪問訪問的指針,實現文件加載。
    void *libm_handle = NULL;//define pointer used for file acess of libpcan.so
    // dlopen 函數還會自動解析共享庫中的依賴項。這樣,如果您打開了一個依賴於其他共享庫的對象,它就會自動加載它們。
    // 函數返回一個句柄,該句柄用於後續的 API 調用
    libm_handle = dlopen("libpcan.so", RTLD_LAZY );
    // 如果返回 NULL 句柄,表示無法找到對象文件,過程結束。否則的話,將會得到對象的一個句柄,可以進一步詢問對象
    if (!libm_handle){
        // 如果返回 NULL 句柄,通過dlerror方法可以取得無法訪問對象的原因
        printf("Open Error:%s.\n",dlerror());
        return 0;
    }
其中用到的dlopen的參數解釋如下:
void *dlopen(const char *filename, int flag);
其中flag有:RTLD_LAZY RTLD_NOW RTLD_GLOBAL,其含義分別爲:
RTLD_LAZY:在dlopen返回前,對於動態庫中存在的未定義的變量(如外部變量extern,也可以是函數)不執行解析,就是不解析這個變量的地址。
RTLD_NOW:與上面不同,他需要在dlopen返回前,解析出每個未定義變量的地址,如果解析不出來,在dlopen會返回NULL,錯誤爲: undefined symbol: xxxx.......
RTLD_GLOBAL:它的含義是它的含義是使得庫中的解析的定義變量在隨後的隨後其它的鏈接庫中變得可以使用。
4,實現動態庫函數到目標函數的映射。
    // 使用 dlsym 函數,嘗試解析新打開的對象文件中的符號。您將會得到一個有效的指向該符號的指針,或者是得到一個 NULL 並返回一個錯誤
    //one-to-one mapping
    char *errorInfo;//error information pointer
    fun_CAN_Init =(funCAN_Init_TYPE)dlsym(libm_handle,"CAN_Init");
    funLINUX_CAN_Open = (funLINUX_CAN_Open_TYPE)dlsym(libm_handle,"LINUX_CAN_Open");
    errorInfo = dlerror();// 調用dlerror方法,返回錯誤信息的同時,內存中的錯誤信息被清空
    if (errorInfo != NULL){
        printf("Dlsym Error:%s.\n",errorInfo);
        return 0;
    }
5,定義訪問硬件的HANDLE(指針),實現自定義函數對於can總線的訪問,訪問完成要關閉對象。
此前文中已有定義。
#define DEFAULT_NODE "/dev/pcan0"
此處實現如下:
    HANDLE pcan_handle =NULL;//void *pcan_handle

    const char  *szDevNode = DEFAULT_NODE;//define const pointer point to device name
    pcan_handle = funLINUX_CAN_Open(szDevNode, O_RDWR | O_NONBLOCK);//use mapping function
    //judge whether the call is success.if pcan_handle=null,the call would be failed
    if(pcan_handle){
        can_set_init();
        printf("receivetest: %s have been opened\n", szDevNode);
    }
    else
        printf("receivetest: can't open %s\n", szDevNode);



    // 調用 ELF 對象中的目標函數後,通過調用 dlclose 來關閉對它的訪問
    dlclose(libm_handle);
這樣便實現了對於libpcan中函數的導入及調用。但尚未深入研究,後期會進行進一步的更新。
該部分完整示例如下:
#include <ros/ros.h>
#include <stdio.h>

#include <dlfcn.h>

#include <libpcan.h>
#include <fcntl.h>
#include <unistd.h>

//define mapping function according to target function in libpcan.h
typedef DWORD (*funCAN_Init_TYPE)(HANDLE hHandle, WORD wBTR0BTR1, int nCANMsgType);
typedef HANDLE (*funLINUX_CAN_Open_TYPE)(const char *szDeviceName, int nFlag);

//the target device name
#define DEFAULT_NODE "/dev/pcan0"

//can init function
bool can_set_init()
{


    return true;
}

int main(int argc, char *argv[])
{
    void *libm_handle = NULL;//define pointer used for file acess of libpcan.so

    //define function pointer,there is a one-to-one mapping between target function and your defined function
    funCAN_Init_TYPE fun_CAN_Init;
    funLINUX_CAN_Open_TYPE funLINUX_CAN_Open;


    // dlopen 函數還會自動解析共享庫中的依賴項。這樣,如果您打開了一個依賴於其他共享庫的對象,它就會自動加載它們。
    // 函數返回一個句柄,該句柄用於後續的 API 調用
    libm_handle = dlopen("libpcan.so", RTLD_LAZY );
    // 如果返回 NULL 句柄,表示無法找到對象文件,過程結束。否則的話,將會得到對象的一個句柄,可以進一步詢問對象
    if (!libm_handle){
        // 如果返回 NULL 句柄,通過dlerror方法可以取得無法訪問對象的原因
        printf("Open Error:%s.\n",dlerror());
        return 0;
    }

    // 使用 dlsym 函數,嘗試解析新打開的對象文件中的符號。您將會得到一個有效的指向該符號的指針,或者是得到一個 NULL 並返回一個錯誤
    //one-to-one mapping
	char *errorInfo;//error information pointer
    fun_CAN_Init =(funCAN_Init_TYPE)dlsym(libm_handle,"CAN_Init");
    funLINUX_CAN_Open = (funLINUX_CAN_Open_TYPE)dlsym(libm_handle,"LINUX_CAN_Open");

    errorInfo = dlerror();// 調用dlerror方法,返回錯誤信息的同時,內存中的錯誤信息被清空
    if (errorInfo != NULL){
        printf("Dlsym Error:%s.\n",errorInfo);
        return 0;
    }

	
    HANDLE pcan_handle =NULL;//void *pcan_handle

    const char  *szDevNode = DEFAULT_NODE;//define const pointer point to device name
    pcan_handle = funLINUX_CAN_Open(szDevNode, O_RDWR | O_NONBLOCK);//use mapping function
    //judge whether the call is success.if pcan_handle=null,the call would be failed
    if(pcan_handle){
        can_set_init();
        printf("receivetest: %s have been opened\n", szDevNode);
    }
    else
        printf("receivetest: can't open %s\n", szDevNode);



    // 調用 ELF 對象中的目標函數後,通過調用 dlclose 來關閉對它的訪問
    dlclose(libm_handle);

    return 0;
}

由於寶寶的qt不能輸入中文,中英混合看着難受,於是乎在附加一個全英文註釋版。
#include <ros/ros.h>
#include <stdio.h>

#include <dlfcn.h>

#include <libpcan.h>
#include <fcntl.h>


//define mapping function according to target function in libpcan.h
typedef DWORD (*funCAN_Init_TYPE)(HANDLE hHandle, WORD wBTR0BTR1, int nCANMsgType);
typedef HANDLE (*funLINUX_CAN_Open_TYPE)(const char *szDeviceName, int nFlag);

//the target device name
#define DEFAULT_NODE "/dev/pcan0"

//can init function
bool can_set_init()
{


    return true;
}

int main(int argc, char *argv[])
{
    //define function pointer,there is a one-to-one mapping between target function and your defined function
    funCAN_Init_TYPE fun_CAN_Init;
    funLINUX_CAN_Open_TYPE funLINUX_CAN_Open;

    void *libm_handle = NULL;//define pointer used for file acess of libpcan.so
    //load libpcan.so using dlopen function,return handle for further use
    libm_handle = dlopen("libpcan.so", RTLD_LAZY );
    if (!libm_handle){
        printf("Open Error:%s.\n",dlerror());//if file can't be loaded,return null,get reason using dlerror function
        return 0;
    }

    char *errorInfo;//error information pointer
    //one-to-one mapping using dlsym function,if return null,mapping would be failed
    fun_CAN_Init =(funCAN_Init_TYPE)dlsym(libm_handle,"CAN_Init");
    funLINUX_CAN_Open = (funLINUX_CAN_Open_TYPE)dlsym(libm_handle,"LINUX_CAN_Open");
    errorInfo = dlerror();//get error using dlerror function,and clear the error list in memory
    if (errorInfo != NULL){
        printf("Dlsym Error:%s.\n",errorInfo);
        return 0;
    }

    HANDLE pcan_handle =NULL;//void *pcan_handle
    const char  *szDevNode = DEFAULT_NODE;//define const pointer point to device name
    pcan_handle = funLINUX_CAN_Open(szDevNode, O_RDWR | O_NONBLOCK);//use mapping function
    //judge whether the call is success.if pcan_handle=null,the call would be failed
    if(pcan_handle){
        can_set_init();
        printf("receivetest: %s have been opened\n", szDevNode);
    }
    else
        printf("receivetest: can't open %s\n", szDevNode);



    //after call the target function in ELF object,close it using dlclose
    dlclose(libm_handle);

    return 0;
}

























發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章