news 2026/10/2 1:11:56

Linux下ST7102电源芯片驱动移植:从I2C枚举到regulator框架实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux下ST7102电源芯片驱动移植:从I2C枚举到regulator框架实战

ST7102驱动移植与调试,说的是把一颗电源管理芯片纳入内核电源管理框架这件事。我这次是在一块ARM Linux主控板上做的,主控跑的内核版本是5.10,板上用ST7102给无线模组供电,要求这颗芯片不仅能输出电压,还必须由系统按需开关、按负载调压,并且和休眠唤醒联动。这个需求看着简单,实际涉及I2C总线枚举、设备树匹配、regulator子系统和硬件时序验证,整个过程从零开始,前后花了三天,踩的坑基本都是“看起来驱动跑起来了,实际上没对”。

如果你的工作也涉及到“给某个小电源芯片写驱动”“调试I2C设备枚举不到”“regulator框架下电压不对劲”这类问题,这篇内容应该能给你一些参考。我尽量把方案选型、设备树写法、驱动代码结构、调试工具用法和常见问题都串起来写,而不是只丢一个dts片段就完事。

1. 项目背景与方案选型:为什么ST7102需要单独做驱动

1.1 需求从哪里来:一个电源轨要能被内核管起来

先说场景。板子上有一颗ST7102电源芯片,早期设计是直接把它当成固定电源用,EN引脚拉高,上电就有3.3V输出,给某个外设模组长期供电。但这个外设模组待机时功耗不小,产品要做低功耗,就必须让这颗芯片在系统休眠时断电,唤醒后再恢复供电;同时不同工作模式下,模组对电压的要求还不一样,低功耗模式想降一点电压,全速模式又需要升回3.3V。

这就不是简单地找个GPIO拉一下EN能解决的事了。虽然GPIO控制开关是完全可行的,但要动态调压、要精确控制上电时序、要跟内核的电源管理机制联动,最合理的做法还是要写一个符合内核regulator框架的驱动,让ST7102变成系统里一个“标准受控电源轨”。

所以这个项目表面上是“移植驱动”,本质上是在做电源管理方案重构:把一个固定上电的LDO变成内核可见、可调、可联动休眠唤醒的电源节点。

1.2 三种方案对比:为什么选regulator框架

我一开始也犹豫过,要不要先拿GPIO硬控顶着用。当时列了三个方案:

方案实现思路优点缺点结论
GPIO硬控EN引脚接GPIO,直接拉高拉低代码量少,调试方便无法调压、无法读状态、和内核电源管理脱钩临时验证可以,产品化不行
用户态I2C控制在脚本里读写/dev/i2c-x,操作寄存器修改灵活,调试最快并发控制差、重启失效、容易踩坏其他设备只适合初期摸底
内核regulator驱动实现regulator_ops,注册到内核regulator子系统支持引用计数、动态调压、休眠联动、debugfs可见代码量略大,涉及设备树和框架学习成本最终选择

选择regulator框架还有一个很实际的原因:同一个电源轨可能有两个以上的使用者。比如无线模组要3.3V,板上另一颗传感器也要从同一路电源取电。如果直接在驱动里“打开就开、关就关”,很容易出现A设备还在工作,B设备已经把电断了的冲突。regulator框架天生带引用计数,最后一个使用者释放时才真正关断,第一个使用者申请时才真正打开,这种“共管”需求是GPIO方案很难做好的。

1.3 需要提前确定的信息

写驱动之前,有几样东西必须先落实,否则后面会反复返工:

  • ST7102的手册,尤其是寄存器定义、输出电压编码表、使能位、状态位。
  • I2C从机地址,以及地址引脚是否需要配置,不能假设默认值。
  • 芯片封装和负载能力,确定它到底是线性调整器还是DC-DC,输出电流够不够用。
  • 内核里regulator子系统的版本对应关系,老内核和新内核的of_regulator解析方式略有差异。

这些信息没理清之前,直接抄一个通用regulator驱动模板是没有意义的。每个芯片的位定义都不一样,同一套框架代码能跑通,但寄存器控制不对,输出就是不对。

2. 硬件连接与最小系统排查:软件调不通先找硬件的茬

2.1 用到的关键引脚与典型连接

