v4l2驱动框架——ctrl_handler 文章目录ctrl_handlerov13850的handler代码应用层使用对焦例子ctrl_handlerV4L2控制框架Control Framework是Linux内核中为视频设备提供统一参数管理机制的子系统。它通过v4l2_ctrl_handler管理所有设备控制项如曝光、增益、白平衡等实现了用户空间与驱动之间的标准化参数交互接口。每个控制项代表一个可调节参数包含以下关键属性id控制项标识符如V4L2_CID_EXPOSUREtype数据类型整数、布尔、菜单等flags属性标志只读、易失、可执行等minimum/maximum取值范围step步进值default_value默认值val/val64当前值存储ov13850的handler代码// 函数功能初始化OV13850传感器的V4L2控制项曝光、增益、消隐等// 参数ov13850 - 指向OV13850传感器私有数据结构的指针// 返回值成功返回0失败返回错误码staticintov13850_initialize_controls(structov13850*ov13850){// 定义局部变量conststructov13850_mode*mode;// 指向当前分辨率模式的指针structv4l2_ctrl_handler*handler;// V4L2控制处理器管理所有控制项structv4l2_ctrl*ctrl;// 单个控制项指针s64 exposure_max,vblank_def;// 最大曝光时间默认垂直消隐值64位有符号整数u32 h_blank;// 水平消隐值32位无符号整数intret;// 函数返回值错误码// 第1行获取控制处理器指针存储在ov13850结构体中handlerov13850-ctrl_handler;// 第2行获取当前传感器工作模式分辨率、帧率等配置modeov13850-cur_mode;// 第3-5行初始化V4L2控制处理器预分配8个控制项的空间// v4l2_ctrl_handler_init初始化控制处理器第二个参数8表示预期会有8个控制项retv4l2_ctrl_handler_init(handler,8);if(ret)// 如果初始化失败returnret;// 返回错误码// 第6行设置控制处理器的互斥锁使用传感器自己的mutex保证线程安全// 当用户空间同时修改多个控制项时用这个锁防止竞争条件handler-lockov13850-mutex;// 第7-10行创建MIPI链接频率控制项只读// v4l2_ctrl_new_int_menu创建整数菜单类型控制项// - V4L2_CID_LINK_FREQ: 控制项ID表示MIPI接口的数据传输频率// - 0, 0: 最小索引和最大索引都是0表示只有1个菜单项// - link_freq_menu_items: 菜单项数组如{891000000}表示891MHzctrlv4l2_ctrl_new_int_menu(handler,NULL,V4L2_CID_LINK_FREQ,0,0,link_freq_menu_items);if(ctrl)// 如果控制项创建成功ctrl-flags|V4L2_CTRL_FLAG_READ_ONLY;// 设置为只读用户不能修改// 第11-12行创建像素率控制项只读// v4l2_ctrl_new_std创建标准控制项// - V4L2_CID_PIXEL_RATE: 控制项ID表示每秒输出的像素数// - 0: 最小值通常为0// - OV13850_PIXEL_RATE: 最大值如4208*3120*30≈394M像素/秒// - 1: 步进值每次调整的变化量// - OV13850_PIXEL_RATE: 默认值与最大值相同固定值v4l2_ctrl_new_std(handler,NULL,V4L2_CID_PIXEL_RATE,0,OV13850_PIXEL_RATE,1,OV13850_PIXEL_RATE);// 第13行计算水平消隐值// hts_def: 水平总时间包括有效像素和消隐期// width: 有效像素宽度// h_blank 总时间 - 有效时间 消隐时间h_blankmode-hts_def-mode-width;// 第14-17行创建水平消隐控制项只读// V4L2_CID_HBLANK: 水平消隐控制项// 所有参数最小值、最大值、默认值都设为h_blank表示这个值固定不可调ov13850-hblankv4l2_ctrl_new_std(handler,NULL,V4L2_CID_HBLANK,h_blank,h_blank,1,h_blank);if(ov13850-hblank)// 如果控制项创建成功ov13850-hblank-flags|V4L2_CTRL_FLAG_READ_ONLY;// 设置为只读// 第18行计算默认垂直消隐值// vts_def: 垂直总行数包括有效行和消隐行// height: 有效图像高度行数// vblank_def 总行数 - 有效行数 消隐行数vblank_defmode-vts_def-mode-height;// 第19-23行创建垂直消隐控制项可调// V4L2_CID_VBLANK: 垂直消隐控制项// ov13850_ctrl_ops: 控制项操作集当用户修改时会调用ov13850_set_ctrl()// vblank_def: 最小值当前模式的默认消隐// OV13850_VTS_MAX - mode-height: 最大值传感器支持的最大总行数减去有效行数// 1: 步进值每次调整1行// vblank_def: 默认值当前模式的消隐值ov13850-vblankv4l2_ctrl_new_std(handler,ov13850_ctrl_ops,V4L2_CID_VBLANK,vblank_def,OV13850_VTS_MAX-mode-height,1,vblank_def);// 第24行计算最大曝光时间// 最大曝光时间 垂直总行数 - 4留出4行余量防止曝光时间过长影响下一帧exposure_maxmode-vts_def-4;// 第25-29行创建曝光时间控制项可调// V4L2_CID_EXPOSURE: 曝光时间控制项单位行时间// OV13850_EXPOSURE_MIN: 最小值如1行时间// exposure_max: 最大值计算得出// OV13850_EXPOSURE_STEP: 步进值如1行// mode-exp_def: 默认值当前模式的默认曝光ov13850-exposurev4l2_ctrl_new_std(handler,ov13850_ctrl_ops,V4L2_CID_EXPOSURE,OV13850_EXPOSURE_MIN,exposure_max,OV13850_EXPOSURE_STEP,mode-exp_def);// 第30-34行创建模拟增益控制项可调// V4L2_CID_ANALOGUE_GAIN: 模拟增益控制项// OV13850_GAIN_MIN: 最小值如1倍对应寄存器值0x00// OV13850_GAIN_MAX: 最大值如16倍对应寄存器值0xFF// OV13850_GAIN_STEP: 步进值如寄存器值增加1// OV13850_GAIN_DEFAULT: 默认增益值ov13850-anal_gainv4l2_ctrl_new_std(handler,ov13850_ctrl_ops,V4L2_CID_ANALOGUE_GAIN,OV13850_GAIN_MIN,OV13850_GAIN_MAX,OV13850_GAIN_STEP,OV13850_GAIN_DEFAULT);// 第35-39行创建测试图案控制项菜单类型可调// V4L2_CID_TEST_PATTERN: 测试图案控制项// ARRAY_SIZE(ov13850_test_pattern_menu) - 1: 最大索引值菜单项数-1// 0: 最小索引值// 0: 默认索引值// ov13850_test_pattern_menu: 菜单字符串数组如{Disabled, Color Bars, ...}ov13850-test_patternv4l2_ctrl_new_std_menu_items(handler,ov13850_ctrl_ops,V4L2_CID_TEST_PATTERN,ARRAY_SIZE(ov13850_test_pattern_menu)-1,0,0,ov13850_test_pattern_menu);// 第40-45行检查控制处理器是否有错误if(handler-error){rethandler-error;// 获取错误码dev_err(ov13850-client-dev,// 打印错误信息到内核日志Failed to init controls(%d)\n,ret);gotoerr_free_handler;// 跳转到错误处理}// 第46行将控制处理器关联到V4L2子设备// 这样用户空间就可以通过子设备节点访问这些控制项ov13850-subdev.ctrl_handlerhandler;// 第47行成功返回0return0;// 第49-53行错误处理标签err_free_handler:// 释放控制处理器及其所有控制项v4l2_ctrl_handler_free(handler);// 第52行返回错误码returnret;}//vm149c的对焦staticintvm149c_init_controls(structvm149c_device*dev_vcm){structv4l2_ctrl_handler*hdldev_vcm-ctrls_vcm;conststructv4l2_ctrl_ops*opsvm149c_vcm_ctrl_ops;v4l2_ctrl_handler_init(hdl,1);v4l2_ctrl_new_std(hdl,ops,V4L2_CID_FOCUS_ABSOLUTE,0,VCMDRV_MAX_LOG,1,VCMDRV_MAX_LOG);if(hdl-error)dev_err(dev_vcm-sd.dev,%s fail error: 0x%x\n,__func__,hdl-error);dev_vcm-sd.ctrl_handlerhdl;returnhdl-error;}应用层使用对焦例子命令行对焦手动v4l2-ctl-d/dev/v4l-subdev3 --set-ctrlfocus_absolute32这里为什么是3是因为子设备注册时候的顺序是csi2phyov13850vm149c(从0开始这个刚好就是3)subdev设备文件intset_focus_absolute(intfd,intvalue){structv4l2_controlctrl;ctrl.idV4L2_CID_FOCUS_ABSOLUTE;ctrl.valuevalue;if(ioctl(fd,VIDIOC_S_CTRL,ctrl)0){perror(set_focus_absolute failed);return-1;}printf(Focus set to %d\n,value);return0;}intmain(){// 打开子设备节点通常是 /dev/v4l-subdevX 或 /dev/v4l2-sensor// 打开VM149C设备节点需要先确认正确的节点intfdopen(/dev/v4l-subdev3,O_RDWR);if(fd0){perror(Failed to open VM149C);return-1;}// 设置焦距范围0-VCMDRV_MAX_LOGset_focus_absolute(fd,0);close(fd);return0;}