简介:需要调试芯视界VI5300系列激光测距芯片的嵌入式开发者,常为IIC通信时序适配问题所困扰。这里分享一套软件模拟IIC驱动代码,已在VI5300上实测通过,适合没有硬件IIC控制器或需要灵活分配引脚的场景,可完成芯片初始化配置、寄存器读取及测距数据获取等功能。压缩包内共12个文件,含6个C源文件与6个H头文件,其中vl53l4xx_i2c.c/h实现了底层IIC读写,VL5300_API目录封装上层调用函数,结构清晰,便于移植到STM32、51、Arduino等常见单片机平台。资源包大小仅29KB,轻量精简,阅读和集成都很方便。目前已有194人学习下载,适合有一定单片机基础、正在开发TOF测距或接近感应产品的工程师参考。通过研读该驱动,能够理解模拟IIC的时序构造、寄存器操作流程与分层设计思路,进而缩短驱动调试周期,快速完成激光测距模块的项目验证。
1. 芯视界VI530x模拟IIC驱动:从「读不到数据」到稳定测距
TOF测距芯片里,VI530x这类激光测距方案在嵌入式圈子里用得不少,但真正把它调通,卡人的往往不是激光和算法,而是最底层的IIC读写。芯片手册写得再清楚,手里没有一套能跑的模拟IIC驱动,照样在地址探测那一步就翻车。所谓模拟IIC,就是不用单片机自带的硬件IIC外设,直接拿两个GPIO口按时序翻转电平,把IIC协议一位一位「扒」出来。这样做的直接好处是:时序完全可控,出问题能用逻辑分析仪一眼定位,甚至能把时钟频率压到从机容限内的任意值。这篇笔记就把VI530x的模拟IIC驱动拆开讲——从GPIO时序原语写到寄存器驱动流程,再补五个高频坑。适合正在选型TOF方案、或者被硬件IIC时序折磨的嵌入式开发者。
2. VI530x TOF芯片:动手写驱动前先搞懂这三件事
2.1 TOF测距原理:VI530x到底帮你做了什么
TOF是Time of Flight的缩写,原理一句话:发射光脉冲,接收反射光,用光的往返时间差算出距离。激光往返速度接近光速,时间差极短,所以芯片内部必须用专门的TDC(时间数字转换器)才能把时间量出来。VI530x内部把VCSEL激光发射器、SPAD单光子探测器、TDC和IIC从机接口封装在一起,对外只暴露一个测距接口。
对单片机来说,这颗芯片本质是个测距黑匣子。我通过IIC告诉它「开始测距」,等它内部忙完,再通过IIC把距离值读回来。中间怎么发射、怎么接收、怎么换算,都不需要我操心。要操心的是:上电顺序对不对、初始化序列走没走全、IIC时序能不能被芯片稳定识别。
VI530x这类芯片一般提供两种测量模式。单次测量适合低功耗场景,测完立刻进休眠;连续测量适合实时性要求高的场景,比如避障、跟随。驱动代码里我会把两个模式都留接口,默认先跑单次,把数据链路打通后再切连续。
还有一个容易忽略的点:VCSEL是激光器件,驱动里激光使能位的操作顺序很讲究。先配置参数、再开激光,这是硬件上的红线。有些翻车案例是因为激光比配置先打开,芯片直接进入异常保护状态,之后所有IIC操作都不响应,只能断电重启。所以初始化函数里激光使能一定放在最后一步。
2.2 VI530x对外接口:IIC从机地址、寄存器映射与中断引脚
VI530x对外引脚不多,核心的就几类,列一张表看得更清楚:
| 引脚/信号 | 方向 | 说明 |
|---|---|---|
| SCL / SDA | 输入/双向 | IIC总线,模拟IIC下由两个GPIO主控 |
| INT | 输出 | 测量完成中断,可配置电平极性 |
| VDD | 电源 | 芯片数字电源 |
| VCSEL供电 | 电源 | 激光发射器供电,对纹波敏感 |
| GND | 电源 | 地 |
IIC从机地址一般由芯片固定,7位地址在手册里能查到。探测它是驱动第一步:读一下设备ID寄存器,比对手册给的值。如果对不上,先别怀疑芯片坏了,大概率是IIC时序或上拉电阻的问题。
寄存器映射方面,VI530x的寄存器大体分为几类:设备信息类(设备ID、版本号)、配置类(测量模式、激光使能、校准参数)、结果类(距离值、状态标志)。我写驱动时会按这个分类建宏,把寄存器地址集中管理,方便换批次芯片时统一改。宏名用可读的英文,地址值单独一列注释标注「以手册为准」,这是让驱动代码能跨批次复用的关键。
中断引脚INT是个好东西。用轮询也能工作,但用INT引脚能做到「测完再读」,省去空转等待。模拟IIC本来就不快,能省一次轮询循环是一次。如果系统里INT引脚紧张,也可以不接,纯靠状态寄存器轮询,代码里两种方案我都保留。
2.3 为什么要用模拟IIC:硬件IIC在TOF场景的三个不顺手
我见过不少人在VI530x上用硬件IIC翻车,然后回来抄模拟IIC的。不是说硬件IIC不行,而是它在这个场景里确实有几个不顺手的地方。
第一,引脚复用冲突。很多单片机只有一个硬件IIC外设,又要接OLED、又要接温湿度传感器、又要接TOF,就得来回切换复用功能,切换稍不注意就把总线时序带乱。用模拟IIC就没有这个负担,任意两个空闲GPIO都能干活。
第二,出错定位难。硬件IIC出问题,分析起来往往像盲盒:寄存器里一堆错误位,但具体是起始条件没发出去,还是ACK没收到,很难一眼看出来。模拟IIC每个位都是亲手翻转的,逻辑分析仪一抓,哪个环节断了清清楚楚。
第三,时钟分频太粗。硬件IIC的时钟源一般是外设时钟整数分频,想跑到100kHz和400kHz之间的某个值,不见得有合适分频系数。模拟IIC的时钟完全由延时函数决定,想跑多慢跑多慢,对于时序容限不清楚的新芯片,这个余量是保命的。
但模拟IIC不是万能的。如果项目里IIC总线上已经挂了好几个高速设备、每帧要传的字节数很大,模拟IIC的速度瓶颈就会拖累主循环。这种情况该选带多路硬件IIC的单片机,或者用硬件IIC加低速延时的混合方案。模拟IIC适合的是传感器这类小数据量、低频访问的场景,TOF恰好属于这一类,所以这个方案在VI530x上特别合适。
3. 模拟IIC总线层:GPIO主控的时序原语与读写封装
3.1 GPIO初始化:开漏输出、外部上拉和引脚选型
模拟IIC的物理层和硬件IIC一样,SCL、SDA两根线,空闲时都是高电平。写驱动第一步是把GPIO配好。我一般用开漏输出模式,配合外部上拉电阻。
/* 引脚定义,按实际主板修改 */ #define VI530X_SCL_PIN GPIO_PIN_6 #define VI530X_SDA_PIN GPIO_PIN_7 #define VI530X_GPIO_PORT GPIOB /* 电平操作宏 */ #define IIC_SCL_H() GPIO_WriteBit(VI530X_GPIO_PORT, VI530X_SCL_PIN, 1) #define IIC_SCL_L() GPIO_WriteBit(VI530X_GPIO_PORT, VI530X_SCL_PIN, 0) #define IIC_SDA_H() GPIO_WriteBit(VI530X_GPIO_PORT, VI530X_SDA_PIN, 1) #define IIC_SDA_L() GPIO_WriteBit(VI530X_GPIO_PORT, VI530X_SDA_PIN, 0) #define IIC_SDA_READ() GPIO_ReadInputDataBit(VI530X_GPIO_PORT, VI530X_SDA_PIN) void VI530X_IIC_GPIO_Init(void) { GPIO_InitTypeDef gpio; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); gpio.GPIO_Pin = VI530X_SCL_PIN | VI530X_SDA_PIN; gpio.GPIO_Mode = GPIO_Mode_Out_OD; /* 开漏输出,可读回输入电平 */ gpio.GPIO_Speed = GPIO_Speed_2MHz; GPIO_Init(VI530X_GPIO_PORT, &gpio); /* 空闲时释放总线,让外部上拉把电平拉高 */ IIC_SCL_H(); IIC_SDA_H(); }GPIO_Mode_Out_OD就是开漏输出。开漏的好处是低电平由单片机主动拉,高电平靠外部上拉电阻,总线上多个设备不会互相打架。我一般在SDA和SCL上各放一个4.7kΩ电阻上拉到VDD。电阻太小功耗大,信号沿也陡;电阻太大上升沿变缓,时序容易出错。引脚选型上,避开下载器占用的引脚、避开板子上飞线较长的位置,能少很多莫名其妙的偶发问题。
3.2 时序原语:起始、停止、发送、接收
模拟IIC的核心是四个时序原语:起始条件、停止条件、发送一字节、接收一字节。把这四个写稳,上层寄存器操作就只是拼装组合。延时函数我单独抽出来,统一控制时序心跳。
/* 微妙级延时,具体循环次数按单片机主频标定 */ static void delay_io(void) { volatile uint32_t i; for (i = 0; i < 40; i++); } /* 起始条件:SCL高电平期间,SDA由高拉低 */ static void IIC_Start(void) { IIC_SDA_H(); IIC_SCL_H(); delay_io(); IIC_SDA_L(); delay_io(); IIC_SCL_L(); } /* 停止条件:SCL高电平期间,SDA由低拉高 */ static void IIC_Stop(void) { IIC_SDA_L(); IIC_SCL_H(); delay_io(); IIC_SDA_H(); delay_io(); } /* 发送1字节,返回从机ACK状态:0=应答,1=无应答 */ static uint8_t IIC_SendByte(uint8_t data) { uint8_t i, ack; for (i = 0; i < 8; i++) { if (data & 0x80) IIC_SDA_H(); else IIC_SDA_L(); data <<= 1; delay_io(); IIC_SCL_H(); /* SCL拉高,从机在上升沿采样 */ delay_io(); IIC_SCL_L(); } /* 第9个时钟:释放SDA,读从机ACK */ IIC_SDA_H(); delay_io(); IIC_SCL_H(); delay_io(); ack = IIC_SDA_READ(); IIC_SCL_L(); return ack; } /* 接收1字节,ack参数:0=主机发ACK继续读,1=主机发NACK结束 */ static uint8_t IIC_RecvByte(uint8_t ack) { uint8_t i, data = 0; IIC_SDA_H(); for (i = 0; i < 8; i++) { data <<= 1; IIC_SCL_H(); delay_io(); if (IIC_SDA_READ()) data |= 0x01; IIC_SCL_L(); delay_io(); } if (ack) IIC_SDA_H(); /* NACK */ else IIC_SDA_L(); /* ACK */ delay_io(); IIC_SCL_H(); delay_io(); IIC_SCL_L(); IIC_SDA_H(); return data; }起始和停止条件的核心是「SCL为高时SDA跳变」。起始是SDA由高拉低,停止是SDA由低拉高,这两个跳变都必须在SCL高电平期间完成,从机靠这个识别总线状态变化。发送字节时,数据位在SCL低电平期间准备好,SCL拉高的瞬间从机采样,所以电平切换和SCL翻转之间的delay_io()不能省。
读字节的ACK方向正好相反。主机读每个字节后,第9个时钟里主机自己控制SDA:发ACK表示「继续读下一字节」,发NACK表示「读到这个字节结束」。这就是连续读时最后一字节必须传NACK的原因,少发或多发都会让从机误解总线状态。delay_io()是整套时序的心跳,我习惯把它做成独立函数而不是硬编码循环,这样换主频时只调一处。标定方法很简单:用逻辑分析仪抓一下,让SCL高电平半周期落在5~6us。太快容易触发从机时序上限,太慢则每次通信耗时成倍增加。
3.3 把原语拼成寄存器读写:从单字节到连续读
有了四个原语,寄存器的读写就是固定套路。写寄存器是纯单向流程:起始、地址加写方向、寄存器地址、数据、停止。读寄存器要多一个Restart(重新起始),在发送完寄存器地址后不停止总线,而是再发一次起始,把方向切成读。
/* 写寄存器:起始→地址+写→寄存器地址→数据→停止 */ uint8_t VI530X_WriteReg(uint8_t devAddr, uint8_t regAddr, uint8_t val) { IIC_Start(); if (IIC_SendByte((devAddr << 1) | 0)) return 1; /* 写方向 */ if (IIC_SendByte(regAddr)) return 1; if (IIC_SendByte(val)) return 1; IIC_Stop(); return 0; } /* 读寄存器:起始→地址+写→寄存器地址→重启→地址+读→数据→停止 */ uint8_t VI530X_ReadReg(uint8_t devAddr, uint8_t regAddr, uint8_t *buf, uint16_t len) { uint16_t i; IIC_Start(); if (IIC_SendByte((devAddr << 1) | 0)) return 1; if (IIC_SendByte(regAddr)) return 1; /* Restart:再一次起始条件,切换为读方向 */ IIC_Start(); if (IIC_SendByte((devAddr << 1) | 1)) return 1; for (i = 0; i < len; i++) { buf[i] = IIC_RecvByte(i == len - 1); /* 最后一字节NACK */ } IIC_Stop(); return 0; }写寄存器是单向流程,读寄存器必须中间插一个Restart。Restart的作用是在不释放总线的情况下把方向从写切成读,省去一次完整的起始-停止周期。IIC_RecvByte的参数i == len - 1就是「最后一字节发NACK」的控制,len传2时正好适配16位距离值的一次性读取。这段代码返回0表示成功,返回1表示某个环节没收到ACK,调用方可以直接打印错误码,整个驱动看起来就像一条有反馈的流水线。devAddr左移一位是IIC协议的标准做法,7位地址加读写位拼成第一个字节,模拟IIC里这个移位要写对,写反了从机直接不理你。
4. VI530x寄存器驱动:地址探测、软复位与距离读取流程
4.1 地址探测:先确认芯片活着再往下走
总线层封装好后,第一件事不是配置测量参数,而是确认芯片在线。地址探测读的是设备ID寄存器,这个值每个芯片固定,比对通过才能继续。这里有个容易踩的坑:设备ID在不同批次芯片上可能有差异,比对时别只认一个值。
/* 以下寄存器地址为示例,实际值以所选型号数据手册为准 */ #define VI530X_DEV_ADDR 0x52 /* 示例7位地址 */ #define VI530X_REG_DEVID 0x00 /* 设备ID寄存器 */ uint8_t VI530X_Probe(void) { uint8_t devId = 0; if (VI530X_ReadReg(VI530X_DEV_ADDR, VI530X_REG_DEVID, &devId, 1)) { return 0; /* 通信失败 */ } /* 比对设备ID,不同批次会有差异,按手册范围匹配 */ if (devId == 0x01 || devId == 0x02) return 1; return 0; }探测函数返回0时先查硬件,别急着改软件。我自己的排查顺序是:先量VDD有没有到位、再看SDA和SCL空闲电平是否为高、最后用这段探测代码反复跑。地址0x52只是示例,真正用的时候以手册为准,写代码时把这个宏单独拎出来,换芯片型号只改一处。ID比对要放宽,直接在手册里确认目标值的允许范围,太严格会把正常批次误判成故障。
4.2 软复位与初始化序列:先配置,再开激光
VI530x上电后不能直接测距,必须走一遍初始化。这一步最重要的是顺序:软复位、确认通信、加载校准、配置模式,最后才使能激光。顺序反了,轻则测量结果异常,重则芯片进入保护状态,只能断电重启。
/* 寄存器地址均为示例,实际以手册寄存器表为准 */ #define VI530X_REG_COMMAND 0x01 /* 命令寄存器 */ #define VI530X_REG_MEAS_MODE 0x02 /* 测量模式寄存器 */ #define VI530X_REG_LASER_EN 0x03 /* 激光使能寄存器 */ #define VI530X_CMD_SOFTRESET 0x80 /* 软复位命令位 */ #define VI530X_CMD_START 0x01 /* 触发单次测量 */ uint8_t VI530X_SoftReset(void) { return VI530X_WriteReg(VI530X_DEV_ADDR, VI530X_REG_COMMAND, VI530X_CMD_SOFTRESET); } uint8_t VI530X_Init(void) { uint8_t buf[8]; /* 第1步:软复位,等从机重启完成 */ VI530X_SoftReset(); delay_ms(20); /* 第2步:读设备ID确认通信正常 */ if (!VI530X_Probe()) return 1; /* 第3步:加载出厂校准数据,寄存器偏移按手册填写 */ VI530X_ReadReg(VI530X_DEV_ADDR, 0x10, buf, 8); /* 示例偏移 */ VI530X_WriteReg(VI530X_DEV_ADDR, 0x20, buf[0]); /* 示例写入 */ /* 第4步:配置测量模式为单次测量 */ VI530X_WriteReg(VI530X_DEV_ADDR, VI530X_REG_MEAS_MODE, 0x01); /* 第5步:最后才使能激光 */ VI530X_WriteReg(VI530X_DEV_ADDR, VI530X_REG_LASER_EN, 0x01); return 0; }初始化顺序最重要的一条:激光使能必须放在最后。VCSEL是先配置后发光的器件,参数没就位就开激光,轻则测量结果乱,重则让芯片进入保护状态。软复位后要延时等从机起来,延时太短会出现地址探测失败,20ms是我常用的起步值,实际可以从10ms到50ms之间试。第3步校准数据最容易出「测量不准」的根源,不同批次寄存器偏移可能不同,务必以手册的OTP段说明为准。我见过有人跳过校准加载,测距偏差几十毫米,后来查了半天才发现是这里省掉了。
4.3 触发测量与结果读取:轮询加超时
初始化完成,接下来就是最常用的操作:触发一次测距,读取距离值。测量是个异步过程,触发命令发出后芯片内部要完成发射、接收、换算一系列动作,所以必须等。我习惯用状态寄存器轮询,加上超时保护,避免从机异常时主循环被卡死。
/* 寄存器地址均为示例,实际以手册寄存器表为准 */ #define VI530X_REG_STATUS 0x04 /* 状态寄存器 */ #define VI530X_REG_RESULT_L 0x05 /* 距离低字节 */ #define VI530X_REG_RESULT_H 0x06 /* 距离高字节 */ #define VI530X_STATUS_DONE 0x01 /* 测量完成标志 */ #define VI530X_MEAS_TIMEOUT 200 /* 轮询上限,约200ms */ uint8_t VI530X_Measure(uint16_t *distance_raw) { uint8_t status = 0; uint8_t buf[2]; uint16_t timeout = 0; if (VI530X_WriteReg(VI530X_DEV_ADDR, VI530X_REG_COMMAND, VI530X_CMD_START)) return 1; /* 轮询状态寄存器,直到测量完成或超时 */ do { delay_ms(1); if (VI530X_ReadReg(VI530X_DEV_ADDR, VI530X_REG_STATUS, &status, 1)) return 1; } while ((status & VI530X_STATUS_DONE) == 0 && ++timeout < VI530X_MEAS_TIMEOUT); if ((status & VI530X_STATUS_DONE) == 0) return 2; /* 超时 */ /* 一次连续读,拿回距离原始值 */ if (VI530X_ReadReg(VI530X_DEV_ADDR, VI530X_REG_RESULT_L, buf, 2)) return 1; *distance_raw = (uint16_t)((buf[1] << 8) | buf[0]); return 0; }测量是异步过程,触发后必须等。轮询时把状态寄存器读和超时绑定在一起,避免从机挂死时主循环永远出不来。连续读两个字节时,第一次读返回ACK,第二次读返回NACK,正好对应3.3节里IIC_RecvByte的i == len - 1逻辑。距离寄存器读出来的原始值先不急着换算成毫米,直接打印出来,用手挡一下看数值是否单调变化——这一步能快速判断驱动通没通。返回值0是成功,1是通信失败,2是超时,调用方可以根据错误码决定是重试还是报故障。
5. VI530x模拟IIC踩坑实录:五个高频问题与排查思路
5.1 地址探测读回全0xFF:先查上拉和电源
现象:用VI530X_Probe()反复探测,读回的设备ID永远是0xFF,要么通信函数直接返回1。
原因:最常见的是SDA、SCL引脚漏配了外部上拉。模拟IIC的开漏模式本身不会主动输出高电平,没有上拉时总线浮空,从机根本没收到有效电平。还有一种情况是上电后立即探测,芯片还没完成内部初始化,寄存器读出来全是复位值或0xFF。
解决:先确认GPIO配置成开漏,SDA、SCL各接4.7kΩ上拉到VDD,空闲时用万用表量两个引脚都应该是高电平。软件上在探测前加一段延时,20ms是我的起步值,有些批次需要50ms。如果还是0xFF,用示波器抓起始条件,看SDA有没有出现「SCL高时往下跳」的波形。没有波形说明电平操作宏或者GPIO配置有问题,有波形但无应答再查从机供电。
5.2 通信偶发失败:时序被中断打断
现象:驱动写好后大部分时间正常,偶尔一次无ACK,复现概率很小,重启后消失,很难稳定触发。
原因:通信过程中开了全局中断,某次SCL高电平期间被中断服务程序拖了几十微秒,超出了从机的SCL高电平超时上限。从机判定总线异常,会直接释放总线,后续所有操作都无应答。
解决:在VI530X_WriteReg、VI530X_ReadReg整段前后关中断,把IIC通信做成临界区,量大的系统用关中断的代价换稳定的通信,完全值得。如果系统实时性要求高,可以把延时函数改成由定时器驱动的忙等,保证延时期间不被切换。本质问题是「时序的确定性」,模拟IIC的优势就在于确定,别让中断把它毁了。
5.3 距离读数固定不变:跳过软复位或激光未开
现象:测距跑起来,手在芯片前面晃,读出的原始值纹丝不动,不管靠近还是远离都是同一个数。
原因:初始化时图省事跳过了软复位,或者校准数据没写回去,芯片内部的测量链路处于非正常状态。另一个高频原因:激光使能位没置位,芯片压根没发射激光脉冲,自然没有反射信号,读数就是空值。
解决:严格按4.2的初始化顺序走:软复位→探测→加载校准→配置模式→最后激光使能。排查时逐项读寄存器确认写进去的值,别假设配置成功了,读回全是0就说明写入没生效。用电流表串进VCSEL供电回路,实测有没有脉冲电流,这个信号比软件调试更能说明问题。
5.4 波形上升沿一堆毛刺:驱动能力与电源都有关
现象:逻辑分析仪抓到的SCL、SDA上升沿有明显振铃,波形边缘不干净,通信偶发误码。
原因:GPIO仍配成推挽输出,或者翻转速度过快,走线寄生电容和驱动强度不匹配造成过冲。VDD纹波大也会污染信号,尤其是VCSEL发射瞬间的大电流会把电源拉出毛刺。
解决:先确认开漏输出模式,这是根基。上拉电阻可以从4.7k换到10k,降低上升电流尖峰。VDD引脚附近加0.1μF和10μF两个电容,VCSEL供电单独滤波,别和数字电源混在一起走线。我遇到过一回玄学问题,最后发现是两个芯片共用一个LDO,测量时互相干扰,分开供电后波形立刻干净了。
5.5 主循环被拖慢:延时参数过分保守
现象:功能完全正常,但加上VI530x驱动后主循环周期明显变长,整个系统响应变慢。
原因:模拟IIC每次字节操作都有延时,读一次距离要经历起始、写地址、写寄存器、重启、读两个字节,加起来几十次延时。如果每段延时都按上限的几倍写,通信耗时就成倍增加。
解决:先把100kHz时序跑通,再逐步减小delay_io里的循环次数,用逻辑分析仪量SCL半周期,压到5us以内。读距离时用连续读,比多次单字节读快得多,省掉重复的起始和地址字节。如果每秒测距一次,模拟IIC的开销完全可接受;如果要求上百Hz连续测距,再考虑换硬件IIC提速度,但底层寄存器流程不用改。
6. 进阶验证与距离换算:让模拟IIC驱动跑得更稳
6.1 逻辑分析仪是你最好的排障工具
模拟IIC写完第一版,别急着接业务代码,先抓波形。对照标准IIC时序图检查四件事:起始条件是不是SCL高时SDA下跳;停止条件是不是SCL高时SDA上跳;每字节第9个时钟是否有ACK对应的低电平;读寄存器流程里Restart位置是否正确。特别是Restart,这是最容易画错的一段。我每次换单片机型号都要重新抓一遍波形,因为GPIO翻转速度变了,延时参数必须跟着调。
6.2 原始值到毫米:先验证单调,再做偏移
距离寄存器读出的原始值先不换算,直接打印。用手挡、放远、贴近,确认数值在随距离变化且方向正确。然后按手册给的换算公式加上零距离偏移,通常在几十毫米范围内校准。温度变化大的场合,需要周期性重新校准,否则低温偏近、高温偏远,偏差量级在毫米到厘米之间。这个偏移值每个模组都略有差异,量产时最好在产线上做一次标定写入Flash。
6.3 一个值得养成的习惯:驱动代码里留一个自检函数
我习惯在驱动里留一个自检函数,作用就是把地址探测、状态寄存器、校准标志位全部读一遍,有问题的直接返回错误码。某次做量产工装时,全靠这个自检函数把焊接不良的板子快速筛出来,省下的排查时间比写这个函数的成本多得多。整个VI530x模拟IIC驱动说难不难,真正值钱的是上电顺序、校准加载、超时保护这些细节。我自己的教训是:新芯片到手永远先写模拟IIC版,把芯片摸透了再优化速度。希望这篇笔记能帮你在自己的板子上少走几趟弯路,希望帮到你。
本文还有配套的精品资源,点击获取