zoukankan      html  css  js  c++  java
  • 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.
    ",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.
    ",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
    ", szDevNode);
        }
        else
            printf("receivetest: can't open %s
    ", 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.
    ",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.
    ",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
    ", szDevNode);
        }
        else
            printf("receivetest: can't open %s
    ", 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.
    ",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.
    ",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
    ", szDevNode);
        }
        else
            printf("receivetest: can't open %s
    ", szDevNode);
    
    
    
        //after call the target function in ELF object,close it using dlclose
        dlclose(libm_handle);
    
        return 0;
    }
    

























  • 相关阅读:
    python
    python
    gitlab
    nodejs
    java
    ElasticSearch 安装与配置 (windows)
    shell脚本批量注释
    C获取系统中CPU核数
    linux内核内存管理
    perf: interrupt took too long (3136 > 3126), lowering kernel.perf_event_max_sample_rate to 63000
  • 原文地址:https://www.cnblogs.com/siahekai/p/11000809.html
Copyright © 2011-2022 走看看