ST7102这类小封装电源芯片,引脚不算多,常见的是SOT-23-5或者SOT-23-6,功能大致是IN、OUT、EN、GND,再加上I2C控制脚(SCL、SDA)和地址配置脚。具体引脚定义以手册为准,但连接思路是通用的:

  • IN引脚接输入电源,附近放一个10uF左右的输入电容,再并联一个0.1uF高频去耦电容,电容要尽量靠近引脚。
  • OUT引脚接负载,输出电容我用的10uF+1uF组合,低ESR的MLCC即可。
  • EN引脚如果没有内部上拉,必须外部处理好,不能悬空。如果打算完全用I2C寄存器控制开关,EN直接接高电平;如果还想保留硬件级别的切断能力,可以接GPIO,由软件在寄存器操作之外再拉一下。
  • SCL、SDA需要外部上拉,上拉电阻常见选2.2k到4.7k,接到对应I2C电源域。
  • 地址配置引脚,如果芯片有这个脚,务必按手册接固定电平,悬空可能导致扫描地址漂移。

我这次调试时遇到的一个很典型的硬件问题就是地址脚悬空,后面会展开讲。

2.2 上电前的PCB排查清单

硬件工程师如果已经把板子画好焊好了,软件拿到板子后别急着烧系统,先做一轮基础检查,能省去大量“软件查了半天结果是硬件问题”的时间:

  • 万用表量ST7102的VIN和GND之间电压是否正常,注意要在芯片引脚端量,不要只量电源入口。
  • 确认EN引脚不是悬空,如果固定接高,量到高电平;如果接GPIO,确认GPIO默认状态不会把电源意外打开或关闭。
  • 量SCL、SDA是否都被上拉到高电平,如果某一根被拉低,I2C总线大概率是被某个设备卡住了。
  • 确认I2C总线上没有地址冲突。同一个I2C总线上如果有两个设备地址重叠,两个芯片会同时应答,通信就会乱。
  • 确认负载电流不超过芯片额定值。LDO类芯片过流后输出电压会掉得很厉害,RDS(on)大的时候甚至直接进入保护。

这些听着基础,但项目现场最容易翻车的就是“电源芯片焊接虚焊”“输出电容贴反”“EN浮空导致芯片状态随机”,这类问题查起来比驱动代码麻烦得多。

2.3 拿到手册后先做一张寄存器表

我在写驱动之前习惯先做一张“寄存器速查表”,把ST7102手册里的关键寄存器、默认值、控制位含义整理出来。以常见实现为例,大概长这样:

| 寄存器名 | 地址 | bit7 | bit6 | bit5-0 | 默认值 | 用途 | | --- | --- | --- | --- | --- | --- | | VOUT | 0x00 | — | — | 输出电压档位 | 0x0F | 调压 | | CTRL | 0x01 | EN | MODE | 保留 | 0x00 | 使能/模式 |

不光是给自己看,后面调电压、查问题、跟硬件同事沟通,都靠这张表。芯片手册的位定义一定要逐个对着看,尤其是使能位,我见过有的芯片是高有效,有的是低有效,还有的是“写1才翻转”,搞错一个bit,整个enable逻辑就是反的。

3. 内核侧驱动移植:设备树、regulator ops和加载方式

3.1 设备树节点怎么写才算对

设备树的作用是告诉内核“I2C2总线上挂着一颗ST7102,它的工作电压范围是多少,初始是否开启”。以ST7102挂到i2c2为例,一个最简的设备树节点是这样的:

&i2c2 { status = "okay"; clock-frequency = <400000>; st7102: st7102@2e { compatible = "vendor,st7102"; reg = <0x2e>; regulators { vdd_xxx_ldo: ldo { regulator-name = "vdd_xxx"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <3300000>; }; }; }; };

这里有几个容易踩的细节:

  • compatible字符串必须和驱动代码里of_device_id写的一模一样,大小写或厂商前缀差一个字符,probe就不会被调用。
  • reg = <0x2e>是I2C从机地址,必须和硬件地址脚配置对应,不是随便填的。
  • regulator-min-microvolt和regulator-max-microvolt是后续调压的边界范围,不能只填最大值,否则consumer调用regulator_set_voltage时内核会做边界检查,超出范围直接返回错误。
  • 调试期间尽量不要加regulator-always-on和regulator-boot-on。加了以后regulator框架会强制保持开启,不利于验证“开/关”逻辑。如果明确要求上电即开,也要知道自己加了这行会有什么效果。

3.2 驱动核心:一个最小可用的ST7102驱动

