抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

本文主要是介绍如何在 libsigrok 想要增加自己的硬件驱动协议时该如何进行开发的一份指导说明

准备

所有的硬件驱动都在 src/hardware 的文件目录底下 在这里可以看到不同厂家实现的不同驱动,尤其是其中的 demo 文件夹下的代码一定要细读 这里是官方提供的示例。
所有的驱动都会有一个 api.c 的文件 这个文件就包含了 整个驱动的入口 在这个文件中关于驱动入口比较重点关注的主要是两个 一个是入口结构的定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static struct sr_dev_driver demo_driver_info = {
    .name = "demo",
    .longname = "Demo driver and pattern generator",
    .api_version = 1,
    .init = std_init,
    .cleanup = std_cleanup,
    .scan = scan,
    .dev_list = std_dev_list,
    .dev_clear = dev_clear,
    .config_get = config_get,
    .config_set = config_set,
    .config_list = config_list,
    .dev_open = std_dummy_dev_open,
    .dev_close = std_dummy_dev_close,
    .dev_acquisition_start = dev_acquisition_start,
    .dev_acquisition_stop = dev_acquisition_stop,
    .context = NULL,
};

在上面的结构体中定义了一些名称和版本号以及其他的一些接口函数,另外一个就是 注册结构体的函数
SR_REGISTER_DEV_DRIVER(demo_driver_info);
这个方法就是注册了上述结构体的一些参数

详解

初始化和销毁

在硬件的生命周期会涉及到两个一个是初始化另外一个是销毁,其中涉及到的初始化和销毁的函数是以下函数

1
2
3
4
5
/** Called when driver is loaded, e.g. program startup. */
int (*init) (struct sr_dev_driver *driver, struct sr_context *sr_ctx);
/** Called before driver is unloaded.
*  Driver must free all resources held by it. */
int (*cleanup) (const struct sr_dev_driver *driver);

其中 init 函数是在驱动加载的时候就会进行的 cleanup 函数是卸载的时候触发

驱动查询相关

在硬件初始化后可以通过 scan 函数扫描当前驱动下有哪些可以使用的信号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/** Scan for devices. Driver should do all initialisation required.
*  Can be called several times, e.g. with different port options.
*  @retval NULL Error or no devices found.
*  @retval other GSList of a struct sr_dev_inst for each device.
*                Must be freed by caller!
*/
GSList *(*scan) (struct sr_dev_driver *driver, GSList *options);
/** Get list of device instances the driver knows about.
*  @returns NULL or GSList of a struct sr_dev_inst for each device.
*           Must not be freed by caller!
*/
GSList *(*dev_list) (const struct sr_dev_driver *driver);
/** Clear list of devices the driver knows about. */
int (*dev_clear) (const struct sr_dev_driver *driver);

其中 scan 的扫描可以扫描当前的

配置相关

如果需要对驱动进行一些配置相关的能力则可以使用这个能力

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/** Query value of a configuration key in driver or given device instance.
*  @see sr_config_get().
*/
int (*config_get) (uint32_t key, GVariant **data,
const struct sr_dev_inst *sdi,
const struct sr_channel_group *cg);
/** Set value of a configuration key in driver or a given device instance.
*  @see sr_config_set(). */
int (*config_set) (uint32_t key, GVariant *data,
const struct sr_dev_inst *sdi,
const struct sr_channel_group *cg);
/** List all possible values for a configuration key in a device instance.
* @see sr_config_list().
*/
int (*config_list) (uint32_t key, GVariant **data,
const struct sr_dev_inst *sdi,
const struct sr_channel_group *cg);

驱动的打开和关闭

驱动如果在初始化的时候 需要独占的时候可以使用如下函数进行驱动的打开和关闭

1
2
3
4
/** Open device */
int (*dev_open) (struct sr_dev_inst *sdi);
/** Close device */
int (*dev_close) (struct sr_dev_inst *sdi);

数据的监听和关闭

在底层驱动中 并不是直接调用获取数据,而是通过函数的回调来告诉上层的函数有新的数据产生 你需要处理新的数据,所以需要对数据进行监听和关闭

1
2
3
4
/** Begin data acquisition on the specified device. */
int (*dev_acquisition_start) (const struct sr_dev_inst *sdi);
/** End data acquisition on the specified device. */
int (*dev_acquisition_stop) (struct sr_dev_inst *sdi);

其中在 dev_acquisition_start 中有一个比较需要关注的方法是下面的方法这个方法是在 session 中添加回调源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
 * Add an event source for a file descriptor.
 *
 * @param session The session to use. Must not be NULL.
 * @param fd The file descriptor, or a negative value to create a timer source.
 * @param events Events to check for.
 * @param timeout Max time in ms to wait before the callback is called,
 *                or -1 to wait indefinitely.
 * @param cb Callback function to add. Must not be NULL.
 * @param cb_data Data for the callback function. Can be NULL.
 *
 * @retval SR_OK Success.
 * @retval SR_ERR_ARG Invalid argument.
 *
 * @since 0.3.0
 * @private
 */
SR_PRIV int sr_session_source_add(struct sr_session *session, int fd,
        int events, int timeout, sr_receive_data_callback cb, void *cb_data)

这个方法中比较关注的是在函数中的 cb 函数,这个函数是能够透传数据至上层

1
2
/** Type definition for callback function for data reception. */
typedef int (*sr_receive_data_callback)(int fd, int revents, void *cb_data);

在 demo/protocol.c 的代码中 实现这个函数的方法是 demo_prepare_data这个方法,在这个代码中 我们可以看到如何透传数据 真实的数据产生在logic_generator这个方法来进行产生 我们以SIGROK为例。我们重点代码是如下代码

1
2
3
4
5
6
7
8
memset(devc->logic_data, 0x00, size);
for (i = 0; i < size; i += devc->logic_unitsize) {
for (j = 0; j < devc->logic_unitsize; j++) {
pat = pattern_sigrok[(devc->step + j) % sizeof(pattern_sigrok)] >> 1;
devc->logic_data[i + j] = ~pat;
}
devc->step++;
}

对于透传的数据只需要上传至 devc->logic_data中就可以了 我们可以看到 demo 中的数据是来源于下面的定义中

1
2
3
4
5
6
7
8
9
10
static const uint8_t pattern_sigrok[] = {
    0x4c, 0x92, 0x92, 0x92, 0x64, 0x00, 0x00, 0x00,
    0x82, 0xfe, 0xfe, 0x82, 0x00, 0x00, 0x00, 0x00,
    0x7c, 0x82, 0x82, 0x92, 0x74, 0x00, 0x00, 0x00,
    0xfe, 0x12, 0x12, 0x32, 0xcc, 0x00, 0x00, 0x00,
    0x7c, 0x82, 0x82, 0x82, 0x7c, 0x00, 0x00, 0x00,
    0xfe, 0x10, 0x28, 0x44, 0x82, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0xbe, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};

第一个数据是 0x4c 换算成 2 进制 0100 1100 代码中需要对其进行右移 然后取反则真实传递的数据是 1101 1001
第二个数据是 0x92 换算成 2 进行 1001 0010 代码中需要对其进行右移 然后取反则真实传递的数据是 1011 0110
对应界面 我们可以看到从 d7-d0 通道 我们可以对应上 以前两个数据为准

D7 D6 D5 D4 D3 D2 D1 D0
1 1 0 1 1 0 0 1
1 0 1 1 0 1 1 0

补充

对应的还需要了解一下 libsigork.h 里面的一些定义