Tool/software:
Hello Expert,
One of my top IPC customers is using ADS7128 to do voltage monitoring. While, the customer read voltage values through ADS7128 registers and always get different values. For example, they read voltage 0 for five times and get 3 different values. The customer has used multimeter to test voltage value as well and all testing results are the same.
#include "ads7128.h"
#include <linux/delay.h>
#include <linux/printk.h>
#include <linux/device.h>
#include <linux/sysfs.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/sort.h>
int ads7128_read_channel_analog(uint8_t channel);
static uint32_t actual_vref_mv = 5000; // 5V量程
static struct i2c_client *ads7128_client;
// ================= sysfs 接口定义 =================
static ssize_t channel_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int channel;
int value;
if (sscanf(attr->attr.name, "channel%d", &channel) != 1)
return -EINVAL;
value = ads7128_read_channel_analog(channel);
if (value < 0)
return value;
return sprintf(buf, "%d\n", value);
}
static ssize_t voltage_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int channel;
int value;
int millivolts;
if (sscanf(attr->attr.name, "voltage%d", &channel) != 1)
return -EINVAL;
value = ads7128_read_channel_analog(channel);
if (value < 0)
return value;
// 计算实际电压 (基于 5V 量程)
millivolts = (value * actual_vref_mv) / 4095;
return sprintf(buf, "%d\n", millivolts);
}
#define CHANNEL_ATTR(num) \
static DEVICE_ATTR(channel##num, 0444, channel_show, NULL)
#define VOLTAGE_ATTR(num) \
static DEVICE_ATTR(voltage##num, 0444, voltage_show, NULL)
CHANNEL_ATTR(0);
CHANNEL_ATTR(1);
CHANNEL_ATTR(2);
CHANNEL_ATTR(3);
CHANNEL_ATTR(4);
CHANNEL_ATTR(5);
CHANNEL_ATTR(6);
CHANNEL_ATTR(7);
VOLTAGE_ATTR(0);
VOLTAGE_ATTR(1);
VOLTAGE_ATTR(2);
VOLTAGE_ATTR(3);
VOLTAGE_ATTR(4);
VOLTAGE_ATTR(5);
VOLTAGE_ATTR(6);
VOLTAGE_ATTR(7);
// 添加参考电压校准接口
static ssize_t vref_calibration_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int ret;
uint32_t known_mv;
ret = kstrtouint(buf, 10, &known_mv);
if (ret < 0) {
dev_err(dev, "Invalid calibration value\n");
return ret;
}
if (known_mv < 4000 || known_mv > 6000) { // 调整范围为 4-6V
dev_err(dev, "Calibration value must be between 4000 and 6000 mV\n");
return -EINVAL;
}
actual_vref_mv = known_mv;
dev_info(dev, "VREF calibrated to %u mV\n", actual_vref_mv);
return count;
}
static ssize_t vref_calibration_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return sprintf(buf, "%u\n", actual_vref_mv);
}
static DEVICE_ATTR(vref_calibration, 0644, vref_calibration_show, vref_calibration_store);
static struct attribute *ads7128_attrs[] = {
&dev_attr_channel0.attr,
&dev_attr_channel1.attr,
&dev_attr_channel2.attr,
&dev_attr_channel3.attr,
&dev_attr_channel4.attr,
&dev_attr_channel5.attr,
&dev_attr_channel6.attr,
&dev_attr_channel7.attr,
&dev_attr_voltage0.attr,
&dev_attr_voltage1.attr,
&dev_attr_voltage2.attr,
&dev_attr_voltage3.attr,
&dev_attr_voltage4.attr,
&dev_attr_voltage5.attr,
&dev_attr_voltage6.attr,
&dev_attr_voltage7.attr,
&dev_attr_vref_calibration.attr,
NULL,
};
static const struct attribute_group ads7128_attr_group = {
.attrs = ads7128_attrs,
};
// ================= sysfs 接口定义结束 =================
// 初始化I2C
int ads7128_i2c_init(struct i2c_client *client) {
uint8_t val, vref_cfg;
uint8_t reg_val;
uint8_t regs[] = {SYSTEM_STATUS_ADDRESS, GENERAL_CFG_ADDRESS,
VREF_CFG_ADDRESS, OSR_CFG_ADDRESS, CHANNEL_SEL_ADDRESS};
char reg_info[256];
int ret;
int i;
int offset = 0;
ads7128_client = client;
actual_vref_mv = 5000; // 重置为 5V 量程
// 通信测试
if (ads7128_single_register_read(SYSTEM_STATUS_ADDRESS, &val) < 0) {
dev_err(&client->dev, "Communication failed\n");
return -EIO;
}
dev_info(&client->dev, "Initial SYSTEM STATUS: 0x%02X\n", val);
// 复位设备
dev_info(&client->dev, "Resetting device\n");
ads7128_reset();
msleep(200); // 确保复位完成
// 检查复位后的状态
if (ads7128_single_register_read(SYSTEM_STATUS_ADDRESS, &val) < 0) {
dev_err(&client->dev, "Post-reset communication failed\n");
} else {
dev_info(&client->dev, "Post-reset SYSTEM STATUS: 0x%02X\n", val);
}
// 设置参考电压为模拟电源(5V)
dev_info(&client->dev, "Setting VREF config to analog supply (0x02)\n");
ret = ads7128_single_register_write(VREF_CFG_ADDRESS, VREF_ANALOG_SUPPLY);
if (ret < 0) {
dev_err(&client->dev, "Failed to set VREF config: %d\n", ret);
return ret;
}
msleep(100); // 等待参考电压稳定
// 验证参考电压设置
if (ads7128_single_register_read(VREF_CFG_ADDRESS, &vref_cfg) < 0) {
dev_err(&client->dev, "Failed to read VREF config\n");
} else {
dev_info(&client->dev, "Current VREF Config: 0x%02X\n", vref_cfg);
if (vref_cfg != VREF_ANALOG_SUPPLY) {
dev_err(&client->dev, "VREF config mismatch! Expected 0x%02X, got 0x%02X\n",
VREF_ANALOG_SUPPLY, vref_cfg);
return -EIO;
}
}
// 校准设备 (必须在参考电压设置后进行)
dev_info(&client->dev, "Performing calibration with stable inputs\n");
ads7128_calibrate();
msleep(100); // 增加校准后稳定时间
// 配置过采样率 - 使用更高精度模式
dev_info(&client->dev, "Configuring OSR for highest precision\n");
ads7128_config_osr(OSR_128_SAMPLES);
// 验证OSR配置
if (ads7128_single_register_read(OSR_CFG_ADDRESS, &val) < 0) {
dev_err(&client->dev, "Failed to read OSR config\n");
} else {
dev_info(&client->dev, "OSR Config: 0x%02X (expected 0x%02X)\n",
val, OSR_128_SAMPLES);
}
// 禁用统计功能
ads7128_disable_stats();
// 清除所有通道设置
ads7128_channel_clear_all();
// 读取并打印关键寄存器状态
for (i = 0; i < ARRAY_SIZE(regs); i++) {
if (ads7128_single_register_read(regs[i], ®_val) == 0) {
int written = snprintf(reg_info + offset, sizeof(reg_info) - offset,
"Reg 0x%02X: 0x%02X | ", regs[i], reg_val);
if (written > 0) offset += written;
} else {
dev_err(&client->dev, "Failed to read register 0x%02X\n", regs[i]);
}
}
dev_info(&client->dev, "Final register status: %s\n", reg_info);
return 0;
}
// 单寄存器读取
int ads7128_single_register_read(uint8_t register_read, uint8_t *data) {
uint8_t write_buf[2] = {SINGLE_READ_FRAME, register_read};
struct i2c_msg msgs[2] = {
{
.addr = ads7128_client->addr,
.flags = 0,
.len = 2,
.buf = write_buf,
},
{
.addr = ads7128_client->addr,
.flags = I2C_M_RD,
.len = 1,
.buf = data,
}
};
int ret = i2c_transfer(ads7128_client->adapter, msgs, 2);
if (ret != 2) {
dev_err(&ads7128_client->dev,
"Single register read failed: reg=0x%02X, ret=%d\n",
register_read, ret);
return -EIO;
}
return 0;
}
// 单寄存器写入
int ads7128_single_register_write(uint8_t register_write, uint8_t data) {
uint8_t buf[3] = {SINGLE_WRITE_FRAME, register_write, data};
struct i2c_msg msg = {
.addr = ads7128_client->addr,
.flags = 0,
.len = 3,
.buf = buf,
};
int ret = i2c_transfer(ads7128_client->adapter, &msg, 1);
if (ret != 1) {
dev_err(&ads7128_client->dev,
"Single register write failed: reg=0x%02X, data=0x%02X, ret=%d\n",
register_write, data, ret);
return -EIO;
}
return 0;
}
// 连续寄存器读取
int ads7128_cont_register_read(uint8_t register_read, uint8_t data[], uint8_t number_registers) {
uint8_t write_buf[2] = {CONTINIOUS_READ_FRAME, register_read};
struct i2c_msg msgs[2] = {
{
.addr = ads7128_client->addr,
.flags = 0,
.len = 2,
.buf = write_buf,
},
{
.addr = ads7128_client->addr,
.flags = I2C_M_RD,
.len = number_registers,
.buf = data,
}
};
int ret = i2c_transfer(ads7128_client->adapter, msgs, 2);
if (ret != 2) {
dev_err(&ads7128_client->dev,
"Continuous register read failed: reg=0x%02X, len=%d, ret=%d\n",
register_read, number_registers, ret);
return -EIO;
}
return 0;
}
// 设置位
int ads7128_bit_set(uint8_t register_set, uint8_t data) {
uint8_t buf[3] = {BIT_SET_FRAME, register_set, data};
struct i2c_msg msg = {
.addr = ads7128_client->addr,
.flags = 0,
.len = 3,
.buf = buf,
};
int ret = i2c_transfer(ads7128_client->adapter, &msg, 1);
if (ret != 1) {
dev_err(&ads7128_client->dev,
"Bit set failed: reg=0x%02X, bit=0x%02X, ret=%d\n",
register_set, data, ret);
return -EIO;
}
return 0;
}
// 清除位
int ads7128_bit_clear(uint8_t register_clear, uint8_t data) {
uint8_t buf[3] = {BIT_CLEAR_FRAME, register_clear, data};
struct i2c_msg msg = {
.addr = ads7128_client->addr,
.flags = 0,
.len = 3,
.buf = buf,
};
int ret = i2c_transfer(ads7128_client->adapter, &msg, 1);
if (ret != 1) {
dev_err(&ads7128_client->dev,
"Bit clear failed: reg=0x%02X, bit=0x%02X, ret=%d\n",
register_clear, data, ret);
return -EIO;
}
return 0;
}
// 读取位
uint8_t ads7128_read_bit(uint8_t address, uint8_t bit) {
uint8_t register_data;
if (ads7128_single_register_read(address, ®ister_data) < 0) {
dev_err(&ads7128_client->dev, "Failed to read bit from reg 0x%02X\n", address);
return 0;
}
return (register_data >> bit) & 0x01;
}
// 配置过采样率
int ads7128_config_osr(uint8_t osr_rate) {
uint8_t reset_byte = 0x0F;
int ret;
// 先清除所有位
ret = ads7128_bit_clear(OSR_CFG_ADDRESS, reset_byte);
if (ret < 0) return ret;
// 如果不需要禁用OSR,设置新的OSR值
if (osr_rate != OSR_DIS) {
ret = ads7128_bit_set(OSR_CFG_ADDRESS, osr_rate);
if (ret < 0) return ret;
}
return 0;
}
// 复位设备
void ads7128_reset(void) {
// 发送复位命令
if (ads7128_bit_set(GENERAL_CFG_ADDRESS, RST) < 0) {
dev_err(&ads7128_client->dev, "Reset command failed\n");
}
// 确保复位完成
msleep(200);
}
// 启用统计
void ads7128_enable_stats(void) {
if (ads7128_bit_set(GENERAL_CFG_ADDRESS, STATS_EN) < 0) {
dev_err(&ads7128_client->dev, "Enable stats command failed\n");
}
}
// 禁用统计
void ads7128_disable_stats(void) {
if (ads7128_bit_clear(GENERAL_CFG_ADDRESS, STATS_EN) < 0) {
dev_err(&ads7128_client->dev, "Disable stats command failed\n");
}
}
// 启动转换
void ads7128_start_conversion(void) {
if (ads7128_bit_set(GENERAL_CFG_ADDRESS, CNVST) < 0) {
dev_err(&ads7128_client->dev, "Start conversion command failed\n");
}
}
// 清除所有通道设置
void ads7128_channel_clear_all(void) {
if (ads7128_single_register_write(CHANNEL_SEL_ADDRESS, 0x00) < 0) {
dev_err(&ads7128_client->dev, "Clear all channels command failed\n");
}
}
// 校准设备 - 增加稳定时间
void ads7128_calibrate(void) {
int timeout = 100; // 100*10ms=1s超时
dev_info(&ads7128_client->dev, "Starting calibration...\n");
// 确保输入稳定
msleep(50);
// 发送校准命令
if (ads7128_bit_set(GENERAL_CFG_ADDRESS, CALIB) < 0) {
dev_err(&ads7128_client->dev, "Calibration command failed\n");
return;
}
// 等待校准完成
while (timeout-- && ads7128_read_bit(GENERAL_CFG_ADDRESS, CALIB_Bit)) {
msleep(10);
}
if (timeout <= 0) {
dev_err(&ads7128_client->dev, "Calibration timeout\n");
} else {
dev_info(&ads7128_client->dev, "Calibration complete\n");
}
}
// 比较函数用于排序
static int compare_uint16(const void *a, const void *b) {
uint16_t arg1 = *(const uint16_t *)a;
uint16_t arg2 = *(const uint16_t *)b;
if (arg1 < arg2) return -1;
if (arg1 > arg2) return 1;
return 0;
}
// 读取模拟通道 - 增强版 (多次采样+中值滤波)
int ads7128_read_channel_analog(uint8_t channel) {
uint8_t recent_read[2];
uint16_t samples[5]; // 5次采样
uint8_t channel_recent_address;
int status;
int i;
uint16_t result;
// 获取通道对应的数据寄存器地址
switch (channel) {
case AN_CH0: channel_recent_address = CH0_RECENT; break;
case AN_CH1: channel_recent_address = CH1_RECENT; break;
case AN_CH2: channel_recent_address = CH2_RECENT; break;
case AN_CH3: channel_recent_address = CH3_RECENT; break;
case AN_CH4: channel_recent_address = CH4_RECENT; break;
case AN_CH5: channel_recent_address = CH5_RECENT; break;
case AN_CH6: channel_recent_address = CH6_RECENT; break;
case AN_CH7: channel_recent_address = CH7_RECENT; break;
default:
dev_err(&ads7128_client->dev, "Invalid channel: %d\n", channel);
return -EINVAL;
}
// 设置通道选择寄存器
ads7128_bit_clear(CHANNEL_SEL_ADDRESS, 0x0F);
status = ads7128_bit_set(CHANNEL_SEL_ADDRESS, channel);
if (status < 0) {
dev_err(&ads7128_client->dev, "Channel select failed for CH%d: %d\n", channel, status);
return status;
}
udelay(200); // 增加通道稳定时间
// 进行5次采样
for (i = 0; i < 5; i++) {
// 重置OSR状态位
status = ads7128_bit_set(SYSTEM_STATUS_ADDRESS, OSR_DONE);
if (status < 0) {
dev_err(&ads7128_client->dev, "Clearing OSR_DONE failed for CH%d: %d\n", channel, status);
}
// 启用统计并启动转换
ads7128_enable_stats();
ads7128_start_conversion();
// 增加转换稳定时间
msleep(10); // 增加等待时间确保转换完成
// 读取数据
status = ads7128_cont_register_read(channel_recent_address, recent_read, 2);
if (status < 0) {
dev_err(&ads7128_client->dev, "Data read failed for CH%d: %d\n", channel, status);
return status;
}
// 组合12位数据
samples[i] = (recent_read[0] << 4) | (recent_read[1] >> 4);
// 调试信息
dev_dbg(&ads7128_client->dev, "CH%d[%d]: raw[0]=0x%02X, raw[1]=0x%02X, value=%d\n",
channel, i, recent_read[0], recent_read[1], samples[i]);
}
// 对采样值进行排序
sort(samples, 5, sizeof(uint16_t), compare_uint16, NULL);
// 取中值作为最终结果
result = samples[2];
dev_dbg(&ads7128_client->dev, "CH%d final value: %d (samples: %d,%d,%d,%d,%d)\n",
channel, result, samples[0], samples[1], samples[2], samples[3], samples[4]);
return result;
}
// 通道校准函数 (在初始化时调用)
void ads7128_calibrate_channels(void) {
int i, j;
uint16_t offset_errors[8] = {0};
int max_offset = 0;
uint32_t sum;
uint8_t recent_read[2];
dev_info(&ads7128_client->dev, "Starting channel offset calibration...\n");
// 将所有通道设置为GND
for (i = 0; i < 8; i++) {
// 设置通道选择寄存器
ads7128_bit_clear(CHANNEL_SEL_ADDRESS, 0x0F);
ads7128_bit_set(CHANNEL_SEL_ADDRESS, i);
msleep(50); // 增加稳定时间
// 读取10次并取平均
sum = 0;
for (j = 0; j < 10; j++) {
if (ads7128_cont_register_read(CH0_RECENT + i, recent_read, 2) < 0) {
dev_err(&ads7128_client->dev, "Failed to read channel %d\n", i);
continue;
}
sum += (recent_read[0] << 4) | (recent_read[1] >> 4);
msleep(5);
}
offset_errors[i] = sum / 10;
if (offset_errors[i] > max_offset) max_offset = offset_errors[i];
dev_info(&ads7128_client->dev, "Channel %d offset: %d\n", i, offset_errors[i]);
}
// 应用偏移校准
if (max_offset > 0) {
for (i = 0; i < 8; i++) {
// 这里可以添加校准寄存器写入逻辑
// 实际硬件支持的话,可以写入校准寄存器
dev_info(&ads7128_client->dev, "Calibrating channel %d with offset %d\n",
i, offset_errors[i]);
}
}
dev_info(&ads7128_client->dev, "Channel offset calibration complete\n");
}
static void ads7128_test_channels(struct i2c_client *client)
{
int i;
dev_info(&client->dev, "Starting ADC channel test\n");
// 测试前稳定时间
msleep(500);
for (i = 0; i < 8; i++) {
int value = ads7128_read_channel_analog(i);
if (value < 0) {
dev_err(&client->dev, "Error reading channel %d: %d\n", i, value);
} else {
// 使用校准后的参考电压计算实际电压
int millivolts = (value * actual_vref_mv) / 4095;
dev_info(&client->dev, "Channel %d: raw=0x%03X (%d) - %d.%03d V (VREF=%dmV)\n",
i, value, value,
millivolts / 1000, millivolts % 1000,
actual_vref_mv);
}
msleep(100);
}
dev_info(&client->dev, "ADC channel test completed\n");
}
static int ads7128_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int ret;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
dev_err(&client->dev, "I2C functionality not supported\n");
return -ENODEV;
}
// 检查设备是否存在
if (i2c_smbus_read_byte(client) < 0) {
dev_err(&client->dev, "Device not found at address 0x%02X\n", client->addr);
return -ENODEV;
}
ret = ads7128_i2c_init(client);
if (ret) {
dev_err(&client->dev, "Device initialization failed\n");
return ret;
}
// 执行通道校准
ads7128_calibrate_channels();
dev_info(&client->dev, "ADS7128 driver attached\n");
// 测试前稳定时间
msleep(1000); // 增加稳定时间
// 测试所有通道
ads7128_test_channels(client);
// 添加 sysfs 属性组
ret = sysfs_create_group(&client->dev.kobj, &ads7128_attr_group);
if (ret) {
dev_err(&client->dev, "Failed to create sysfs attributes\n");
// 可以继续加载驱动,但不会有 sysfs 节点
}
return 0;
}
static int ads7128_remove(struct i2c_client *client)
{
sysfs_remove_group(&client->dev.kobj, &ads7128_attr_group);
dev_info(&client->dev, "ADS7128 driver removed\n");
return 0;
}
static const struct of_device_id ads7128_of_match[] = {
{ .compatible = "elfboard,ads7128" },
{}
};
MODULE_DEVICE_TABLE(of, ads7128_of_match);
static const struct i2c_device_id ads7128_id[] = {
{ "ads7128", 0 },
{}
};
MODULE_DEVICE_TABLE(i2c, ads7128_id);
static struct i2c_driver ads7128_driver = {
.driver = {
.name = "ads7128",
.of_match_table = ads7128_of_match,
},
.probe = ads7128_probe,
.remove = ads7128_remove,
.id_table = ads7128_id,
};
module_i2c_driver(ads7128_driver);
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("TI ADS7128 ADC Driver");
MODULE_LICENSE("GPL");
Do you mind helping to check their software and hardware?
Best regards,
Wenting
