揭秘ToastNotifications设计模式:观察者模式在通知系统中的应用
2026/7/4 5:47:00
v4l2_ctrl_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结构体中)handler=&ov13850->ctrl_handler;// 第2行:获取当前传感器工作模式(分辨率、帧率等配置)mode=ov13850->cur_mode;// 第3-5行:初始化V4L2控制处理器,预分配8个控制项的空间// v4l2_ctrl_handler_init初始化控制处理器,第二个参数8表示预期会有8个控制项ret=v4l2_ctrl_handler_init(handler,8);if(ret)// 如果初始化失败returnret;// 返回错误码// 第6行:设置控制处理器的互斥锁,使用传感器自己的mutex保证线程安全// 当用户空间同时修改多个控制项时,用这个锁防止竞争条件handler->lock=&ov13850->mutex;// 第7-10行:创建"MIPI链接频率"控制项(只读)// v4l2_ctrl_new_int_menu创建整数菜单类型控制项:// - V4L2_CID_LINK_FREQ: 控制项ID,表示MIPI接口的数据传输频率// - 0, 0: 最小索引和最大索引都是0,表示只有1个菜单项// - link_freq_menu_items: 菜单项数组,如{891000000}表示891MHzctrl=v4l2_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_blank=mode->hts_def-mode->width;// 第14-17行:创建"水平消隐"控制项(只读)// V4L2_CID_HBLANK: 水平消隐控制项// 所有参数(最小值、最大值、默认值)都设为h_blank,表示这个值固定不可调ov13850->hblank=v4l2_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_def=mode->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->vblank=v4l2_ctrl_new_std(handler,&ov13850_ctrl_ops,V4L2_CID_VBLANK,vblank_def,OV13850_VTS_MAX-mode->height,1,vblank_def);// 第24行:计算最大曝光时间// 最大曝光时间 = 垂直总行数 - 4(留出4行余量,防止曝光时间过长影响下一帧)exposure_max=mode->vts_def-4;// 第25-29行:创建"曝光时间"控制项(可调)// V4L2_CID_EXPOSURE: 曝光时间控制项(单位:行时间)// OV13850_EXPOSURE_MIN: 最小值(如1行时间)// exposure_max: 最大值(计算得出)// OV13850_EXPOSURE_STEP: 步进值(如1行)// mode->exp_def: 默认值(当前模式的默认曝光)ov13850->exposure=v4l2_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_gain=v4l2_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_pattern=v4l2_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){ret=handler->error;// 获取错误码dev_err(&ov13850->client->dev,// 打印错误信息到内核日志"Failed to init controls(%d)\n",ret);gotoerr_free_handler;// 跳转到错误处理}// 第46行:将控制处理器关联到V4L2子设备// 这样用户空间就可以通过子设备节点访问这些控制项ov13850->subdev.ctrl_handler=handler;// 第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*hdl=&dev_vcm->ctrls_vcm;conststructv4l2_ctrl_ops*ops=&vm149c_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_handler=hdl;returnhdl->error;}命令行,对焦手动
v4l2-ctl-d/dev/v4l-subdev3 --set-ctrlfocus_absolute=32这里为什么是3,是因为子设备注册时候的顺序是csi2,phy,ov13850,vm149c(从0开始这个刚好就是3)subdev设备文件
intset_focus_absolute(intfd,intvalue){structv4l2_controlctrl;ctrl.id=V4L2_CID_FOCUS_ABSOLUTE;ctrl.value=value;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设备节点(需要先确认正确的节点)intfd=open("/dev/v4l-subdev3",O_RDWR);if(fd<0){perror("Failed to open VM149C");return-1;}// 设置焦距(范围0-VCMDRV_MAX_LOG)set_focus_absolute(fd,0);close(fd);return0;}