尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

STM32通过SMBus读取MLX90614:时序配置与PEC校验

STM32通过SMBus读取MLX90614:时序配置与PEC校验 简介面向STM32嵌入式开发者的SMBus主机驱动资源主要解决MLX90614红外温度传感器通过SMBus/I2C接口与STM32通信的问题适用于智能家居、健康监测等非接触测温场景。压缩包共18个文件约21KB包含7个C源码、6个头文件及CMake构建脚本、编译链接配置等其中mlx90614.c/smbus.c等主要实现SMBus底层时序与温度数据读取可直接移植到STM32F1/F4系列工程。目前已有287人学习下载。内容从硬件连接、HAL库配置到MLX90614寄存器读写与CRC错误处理均有覆盖并给出可运行示例代码便于开发者快速验证传感器通信与温度换算逻辑。整体精简紧凑适合正在调试I2C/SMBus总线或需要快速集成MLX90614的工程师参考。1. 把 MLX90614 接上 STM32为什么不需要“第二套 I2C”一个看起来只要四根线的红外温度传感器会在没有示波器时让调了三天总线的人怀疑人生。MLX90614 的对外接口是 SMBus而多数 STM32 例程只按普通 I2C 写。两者电气电平一致命令时序也相似但 SMBus 在“读寄存器”这件事上多了 PEC 校验、超时处理和时钟拉伸约束。标题里的 stm32-smbus-master 表达的是这样一种需求把 STM32 的硬件 I2C 外设按 SMBus 主站的规矩工作去持续读取 MLX90614 的环境温度和物体温度。下面按协议准备、CubeMX 配置、读寄存器、故障排查和量产技巧五步讲完新手可以照抄长期做嵌入式的也能顺便检查自己的驱动是否经得起低温环境考验。2. 先把 SMBus 主站的底子打好MLX90614 的协议细节与 STM32 外设配置MLX90614 的数据手册里直接写了它的接口符合 SMBus 2.0而不是 I2C。这不是文字游戏。SMBus 标准里最容易被忽略的是“最小总线时钟”和“总线超时”这两条在 I2C 总线规范里不存在。STM32 的 I2C 外设从 F1 到 H7 都具备 SMBus 相关位但 CubeMX 默认生成的 I2C 配置不会帮你打开它们。先把协议要求列出来再落到寄存器配置最后给一个可以直接进函数的读时序骨架。2.1 SMBus 与 I2C 在 MLX90614 场景下不能混用的 4 个原因很多教程喜欢说“MLX90614 是 I2C 接口”然后直接调用HAL_I2C_Mem_Read。这么做在常温下能读回来数据但到了批量验证时问题会集中在以下四点。比较项普通 I2CSMBusMLX90614 所在规范最小时钟无可低至 010 kHz防止总线长期占用总线空闲超时无主机应在 25~35 ms 内超时复位PEC 校验可选多数从机不支持读数据后必须/可选择跟 1 字节 CRC-8NACK 处理主机自己决定是否重试收到 NACK 后要释放总线并等待 STOP第一点是时钟下限。对 I2C 而言主设备把 SCL 拖到很低只是为了兼容慢速从机这没问题。但对 SMBus 来说从机内部有看门狗定时器如果时钟被拉低超过约定时间从机会把自己复位于是总线从死锁里恢复回来。MLX90614 也内置了这层保护所以外部主机最好有超时退出机制。第二点是读取校验。普通 I2C 读温度时如果认为读到的 16 位数据只受噪声影响那就低估了工业现场的干扰。SMBus 的 PEC 字节由从机根据发送内容计算能同时校验地址、命令和数据三部分。MLX90614 默认不强制要求 PEC但它支持这个字节。实际项目里建议把 PEC 打开作为验收项而不是开发期不开、出货后也不开。第三点是地址仲裁。I2C 允许挂多个主设备但 SMBus 规定了更为严格的地址解析协议ARP用于热插拔设备。MLX90614 并未实现 ARP所以不要在一个已经挂载了完整 SMBus 设备组的总线上直接把 MLX90614 怼上去它可能会和外设的 ARP 流程冲突。单独的 SMBus master 连接 MLX90614 是最安全的用法这正是标题里 master 一词的含义。第四点是重复起始。SMBus 读两个字节的标准过程是 START 地址写位 命令 REPEATED START 地址读位 数据低 数据高 PEC。中间是 repeated start不是 STOP 后再 START。HAL 库中如果分开调用HAL_I2C_Master_Transmit和HAL_I2C_Master_Receive两个函数之间会插入 STOP这会让 MLX90614 把命令当成一次完整的写事务后面的读操作步调就会错乱。注意我用逻辑分析仪定位过好几次这类问题波形能看到一个明显的 STOP 和随后的 START而不是地址之间连续的低电平。遇到这种情况时优先改用HAL_I2C_Mem_Read不要自己拼两段传输。2.2 用 STM32CubeMX 把 I2C 外设配置成 SMBus 主站的最小步骤先明确一点CubeMX 生成代码时普通系列默认生成 I2C 初始化但不生成专门 SMBus 句柄。在较新的 STM32 系列如 L4/L5/H7 上HAL 库的I2C_InitTypeDef里有SMBusMode和PECEnable字段CubeMX 的 Pinout 界面里也能找到对应下拉框。在 F1/F0 这类老系列上硬件寄存器里也有 SMBus 位只是 HAL 没有把专用 SMBus API 完整暴露因此只需要按 I2C 模式生成然后在初始化完成后手动打开I2C_CR1_SMBUS。下面这段是 CubeMX 生成后修改到MX_I2C1_Init()里的典型代码以 F1 HAL 库为例。hi2c1.Instance I2C1; hi2c1.Init.ClockSpeed 100000; hi2c1.Init.DutyCycle I2C_DUTYCYCLE_2; hi2c1.Init.OwnAddress1 0x00; hi2c1.Init.AddressingMode I2C_ADDRESSINGMODE_7BIT; hi2c1.Init.DualAddressMode I2C_DUALADDRESS_DISABLE; hi2c1.Init.GeneralCallMode I2C_GENERALCALL_DISABLE; hi2c1.Init.NoStretchMode I2C_NOSTRETCH_DISABLE; HAL_I2C_Init(hi2c1); // 在支持 SMBus 位控制的老系列上手动打开超时与从机控制位 I2C1-CR1 | I2C_CR1_SMBUS;代码里四个容易被改坏的参数ClockSpeed设置为 100 kHz因为 MLX90614 的 SMBus 不支持 400 kHz即使你的 I2C 外设跑 400 kHz 很轻松这里也要压住。OwnAddress1设成 0x00 是主站惯例表示本机不作为从机响应地址。NoStretchMode保持 DISABLE允许从机拉伸时钟。MLX90614 在内部 ADC 转换期间会短暂拉低 SCL如果禁用了时钟拉伸那几微秒的数据采样就会错位。GeneralCallMode保持 DISABLESMBus 虽然支持广播呼叫但 MLX90614 不响应它。如果用的是 L4/H7 的 HAL 写法则应该在一开始就把Init.PECEnable设为I2C_PECENABLE_DISABLE等基本读通之后再启用硬件 PEC。先关掉 PEC 的原因是硬件 PEC 功能会自动在数据流末尾插入一个 CRC 字节一旦总线波形本身不标准很容易把问题误判成校验码不匹配。分阶段验证永远比一次全开容易定位。2.2.1 外部上拉和 GPIO 开漏配置CubeMX 在生成 GPIO 初始化时会自动把 I2C1_SCL/I2C1_SDA 配置为开漏输出除非你手工编辑了引脚模式。开漏加外部上拉的必要性SMBus 是多主机总线任何设备都能拉低 SDA开漏输出正好保证这一点。如果测到 SDA 恒为高电平、写不到数据大概率是把引脚设成了推挽。上拉电阻取值不能照抄 Arduino 模块上的 10 kΩ。100 kHz 总线建议 4.7 kΩ如果线长超过 20 厘米换 2.2 kΩ。电阻太大会让 SCL 下降沿过缓逻辑分析仪能看到梯形波形但主站内部可能已把电平判成无效。2.3 主模式读寄存器的最短命令骨架确认硬件引脚和时钟后先不急着在工程里写大量业务逻辑跑通一个最简单的“读环境温度”再说。这里用HAL_I2C_Mem_Read实现 SMBus 读字因为它天然满足重复起始条件。#define MLX90614_7BIT 0x5A #define MLX90614_ADDR ((MLX90614_7BIT) 1) #define MLX90614_TA_REG 0x06 #define MLX90614_TOBJ_REG 0x07 uint8_t mlx90614_read_raw(uint8_t reg, uint16_t *raw) { uint8_t buf[2] {0, 0}; HAL_StatusTypeDef ret; ret HAL_I2C_Mem_Read(hi2c1, MLX90614_ADDR, reg, I2C_MEMADD_SIZE_8BIT, buf, 2, 50); if (ret HAL_OK) { *raw (uint16_t)(buf[0] | (buf[1] 8)); return 0; } return 1; }这个函数最关键的是第二个参数MLX90614_ADDR。SMBus 规范里的从机地址是 7 位 0x5A而在 HAL 的地址参数里必须左移一位变成 0xB4方向位由库内部处理。不要直接在宏里写(0x5A 1) | 1读取地址交给HAL_I2C_Mem_Read解决否则部分 HAL 版本会自动再补一位地址就变成 0xEB从机根本不会 ACK。reg参数是命令字节对应 MLX90614 内存映射中的 RAM 或 EEPROM 偏址本例的 0x06 表示环境温度。超时参数 50 ms 是合适的SMBus 允许从机拉伸时钟给 10 ms 在某些电压跌落时容易误触发。至此总线已经能按 SMBus 主站身份工作。下一步就是解析读回来的 16 位数据以及决定要不要做 PEC 校验。3. 读 MLX90614 的 TA/TObj 寄存器数据格式与 PEC 校验实现MLX90614 内部有 RAM、EEPROM 和 PWM 输出三种读取渠道。SMBus 方式最常读的两个命令是 0x06 和 0x07前者是环境温度传感器自身温度后者是物体表面温度热电堆经过内部补偿后的结果。读回来的是 16 位原始 ADC 计数如果不和 0.02℃/LSB 这条换算率挂钩数据看起来就是不稳定的死数。这一部分把换算公式、软件 CRC-8 以及常见 PEC 失败原因一次说清。3.1 RAM 区的环境温度和物体温度不是同一个量纲MLX90614 的寄存器宽度是 16 位但每个 RAM 单元在芯片内部排列是按 SMBus 的 word 读出来的低字节在前还是高字节在前取决于你用的读取函数。HAL_I2C_Mem_Read按先低后高把数据填进 buf[0]、buf[1]代码里左移拼接即可。温度换算公式在数据手册里写得很接近寄存器命令含义原始值到摄氏度的换算0x06环境温度 TARAW × 0.02 - 273.150x07物体温度 TObjRAW × 0.02 - 273.15表里的换算公式已经包含从开尔文到摄氏度的偏移。0x07 返回的物体温度在接近环境温度时容易受热噪声影响这是热电堆传感器的物理特性不是 SMBus 的时序问题。校验时不要把 TObj 直接与水银温度计做绝对值比对先看 0x06 的 TA 是否符合室温。换算代码可以写得更直白float mlx90614_ta(void) { uint16_t raw 0; if (mlx90614_read_raw(MLX90614_TA_REG, raw) ! 0) { return -999.0f; } return (float)raw * 0.02f - 273.15f; }-999作为错误标记是嵌入式里的常见做法保证上层不会把上一次的正常值当成当前值。0.02f对应 1 LSB 就是 0.02℃低位寄存器越小时这个浮点乘法的舍入误差约 0.005℃可以忽略。如果目标 MCU 是 Cortex-M0建议把这个函数里的浮点运算全部改成定点raw * 2 / 100 - 27315 / 100不过要看清哪里需要保留小数位。3.2 带 PEC 的读取软件 CRC-8 实现MLX90614 支持在数据后追加一个 PEC 字节由 SMBus 规范规定的 CRC-8 计算得到。和 STM32 硬件 I2C 自动生成 PEC 不同软件方式读起来更直观也更容易在逻辑分析仪上核对。计算多项式是经典的x^8 x^2 x 1也就是十六进制0x07初始值为 0。uint8_t smbus_crc8(const uint8_t *data, uint8_t len) { uint8_t crc 0x00; uint8_t i, bit; for (i 0; i len; i) { uint8_t byte data[i]; for (bit 0; bit 8; bit) { uint8_t msb (crc 0x80) 7; crc 1; if (byte 0x80) { crc | 1; } byte 1; if (msb) { crc ^ 0x07; } } } return crc; }这段是按位计算的不需要查表可读性比查表版高很多。crc初始值为 0x00注意不是 0xFF这是 SMBus 和普通 CRC-8 的区别多项式 0x07 不包含最高位因为最高位参与移位时已经被隐式消去。如果用在 F1 这类没有硬件 PEC 的老芯片上8 字节输入的计算量约几十微秒完全可以放在每次读取之后。使用它的完整读取函数如下#define MLX90614_ADDR_READ (MLX90614_ADDR | 0x01) uint8_t mlx90614_read_with_pec(uint8_t reg, uint16_t *raw) { uint8_t buf[3] {0, 0, 0}; uint8_t crc_input[5]; HAL_StatusTypeDef ret; ret HAL_I2C_Mem_Read(hi2c1, MLX90614_ADDR, reg, I2C_MEMADD_SIZE_8BIT, buf, 3, 50); if (ret ! HAL_OK) { return 1; } crc_input[0] MLX90614_ADDR; // 地址字节写方向 crc_input[1] reg; // 命令字节 crc_input[2] MLX90614_ADDR_READ; // 地址字节读方向 crc_input[3] buf[0]; // 数据低字节 crc_input[4] buf[1]; // 数据高字节 if (smbus_crc8(crc_input, 5) ! buf[2]) { return 2; // PEC 错误 } *raw (uint16_t)(buf[0] | (buf[1] 8)); return 0; }为什么读 3 个字节而不是读 2 个字节再自己补 CRC因为 MLX90614 只有在主站仍返回 ACK 时才会继续发送 PEC如果读满 2 字节后就 NACK 并产生 STOP从机不会有机会发出 PEC。HAL_I2C_Mem_Read在请求 3 字节时会在最后一个字节即 buf[2] 接收完毕后发送 NACK这正是 SMBus 读取 PEC 需要的握手。CRC 输入顺序必须与总线上的字节顺序一致先写地址字节再命令字节再读地址字节之后才是数据低高字节。有人把地址算成 0x5A 而不是 0xB4导致 CRC 一直不通过这是最常见的低级错误。3.3 PEC 使能还是不使能取决于系统可靠性要求不是所有场景都需要打开 PEC。消费类原型和调试阶段可以不开省得每次排查多一个变量。但如果产品要做 EMC 认证或者传感器线缆要穿过电机驱动器附近PEC 必须保留。启用 PEC 后如果坚持一帧一读程序对错误的处理也简单读到2就丢弃本次数据不重试等下一帧。因为 MLX90614 的 ADC 在后台一直以约 8~16 ms 的周期刷新寄存器重试反而可能读到新旧交错的数据。如果项目里用的是 STM32H7 或 L4 的硬件 PECCubeMX 里把PECEnable打开后API 的返回值可能直接是HAL_ERROR也可能把 PEC 存入专用寄存器具体取决于你的 HAL 版本。我一般先不开硬件 PEC而是把软件 CRC 养熟再考虑用硬件 PEC 节省主循环时间。4. 实战中绕不开的 4 类故障NACK、死锁、数据跳动与读取慢跑通最小读函数只是第一步。真正让工程卡住的往往不是温度换算而是三种总线级表现地址阶段 ACK 一直没回来、某次读写后 SCL/SDA 被拉死、以及温度值每隔几分钟跳一个 3℃ 的毛刺。这一部分直接对着这三种现象给出定位手段最后把 SMBus 速率边界也一并说明。4.1 用逻辑分析仪抓 SMBus 时序先看重复起始是否存在调试 SMBus 最有效的工具不是调试器是逻辑分析仪。以常见的 16 通道逻辑分析仪为例采样率设 1 MHz触发方式选 I2C 的 START 条件即可。抓完一帧后直接按 I2C 协议解码把地址设为 0x5A。需要重点看三个位置第一命令字节后面紧跟的是 repeated start 还是 STOP。如果是 STOP说明读写函数仍然是用两次独立传输拼起来的MLX90614 不认这种格式。解决办法换成前面代码里的HAL_I2C_Mem_Read或者用寄存器级状态机保证不产生 STOP。第二ACK 位是否按时出现。正常的地址 ACK 会在 SCL 第 9 个时钟的低电平期间被拉低。如果 ACK 位一直为高说明从机没有响应问题大概率在地址宏或硬件上拉。第三数据字节的 MSB/LSB 顺序。HAL 读出的 buf[0] 是第一个字节也就是 SMBus 定义中的低位字节。如果按大端习惯把 buf[0] 左移 8 位得到的温度会完全错误表现为主机端读数忽高忽低而不是固定错误。用逻辑分析仪还能验证 PEC解码器不会自动算 SMBus 的 CRC但可以把抓到的字节手动喂给上一节的smbus_crc8在 Python 里直接算一遍和波形里的最后一字节比对。做到这一步总线协议层面的问题基本排除。4.2 NACK、总线挂死在启动阶段的检查清单如果启动后第一读就卡在HAL_I2C_Mem_Read超时返回按下面顺序查不要先怀疑传感器。从机地址MLX90614 的地址由内部 EEPROM 决定出厂默认 0x5A。某些模块预留了地址选择跳线如果改过程序里要对应变化。不要把 7 位地址直接填进 HAL 参数左移后是 0xB4。引脚是否开漏检查 CubeMX 生成的 GPIO 初始化。GPIO 输出类型为GPIO_MODE_AF_OD即复用开漏。如果被设成AF_PPSDA 高电平将由 MCU 驱动一旦总线上有其它设备拉低就会形成电流灌入。上拉电阻MLX90614 模块大多自带 10 kΩ这个值在 10 cm 短线内能工作但最好并联一个 4.7 kΩ 保证速率。电压域MLX90614 供电 3.3VSTM32 也是 3.3V 时必须确保两者的 VDD 相同。如果有 5V 的 Arduino 模块集成电平转换STM32 侧只接上拉不接 5V 就不会损坏。总线挂死还有一个特殊原因SMBus 从机在等到合法 STOP 前会一直保持 SCL 低电平来拉伸时钟。若初始化过程中发生了意外复位主机的控制器可能已经复位而从机还在等待数据结束。此时无论主机怎么发送 START总线上都是低电平。解决方法是给从机 9 个时钟脉冲的恢复序列代码可以是 GPIO 模拟也可以直接断电重启。如果板子已经大量生产最好在应用启动时对 SMBus 总线执行一次恢复序列这是我量产前习惯加的一段动作。void smbus_recover(void) { // 这里以 PB6SCL、PB7SDA 为例实际用 CubeMX 生成的引脚宏替换 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET); // SDA 释放为高 for (uint8_t i 0; i 9; i) { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_RESET); HAL_Delay(1); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET); HAL_Delay(1); } // 补一个 STOP 条件SCL1 时 SDA 从低变高 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET); HAL_Delay(1); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET); HAL_Delay(1); MX_I2C1_Init(); }这段恢复代码的原理是从机在 SCL 连续收到 9 个边沿后会放弃当前未完成的事务最后补一个 STOP 条件让它完成状态机复位。HAL_Delay(1)在 100 kHz 下是相当长的等待能保证从机内部的 SMBus 超时计数器走到一个稳定状态。调用完再执行MX_I2C1_Init让外设回到干净的 IDLE 状态。4.3 用 Free master 观察温度曲线确认不是“假数据”数据校验通过后如果还是觉得温度跳变不对建议在固件里把 raw 值和换算后的温度通过串口以 2~5 Hz 的频率下发上位机用 NXP FreeMASTER 的 Oscilloscope示波器查看。FreeMASTER 的 Float 变量波形比串口打印直观得多也更容易发现周期性毛刺。使用 FreeMASTER 示波器观察时把 TA 和 TObj 两个变量同时加入记录器采样周期设 100 ms 足够发现大多数噪声。观察到的现象有三种TObj 在环境温度附近无规律跳 1~2℃说明传感器靠近发热体或被阳光直射TObj 每隔固定 N 帧跳一次说明触发了 EEPROM 擦写或者 ADC 滤波系数更新需要查读寄存器以外的命令TObj 和 TA 一起同步漂移则要考虑 MLX90614 的供电电压波动。这里要特别说明FreeMASTER 只负责把变量可视化它本身不会修正 SMBus 异常。当曲线出现“断崖式下落”时回到逻辑分析仪抓原始总线大概率能看到一帧长度异常的波形。把 FreeMASTER 当作观察工具不要把它当成调试借口。4.4 速率边界MLX90614 只能到 100 kHz别学普通 I2C 开 400 kHzMLX90614 的 SMBus 接口在数据手册中标注的 SCL 时钟范围是 10~100 kHz。很多工程师从 OLED 驱动那边习惯性把 I2C 设为 400 kHz然后发现偶尔返回HAL_ERROR。实际上这不是 MCU 性能问题而是芯片内部的施密特触发器在 400 kHz 下没有足够的建立时间。把时钟改成 100 kHz 后几乎能连续读取上千次不出错。在代码里这对应hi2c1.Init.ClockSpeed 100000。如果确实需要更高刷新率不应该提高时钟而应该减少读取次数。MLX90614 内部 ADC 等效转换时间约 8 msSMBus 读一帧命令数据PEC在 100 kHz 下约消耗 0.36 ms所以 100 Hz 刷新率完全够用。再往上提高时钟没有意义ADC 数据率已经限制了信息量。如果项目点名要求“更快”那就改用 MLX90614 的 PWM 输出模式把温度编码成占空比信号用 STM32 定时器输入捕获来解调。这时候 SMBus 只用于配置 PWM 输出格式读取由定时器完成系统延迟能从 SMBus 的毫秒级降到微秒级这是另一个独立分支。5. 让 SMBus 主站更耐用的三个进阶点多从机、滤波与 bootloader 配合5.1 挂多片 MLX90614改从机地址要按写 EEPROM 流程来一片 90614 用 0x5A两片就冲突。要挂第二片需要在出厂默认地址下向 EEPROM 地址 0x00自定地址写入新地址。写 EEPROM 时 SMBus 命令字节是 0x20数据为两个字节低字节为地址高字节固定 0x00最后跟 PEC。写完等 5 ms 让 EEPROM 完成烧写再把全新目标地址的器件挂到总线上。使用前必须先把模块断电再上电否则器件仍按旧地址响应。这个流程和 PWM 模式配合可以实现一块 MCU 读多路 SMBus 温度传感器但每条总线的电容通常在 400 pF 以内挂多片时把上拉调到 2.2 kΩ。5.2 软件滤波EMA 比动内部滤波器系数更好调MLX90614 内部有数字滤波器系数但调整它们要写 EEPROM拆装壳体后校准时容易出问题。在批量产品里我更依赖主机侧做滤波因为改代码比改校准文件容易。常用的是一阶 EMA 滤波#define EMA_ALPHA 0.2F static float temp_filtered 0.0F; temp_filtered (1.0F - EMA_ALPHA) * temp_filtered EMA_ALPHA * new_temp;EMA_ALPHA取 0.2对约 8 ms 更新一次的 ADC 来说等效时间常数约为 40 ms既能滤掉电源纹波引起的跳变又不会让手靠近传感器的反应显得迟钝。如果读数仍有毛刺可以再在 EMA 之前加一个 3 次中值滤波代码逻辑简单延迟只增加两帧。5.3 bootloader 跳转前复位 I2C 外设杜绝第二次初始化失败量产件往往先跑 IAP bootloader再跳转进 App。如果 bootloader 里初始化过 I2C/SMBus 来读取温度跳转前没有 DeInit 外设App 的HAL_I2C_Init可能看到 BUSY 标志从而卡死。跳转函数里需要加三行HAL_I2C_DeInit(hi2c1); HAL_GPIO_DeInit(GPIOB, GPIO_PIN_6 | GPIO_PIN_7); HAL_Init(); /* 然后执行 App 跳转 */HAL_I2C_DeInit会把 I2C 的 CR1 清零释放 SCL/SDAHAL_GPIO_DeInit把引脚恢复成模拟输入避免两边争用。如果 SMBus 从机当时正在响应总线可以先调用smbus_recover()再 DeInit。本文还有配套的精品资源点击获取
返回列表