EEPROM I2C 驱动实战教程:完整驱动代码实现与详解


📖 推荐阅读:《Yocto项目实战教程:高效定制嵌入式Linux系统
🎥 更多学习视频请关注 B 站:嵌入式Jerry


EEPROM I2C 驱动实战教程:完整驱动代码实现与详解

一、项目背景

本篇博文详细展示如何使用 Linux 驱动模型实现一个 I2C 接口的 EEPROM 驱动,以广泛应用的 AT24C256 芯片为例。EEPROM 常用于嵌入式设备中保存关键配置信息,例如设备序列号、校准数据、运行参数等。

本文将完整地给出 EEPROM 驱动的实现过程,包括设备树配置、驱动模块代码实现、sysfs 接口创建及用户空间访问测试,并对关键代码进行详细注释和说明。

二、硬件及设备树配置说明

AT24C256 芯片为 I2C 接口的 EEPROM,容量为 256Kbit (32KB),具有以下特性:

  • I2C 地址:0x50
  • 单页大小:64 字节
  • 总容量:32KB

设备树配置如下:

&i2c1 {
    status = "okay";

    eeprom@50 {
        compatible = "atmel,24c256";
        reg = <0x50>; // I2C 地址
        pagesize = <64>; // EEPROM 单页写入大小
    };
};

三、EEPROM 驱动代码及详细注释

在这里插入图片描述

完整的驱动代码如下,其中包含 sysfs 接口实现以及关键函数的详细注释说明。

#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/uaccess.h>

static struct class *eeprom_class;
static struct device *eeprom_dev;
static u16 eeprom_addr = 0; // 当前操作的EEPROM地址
static struct i2c_client *eeprom_client;

// 显示当前EEPROM地址
static ssize_t read_addr_show(struct device *dev,
                              struct device_attribute *attr, char *buf)
{
    return sprintf(buf, "0x%04x\n", eeprom_addr);
}

// 设置EEPROM地址
static ssize_t read_addr_store(struct device *dev,
                               struct device_attribute *attr, const char *buf, size_t count)
{
    sscanf(buf, "%hx", &eeprom_addr);
    return count;
}

// 读取EEPROM当前地址的数据
static ssize_t data_show(struct device *dev,
                         struct device_attribute *attr, char *buf)
{
    u8 addr_buf[2];
    u8 data;
    struct i2c_msg msgs[2];

    // 将16位地址拆分成高低字节
    addr_buf[0] = eeprom_addr >> 8;
    addr_buf[1] = eeprom_addr & 0xff;

    // I2C消息:发送EEPROM读取地址
    msgs[0].addr = eeprom_client->addr;
    msgs[0].flags = 0;
    msgs[0].len = 2;
    msgs[0].buf = addr_buf;

    // I2C消息:从EEPROM读取1字节数据
    msgs[1].addr = eeprom_client->addr;
    msgs[1].flags = I2C_M_RD;
    msgs[1].len = 1;
    msgs[1].buf = &data;

    // 执行I2C传输
    if (i2c_transfer(eeprom_client->adapter, msgs, 2) != 2)
        return -EIO;

    return sprintf(buf, "0x%02x\n", data);
}

// 创建读写地址属性节点
static DEVICE_ATTR_RW(read_addr);
// 创建数据只读属性节点
static DEVICE_ATTR_RO(data);

// EEPROM设备probe函数,设备树匹配成功后调用
static int eeprom_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    eeprom_client = client;

    // 创建sysfs设备类
    eeprom_class = class_create(THIS_MODULE, "eeprom_demo");
    // 创建设备节点
    eeprom_dev = device_create(eeprom_class, NULL, 0, NULL, "eeprom0");

    // 创建sysfs接口文件
    device_create_file(eeprom_dev, &dev_attr_read_addr);
    device_create_file(eeprom_dev, &dev_attr_data);

    dev_info(&client->dev, "EEPROM driver successfully probed\n");
    return 0;
}

// EEPROM设备remove函数,驱动移除时调用
static int eeprom_remove(struct i2c_client *client)
{
    // 移除sysfs接口文件
    device_remove_file(eeprom_dev, &dev_attr_read_addr);
    device_remove_file(eeprom_dev, &dev_attr_data);

    // 销毁设备节点及类
    device_destroy(eeprom_class, 0);
    class_destroy(eeprom_class);
    return 0;
}

static const struct i2c_device_id eeprom_ids[] = {
    { "24c256", 0 },
    {}
};

static const struct of_device_id eeprom_dt_ids[] = {
    { .compatible = "atmel,24c256" },
    {}
};

MODULE_DEVICE_TABLE(i2c, eeprom_ids);

// 定义并注册I2C驱动结构体
static struct i2c_driver eeprom_driver = {
    .driver = {
        .name = "eeprom_demo",
        .of_match_table = of_match_ptr(eeprom_dt_ids),
    },
    .probe = eeprom_probe,
    .remove = eeprom_remove,
    .id_table = eeprom_ids,
};

module_i2c_driver(eeprom_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Embedded Engineer");
MODULE_DESCRIPTION("Detailed EEPROM I2C driver example for AT24C256");

四、驱动加载与用户空间测试

编译并加载驱动到 Linux 内核后,可进行如下测试:

# 设置EEPROM读取地址为0x0010
echo 0x0010 > /sys/class/eeprom_demo/eeprom0/read_addr

# 读取EEPROM对应地址的数据
cat /sys/class/eeprom_demo/eeprom0/data

上述命令执行成功后,终端将显示 EEPROM 中 0x0010 地址的数据。



📖 推荐阅读:《Yocto项目实战教程:高效定制嵌入式Linux系统
🎥 更多学习视频请关注 B 站:嵌入式Jerry


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值