驱动代码的核心不复杂,就是实现regulator_ops里那几个函数,然后通过I2C操作寄存器。下面是一个简化但完整的结构,实际项目和这个差别不会太大:

#include <linux/module.h> #include <linux/i2c.h> #include <linux/regmap.h> #include <linux/regulator/driver.h> #include <linux/regulator/of_regulator.h> #define ST7102_REG_VOUT 0x00 #define ST7102_REG_CTRL 0x01 #define ST7102_EN_BIT 0x80 static const struct regmap_config st7102_regmap_config = { .reg_bits = 8, .val_bits = 8, .max_register = 0x0F, }; static int st7102_enable(struct regulator_dev *rdev) { struct regmap *regmap = rdev_get_regmap(rdev); return regmap_update_bits(regmap, ST7102_REG_CTRL, ST7102_EN_BIT, ST7102_EN_BIT); } static int st7102_disable(struct regulator_dev *rdev) { struct regmap *regmap = rdev_get_regmap(rdev); return regmap_update_bits(regmap, ST7102_REG_CTRL, ST7102_EN_BIT, 0); } static int st7102_is_enabled(struct regulator_dev *rdev) { struct regmap *regmap = rdev_get_regmap(rdev); unsigned int val; int ret; ret = regmap_read(regmap, ST7102_REG_CTRL, &val); if (ret) return ret; return !!(val & ST7102_EN_BIT); } static int st7102_list_voltage(struct regulator_dev *rdev, unsigned int selector) { /* 假设selector 0对应1.8V,每个档位100mV */ if (selector > 15) return -EINVAL; return 1800000 + selector * 100000; } static int st7102_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV, unsigned int *selector) { struct regmap *regmap = rdev_get_regmap(rdev); unsigned int sel; int voltage; for (sel = 0; sel <= 15; sel++) { voltage = st7102_list_voltage(rdev, sel); if (voltage >= min_uV && voltage <= max_uV) break; } if (sel > 15) return -EINVAL; regmap_update_bits(regmap, ST7102_REG_VOUT, 0x0F, sel); if (selector) *selector = sel; return 0; } static const struct regulator_ops st7102_regulator_ops = { .enable = st7102_enable, .disable = st7102_disable, .is_enabled = st7102_is_enabled, .list_voltage = st7102_list_voltage, .set_voltage = st7102_set_voltage, }; static const struct regulator_desc st7102_reg_desc = { .name = "st7102_ldo", .ops = &st7102_regulator_ops, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, .n_voltages = 16, }; static int st7102_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct regulator_config config = { .dev = dev }; struct regmap *regmap; struct regulator_dev *rdev; regmap = devm_regmap_init_i2c(client, &st7102_regmap_config); if (IS_ERR(regmap)) return PTR_ERR(regmap); rdev = devm_regulator_register(dev, &st7102_reg_desc, &config); if (IS_ERR(rdev)) return PTR_ERR(rdev); return 0; } static const struct of_device_id st7102_of_match[] = { { .compatible = "vendor,st7102" }, {} }; MODULE_DEVICE_TABLE(of, st7102_of_match); static const struct i2c_device_id st7102_i2c_id[] = { { "st7102", 0 }, {} }; MODULE_DEVICE_TABLE(i2c, st7102_i2c_id); static struct i2c_driver st7102_i2c_driver = { .driver = { .name = "st7102", .of_match_table = st7102_of_match, }, .probe = st7102_probe, .id_table = st7102_i2c_id, }; module_i2c_driver(st7102_i2c_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("ST7102 regulator driver");

几个值得说的点:

  • 用regmap而不是直接调i2c_smbus_read_byte_data/write_byte_data,好处是内核帮你做了缓存、位操作和一部分错误处理,代码也更简洁。前提是Kconfig里要select REGMAP_I2C。
  • devm_regulator_register和devm_regmap_init_i2c都是devm接口,probe失败或驱动卸载时资源自动释放,不用手动清理。
  • set_voltage里的循环写法是逐档遍历,找到第一个在[min_uV, max_uV]范围内的电压档位。实际工程中更合理的做法是先算好selector再写寄存器,避免每次调压都慢吞吞遍历。
  • 这个代码里没有处理 suspend/resume,后面调试到休眠唤醒问题时会发现少了它不行。

3.3 Kconfig和Makefile让驱动能编进去

驱动的Kconfig可以写成这样:

