实战指南:SGM58031 ADC从寄存器到驱动的全流程调试(含Linux 6.1适配、编译报错、I2C超时与寄存器精准读取)

电子说

1.4w人已加入

描述

SGM58031是一款低功耗、高精度的16位Σ-Δ型I2C接口ADC芯片,广泛应用于工业采集、消费电子、医疗设备等场景。在Linux 6.1系统下适配该芯片驱动时,常遇到“iio_trigger_alloc ID错误”“remove函数返回值不兼容”“I2C驱动超时”“i2ctool读取值跳变不真实”“ADC采样值无变化”等问题。本文结合实战调试经验,从芯片基础特性、Linux 6.1驱动适配、编译排错、设备树配置、驱动节点操作、寄存器读取、实战调试维度,系统讲解SGM58031的全流程调试方法。

adc

一、SGM58031芯片基础特性

1.1 通道特性

adc

SGM58031支持单端/差分输入模式,核心输入通道配置如下:

•差分通道:AIN0(+)-AIN1(-)、AIN0(+)-AIN3(-)、AIN1(+)-AIN3(-)、AIN2(+)-AIN3(-),适用于高精度小信号采集;

•单端通道:AIN0-GND、AIN1-GND、AIN2-GND、AIN3-GND,参考地为芯片GND;

•输入范围:由内部PGA(可编程增益放大器)和参考电压共同决定,具体见下表。

1.2 增益配置(与数据手册一致)

SGM58031内置可编程增益放大器(PGA),增益档位由CONFIG寄存器的PGA位段配置,核心档位与满量程(FS)对应关系如下(基于内部参考电压):

PGA Setting 对应量程(FS, V) 适用场景
2/3 ±6.144V 大信号采集(最大输入范围)
1 ±4.096V 大信号采集
2 ±2.048V 中等信号采集
4 ±1.024V 小信号采集
8 ±0.512V 微伏级小信号高精度采集
16 ±0.256V 超小信号高精度采集

注:模拟输入电压不得超过满量程限制,否则会导致ADC饱和,采样值固定为±32767。

1.3 核心寄存器说明

SGM58031的寄存器地址宽度为8位,数据宽度为16位,核心寄存器功能如下:

寄存器地址 名称 核心功能
0x00 CONV_REG 存储最新16位ADC转换结果,只读
0x01 CONFIG_REG 配置采样模式、增益、采样率、通道选择、转换模式(单次/连续),读写
0x02 LOW_THRESH 低阈值寄存器,用于比较器功能配置
0x03 HIGH_THRESH 高阈值寄存器,用于比较器功能配置
0x04 CONFIG2 扩展配置寄存器,配置参考电压、休眠模式等
0x05 ID_REG 芯片ID寄存器,固定值0x5803(只读,用于识别芯片)

二、Linux 6.1驱动适配篇:核心API兼容修改

Linux 6.1内核对IIO子系统的部分API做了调整,SGM58031驱动需针对性修改以适配,核心修改点包含以下两项:

2.1 iio_trigger_alloc ID参数错误修复

问题现象

编译过程中出现类似报错:

 

error: invalid argument type for iio_trigger_alloc, expected device id type but got xxx

 

报错原因是Linux 6.1对devm_iio_trigger_alloc的dev%d参数要求使用iio_device_id(indio_dev)获取合法设备ID,而非自定义数值。

修复代码

 

// 仅修改ID参数,保留原有逻辑data->trig = devm_iio_trigger_alloc(dev, "%s-dev%d",    indio_dev->name,    iio_device_id(indio_dev));

 

2.2 remove函数返回值调整为void类型

问题现象

编译时出现返回值不兼容报错:

 

error: conflicting types for 'sgm58031_remove'; have 'int(struct i2c_client *)' but expected 'void(struct i2c_client *)'

 

修复代码

 

static void sgm58031_remove(struct i2c_client *client){dev_info(&client->dev, "SGM58031 driver unloadedn");}

 

配套修改:驱动注册结构体

 

