S32K144最让人上头的一点,就是它那颗 LPI2C 模块。以前写单片机I2C,要么用GPIO软件模拟,要么用传统I2C外设,遇到从机时钟拉伸、总线被拉死、多设备冲突就得跟寄存器死磕半天。S32K144的LPI2C不一样,Master/Slave模式都能配,有两路LPI2C可独立使用,还带FIFO、中断和超时机制,做多设备通信是真的省心。这篇文章把我配置LPI2C主从模式、把多个传感器和存储器挂到同一条总线上跑通的完整过程,以及中途踩过的所有坑都整理成文,给准备在S32K144上做多设备通信的朋友做个参考。
先说清楚适合谁看:如果你刚拿到S32K144开发板,想在一条I2C总线上同时挂几个传感器、EEPROM或者IO扩展芯片;或者你之前只用SDK封装函数,想弄明白LPI2C底层到底怎么配置主从模式;再或者你已经在调试,却发现从机总是不应答、总线莫名其妙卡死,那这篇文章很适合你。
1. LPI2C选型与多设备通信方案设计
1.1 为什么用LPI2C而不直接拿GPIO软件模拟
很多做单片机的朋友习惯用GPIO模拟I2C,程序里自己翻转SDA和SCL来拼起始、停止、ACK这些时序。软件模拟的好处是引脚灵活,想挂哪就挂哪,但坏处也很明显:主循环一旦被中断打断时序容易乱,跑400kHz时对延时精度要求高,一旦程序里有个耗时任务,通信质量就直线往下掉。
LPI2C是S32K144硬件自带的低功耗I2C外设,控制器自动处理起始条件、停止条件、地址匹配、ACK/NACK检测,还有收发FIFO。这意味着MCU不需要在每传输一个字节时都去盯信号翻转,只要往FIFO里填数据或者读数据就行。LPI2C还支持DMA触发,数据量大时可以直接搬数据,CPU空闲下来干别的事。
S32K144上具体有两个LPI2C外设,也就是LPI2C0和LPI2C1。如果你的项目同时需要主节点和从节点两种角色,可以用LPI2C0做Master,LPI2C1做Slave,两者互不干扰。LPI2C模块在寄存器层面通过MCR寄存器的MST位切换主从角色,从硬件设计上就比软件模拟I2C稳固得多。
我在项目里之所以选LPI2C,还有一个实际原因:板子上外设多,除了I2C还有CAN、UART、SPI,引脚复用得精打细算。LPI2C引脚可以灵活复用,每个LPI2C模块都有多组可选引脚,这样布局走线就宽裕不少。
1.2 Master/Slave模式下硬件架构简析
理解LPI2C主从模式之前,先把模块内部的几个角色关系搞清楚。LPI2C不是一堆寄存器堆在一起,它内部其实分了两套逻辑:主机接口和从机接口。
主机接口负责主动控制总线:发起START、发送地址、发送数据、接收数据、产生STOP,监控ACK/NACK状态。从机接口则被动等待:监听地址匹配,匹配成功后接收数据或发送数据。当MCR的MST位写1时,模块运行在主机模式;MST写0时,模块进入从机模式。
有些资料会说LPI2C的Master和Slave可以“同时工作”,实际上指的是芯片上有两个独立LPI2C模块,每个模块在同一时刻只能选一个角色。别指望同一个LPI2C外设既当主又当从,除非你用中断频繁切换模式,但那样很容易造成时序混乱,不建议在产品代码里这么干。
LPI2C内部还带了数据匹配功能,可以作为地址过滤器使用。比如你要在总线上监听某个地址的数据,可以配置匹配寄存器,当地址和数据符合条件时触发中断或DMA请求。这个特性做多设备监控时非常有用。
S32K144的LPI2C还支持10位地址,但实际项目里7位地址完全够用。一条标准I2C总线理论上最多能挂127个从设备,实际受限于总线电容和地址冲突,常见做法是控制在8个设备以内。
1.3 地址规划和总线负载评估
多设备通信的第一步不是写代码,而是先规划地址表。我这次在S32K144总线上挂了四个设备,地址分配如下:
| 设备 | 芯片型号 | 从机地址 | 数据量 | 用途 |
|---|---|---|---|---|
| 温湿度传感器 | SHT30 | 0x44 | 6字节 | 采集温湿度 |
| EEPROM | AT24C02 | 0x50 | 8字节/页 | 存配置参数 |
| IO扩展器 | PCAL6416 | 0x20 | 2字节 | 控制外部IO |
| OLED显示屏 | SSD1306 | 0x3C | 显存 | 状态显示 |
这个地址表不是拍脑袋定的,每个设备都有固定地址或者通过硬件引脚配置地址。例如AT24C02的A0/A1/A2引脚接地后地址就是0x50;PCAL6416的A0/A1/A2引脚配置组合也影响了地址。如果两个设备地址一样,总线就会出问题:主设备发送地址后,两个从机都可能应答,把控SDA线,甚至造成总线短路风险。
规划好地址后还要评估总线负载。I2C总线是开漏结构,所有设备共用一个上拉电阻网络。设备数量越多,总线电容越大,上拉电阻取值就需要相应调整。工程上常用经验值:
| 通信速率 | 建议上拉电阻 | 适用场景 |
|---|---|---|
| 100kHz标准模式 | 4.7kΩ | 低速传感器、少量设备 |
| 400kHz快速模式 | 2.2kΩ | 中等速率、多设备 |
| 1MHz快速+模式 | 1kΩ | 短距离、高速传输 |
我这次用400kHz快速模式,上拉电阻选了2.2kΩ。如果线缆比较长或者设备特别多,优先降低速率到100kHz,而不是盲目调小上拉电阻,否则低电平可能超过从机允许的VOL阈值,通信照样会出错。
2. 模式配置前必须处理的引脚与时钟
2.1 引脚复用(MUX)与开漏配置
S32K144的引脚默认不是LPI2C功能,必须在PORT模块的PCR寄存器里把引脚复用成LPI2C对应的ALT功能。不同封装、不同引脚对应的ALT值不一样,具体要查芯片引脚复用表。我这里以LPI2C0挂到PTB2/PTB3为例做个示意:
// 把 PTB2/PTB3 配成 LPI2C0 的复用功能 // 具体 ALT 值以你所用芯片封装的数据手册为准 PORTB->PCR[2] = PORT_PCR_MUX(5); // LPI2C0_SCL PORTB->PCR[3] = PORT_PCR_MUX(5); // LPI2C0_SDA这里有个非常容易踩坑的地方:I2C是开漏总线,引脚配置时要确保外部有上拉电阻。如果板上没有外接上拉,有的S32K124引脚可以用内部上拉,但内部上拉阻值偏大,多设备高速通信容易边缘变缓,我还是建议硬件上加上2.2k到4.7k的外部上拉。
另外要注意PCR里的其他位,比如是否使能了拉普或者拉低。有些默认状态可能是下拉,I2C空闲时需要SCL和SDA都为高,如果引脚被内部下拉拉低,总线永远都忙。我一开始就是没清内部下拉,总线状态一直不正常,排查了半天。
如果你用的是S32K1xx SDK,初始化引脚也可以直接用Port驱动接口,比如PORT_SetPinMux()。但底层原理是一样的,核心就是把PCR中MUX字段设对。
2.2 时钟树与波特率分频计算
LPI2C功能时钟来源于PCC时钟分配模块。S32K144里每个外设都有对应的PCC寄存器,可以选不同的时钟源。LPI2C一般推荐从SOSC、FIRC或者SPLL分频过来的时钟,具体选哪个取决于你系统时钟树怎么搭。
LPI2C波特率计算公式主线是:
SCL频率 = LPI2C功能时钟 / (2 * (CLKLO + CLKHI))CLKLO和CLKHI分别对应SCL低电平和SCL高电平的计数长度,它们在LPI2C的MCFGR1寄存器里配置。我这次系统里LPI2C功能时钟选了12MHz,目标速率400kHz,算一下:
计数 = 12MHz / 400kHz / 2 = 15所以CLKHI+CLKLO加起来要等于15。我让CLKHI=7,CLKLO=8,代入式子:
SCL = 12MHz / (2 * (7 + 8)) = 400kHz正好命中。如果你用100kHz标准模式,那总数就是60,CLKHI和CLKLO可以各取30。注意CLKLO和CLKHI的取值范围有限,超出范围就要先通过MCFGR1里的PRESCALE字段做预分频。计算逻辑不复杂,但容易漏了PRESCALE导致实际频率对不上。
还要强调一点:从机的最高通信速率是固定的,比如某传感器只支持100kHz,那就算主控能跑400kHz,总线整体也只能降到100kHz。总线速率取决于最慢的一个设备,不是取平均值。
2.3 初始化顺序与复位策略
LPI2C配置顺序有讲究。推荐流程是:
- 打开PCC外设时钟
- 配置引脚复用
- 复位LPI2C模块
- 配置主从模式和滤波器参数
- 使能LPI2C模块
很多人上来直接改MCR里的MST位参数配模式,模块还处于使能状态,配置很容易失效。我习惯先关模块:
PCC->PCCn[PCC_LPI2C0_INDEX] = PCC_PCCn_CGC_MASK; // 使能 LPI2C0 时钟 // 复位模块 LPI2C0->MCR = LPI2C_MCR_RST_MASK; LPI2C0->MCR = 0x00;复位完之后,模块处于关闭状态,这时候再写MCR、MCFGR这些寄存器才不容易出问题。初始化完成后,最后再置位MEN使能模块。
这里有一个实际体会:不要在初始化完成前把模块提前使能,否则模块检测到总线上有异常电平就会把BUSY位置上,后面所有通信都会卡在“等待总线空闲”。如果出现这种情况,先把模块重新复位,再按顺序走一遍初始化。
3. Master模式配置与多从机访问
3.1 主模式初始化参数与寄存器设置
主模式配置的核心思路是:把LPI2C切到主机模式,设定波特率、滤波、超时时间,然后通过命令寄存器MTDR来发起事务。
一个简单的寄存器级初始化示意如下:
// 关闭模块 LPI2C0->MCR &= ~LPI2C_MCR_MEN_MASK; // 切主机模式 LPI2C0->MCR |= LPI2C_MCR_MST_MASK; // 配置预分频和时钟高低电平计数 LPI2C0->MCFGR1 = LPI2C_MCFGR1_PRESCALE(0U) | LPI2C_MCFGR1_CLKHI(7U) | LPI2C_MCFGR1_CLKLO(8U); // 重新使能模块 LPI2C0->MCR |= LPI2C_MCR_MEN_MASK;需要注意的是,MCR中的MST位不只是模式选择,模块在主机模式下会根据MST位的写入沿自动产生START或STOP时序。这个行为跟传统外设不一样,所以操作的时候要格外小心,别在事务中途随意翻转MST位。
实际SDK封装得比较友好,用NXP的S32K1xx SDK,代码可以简化成:
lpi2c_master_config_t masterConfig; LPI2C_DRV_MasterInit(0, &masterConfig, 12000000U); LPI2C_DRV_MasterSetBaudRate(0, 400000U);但理解底层寄存器还是有用的,因为在调试异常时序时,SDK函数不会直接告诉你哪一步出了问题。
3.2 一次完整读写的命令序列
主模式下发一次写操作,完整流程可以拆成五步:
- 等待总线空闲
- 发送START条件并携带从机地址和写位
- 等待从机ACK应答
- 逐字节发送数据,每个字节等待ACK
- 发送STOP条件
LPI2C主机模式下,上述步骤通过写MTDR命令寄存器完成。第一个命令通常是START加地址,命令码大概类似0b010,再拼上7位地址和读写位。收到地址后主机要检查状态寄存器的ACK标志:
// 示意:发送 START + 地址 + 写位 LPI2C0->MTDR = LPI2C_MTDR_CMD(0b010) | LPI2C_MTDR_ADDR(0x50U) | LPI2C_MTDR_RW(0U); // 等待地址发送完成并检查ACK,具体状态位名以参考手册为准 while ((LPI2C0->MSR & 0x... ) == 0) {}写数据时同样往MTDR里写命令,命令类型是发送数据。注意LPI2C有FIFO,可以一次预写多个字节,但要控制写入数量,别让FIFO溢出。
读流程稍微不同:发送地址加读位后,主机需要为每个字节提供时钟。LPI2C主接收数据时,每收完一个字节,在主端最后一个字节时不应该ACK,要主动发送NACK,然后发STOP。多字节读取时,倒数第二个字节之后就要为最后一个字节准备NACK,这是I2C协议的规定。
这里有一个不少新手会犯的错:往MTDR里写地址时,不手动把地址左移一位。I2C地址字节的结构是“7位从机地址 + 1位读写标志”,比如从机地址0x50,写操作的地址字节是0xA0,读操作是0xA1。如果直接拿0x50当地址字节发送,从机根本不会应答。LPI2C驱动内部通常会帮你处理移位,但你要是自己操作寄存器,这个细节绝对不能忘。
3.3 多从机轮询机制与地址切换经验
多从机通信就是主设备在总线上按地址轮询各个从机。代码层面看起来很简单,无非是把读写函数包一层,参数传不同的从机地址。但实际工程里要处理几个问题。
第一个是访问节奏。不同从机对访问间隔的要求完全不同。比如SHT30温湿度传感器,每次发起测量后要等几十毫秒才能读结果;AT24C02写入后需要几毫秒的写周期;PCAL6416这种IO扩展器则可以随时读写。如果所有设备都在一个100ms周期里轮询,温湿度和IO扩展器没问题,EEPROM写入时一旦触发写周期,紧接着的访问就会NACK。
我在实际代码里用了一个简单的状态机,把外设按“快速响应、慢速响应、写入后延时”三类分开调度。每个从机访问之间插入不同延时,避免主机连发导致从机没准备好。
第二个是多从机总线上需要有超时重试机制。I2C硬件本身没有硬件超时,LPI2C虽然有超时计数器,但也要在配置里显式使能并设定参数。一旦某个从机死机或者总线被拉低,主机可能一直卡在等待ACK的状态,这时候靠超时中断跳出来,然后对总线做恢复处理,比无限死等强得多。
4. Slave模式配置与主机请求响应
4.1 从模式地址匹配与中断响应
如果把S32K144配置成LPI2C从机,常见场景是这颗芯片作为子控制器,等待上级主控读取数据或下发控制指令。从模式配置的主线是:关模块、设置从机地址、使能从机中断、再开模块。
一个从模式初始化的示意:
// 关闭模块 LPI2C0->MCR &= ~LPI2C_MCR_MEN_MASK; // 设置从机地址,比如 0x40 LPI2C0->SADR = LPI2C_SADR_ADDR0(0x40U); // 使并从机地址匹配中断 LPI2C0->SIER |= LPI2C_SIER_AMFIE_MASK; // 使能模块 LPI2C0->MCR |= LPI2C_MCR_MEN_MASK;从机模式使能后,总线上发生地址匹配时,LPI2C会置地址匹配标志并触发中断。这个中断入口里要做的事情很明确:判断主机是读还是写,读就往从机发送FIFO里填充数据,写就从接收FIFO里读取数据。
有一点需要注意:从机在响应主机读请求前,如果发送FIFO还没有数据,从机会把SCL拉低,制造所谓的“时钟拉伸”,让主机等一等。这是I2C协议允许的,但对S32K144从机来说,如果MCU处理中断不及时,SCL会被长时间拉低,主机那边就会表现为通信超时。因此从机模式的等待延迟尽可能短,中断优先级要拔高。
4.2 从机收发缓冲与背靠背数据管理
LPI2C从机有自己的收发FIFO,深度有限,大概几个字节。数据吞吐量不大时没问题,但主机一次性发几十个字节过来,从机FIFO很容易溢出。
我在做从机数据接收时,习惯在地址匹配中断后立刻读取FIFO里的数据,并且清空FIFO确认标志。如果一次事务里主机连续发很多字节,MCU中断服务程序要保证每收到一个字节就及时读走,不能等到全部接收完再处理。
发送方向也是一样的道理。主机发起读请求之前,从机要提前把应答数据准备好。如果主机一次读32字节,而发送FIFO深度只有4字节,MCU需要在发送FIFO空标志置位时不断填充数据,一旦填充不及时,从机就会时钟拉伸等待,主机端速度被拖慢。
实际项目里更多用DMA配合从机收发,减少中断频繁进出的CPU开销。LPI2C的DMA请求可以在每个FIFO空或满时触发,数据量大时优势明显。我从单字节中断换成DMA模式之后,CPU占用率降了将近一半。
4.3 多设备组网中的从机角色补充说明
多设备组网中,S32K144的从机角色还有一个常见用途:做网关或者协议转换器。比如上级主控通过I2C读取S32K144的状态,S32K144内部再通过其他接口采集传感器数据,形成一个桥接节点。
这种场景下,从机地址的选择非常重要。总线上每一个设备地址必须唯一,所以设计初期就要把设备地址纳入整体规划。如果确实遇到地址冲突,一种办法是把S32K144的从机地址做成可配置的,例如通过DIP开关或者EEPROM存储地址参数,这样同一硬件可以在不同设备上复用。
从机模式的另一个细节是:总线上如果同时有多个主机,从机必须能在一次地址匹配被两个主机交替访问。I2C协议本身不允许总线同时被两个主机占用,多主机通过仲裁决定谁先访问。S32K144作为从机时,不用自己处理仲裁逻辑,但需要做好多次快速地址匹配的准备,防止漏处理。
从机模式下,可以把高字节地址掩码寄存器利用起来,让同一个从地址响应对应的地址范围。但我不建议一上来就玩地址掩码,优先把单个地址的事务跑稳,再考虑复杂匹配。
5. 多设备通信联调的完整过程
5.1 先把单从机调通:最小工程对照
多设备通信联调最忌讳一上来把四个设备全挂上。我习惯先只挂一个从机,把单设备读写跑通,再逐步增加。
我的调通步骤是这样的:先选EEPROM AT24C02做第一个设备,因为时序简单、容易验证。第一步向地址0x50发一个设备地址扫描,也就是发送START加0xA0地址字节,看从机是否返回ACK。如果逻辑分析仪上能看到ACK,说明地址、引脚、上拉、时钟都没问题。
接下来做一次写操作:往EEPROM地址0x00写入一个字节0x5A,等待5ms写周期,然后从0x00读回数据。读到0x5A就算打通了第一层。
这段过程里最简单有效的工具是逻辑分析仪。很多逻辑分析仪自带I2C解码器,抓完波形后能直接解析出地址字节和数据内容,比在示波器上数脉冲快太多。
5.2 挂载多个从机的联调顺序
单设备打通后再逐步增加设备。我第二次挂的是IO扩展器PCAL6416,第三次挂温湿度传感器SHT30,最后才是OLED显示屏。
为什么OLED放最后?因为OLED需要初始化序列,一次要发不少命令和数据,调试时波形比较乱,适合放在链路稳定之后再处理。
多设备同时挂上后,我用了一个循环扫描脚本,每隔500ms依次读取每个设备的状态:
| 序号 | 操作 | 预期结果 |
|---|---|---|
| 1 | 读SHT30温湿度数据 | 6字节,温度湿度数据符合常识 |
| 2 | 写AT24C02再读回 | 读回数据和写入一致 |
| 3 | 写PCAL6416输出寄存器 | 外部LED点亮 |
| 4 | 写OLED显示字符 | 屏幕刷新正常 |
如果某一步失败,优先拔掉其他设备,只保留出问题的设备,缩小排查范围。这种做法很笨但非常有效,能很快把问题定位到“设备本身”“软件时序”还是“总线干扰”。
5.3 用逻辑分析仪验证多设备响应波形
联调时我最常看的是逻辑分析仪抓下来的总线时序。I2C波形看起来简单,但解码时有几个关键点。
首先是起始条件:SCL保持高电平时,SDA从高电平跳变到低电平。停止条件相反:SCL高电平时,SDA从低电平跳变到高电平。如果总线上出现连续的乱七八糟的沿,很可能是有设备在错误时间驱动了SDA。
其次是地址字节。逻辑分析仪的I2C解码器会显示7位地址、读写标志和ACK/NACK状态。比如我发0xA0,解码器会显示“Addr=0x50 W”,后面跟一个ACK。如果显示NACK,说明这个地址没有从机应答。
最后是读操作时的ACK时序。主机读数据时,最后一个字节必须由主机回NACK,表示“后面不读了”,然后主机发STOP。如果主机在最后一个字节回了ACK,从机会继续把SDA驱动低,导致停止条件不完整,总线状态异常。
逻辑分析仪采样率建议不低于8MHz,否则400kHz的快速模式波形边缘不够平滑,解码容易出错。长期调试时我给采样率设到25MHz,数据量虽然大,但波形干净很多。
6. 常见问题与排查技巧实录
6.1 总线一直忙或SDA被拉低
多设备I2C最常见的问题就是总线忙,发送事务时发现BUSY位一直置1。排查时先拿万用表量SCL和SDA电平。正常空闲状态两个引脚都应该是高电平。如果SDA或者SCL其中一个被拉低,说明有设备还在占用总线,或者某个从机没有正常释放总线。
我遇到过的情况包括:某个传感器上电异常,SDA输出被拉死;还有一个EEPROM的WP引脚悬空,导致写入过程卡死。解决方法是给问题设备单独复位,或者干脆断开它的电源,排查完再接上。
如果是因为通信过程中主机异常终止导致从机处于半通信状态,可以用GPIO模拟9个SCL时钟脉冲来“解锁”SDA。具体做法是:SDA保持释放,SCL手动翻转9次,让处于半途的从机完成当前字节传输并释放SDA。这个办法对绝大多数I2C设备有效。
6.2 地址匹配不上、总是不ACK
从机地址不ACK的排查顺序:先看逻辑分析仪实际发出去的地址字节对不对,再看从机供电和复位引脚,再看它的硬件地址配置引脚有没有焊接正确。
有一类问题是硬件上的。比如AT24C02的A0/A1/A2引脚悬空,芯片内部可能读到不确定电平,导致实际地址不是0x50而是别的值。还有不少OLED模块上有I2C地址选择电阻,有的模块默认0x3C,焊接改动后变成0x3D。
另一类是软件上的。自己操作MTDR寄存器时,如果没有把7位地址左移一位,地址字节就变成了0x28而不是0x50,从机当然不会ACK。建议先统一用SDK提供的驱动函数,它内部会帮你处理地址移位,等调通后再自己控制寄存器也不迟。
6.3 时钟拉伸引发的主从同步异常
S32K144作为主机时,如果从机在响应前需要处理时间,会把SCL拉低,制造时钟拉伸。LPI2C主机模块本身支持这个操作,但如果从机拉低时间太长,主机会一直等待。
我在调试一个温湿度传感器时发现,它每次测量后需要等待几十毫秒才能读数据。如果读得太快,传感器会一直拉低SCL等待内部转换完成。这时候代码不能用同步阻塞的方式傻等,需要加超时。LPI2C模块里通常有总线超时配置,合理设置超时时间,超时后复位总线并重试。
从机模式下的时钟拉伸问题则是另一回事。S32K144作为从机如果来不及填充发送FIFO,它会自动拉低SCL,这个机制是好的,但要是MCU的中断响应被更高优先级任务堵住,SCL会一直低电平,主机那边就表现为总线卡死。解决办法是给LPI2C从机中断一个足够高的优先级,或者在中断里只做FIFO填充,不做复杂数据处理。
6.4 电平与上拉失配:5V/3.3V混合系统的坑
S32K144是3.3V供电,很多市场上常见的模块却是5V逻辑,比如老式EEPROM板子、某些OLED屏。直接把5V设备接到3.3V总线上,轻则通信不稳定,重则烧引脚。
看数据手册时要注意引脚是否有5V容忍标记。S32K144很多引脚是5V容忍,但并不是所有引脚都带FT标记。如果确认不支持,就必须用双向电平转换芯片,比如PCA9306,或者用带电平转换的I2C模块。
上拉电阻的阻值同样会影响电平。上拉电阻选太小,比如1kΩ,设备低电平输出时灌电流过大,可能超过从机的最大灌电流;选太大,比如10kΩ,400kHz下上升沿变得平缓,容易造成误采样。我调试多设备时先按2.2kΩ起步,如果设备多了再加上总线电容,速率降到100kHz,很少出电平问题。
7. 实战心得与后续扩展
7.1 推荐配置顺序和代码规范
经历过几次折腾后,我整理了一套固定的LPI2C配置顺序,每次都用这个流程,踩坑概率低很多:
- 先确认硬件上的外部上拉电阻,没有上拉就不调软件
- 打开PCC外设时钟
- 配置引脚复用为LPI2C功能
- 复位LPI2C模块
- 设置主从模式、分频、滤波、超时
- 使能LPI2C模块,先跑一个“地址扫描”确认总线上所有设备
- 再开始具体事务通信
代码方面,我习惯把所有LPI2C操作封装成独立函数,比如LPI2C_WriteReg、LPI2C_ReadReg、LPI2C_CheckDevicePresent,主循环里不要出现大段裸寄存器操作。每个函数里都带超时判断和返回值状态,这样出问题时能快速定位是哪个设备、哪一步失败。
7.2 从单主多从到多主仲裁的演进方向
调通单主多从后,如果项目还要再上一个台阶,可以考虑多主机架构。LPI2C支持多主机仲裁,两个主设备同时发起总线时,SDA为低的一方会赢得仲裁,输掉的一方需要放弃当前传输并等待总线空闲后重试。
多主机实际工程里要注意两点:一是每个主机都要有重发机制,因为仲裁失败是常态而不是异常;二是总线上需要I2C协议规范里的“总线空闲”检测,两个主机不能在总线上同时动作。LPI2C的状态寄存器里有总线忙标志,发送前等待该标志清零,再发起START,能大幅降低仲裁冲突概率。
7.3 后续可以尝试的扩展玩法
LPI2C两路都用完后,如果还要扩展更多总线,可以外接I2C多路复用器,比如TCA9548A,它可以把一条总线扩展成8路,每路再挂一批设备,彻底解决地址冲突问题。
还有一种玩法是用FlexIO模拟一路额外的I2C主机,S32K144的FlexIO做这个并不算难,只是配置起来要比LPI2C麻烦一些。FlexIO的好处是完全可编程,甚至可以实现非标准的单线协议。
如果追求极致性能,可以把LPI2C收发都改成DMA驱动,配合环形缓冲区收发数据,CPU占用能压得非常低。我后来在量产项目里就是这么做的,通信稳定性比中断驱动模式好不少,尤其是在主循环任务很多、中断响应不及时的场景下。所以如果你手上正好有S32K144,花点时间把LPI2C这块啃透,后面做多设备通信会顺手很多。