config REGULATOR_ST7102 tristate "ST7102 regulator driver" depends on I2C select REGMAP_I2C help Support for ST7102 voltage regulator.

Makefile里面加一行:

obj-$(CONFIG_REGULATOR_ST7102) += st7102.o

编译时有两种方式:直接编进内核,或者编成模块。推荐调试阶段编成模块,方便反复改代码后单独加载;确认稳定后再改成内建。如果编进内核,要确保CONFIG_REGULATOR_ST7102=y,并且CONFIG_REGULATOR本身是打开的。

4. 调试环境搭建:串口日志、I2C工具和示波器三板斧

4.1 内核日志先要能看到,不然一切白搭

驱动调试第一步不是拿示波器,是确认内核日志能出来。我一般会在调试期间把printk等级调低:

echo 8 > /proc/sys/kernel/printk

这样dev_dbg之外的大部分日志都会直接打到串口上。如果用了dev_dbg,内核默认不开,可以把驱动里的dev_dbg临时改成dev_info,也可以打开动态调试再单独开关。动态调试的开关方式:

echo "file drivers/regulator/st7102.c +p" > /sys/kernel/debug/dynamic_debug/control

前提是内核打开了CONFIG_DYNAMIC_DEBUG和CONFIG_DEBUG_FS。串口调试助手这边,波特率一般115200,8位数据、1位停止、无校验,接好GND后就能看到内核启动日志和驱动的dev_info输出。

4.2 i2cdetect、i2cget、i2cset是排查I2C问题的标准工具

i2c-tools是嵌入式Linux下排查I2C设备最常用的工具集。在调试ST7102时,我常用的命令就这几个:

# 查看系统里有哪些I2C总线 i2cdetect -l # 扫描I2C2总线上的设备地址 i2cdetect -y 2 # 读取设备地址为0x2e的0x00寄存器 i2cget -y 2 0x2e 0x00 # 写一个字节到0x01寄存器 i2cset -y 2 0x2e 0x01 0x80 # 直接dump整块寄存器 i2cdump -y 2 0x2e

使用时要特别注意:i2cdetect -y会对总线上所有地址发送探测读操作,某些对未知I2C操作反应异常的设备可能会因此改变状态。工业现场I2C总线上如果还挂了其他重要芯片,先看一下总线上有哪些设备再扫,不要无脑执行。

还有一个很实用的调试技巧:在驱动还没写之前,先用i2cset手动把CTRL寄存器的EN位置1,然后测量芯片VOUT引脚有没有电压输出。这一步能验证硬件通路和寄存器定义是否正确,相当于把“芯片能不能被I2C控制”和“驱动代码有没有bug”两个问题拆开了。

4.3 动态电压和时序还得靠示波器

万用表量稳态电压没问题,但ST7102在负载跳变或者系统休眠唤醒瞬间的表现,必须用示波器看。尤其要注意这几个信号:

  • EN引脚的上升沿和下降沿是否干净,有没有抖动或慢爬升。
  • VOUT在上电瞬间的建立时间,正常应该在芯片手册规定的范围内。
  • 负载从轻载切到重载时,VOUT有没有瞬间跌落到负载要求的阈值以下。
  • I2C的SCL、SDA时序,特别是频率是否匹配,上拉电阻是否足够小,波形上升沿有没有太缓。

示波器探头记得打到10x档,带宽限制先打开,很多电源噪声是高频干扰,不开带宽限制容易看到一团毛刺。测量点尽量贴近芯片引脚,不要夹在长长的飞线上。

5. 实战调试全过程:从I2C枚举不到到动态调压生效

5.1 第一阶段:i2cdetect根本扫不到0x2e

第一次上电后的现象很直接:i2cdetect -y 2打印的地址列表里根本没有0x2e,全是--。这个阶段我一般不做任何软件怀疑,直接拿万用表量硬件。

排查过程是这样的:先量ST7102的VIN引脚,3.3V正常;量EN引脚,高电平,正常;量SCL和SDA引脚,发现SDA电平只有1.1V左右,明显不是正常的高电平。继续查,发现这颗芯片的地址配置脚ADDR悬空,导致芯片内部地址选择逻辑不稳定。手册上明确要求ADDR接GND或者接VCC,悬空会导致I2C从机地址漂移,芯片会随机应答或者干脆不应答。

把ADDR脚按手册接到GND后,i2cdetect -y 2立刻能看到0x2e。这个问题的教训是:拿到板子先核对手册里所有“必须固定电平”的引脚,不要想当然认为悬空等于默认。

