问题描述如下:
我在linux下使用V4L2对免驱摄像头进行操作,由于项目需求,需要通过USB线获取
摄像头板的GPI的状态,这个属于扩展功能,摄像头工程师是这么做的:由于我们的摄像头的GAMMA值不会被用到,因此当摄像头板检测到GPI电平有变化时,它就将
GAMMA对应的寄存器的值设置成另外一个值,然后我通过V4L2的函数ioctl,参数为VIDIOC_G_GAMMA来获取当前的GAMMA值,但是读不到其变化。问题是,我在windows下又可以读到
GAMMA值的变化,似乎是linux下的底层驱动没有刷新(个人感觉,但又找不到刷新的相关函数),
求大神指导
针对这个问题,建议尝试使用 V4L2 的事件通知机制来获取 GPI 的状态变化。可以通过以下步骤实现:
1、使用 ioctl 函数打开事件通知功能:
int enable_event(int fd, uint32_t type, uint32_t id)
{
struct v4l2_event_subscription sub;
memset(&sub, 0, sizeof(sub));
sub.type = type;
sub.id = id;
return ioctl(fd, VIDIOC_SUBSCRIBE_EVENT, &sub);
}
其中,fd 是摄像头设备文件描述符,type 是要订阅的事件类型,可以是 V4L2_EVENT_ALL 或者其他具体类型,id 是要订阅的事件 ID,可以是 V4L2_EVENT_CTRL 等。
2、在主循环中等待事件通知,如果收到 V4L2_EVENT_CTRL 类型的事件,说明 GPI 状态发生了变化,此时可以调用 ioctl 函数获取 GAMMA 值:
void main_loop(int fd)
{
/* 订阅事件 */
if (enable_event(fd, V4L2_EVENT_CTRL, 0x009a0901) == -1) {
printf("Error: Cannot enable event\n");
return;
}
while (1) {
fd_set fds;
FD_ZERO(&fds);
FD_SET(fd, &fds);
struct timeval timeout;
timeout.tv_sec = 2;
timeout.tv_usec = 0;
int ret = select(fd + 1, &fds, NULL, NULL, &timeout);
if (ret == -1) {
printf("Error: Cannot waiting for data\n");
return;
} else if (ret == 0) {
printf("Error: No data coming in time\n");
return;
}
/* 处理事件 */
struct v4l2_event event;
if (ioctl(fd, VIDIOC_DQEVENT, &event) == -1) {
printf("Error: Cannot dequeue event\n");
return;
}
if (event.type == V4L2_EVENT_CTRL && event.u.ctrl.changes & V4L2_CTRL_CHG_EVENT_VALUE) {
/* GPI 状态发生了变化 */
uint32_t gamma;
if (ioctl(fd, VIDIOC_G_GAMMA, &gamma) == -1) {
printf("Error: Cannot get gamma value\n");
return;
}
printf("Gamma value: %d\n", gamma);
}
}
}
在上述代码中,0x009a0901 是对应 GPI 状态的控制 ID,可以根据摄像头驱动程序的文档或者源代码中找到。
需要注意的是,不同的摄像头设备可能会有所不同,这里提供的方法仅供参考。