尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

STSW-IMG053移植到STM32N6570-DK:I3C总线适配与Platform层改造实战

STSW-IMG053移植到STM32N6570-DK:I3C总线适配与Platform层改造实战 接手这类指导移植需求的时候我通常不会先急着翻代码而是先把三块东西——软件包、扩展板、主控板的对应关系摸清楚再动手。这次要做的是把ST的FlightSense软件包STSW-IMG053从原本配套的评估环境迁移到X-NUCLEO-53L9A1扩展板与STM32N6570-DK主控板的组合上并且通信总线要落到I3C上。标题里给的信息其实已经说明了三件事软件包是谁、硬件底板是谁、总线用什么。但真正移植起来问题远比三件事复杂比如软件包内部对I2C/SPI的路径依赖、N6570-DK默认引脚分配和扩展板Arduino接口的冲突、I3C与普通I2C在驱动模型上的差异每一条都可能让你卡上大半天。这篇文章我把整个移植过程按实际操作顺序拆开讲包括前期文档怎么看、工程怎么建、I3C适配层怎么改、跑通之后怎么验证。你如果正好要把STSW-IMG053往N6570-DK上搬照着这个思路走会省掉很多弯路。1. 先搞清三块东西各自的角色1.1 STSW-IMG053到底是什么STSW-IMG053其实就是意法半导体为53L9系列ToF传感器提供的一个完整软件包我习惯叫它FlightSense SDK。它不只是驱动层还包括了传感器初始化序列、配置表、测距/深度数据解析、以及最核心的算法库——这些库通常以静态库.a的形式提供你拿不到源码但是能调用它来输出Depth Map深度图或Multi-Zone测距结果。对移植来说最关键的不是库本身而是它和硬件之间那一层抽象。这一层决定了你把软件包换到另一块主板上时要改哪些地方。STSW-IMG053在设计上留了Platform Abstraction或者说Platform层它把对底层I2C/SPI的操作封装成了几个函数比如Write/Read寄存器、Reset、Boot等等。你的移植工作90%都集中在这层。1.2 三块硬件怎么连这里面有三个物理实体X-NUCLEO-53L9A1传感器扩展板需要插到主控板的Arduino兼容排针上。STM32N6570-DK主控评估板作为运行STSW-IMG053的基础平台。I3C总线连接两者的通信链路。我在STM32N6570-DK的板子上看了一圈引脚分配这块板子的Arduino接口部分扩展了I3C信号——也就是SCL、SDA这两根线在标准Arduino排针里走的是A5/A4区域但实际上I3C不是简单的I2C改名它是全新的总线时序和设备模型后面我会单独讲。1.3 为什么是I3C而不是I2C这是很多人一开始会问的问题。X-NUCLEO-53L9A1扩展板上的VL53L9传感器本身是支持I3C的而STM32N6570-DK的I3C外设也具备完整的主控制器能力。既然硬件两边都支持那STSW-IMG053默认为什么要用I2C原因很简单兼容性和简化。ST评估板上的默认例程多数走的是更传统、门槛更低的I2C模式。但I3C能带来更快的通信速率最高约12.5MHz而I2C常见最高3.4MHz和更可靠的热插拔机制所以当你把整套东西搬到N6570-DK这种新平台时把总线切到I3C是有实际意义的。唯一的障碍在于STSW-IMG053默认的Platform层驱动里I3C相关代码是有的但它并不是你的首选路径——除非你把编译宏切到相应模式。这就是移植里最容易踩坑的地方。提示移植的第一步不是写代码而是确认你的软件包版本。不同版本的STSW-IMG053对I3C支持程度不同先看Release Note里有没有53L9_I3C相关的关键词。2. 软件工程准备文档、例程和器件树2.1 拿到软件包之后先翻哪个目录X-CUBE-IMG053或者STSW-IMG053解压后的目录结构通常类似下面这样stsw-img053/ ├── Drivers/ │ ├── BSP/ │ └── Components/ ├── Middlewares/ │ └── ST/ │ └── STSW_IMG053/ │ ├── Core/ │ ├── Platform/ │ └── App/ ├── Projects/ │ ├── NUCLEO-F401RE/ │ ├── NUCLEO-L476RG/ │ └── STM32N6xx... └── Utilities/你需要重点关注的文件夹Middlewares/ST/STSW_IMG053/Platform/底层I/O抽象这是移植主战场。Projects/STM32N6xx/给N6系列准备的工程它和N6570-DK最接近。Drivers/BSP/如果它里面已经有53L9的BSP驱动那说明扩展板硬件基本被软件包原生支持。2.2 N6570-DK的例程别直接照抄STM32N6570-DK这块板子在ST的生态里相对比较新。需要注意它的默认CubeMX工程里I3C相关的引脚可能已经分配给其他外设了。如果你把Projects/STM32N6xx/Examples/下的I3C例程直接复制过来大概率遇到引脚冲突或时钟配置报错。我建议的工程起点是直接去Projects/STM32N6570-DK/Applications/里找一个最接近的示例工程然后在这个基础上修改。实在找不到就直接新建STM32CubeMX工程选板卡STM32N6570-DK启用I3C1外设。这是最干净的路子虽然要花点时间配置I/O。2.3 认识CubeMX里I3C外设的配置项CubMX里I3C的配置界面和I2C长得有点像但多出了不少I3C专有项。你需要关注的是I3C Speed Mode通常选I3C或Mixed。如果只和53L9通信选I3C模式就够了。I2C Address / Dynamic Address53L9的静态I2C地址要在软件包里查。一般VL53L9的默认地址是0x52。这个地址在I3C协议里会成为它的Static Address用于动态地址分配的起点。Push-Pull vs Open-DrainI3C用Push-Pull传输数据这是比I2C快的主要原因。CubeMX里会问你I2C兼容模式要不要开建议关掉避免降速。Frequency初始频率一般是12.5MHz但对53L9来说不必一上来就拉满5MHz左右会更稳。如果你看到CubeMX生成的I3C初始化函数里含有I3C_ActivateMaster或I3C_AssignDynamicAddress这类调用说明已经进入状态了。3. I3C和I2C的本质差异以及移植里的隐形坑3.1 从主从模型到多主控传统的I2C模式里MCU是Master传感器是Slave一切通信由MCU发起。但I3C引入了动态地址分配、IBIIn-Band Interrupt和Hot-Join机制。这意味着传感器不再完全被动它可以主动向主控发出中断请求而且每个设备总线的地址是可以由主控临时分配的不是焊死在芯片里。这个差异直接影响Platform层代码。在I2C下你调用platform_i2c_write()传入地址0x52就完事了。但在I3C下你首先要给传感器分配一个动态地址之后所有通信都用这个动态地址不是出厂静态地址。3.2 静态地址和动态地址的关系VL53L9传感器出厂时会有一个静态地址在STSW-IMG053源码里通常是VL53L9_I2C_ADDRESS初始值一般是0x52但在I3C总线上你这个0x52只用于开始阶段。I3C主控上电后并不会直接发读写命令而是会发一个ENTDAAEnter Dynamic Address Assignment的流程传感器收到后主控就会给它分配一个新的动态地址这个地址也许变成0x0C、0x15具体看配置。此后所有通信走这个动态地址。STSW-IMG053的Platform层在切换到I3C模式时会自己封装这段流程。你要做的不是自己去写协议而是确保底层的发送函数能正确地把这层处理包起来。3.3 用一句话理解I3C初始化我把I3C模式的Platform移植工作用一句话总结把I2C每次读写时传入设备地址改成I3C初始化时先分配一次动态地址然后把后续所有读写的目标地址替换为动态地址。听起来容易实际代码里各种封装会让地址在多个文件里传递。你找不到在哪改地址是很正常的。我后面会给出定位方法。4. 实操移植步骤从复制工程到第一帧数据以下是我在N6570-DK上移植STSW-IMG053到X-NUCLEO-53L9A1时一步步实际操作的完整记录。说明一下不同版本的软件包文件路径略有差异但核心逻辑通用。4.1 建立基础工程开启I3C先在CubeMX里新建工程选择STM32N6570-DK开发板然后在左侧Connectivity里找到I3C1勾选I3C模式不是I2C模式也不是I3CI2C混合模式配置Timing建议初始SCL Frequency 5 MHz不是12.5MHz理由后面会讲引脚分配确认SCL和SDA分别对应板子上的哪个PinN6570-DK的I3C1信号会走到底层排针要对照原理图确认它和X-NUCLEO-53L9A1的排针位置一致生成工程后把STSW-IMG053里的Middlewares和Drivers目录整个拷贝到工程里并在KEIL/IAR/STM32CubeIDE中添加相应源文件路径。4.2 打开Platform抽象层定位I2C相关函数打开Middlewares/ST/STSW_IMG053/Platform/目录下的platform_vl53l9.c不排除在不同版本里有不同命名但platform_前缀基本不跑。搜索以下关键词I2CI3CVL53L9_I2C_ADDRESS如果工程配置正确你会看到类似这样的代码int32_t platform_WriteReg(uint16_t address, uint16_t reg, uint8_t *pdata, uint32_t len) { HAL_I2C_Mem_Write(hi2c1, address, reg, ...); }当软件包启用I3C模式时这个函数内部会变成int32_t platform_WriteReg(uint16_t address, uint16_t reg, uint8_t *pdata, uint32_t len) { HAL_I3C_Mem_Write(hi3c1, address, reg, ...); }问题是编译时由哪个宏控制在platform.h或者软件包全局头文件里通常会有#define VL53L9_USE_I3C 1或#define USE_I3C你需要在工程里打开这个宏然后确认platform_WriteReg/ReadReg切到了HAL_I3C_*分支。4.3 把I2C句柄替换为I3C句柄这一步是新手最容易犯的错误你打开了VL53L9_USE_I3C宏但Platform层里的hi2c1这个句柄符号还在。要么你把CubeMX生成的hi3c1直接替换进代码要么你在I3C的初始化文件里额外定义一个与hi2c1同名的别名。我推荐直接替换因为后面调试时少一层干扰。// 原来是 extern I2C_HandleTypeDef hi2c1; // 改成 extern I3C_HandleTypeDef hi3c1;读函数同理。4.4 初始化I3C并完成动态地址分配STSW-IMG053的软件架构里传感器的初始化顺序很重要。在调用VL53L9_Init()之前必须先确保总线已经进入I3C模式并完成地址赋值。如果Platform层没有自动处理动态地址你可以在CubeMX生成的main()里主动调用一次I3C启动流程。比较典型的代码如下以STM32CubeHAL为例uint32_t target_addr 0x52; // VL53L9静态地址 I3C_Init(...); // HAL已经在MX_I3C1_Init里做了 I3C_ActivateMaster(hi3c1); I3C_AssignDynamicAddress(hi3c1, target_addr, dynamic_addr); // 之后的通信都用 dynamic_addr有些版本的HAL库或者软件包会把这层封装到VL53L9_Init()内部你要做的只是在main()里先调用MX_I3C1_Init()然后调用外设抽象初始化函数再调用VL53L9_Init()。提示如果运行后发现传感器没响应用逻辑分析仪看I3C总线波形。如果看到主控一直发数据但无ACK多半是动态地址分配没成功。4.5 编译并处理报错我把编译时常见的报错和解决办法列出来这些是我实际遇到过的报错特征原因解决办法undefined reference to hi2c1Platform层代码还在引用I2C句柄全局替换为hi3c1或定义兼容别名implicit declaration of HAL_I3C_Mem_Write没有包含I3C的HAL头文件或者CubeMX没有使能I3C外设检查stm32n6xx_hal_conf.h里的HAL_I3C_MODULE_ENABLEDVL53L9_USE_I3C is undefined宏没定义在编译器预定义里加VL53L9_USE_I3C1传感器NACK动态地址未分配先确认主控I3C主线波形再查地址分配逻辑一启动就HardFaultPlatform层回调或者中断配置问题检查I3C中断优先级和时钟树编译通过只是第一步。真正坑人的是后面跑起来但数据全是0或者干脆就卡在初始化状态。5. 硬件联动X-NUCLEO-53L9A1上的跳线与电源5.1 先看扩展板上有没有地址选择电阻很多ST的扩展板会预留地址焊盘或跳线。X-NUCLEO-53L9A1上也可能有类似的设计一组用来选择0x52还是0x54的焊盘或者I2C/I3C模式选择的电阻。我的做法是拿万用表测一下扩展板正面的丝印看有没有写着ADR或SA0的跳线。如果有确保它和你在代码里配置的静态地址一致。5.2 电源别只靠Arduino的3.3V53L9这类ToF传感器在做深度测量时瞬时电流不小。如果X-NUCLEO扩展板上有额外的电源输入端子比如一个2.54mm的VIN焊盘强烈建议外接一个3.3V的稳压电源别完全依赖排针上的3.3V。排针的3.3V在短距离通信、轻负载场景下够用但跑深度图连续输出时压在STM32N6570-DK的板上稳压器上容易掉电压I3C信号质量也会跟着劣化。5.3 确认I3C的上拉电阻I2C总线通常需要外部上拉电阻但I3C在Push-Pull模式下不需要传统I2C那种10kΩ级别的大上拉电阻。过多的上拉反而会限制信号上升沿拖慢速度。如果扩展板上已经为I2C模式放好了上拉电阻在I3C模式下可能不用太担心但如果你看到板子上有较大的上拉电阻比如4.7kΩ甚至10kΩ建议确认一下是否会影响5MHz速率的信号完整性。实际调试中如果波形上升沿太缓可以尝试把上拉电阻去掉或者降速到3.4MHz。6. 实测验证移植成功与否的判断标准6.1 跑官方自检函数STSW-IMG053里几乎总会带一个自我检测例程比如VL53L9_CheckDeviceReady、VL53L9_GetSensorId或者更完整的VL53L9_Calibrate。移植后的第一件事就是调这类函数。如果传感器ID能正常读出说明I3C底层链路通了地址分配方式也对了。6.2 验证一帧深度数据能读出ID只是开始。真正验证I3C是否稳定要看连续读取深度图数据时有没有丢帧、CRC错误、总线挂死。把传感器配置成连续测距模式然后循环读取数据看读取一帧数据的时间是否稳定有没有偶发的NACK或超时长时间跑比如30分钟后总线是否还活着6.3 老生常谈逻辑分析仪如果没有逻辑分析仪I3C移植就是盲人摸象。不需要买很贵的能解I3C的型号就可以。把SCL和SDA两引脚接到分析仪上抓上电初始化阶段的数据——你能清楚地看到主控先发广播地址、再发RSTDAA/ENTDAA最后分配动态地址。这一连串事件都正常才说明HAL层对I3C的封装没有白忙。7. 我踩过的一个印象最深的坑写这篇记录时我想起一个让排查了两天的问题软件包明明定义了VL53L9_USE_I3C但每次初始化都卡死在等待传感器Ready的状态。用I2C模式一切正常切I3C就不行。后来发现STSW-IMG053里不只一个文件在判断这个宏——platform.c里判断的是编译期宏但vl53l9_api.c里的某些API函数走的是另一套配置切换I3C后它会尝试重设传感器地址而我当时用的是I3C动态地址它却拿静态地址去写。解决办法是在初始化之前先调用一个专门的VL53L9_SetAddress()或者确认软件包文档里对I3C模式的初始化顺序有没有特殊要求。这类寄存器读写前期和后期的地址感知不一致问题在I3C下特别容易发生因为静态地址到动态地址的转换发生在总线初始化那一段里但软件包上层的状态机并不知道这个动态地址是什么时候替换进去的。我个人的习惯是在Platform层封装一个全局变量vl53l9_current_i3c_address每次上层传下来的地址统一经过这个变量替换后再发I3C命令。这样一来不管你软件包内部逻辑怎么绕实际发到总线上的地址永远来自同一份值排查起来就很简单。8. 进阶一点点I3C模式下的速度和功能增益8.1 速率不是越高越好I3C理论上能跑到12.5MHz但53L9本身的设计和板级走线不见得能承受这么高的速率。我的建议是先跑3.4MHz或者5MHz确保一条链路稳定后再往更高调。如果你发现数据偶发CRC错优先降速而不是加长延时。8.2 IBI的中断价值VL53L9支持In-Band Interrupt即它可以在某些事件发生时主动在主线上发中断不用MCU轮询。对低功耗唤醒场景来说是巨大的优势。移植完基本功能后如果时间充裕我建议把IBI也调通——从Platform层注册一个中断回调代替原来通过GPIO引脚触发中断的方式。在N6570-DK上I3C外设本身就能响应IBI关键是初始化时把I3C_SetIBI相关的使能配置打开。8.3 长期稳定性的检查项最后分享一个长期跑稳定性测试的检查清单每秒读取深度帧数连续跑8小时统计超时次数总线异常恢复机制PCF在I3C里也有看看被总线错误打断后能不能重新初始化从待机模式唤醒后I3C总线和传感器是否需要重新做动态地址分配我测试发现某些状态下唤醒后确实需要重新运行地址分配流程这个不是Bug是I3C的Hot-Join机制的正常体现代码里要有对应的重连逻辑。移植STSW-IMG053到N6570-DK这件事说难不难但确实不是改一两行就能跑通的。核心在于把I3C的地址模型搞明白再把Platform层切干净、把宏定义捋顺剩下的就是总线级别的调试功夫。按上面这个流程走你应该能在两三天内看到传感器通过I3C输出的第一帧数据。
返回列表