5.2 第二阶段:设备树写好了,probe却没有执行

I2C枚举成功后,我在设备树里加了ST7102节点,编译烧录后重启,发现驱动没有加载,/sys/kernel/debug/regulator/regulator_summary里也看不到ST7102。

排查思路是这样一层层来的:

  • 先确认dtb真的更新了,用strings或者直接看/sys/firmware/devicetree/base里的节点是否存在。当时我发现设备树源码改了,但烧录脚本刷的是旧的dtb,属于低级错误。
  • 然后确认compatible是否匹配,查看/sys/bus/i2c/devices/2-002e/name显示的是不是预期名字。如果不匹配,驱动不会bind。
  • 最后确认内核配置有没有打开,编译内核对CONFIG_REGULATOR_ST7102是y还是空。

这个阶段的经验是:probe没调用的时候,不要急着往驱动里加打印,先一层层把设备树、配置、驱动匹配关系查清楚。probe不执行的大部分原因不是驱动代码问题,而是系统根本没走到匹配那一步。

5.3 第三阶段:probe执行了,电压输出却是默认值

probe成功、regulator节点也注册了,但实测VOUT输出始终是芯片默认电压,不是我在设备树里配的期望电压。这里牵扯到regulator框架的一个关键机制:probe成功不代表驱动会立刻去设置电压。

regulator框架的电压输出,是在有consumer真正使用它时才会被“消费”并生效。也就是说,如果没有任何驱动调用regulator_get、regulator_enable、regulator_set_voltage,这颗芯片的输出电压大概率还是硬件默认值。调试阶段验证驱动是否正常,可以用用户态接口直接操作:

# 查看regulator状态 cat /sys/kernel/debug/regulator/regulator_summary # 手动打开 echo 1 > /sys/class/regulator/regulator.X/device/...

更直接的办法是先写一个简单的consumer驱动,或者在probe函数里用regulator_get之后主动设置一次电压,这样就能确认驱动写的寄存器是否真的改变了输出。当时就是用i2cget读寄存器的值和实测VOUT做对比,发现VOUT寄存器被写成了期望值,但芯片输出还是不对,后来才意识到我写寄存器用的位域和芯片手册定义对不上,使能位搞错了一位,改过来之后输出立刻正常。

5.4 第四阶段:休眠唤醒后电源回不来

动态调压和开关测试都过了,一测系统休眠唤醒就露馅。现象是:系统suspend后,ST7102的输出被关了,唤醒后外设一直不上电,regulator_summary里ST7102的enable_count是0。

原因在于regulator框架在系统进入suspend时,会主动把regulator按设备树里配置的suspend状态关掉,我的驱动没有实现set_suspend_enable或者配置regulator-suspend-microvolt,所以唤醒后不会自动按照预期恢复。

解决方式有几个,看产品需求:

  • 如果要求唤醒后恢复到默认开启状态,配置设备树时把regulator-boot-on加上,同时驱动里实现挂起恢复时的状态重写逻辑。
  • 如果要求休眠时关断、唤醒时打开,需要在regulator_desc里实现set_suspend_enable/set_suspend_disable,并在suspend/resume的阶段做对应寄存器操作。
  • 如果外设允许在系统休眠期间保持供电,可以在设备树里用regulator-state-mem配置成开启,这样内核suspend时就不会关它。

这一步接线比较绕,我的做法是在驱动里加一组suspend和resume回调,用debugfs的日志确认suspend前后寄存器状态的变化,再根据实际输出调整策略。

5.5 动态调压真正用起来:把电压档位对应到工作模式

ST7102驱动调通后,动态调压的实际场景是给无线模组的功耗优化。无线模组在低功耗模式下把电压降到1.9V,全速发射时升到3.3V,这一个切换动作如果由用户态每秒钟操作一次I2C,稳定性和实时性都不可控。正确做法是让模组驱动去获取ST7102 regulator句柄并调用内核API:

struct regulator *vdd = regulator_get(&pdev->dev, "vdd-xxx"); if (IS_ERR(vdd)) return PTR_ERR(vdd); regulator_set_voltage(vdd, 1900000, 1900000); regulator_enable(vdd); // 业务切换后 regulator_set_voltage(vdd, 3300000, 3300000);

