
简介AN41908自动聚焦芯片的SPI驱动源码专为需要精确控制镜头对焦的嵌入式开发场景设计。该驱动承担操作系统与AN41908之间的指令翻译初始化时配置SPI时钟频率与数据模式运行中通过读写寄存器下发聚焦命令并解析位置反馈同时处理通信异常与电机限位等边界情况。压缩包共9个文件包含6个C语言源码、2个头文件和1个Makefile整体仅16KB模块划分清晰核心通讯、变焦控制、聚焦执行等功能相互独立头文件统一对外接口便于按需修改与移植。已有1204人学习下载。通过研读这份源码开发者可快速理解自动聚焦驱动的完整工作流掌握寄存器级控制与SPI通讯调试技巧并能基于现有代码扩展自定义聚焦策略适合驱动开发入门及镜头模组调试场景。1. AN41908 聚焦驱动一包源码和一颗芯片怎么把它跑成能用的镜头控制拿到 an41908a.zip 的人多半正被镜头聚焦折腾电机有了驱动芯片用的是 AN41908厂商给了一个驱动源码包但解压之后不知道先看哪个文件、改哪一行、怎么验证它真的在工作。这个包在聚焦方案里的角色一句话能讲清主控通过 SPI 总线下发目标位置AN41908 把位置指令转成聚焦电机上一段可控的电流让镜头停在正确的焦点上。适合这几类人做摄像头模组对焦的嵌入式工程师、调内窥镜光学系统的现场应用工程师以及自动对焦镜头产线上要复测驱动一致性的人。下面按一线做法拆开讲先看懂寄存器与时序再把源码编进内核理清位置写入路径最后给出最常见的几种翻车现场和对应的验证手段。2. 读懂 AN41908 的寄存器模型与 SPI 时序写对第一帧前必须搞清的三件事驱动源码能不能用不取决于代码写得多漂亮取决于你对你手上的这颗芯片理解到什么程度。SPI 驱动看起来就那一两百行但如果寄存器模型理解错了编进去之后电机只会抖动甚至完全不动。所以先别急着编译把三件事搞清楚芯片在系统里的位置、寄存器怎么分类、SPI 帧怎么写。2.1 聚焦驱动芯片在系统里的位置主控、SPI 总线与电机的三角关系这套系统的三角关系非常清晰主控是 SPI Master负责发命令AN41908 是 SPI Slave负责解析命令并驱动电机电机是执行机构把电流变成镜头位移。主控不直接操作电机功率线而是通过 SPI 把目标位置写进芯片芯片内部再做功率放大和电流控制。这个设计的好处是主控的 GPIO 资源省下来了而且功率部分离逻辑器件远不会因为电机启停把主控拉复位。选 SPI 而不是 I2C对这个场景有实际理由SPI 是全双工能在一笔事务里同时完成“发命令”和“收状态”帧结构灵活寄存器地址和数据可以按芯片需求任意拼速率高写一次位置指令在微秒级完成对聚焦这种需要频繁微调的场合更跟手。I2C 只有两根线抗干扰要求高速率还被上拉电阻卡住。当然这不是说 I2C 不能用只是 AN41908 这类聚焦驱动SPI 是更常见也更稳的选择。调试时理解这层关系能帮你快速定位问题。电机不动先看 SPI 通没通电机在动但位置不对再看寄存器值换算电机发热查驱动电流配置。每一类现象都对应着三角关系里的一个具体环节不会毫无头绪。2.2 寄存器模型先分类ID 寄存器、配置寄存器、位置与状态寄存器AN41908 这类芯片的寄存器看着一大堆实际按功能分四类就够用不必一上来就把每个地址背下来。这四类分别是 ID 与版本类、控制类、配置类、位置与状态类。它们各自的读写属性和调试作用完全不同。寄存器类别读写方向典型内容调试时的作用ID / 版本只读芯片型号、版本号判断 SPI 通路是否通、帧格式是否对控制读写使能、待机、软复位上电后第一件事是解除待机配置读写驱动电流、衰减模式、时钟分频直接影响电机抖动、发热、噪音位置与状态读写目标位置、当前位置、BUSY 标志聚焦的主通道与状态判断我拿到一个驱动包第一步永远是找 ID 寄存器。因为 SPI 驱动最怕的就是“看起来在通信实际上全是错的数据”ID 寄存器只读且内容是固定的只要主控能把这个值读回来就说明硬件连接、SPI 模式、帧格式、片选时序全都对了。这一步验证通过后面才值得往下调。位置与状态寄存器是聚焦控制的核心。目标位置寄存器负责接收新的位置指令状态寄存器里的 BUSY 位告诉主控上一次移动是否结束。这两个寄存器配合不好就会出现命令被吞、镜头原地不动或者走到一半停住的问题后面第 4 章会详细说。2.3 第一帧怎么写SPI 模式、帧长度与片选时序写第一帧之前必须先确认三个参数SPI 模式、帧长度、片选时序。这三个参数任何一个不对写进去的寄存器全部无效。而且它们不是靠猜的要从数据手册的时序图里读或者直接拿逻辑分析仪抓另一块已知正常的板子。SPI 模式由 CPOL 和 CPHA 决定也就是时钟空闲电平和数据采样沿。AN41908 这类芯片常见的是 Mode 0 或 Mode 3但不同批次封装可能不同。设备树里对应spi-cpol和spi-cpha两个属性如果驱动源码里硬编码了 mode要在probe里设置spi-mode。这里我吃过亏Mode 0 能读回 0xFF看着没反应换成 Mode 3 帧全乱了最后用逻辑分析仪对时序才确定芯片实际是按 Mode 0 采样。帧长度多数是 16 位或 24 位。16 位帧常见于地址 8 位、数据 8 位的简单封装24 位帧则是地址 8 位、数据 16 位聚焦位置分辨率超过 8 位时必然用这个。驱动源码里的buf[0]、buf[1]、buf[2]三段就是典型的 24 位帧高字节先发片选拉低后依次移出。片选时序也有讲究CS 拉低后要留出建立时间再送时钟传输结束后 CS 拉高两次 CS 之间要有足够的释放时间。有些初学者直接连着发多帧帧与帧之间 CS 没拉高芯片会把两帧当成一帧解析。这个问题在低速时钟下不明显把 SPI 时钟提上去之后立刻暴露。提示如果数据手册暂时不在手边先按 1MHz 时钟、Mode 0、24 位帧起步然后用“读 ID 是否成功”来验证不要盲目调参数。3. 把 an41908a.zip 变成可加载驱动源码结构、设备树与最小编译路径原理看完了接下来要动真格。这一章的目标是让你拿到 an41908a.zip 之后能在一个小时内在自己的板子上把驱动加载起来。不需要先看懂全部源码只需要确认三件事这个源码是给哪个内核准备的、设备树节点怎么加、编译加载后怎么确认 probe 成功。3.1 解压后先别急着编译看 Makefile 和头文件判断源码给哪个环境准备的拿到驱动源码第一件事不是打开.c文件从头读而是先看构建方式。有的大型驱动源码包走 CMake 或脚本生成比如 USRP UHD 驱动源码要先跑 bootstrap 再 cmake 生成构建系统AN41908 这类单芯片驱动基本就是一个朴素 Makefile直接看它引用了哪个内核头文件路径就行。先执行tar -tzf an41908a.zip或直接解压看目录里有什么。一个典型的源码包会包括芯片数据手册 PDF、.c驱动文件、.h头文件、Makefile有时还有设备树示例片段和应用层测试代码。如果你的包里没有数据手册先去把手册补上没有手册继续往下走就是盲调。接着打开 Makefile 看这几行obj-m an41908.o KDIR : /lib/modules/$(shell uname -r)/buildobj-m表示源码是作为外部模块编译的不修改内核主树KDIR指向的是目标板内核的构建目录。如果板子上是 5.10 内核而包里的头文件还在用 3.x 时代的宏你要么自己改一些兼容宏要么换一个与内核版本匹配的 SDK。我遇到过源码在 4.19 上好好的换到 6.1 内核编译直接报错原因是旧版 SPI API 的spi_master结构体成员变了这类兼容问题几乎每个老驱动包都会碰到。再看.c文件里的compatible字符串比如vendor,an41908这类把它记下来。这个字符串要和设备树里的完全一致否则of_match_table匹配不上probe 根本不会被调用。3.2 设备树节点让 SPI 控制器知道 AN41908 挂在哪、怎么挂如果你手上的板子已经有空闲的 SPI 控制器和片选引脚那设备树只需要在对应 SPI 节点下加一个子节点。关键信息有三个挂在哪个 SPI 控制器、用第几路片选、SPI 工作模式是什么。下面是实际使用中最常见的设备树写法spi0 { status okay; pinctrl-names default; pinctrl-0 spi0_pins; cs-gpios gpio1 7 GPIO_ACTIVE_LOW; an419080 { compatible vendor,an41908; /* 必须与驱动中的 of_match_table 一致 */ reg 0; /* 0 表示使用第 0 路片选 */ spi-max-frequency 1000000; /* 1MHz 起步稳定后再提速 */ enable-gpios gpio1 8 GPIO_ACTIVE_HIGH; /* 如果芯片实际工作在 Mode 3追加 spi-cpol 和 spi-cpha 两个属性 */ }; };这里每个属性都有实际含义。cs-gpios指定片选引脚可能是 GPIO 也可能是 SPI 控制器内部片选看硬件原理图子节点的reg 0表示它占用第 0 路片选和cs-gpios里的第一个gpio1 7对应。spi-max-frequency直接决定 SPI 时钟源的分频结果聚焦芯片对速率要求不高1MHz 起步是稳妥选择。enable-gpios是芯片的使能脚如果硬件上把它直接接死了这行可以删掉。有一个常见错误只改兼容字符串忘了核对spi-cpol和spi-cpha。设备树里这两个属性不追加SPI 核心默认按 Mode 0 工作而驱动源码里如果已经执行了spi-mode SPI_MODE_3两者就会冲突最终以最后一次设置为准问题非常隐蔽。我的建议是设备树不写模式属性模式完全由驱动源码设置这样只维护一处。3.3 最小编译与加载路径insmod 之后怎么确认 probe 成功设备树改好并编进内启动之后开始编驱动模块。交叉编译时 Makefile 要带上架构和交叉编译工具链前缀否则你 x86 电脑编出来的.ko在 ARM 板子上是加载不了的。一个可用的最小 Makefile 如下# 目标板交叉编译示例 obj-m an41908.o KDIR : /home/work/linux-build # 指向目标板内核的 build 目录 CROSS_COMPILE : arm-linux-gnueabihf- ARCH : arm all: $(MAKE) -C $(KDIR) M$(PWD) ARCH$(ARCH) CROSS_COMPILE$(CROSS_COMPILE) modules clean: $(MAKE) -C $(KDIR) M$(PWD) ARCH$(ARCH) CROSS_COMPILE$(CROSS_COMPILE) cleanKDIR不是只填一个路径它必须指向目标板内核源码经过配置和编译后生成的 build 目录里面要有Module.symvers和.config。如果你只在刷固件时拿到过 zImage手上没有完整内核源码树那这个模块是编不出来的——这也是驱动源码落地中最常卡住的一步。解决方法是找 BSP 厂商要对应版本的 kernel source而不是自己瞎猜路径。编译通过后拷贝到目标板按下面的顺序加载并确认insmod an41908.ko dmesg | tail -20 ls /sys/bus/spi/devices/加载后看dmesg如果出现an41908 spi0.0: probe success或类似日志说明驱动和设备树已经对上了。然后看/sys/bus/spi/devices/下有没有spi0.0这个目录有就代表 SPI 控制器注册了这个设备。如果 dmesg 里没有任何和 an41908 相关的输出先确认设备树有没有编进内核再确认compatible字符串和驱动of_match_table是否一字不差。这里对不上的情况占了 probe 失败原因的七成以上。4. 聚焦位置怎么从寄存器走到镜头初始化序列与位置写入的实现拆解驱动能加载、probe 成功只是打开了门。真正让镜头动起来需要理解聚焦控制的两条路径一条是上电初始化把芯片从默认状态带到可工作状态另一条是位置写入把应用层算好的目标位置经 SPI 送进芯片。这一章拆开讲这两条路径的代码实现与参数选择。4.1 内核态 SPI 读写封装spi_message 与 spi_transfer 的用法在 Linux 内核里SPI 从设备驱动的读写最后都要落到struct spi_transfer和struct spi_message上。一个spi_message里可以挂多个spi_transfer内核保证一次spi_sync调用中整个 message 的片选不会被其他事务打断这个特性对读操作至关重要因为读操作必须“先发地址、再收数据”且中途片选不能释放。实际封装时我一般用spi_write_then_read这个快捷函数它在内部完成 message 和 transfer 的组装代码更少行为也更安全。写寄存器函数如下static int an41908_write_reg(struct an41908_dev *dev, u8 reg, u16 val) { u8 buf[3]; /* 帧格式地址字节最高位为写标志 数据高字节 数据低字节 * 地址偏移和标志位位置以数据手册为准这里是通用占位写法 */ buf[0] reg | AN41908_SPI_WR; buf[1] (val 8) 0xff; buf[2] val 0xff; /* spi_write_then_read 在内部把 txbuf 发出去CS 自动管理 */ return spi_write_then_read(dev-spi, buf, sizeof(buf), NULL, 0); }读寄存器用的是同一个函数的双向版本。重点在于tx和rx在同一个spi_message里片选全程不释放否则芯片会认为一次传输已经结束static int an41908_read_reg(struct an41908_dev *dev, u8 reg, u16 *val) { u8 tx[3], rx[3]; int ret; tx[0] reg | AN41908_SPI_RD; /* 读标志具体位置看手册 */ tx[1] 0; tx[2] 0; ret spi_write_then_read(dev-spi, tx, 3, rx, 3); if (ret) return ret; *val (rx[1] 8) | rx[2]; return 0; }注意spi_write_then_read的txbuf和rxbuf长度相等都是 3 字节。有的芯片读操作在发完地址字节后需要额外的时钟才能移出数据你的帧长度就要按手册把这两个 3 改成实际值。另外回调函数里要避免频繁动态分配短帧直接在栈上建缓冲区就够实时性要求高的地方可以考虑用spi_async走中断完成路径但聚焦这种毫秒级运动根本不需要那么激进。4.2 聚焦初始化的先后顺序上电稳定与寄存器配置上电后直接写目标位置这是新手最容易犯的错。AN41908 上电后内部 LDO、振荡器、驱动级都需要时间稳定寄存器处于默认态待机可能还没解除。此时写任何位置命令芯片要么忽略要么按错误的驱动电流执行电机会抖动甚至过流。初始化顺序必须按“稳定 → 确认身份 → 配置 → 回零”来static int an41908_hw_init(struct an41908_dev *dev) { int ret; u16 chip_id; /* 1. 上电后等电源和内部 LDO 稳定 */ usleep_range(10000, 20000); /* 2. 读 ID确认 SPI 通路和帧格式正确 */ ret an41908_read_reg(dev, AN41908_REG_ID, chip_id); if (ret) return ret; if (chip_id ! dev-expected_id) dev_warn(dev-spi-dev, ID mismatch: 0x%04x\n, chip_id); /* 3. 解除待机写入驱动电流和衰减模式配置 */ ret an41908_write_reg(dev, AN41908_REG_CTRL, AN41908_CTRL_STANDBY_OFF); if (ret) return ret; ret an41908_write_reg(dev, AN41908_REG_DRIVE_CFG, dev-drive_cfg); if (ret) return ret; /* 4. 等待内部状态机就绪 */ usleep_range(5000, 10000); /* 5. 位置回零后续应用从已知起点开始对焦 */ return an41908_set_focus(dev, 0); }这里有几个参数要说明。usleep_range(10000, 20000)给的是 10 到 20 毫秒的窗口内核会在这个范围内安排睡眠比msleep(20)更高效。dev-drive_cfg是驱动电流配置需要根据你镜头模组的直流电阻和工作电压来调电流太小镜头推不动太大电机会发烫。AN41908_CTRL_STANDBY_OFF是解除待机的标志不要省这一步有的芯片出厂默认就是待机态。初始化完成后建议做一次位置回零。如果镜头没有机械限位开关回零就是把位置寄存器写成 0如果有光耦或者霍尔传感器要加一段专门的归零算法。这一步不做应用层的对焦逻辑就无法保证起点一致性产线上十台设备十种起点全乱套。4.3 位置码换算、写入与回读确认内核驱动和 spidev 怎么选位置写入看起来只是把目标值写进寄存器但实际有三个容易被忽略的动作等待 BUSY 清零、位置码限幅、写后回读。把这三个动作做全聚焦控制的可靠性才会真正上来。下面是一个可用的set_focus实现static int an41908_set_focus(struct an41908_dev *dev, u16 position) { u16 status, value; int timeout 50; int ret; /* 上一次移动还没结束新命令可能被丢弃必须等待 */ do { ret an41908_read_reg(dev, AN41908_REG_STATUS, status); if (ret) return ret; if (!(status AN41908_STATUS_BUSY)) break; usleep_range(2000, 5000); } while (--timeout); if (!timeout) return -EBUSY; /* 位置寄存器是 10bit 分辨率超出上限直接限幅 */ if (position AN41908_FOCUS_MAX) position AN41908_FOCUS_MAX; ret an41908_write_reg(dev, AN41908_REG_FOCUS, position); if (ret) return ret; /* 写后回读确认芯片真的收到目标值 */ return an41908_read_reg(dev, AN41908_REG_FOCUS, value); }timeout 50是循环次数的上限每次循环最多睡 5 毫秒所以最坏情况等 250 毫秒就会返回-EBUSY。这个值要看你镜头从一端走到另一端实际需要多久如果镜头行程很长50 次可能不够直接调大。位置码的限幅用的是AN41908_FOCUS_MAX对应芯片位置寄存器的最大分辨率值比如 10bit 就是 1023。如果上层传了 1500直接写进去会溢出到低 10 位镜头会诡异地在另一个位置停下来。写到这一步会有一个选型问题到底用内核驱动还是直接在用户态用 spidev 控制这个选择值得展开两类方案各有各的适用场景。对比项内核驱动spidev 用户态开发速度慢要改设备树和 Makefile快open 节点后直接 xfer并发访问可加互斥锁多进程安全默认不防并发需应用层自己锁实时性可在中断上下文完成关键操作受用户态调度影响抖动大适合阶段量产、需要稳定复现验证寄存器、调初始参数我的习惯是第一块样板用 spidev 快速验证芯片能不能工作确定寄存器配置后再写内核驱动。如果一开始就全文在内核态调每改一个寄存器值都要重新编译模块调试效率太低。5. AN41908 驱动上机排查四个典型故障的现象、根因与解决这一章是把之前踩过的坑集中摆出来。每一类故障我都按“现象 → 原因 → 解决”来写这些基本都是 SPI 聚焦驱动上机时最高频的问题值得收藏下来对照排查。5.1 电机只抖动不转动SPI 模式与帧格式是头号嫌疑现象给驱动写了一个目标位置镜头没有移动只是原地轻微抖动像被卡住或者在挣扎。电压和电流都测了供电没问题。原因SPI 模式或帧格式不对芯片收到了数据但解析不出来。最常见的情况是 CPOL、CPHA 配置错误导致数据在错误的时钟沿被采样地址和数据位全部错位另一种常见原因是帧长度设错寄存器地址被当成数据的一部分芯片内部状态机完全错乱。驱动电流配置大幅偏小也会表现为抖动但概率低一些。解决先用逻辑分析仪抓 MOSI 和 SCLK把你的帧和手册时序图逐位对照。重点看数据在时钟上升沿还是下降沿被采样空闲时时钟是高还是低。确定完 SPI 模式后回到设备树里把spi-cpol和spi-cpha改对或者直接在内核驱动的probe里覆盖spi-mode。如果时序完全正确仍然抖动再检查驱动电流寄存器逐步往上加别一次跳到最大。5.2 能读回 ID 却写不进位置先查写保护和帧方向位现象读 ID 寄存器返回值正常说明 SPI 通信基本通了但写位置寄存器后镜头纹丝不动读回目标位置寄存器还是原来的值。原因读通路正常不代表写通路正常。这类芯片的写操作往往有保护机制比如需要在控制寄存器里先解除写保护或者帧地址字节的最高位不是读写标志位而是一个单独的方向位。方向位写错芯片会把数据当成非法命令拒绝。还有一种是写时序要求更严格CS 释放时间不够芯片没有锁存数据。解决重点检查两处。第一看驱动源码里的写标志值AN41908_SPI_WR和读标志值AN41908_SPI_RD是否和手册一致确认方向位到底是高电平写还是低电平写第二查手册里有没有 unlock 或“写保护”寄存器如果需要先解锁必须在每次初始化或目标位置写入前补一条命令。spi_write_then_read已经保证了 CS 不会提前释放所以时序问题通常只剩帧格式和标志位这两个嫌疑。5.3 跑一段时间位置丢失或芯片失联寄存器被复位缺周期刷新现象设备正常对焦几分钟或几十分钟后镜头突然回到某个固定位置或者芯片不再响应 SPI 命令必须重新初始化才能恢复。原因芯片触发了掉电复位或看门狗复位。聚焦电机启动瞬间的电流尖峰如果没做好滤波会把芯片电源拉到复位门限以下或者驱动电流配置过高长时间运行导致芯片温度保护触发还有一类更容易被忽略的因素就是寄存器本身是易失的掉电后恢复默认值而你的驱动只在 probe 时初始化过一次没有定期刷新。解决先用示波器挂在芯片电源引脚上看有没有瞬间跌落有跌落就加大储能电容或者调整电机电流斜坡。排除硬件后在驱动里加一个周期刷新任务比如用内核delayed_work每 500 毫秒重新写一次关键配置寄存器读一次状态寄存器如果发现状态异常就主动重新初始化。这个刷新任务不用很复杂但它能把寄存器复位问题兜住让设备在无人值守时也能自恢复。5.4 用户态 spidev 测试通过应用一跑就卡死并发访问与片选复用现象单独用 spidev 写寄存器一切正常镜头转向精准但应用程序一跑起来要么写寄存器超时要么 SPI 设备和 GPIO 报 busy严重时整个应用直接卡住。原因应用层和测试脚本同时打开了/dev/spidevX.X两个进程没有任何互斥SPI 帧互相穿插芯片被截断的帧搞懵。另一个隐藏原因是片选引脚被设备树里其他节点也声明了SPI 控制器加锁时发现 GPIO 已经被占用直接返回-EBUSY。解决应用层所有访问 SPIDEV 的地方统一走一个带mutex的封装模块每次传输都加锁传输完立即释放。同时在设备树里全局搜一遍片选 GPIO 是否被复用别只看 SPI 节点有时候一个 LED 节点或按键节点悄悄把它占用了。如果你发现应用层加锁后还有偶发卡顿建议直接迁移到内核驱动用内核的spi_lock和原子操作替代用户态锁这才是量产该有的稳定级别。提示以上四个故障有一个共同的排查前提——先确认 SPI 通路正常。我见过有人为了“寄存器写不进”折腾了两天最后发现是 MISO 引脚焊盘虚焊。先把 ID 读回来再谈其他。6. 验证 AN41908 聚焦驱动是否真的在工作抓波形、回读寄存器与位置往返自测驱动写完、能 probe、能初始化最后一步是系统性地验证它确实在工作而不是碰巧动了。我每次都做三件事抓波形、回读寄存器、位置往返自测。这三步做完驱动才算真正交付。第一步是抓波形。逻辑分析仪接四根线CS、SCLK、MOSI、MISO采样率调到 10MS/s 以上用 SPI 协议解码器配置好模式。抓一段初始化序列确认第一帧确实是读 ID地址字节方向位正确。再抓一次set_focus写位置寄存器确认帧里的数据位和你要写的目标值一致。这一步能发现所有设备树参数和源码帧格式方面的问题。第二步是回读寄存器。驱动里把snprintf开一个 debugfs 节点或者直接通过应用层 ioctl 回读关键寄存器。系统跑起来后读目标位置寄存器确认写入值和读回值一致再读状态寄存器确认 BUSY 位随电机运动变化。回读过程中如果看到某个寄存器值无端跳变优先查电源噪声和 SPI 线缆长度。第三步是位置往返自测。用 spidev 或应用层接口写一个循环让镜头在近端、远端、中间几个位置往返走每个位置停留一会儿。下面是一个简洁的 Python 自测脚本import spidev import time spi spidev.SpiDev() spi.open(0, 0) # 按实际节点调整 /dev/spidevX.X spi.max_speed_hz 1_000_000 REG_FOCUS 0x20 # 占位值按手册替换 WR 0x80 positions [0, 256, 512, 768, 1023, 512, 0] for pos in positions: tx [REG_FOCUS | WR, (pos 8) 0xFF, pos 0xFF] spi.xfer2(tx) time.sleep(0.2) # 留出电机移动时间 # 这里可配合图像清晰度统计形成“位置-清晰度”曲线脚本里spi.xfer2保证一次事务内多字节不被打断替代内核态spi_write_then_read对应的用户态 API。跑完这个循环听电机声音是否平滑看镜头是否每次都停在同一位置。有条件的话配合摄像头抓帧做清晰度评分把位置码和清晰度画成曲线能直观看到对焦峰值在哪一档。做完整套验证我会顺手把初始化序列和验证命令固化成脚本放进工程目录方便产线复测时直接跑。以后换镜头型号或复现现场问题先跑这套脚本就能分清是驱动问题还是机构问题不用每次都拆机抓波形。希望帮到你。本文还有配套的精品资源点击获取