static struct i2c_driver sgm58031_driver = {    .driver = {        .name = "sgm58031",        .of_match_table = sgm58031_of_match,    },    .probe = sgm58031_probe,    .remove = sgm58031_remove, // 匹配void类型的remove函数    .id_table = sgm58031_id,};module_i2c_driver(sgm58031_driver);

 

三、编译篇:驱动编译报错排查与解决

3.1 常见编译报错类型及解决思路

1.函数指针类型不兼容:incompatible pointer type 'sgm58031_read_raw'

○解决方式:确保回调函数签名与IIO子系统要求一致。

2.Linux 6.1特有报错

○iio_trigger_alloc ID错误:使用iio_device_id(indio_dev)替换自定义ID参数;

○remove函数返回值不兼容:将remove函数调整为void类型,无返回值。

四、设备树篇:I2C与SGM58031配置详解

4.1 I2C控制器配置(以RK3588的i2c4为例)

 

&i2c4 {    status = "okay";                      pinctrl-names = "default";            pinctrl-0 = <&i2c8m2_xfer>;           clock-frequency = <100000>;       
    sgm58031@48 {        compatible = "sgmicro,sgm58031";          reg = <0x48>;                             default-gain = <0>;               // 默认增益1倍(对应±4.096V量程)        status = "okay";                      };};

 

4.2 引脚配置(pinctrl)与内部上拉

 

i2c8m2_xfer: i2c8m2-xfer {    rockchip,pins = <        <1 RK_PD6 9 &pcfg_pull_up_smt>,  // SCL引脚,开启内部上拉        <1 RK_PD7 9 &pcfg_pull_up_smt>   // SDA引脚,开启内部上拉    >;};

 

4.3 引脚占用检查

通过以下命令查看引脚复用状态,确认I2C引脚未被其他外设占用:

 

cat /sys/kernel/debug/gpio cat /sys/kernel/debug/pinctrl/pinctrl-rockchip-pinctrl/pinmux-pins

 

五、驱动节点操作与配置(sysfs接口)

SGM58031驱动适配Linux IIO子系统后,所有配置和采样操作均可通过/sys/bus/iio/devices/iio:device1/下的sysfs节点完成,以下是核心节点的操作方法:

5.1 查看设备基本信息

 

# 进入设备目录cd /sys/bus/iio/devices/iio:device1/# 查看设备名称(确认驱动匹配成功)cat name# 输出:sgm58031

 

5.2 读取ADC采样原始值

SGM58031提供4个差分通道和4个单端通道的原始采样值,读取方式如下:

 

# 读取差分通道0(AIN0-AIN1)原始值cat in_voltage0-voltage1_raw# 读取单端通道0(AIN0-GND)原始值cat in_voltage4_raw# 读取单端通道1(AIN1-GND)原始值cat in_voltage5_raw# 读取单端通道2(AIN2-GND)原始值cat in_voltage6_raw# 读取单端通道3(AIN3-GND)原始值cat in_voltage7_raw

 

注:原始值为16位有符号整数,需结合增益(scale)换算为实际电压。

5.3 配置增益(量程)

增益(scale)决定ADC的输入量程,操作方式如下:

 

# 查看支持的增益值(仅可写入这些值)cat in_voltage_scale_available# 输出:6144 4096 2048 1024 512 256(对应PGA设置2/3、1、2、4、8、16)# 配置单端通道0(AIN0-GND)的增益为±4.096V(对应值4096)echo 4096 > in_voltage4_scale# 验证配置结果cat in_voltage4_scale# 输出:4.096000000(驱动自动换算为mV/LSB)

 

注:所有通道的增益配置是全局生效的,修改任意通道的scale节点,所有通道的量程都会同步更新。

5.4 设置采样率

采样率决定ADC的转换速度,操作方式如下:

 

# 查看支持的采样率cat sampling_frequency_available# 输出:6 12 25 50 100 200 400 800 960(单位:SPS)# 设置采样率为100SPSecho 100 > sampling_frequency# 验证配置结果cat sampling_frequency# 输出:100

 

5.5 配置比较器功能

比较器用于触发中断,当采样值超出阈值时触发,操作方式如下:

 

# 设置比较器低阈值(十进制16位值)echo 128 > comp_low_thresh# 设置比较器高阈值(十进制16位值)echo 256 > comp_high_thresh# 配置比较器触发队列(0=1次超阈值触发,1=2次,2=4次,3=禁用)echo 0 > comp_queue

 

5.6 设置转换模式与特殊功能

 

# 设置转换模式:0=连续转换,1=单次转换echo 0 > conv_mode# 配置外部参考电压:0=内部参考,1=外部参考(需硬件支持)echo 0 > ext_ref# 配置断线检测(Burnout):0=关闭,1=开启(用于检测传感器断线)echo 0 > burnout# 配置I2C总线泄漏阻断:0=关闭,1=开启(提升总线稳定性)echo 0 > bus_leakage

 

六、寄存器篇:精准读取(内核打印 vs i2ctool)

6.1 i2ctool读取寄存器值跳变、不真实的核心原因

核心原因是SGM58031单个寄存器地址存储16位数据,但i2ctool默认按8位读取,拆分读取过程中易获取无效值;辅助原因是i2ctool无锁保护,与驱动并发读写时会读取到“半写”状态的值,进一步加剧数值跳变。

6.2 解决方案:内核驱动中打印寄存器值

6.2.1 内核读取函数(集成Linux 6.1适配,保留原有逻辑)

 

int readreg(struct iio_dev *indio_dev){    int val;    struct sgm58031_data *data = iio_priv(indio_dev);    u16 read_config = regmap_read(data->regmap, SGM58031_REG_CONFIG, &val);if (read_config < 0) {dev_err(&data->client->dev, "Failed to read config: %dn", read_config);return read_config;}    printk("xsc config: 0x%02xn", val);    read_config = regmap_read(data->regmap, 2, &val);if (read_config < 0) {dev_err(&data->client->dev, "Failed to read config: %dn", read_config);return read_config;}    printk("xsc 2: 0x%02xn", val);    read_config = regmap_read(data->regmap, 3, &val);if (read_config < 0) {dev_err(&data->client->dev, "Failed to read config: %dn", read_config);return read_config;}    printk("xsc 3: 0x%02xn", val);    read_config = regmap_read(data->regmap, 4, &val);if (read_config < 0) {dev_err(&data->client->dev, "Failed to read config: %dn", read_config);return read_config;}    printk("xsc 4: 0x%02xn", val);    read_config = regmap_read(data->regmap, 5, &val);if (read_config < 0) {dev_err(&data->client->dev, "Failed to read config: %dn", read_config);return read_config;}    printk("xsc 5: 0x%02xn", val);    read_config = regmap_read(data->regmap, 0, &val);if (read_config < 0) {dev_err(&data->client->dev, "Failed to read config: %dn", read_config);return read_config;}    printk("xsc 0: 0x%02xn", val);    return 0;}// probe函数中集成Linux 6.1的trigger修复static int sgm58031_probe(struct i2c_client *client,                          const struct i2c_device_id *id){    // 原有probe逻辑    struct iio_dev *indio_dev = devm_iio_device_alloc(&client->dev, sizeof(struct sgm58031_data));    struct sgm58031_data *data = iio_priv(indio_dev);
    // Linux 6.1 trigger修复    data->trig = devm_iio_trigger_alloc(&client->dev, "%s-dev%d",                                        indio_dev->name,                                        iio_device_id(indio_dev));    // 调用寄存器读取函数    readreg(indio_dev);    // 后续逻辑    return 0;}// void类型remove函数static void sgm58031_remove(struct i2c_client *client){dev_info(&client->dev, "SGM58031 driver unloadedn");}

 

6.2.2 查看内核打印日志

通过以下命令查看寄存器打印结果:

 

dmesg -w | grep "xsc"

 

输出示例:

 

[  650.016361] xsc config: 0x4298[  650.016400] xsc 2: 0x8000[  650.016409] xsc 3: 0x7fff[  650.016415] xsc 4: 0x00[  650.016422] xsc 5: 0x80

 

6.3 i2ctool与内核打印的核心对比

维度 i2ctool(i2cget) 内核printk + regmap_read
读取宽度 默认8位,易拆分/截断16位值 原生16位,匹配芯片架构
并发保护 无锁机制,数值易跳变 带mutex_lock互斥锁,读取原子化
适用场景 快速验证设备是否在线 精准调试寄存器配置

七、调试篇:从现象到根因(实战案例)

7.1 案例1:I2C驱动超时

核心原因与解决方式

1.外部无上拉电阻:在SDA、SCL引脚各添加4.7kΩ上拉电阻,连接至3.3V电源;

2.内部上拉未配置:在设备树pinctrl节点中配置&pcfg_pull_up_smt属性;

3.引脚占用:检查设备树中其他节点的pinctrl配置,修改或禁用占用I2C引脚的节点。

7.2 案例2:i2ctool读取值跳变

核心原因:SGM58031单个地址存储16位值,i2ctool默认按8位读取,拆分读取易获取无效值;

解决方案:使用内核函数readreg读取寄存器值,通过regmap_read获取完整16位值,避免数值跳变问题。

7.3 案例3:ADC采样值始终无变化

现象

驱动加载正常、I2C通讯无异常,但改变输入电压后,CONV_REG(0x00)的采样值始终固定,无任何变化。

排查过程

1.寄存器配置核查:读取CONFIG_REG确认通道选择、增益、转换模式(连续模式)配置正确;

2.时序分析:通过逻辑分析仪抓取I2C时序,确认寄存器读写时序、ADC转换时序符合数据手册要求;

3.硬件核查:最终发现ADC输入引脚未有效连接待测电压(焊接虚焊/杜邦线接触不良),导致ADC输入端无电平变化,采样值固定。

解决方式

1.检查ADC输入引脚焊接状态,重新焊接确保接触良好;

2.确认待测电压源正常输出,且通过合适的限流/分压电路连接至ADC输入引脚;

3.验证:调整待测电压,读取CONV_REG值随电压变化,确认问题解决。

注意事项

ADC采样值无变化是调试中高频问题,优先排查硬件连接(输入引脚、参考电压、GND),再核查寄存器配置和时序,避免过度聚焦软件问题而忽略基础硬件故障。

八、避坑指南:高频错误总结

1.Linux 6.1适配

○iio_trigger_alloc:采用iio_device_id(indio_dev)获取合法设备ID;

○remove函数:调整为void类型,无返回值。

2.I2C超时:按“外部上拉电阻→内部上拉配置→引脚占用”的顺序排查解决。

3.寄存器读取:放弃i2ctool裸操作,使用readreg函数读取,保证数据准确性。

4.设备树配置:确保I2C控制器、设备节点的status属性均配置为"okay",引脚配置与硬件实际接线一致。

5.ADC采样值无变化:优先核查输入引脚硬件连接,再排查寄存器配置和时序。

6.增益配置:严格遵循数据手册的PGA Setting与满量程对应关系,避免因量程不匹配导致采样饱和或精度不足。

7.sysfs操作:增益配置需写入in_voltage_scale_available中的合法值,采样率需匹配支持的SPS档位,避免配置不生效。

九、总结

SGM58031在Linux 6.1下的调试核心要点为:

1.内核API适配:仅修改iio_trigger_alloc参数和remove函数返回值,保留驱动核心逻辑;

2.硬件基础:掌握芯片通道、增益、寄存器特性,为调试提供理论依据;

3.驱动操作:通过sysfs节点完成采样读取、增益配置、采样率设置等操作,无需修改内核代码;

4.故障排查:按“硬件连接→I2C通讯→寄存器配置→软件逻辑”的顺序排查,优先解决基础硬件问题;

5.寄存器读取:通过内核驱动内的regmap_read接口读取,规避i2ctool的8位读取缺陷。

该调试思路可迁移至其他16位寄存器架构的I2C外设,为同类ADC芯片的驱动适配和调试提供参考。

审核编辑 黄宇

 

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分