调用regulator_set_voltage后会直接走到ST7102驱动的set_voltage回调里,内核还会检查电压范围和consumer数量,比手动操作寄存器安全得多。这里要特别注意:电压切换不能无限快,LDO输出电容充放电需要时间,一般要留5到10毫秒的稳定时间,否则下一个模块上电时会看到电压还在爬升。

6. 常见问题与排查技巧实录

6.1 一张速查表解决重复性问题

调试过程中遇到的所有问题,最后基本都可以归到下面这几类:

现象常见原因排查手段
i2cdetect扫不到地址供电异常、上拉电阻问题、ADDR引脚悬空、总线冲突万用表量VIN/EN/SCL/SDA,查手册确认地址脚
probe不执行compatible不匹配、设备树没烧进去、内核配置没开查 /sys/bus/i2c/devices/2-002e/name,确认dtb和config
输出电压一直是默认值寄存器位定义错、使能位没写、consumer没获取regulatori2cget读寄存器、量VOUT、debugfs看enable_count
负载稍大电压就掉输出电容不足、负载超额定、散热焊盘虚焊示波器看VOUT跌落,加电容或换封装更大的芯片
休眠唤醒后无法恢复缺少suspend/resume逻辑、设备树没配suspend状态看regulator_summary,在驱动里实现suspend回写
I2C读出来全是0xFF芯片没上电、I2C地址不对、芯片损坏量供电、用i2cset读其他寄存器确认通信链路

6.2 驱动调试里几个值得记下来的习惯

第一,拿到新芯片先“裸跑”I2C,不要一上来就写完整驱动。用i2c-tools手动读写一遍关键寄存器,确认芯片能被控制、寄存器map和手册一致,再开始写代码。这个步骤能筛掉一大半硬件问题和手册理解问题。

第二,调试阶段所有的寄存器读写都要留日志。我在ST7102驱动里临时加过一个宏,每次写寄存器都把地址、原始值、新值打印出来,对比芯片手册很快就能发现问题。调完再统一删掉,不要长期保留。

第三,regulator框架的“不做事”也是一种状态。probe成功后没有consumer申请电源,电压就不会按你想的来。遇到这种现象,先查regulator_summary,再看有没有其他设备绑定到这个regulator上,最后再怀疑驱动逻辑。

第四,示波器和万用表结论不一致时,以示波器为准。万用表只能看到平均电压,瞬态跌落和纹波是看不到的。我在ST7102调试时遇到过万用表测3.3V很稳,但无线模组一发射就重启的怪问题,最后就是输出瞬间压降过大导致的。

6.3 如果条件允许,给这个项目做一个最小验证板

如果你不是只改一次板卡,而是要把ST7102用到多个产品上,建议画一块最小验证板,把ST7102单独引出去,用独立的I2C总线挂在主控上。这样做的好处是调试时不会影响到主板上其他I2C设备,也方便用飞线接电子负载或示波器。我当时没有这个条件,直接在主板上调,好几次I2C扫描疑似影响到同总线上的传感器,排查起来要多花一倍时间。

这个项目做下来,我个人最大的体会是:电源芯片的驱动,代码本身不难,难在“你信不信你写的每一个寄存器操作”。ST7102这种芯片,寄存器就那么几个,驱动也就几百行,但每一个bit错了,输出就是不对,而且表现形式会特别隐蔽,可能是系统休眠后回不来,也可能是动态调压时电压跳变瞬间掉电。我建议所有做这类移植的人,先把硬件通路和寄存器map手工验证一遍,再谈写代码,顺序反了,后面全是坑。

最后再分享一个小技巧:每次改完设备树或者驱动,第一时间把regulator_summary和i2cdump的结果存到日志文件里,和源码改动放同一份提交记录。这样出了问题,翻提交记录就知道哪次改动动了寄存器,不然你永远在猜是软件问题还是硬件问题。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/2 1:11:32

F28335 ePWM基础配置实战:电机控制的硬件时序根基

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/2 1:11:30

基于RNN与LSTM的航班延误预测:从论文到工程落地

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/2 1:11:17

从50nA休眠电流看低功耗MCU设计:芯片与系统级优化

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/2 1:11:02

Beta分布:从直觉到实战,用贝叶斯更新量化不确定性

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/2 1:10:41

RS-LiDAR-16 ROS快速上手:从网线直连到rviz稳定显示点云

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/2 1:10:29

Linux运维面试题:从命令到故障决策的真实能力图谱

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华