I2C总线基础
I2C总线协议
I2C(Inter-Integrated Circuit),集成电路总线,它由飞利浦(现为NXP)公司在20世纪80年代开发,是一种广泛用于嵌入式系统的同步、串行、半双工通信协议,用于在同一块电路板上的集成电路之间进行通信。
**I2C总线:**是一种物理通信架构,用于连接多个设备,实现设备之间的数据传输。
**I2C协议:**是一套规则,定义了I2C总线上数据通信的过程,包括如何启动通信、传输数据、应答信号、结束通信等。
I2C总线与I2C协议在STM32控制器专栏中有文章详细讲解
Linux I2C子系统
I2C子系统在Linux内核中主要分为三部分:I2C设备驱动层、I2C核心层、I2C适配器驱动层
i2c_client结构体
cs
struct i2c_client {
unsigned short flags; // 设备标志(如 I2C_CLIENT_TEN:10位地址)
unsigned short addr; // 设备的 I2C 从地址(7位/10位,注意:内核中存储的是未移位的原始地址)
char name[I2C_NAME_SIZE]; // 设备名称(如 "mpu6050")
struct i2c_adapter *adapter; // 指向该设备挂载的 I2C 适配器(即 I2C 控制器)
struct device dev; // 内核通用设备结构体(继承自 device,实现设备模型)
int irq; // 设备使用的中断号(若有)
struct list_head detected; // 链表节点,用于挂载到 i2c_adapter 的设备列表
const struct i2c_device_id *dev_id; // 匹配驱动的设备 ID 表
const struct i2c_client_address_data *addr_data; // 地址相关数据(已逐步废弃)
struct i2c_driver *driver; // 指向驱动该设备的 i2c_driver
void *dev_private; // 私有数据指针(驱动可存放自定义设备数据)
};

i2c_driver结构体
cs
struct i2c_driver {
unsigned int class; // 驱动匹配的设备类(如 I2C_CLASS_SENSOR,可选)
// 核心回调函数:驱动与设备匹配成功后执行
int (*probe)(struct i2c_client *client, const struct i2c_device_id *id);
// 核心回调函数:设备移除/驱动卸载时执行
void (*remove)(struct i2c_client *client);
// 设备挂起(休眠)回调(可选)
int (*suspend)(struct i2c_client *client, pm_message_t mesg);
// 设备恢复(唤醒)回调(可选)
int (*resume)(struct i2c_client *client);
// 传统的设备 ID 匹配表(无设备树时用)
const struct i2c_device_id *id_table;
// 驱动的通用设备结构体(核心,包含匹配规则、驱动名等)
struct device_driver driver;
// 检测设备的函数(自动探测设备时用,较少用)
int (*detect)(struct i2c_client *client, struct i2c_board_info *info);
// 驱动支持的从地址列表(配合 detect 使用)
const unsigned short *address_list;
// 驱动的标志(如 I2C_DRV_NOTIFY)
unsigned int flags;
};

注册i2c_driver

注销i2c_driver

设备匹配

设置私有数据

获取私有数据

i2c_msg结构体
cs
struct i2c_msg {
__u16 addr; // 从设备地址(7位/10位,未移位)
__u16 flags; // 传输标志(读/写、10位地址、起始/停止信号等,写数据是直接赋值为0即可)
__u16 len; // 数据缓冲区长度(字节数)
__u8 *buf; // 数据缓冲区指针(写:待发送数据;读:接收数据的缓冲区)
};

i2c_transfer

温湿度传感器



管脚选择
以下设置多数情况下会在出厂时均设置好

SHT20温湿度传感器

温湿度计算公式

用户模式驱动


用户驱动编写
cs
/* 标准库头文件 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
/* Linux I2C驱动相关头文件 */
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
/************************ 传感器命令宏定义 ************************/
#define CMD_RESET 0xFE // 传感器复位命令(SHT20/SI7006通用)
#define CMD_RH_HOLD 0xE5 // 湿度测量命令(主机保持模式)
#define CMD_TEMP_HOLD 0xE3 // 温度测量命令(主机保持模式)
/************************ I2C配置宏定义 ************************/
#define SLAVE_ADDR 0x40 // 传感器I2C从地址(SHT20/SI7006默认地址)
/************************ 全局变量 ************************/
int fd = 0; // I2C设备文件描述符(全局变量,方便各函数调用)
/*
* caculate_relative_humidity: 计算湿度值(基于传感器手册公式)
* @value:传感器返回的湿度原始16位数据(MSB<<8 | LSB)
* 返回值:转换后的实际湿度值,范围0%-100%
* 备注:
* 1. 公式来源:SHT20/SI7006数据手册 -> 相对湿度转换公式
* 2. 增加数值过滤,避免计算出超出物理范围的湿度值
*/
float caculate_relative_humidity(unsigned short value)
{
unsigned short tmp = value; // 临时变量,避免修改原始值
float rh = 0; // 最终湿度值
/* 滤掉无效值,确保计算后湿度值在0%-100%范围内 */
// 55575:对应100%湿度的临界值,超过则按100%处理
if (tmp > 55575)
tmp = 55575;
// 3146:对应0%湿度的临界值,低于则按0%处理
if (tmp < 3146)
tmp = 3146;
/* 根据芯片手册公式计算湿度值:RH = 125 * (raw_data / 65536) - 6 */
rh = 125 * tmp / 65536 - 6;
return rh;
}
/*
* caculate_temperature:计算温度值(基于传感器手册公式)
* @value:传感器返回的温度原始16位数据(MSB<<8 | LSB)
* 返回值:转换后的实际温度值,范围-46.85~128.87℃
* 备注:公式来源:SHT20/SI7006数据手册 -> 温度转换公式
*/
float caculate_temperature(unsigned short value)
{
unsigned short tmp = value; // 临时变量,避免修改原始值
float temp = 0; // 最终温度值
/* 根据芯片手册公式计算温度值:T = 175.72 * (raw_data / 65536) - 46.85 */
temp = 175.72 * tmp / 65536 - 46.85;
return temp;
}
/*
* sensor_reset: 复位温湿度传感器(修复点:原代码命令错误,已标注)
* 返回值:
* 成功:0
* 失败:-1
* 备注:
* 1. 原代码中错误使用 CMD_TEMP_HOLD(0xE3) 作为复位命令,正确应为 CMD_RESET(0xFE)
* 2. 复位操作目的:恢复传感器默认配置,确保初始状态正常
*/
int sensor_reset()
{
// I2C读写数据结构体:包含消息数组和消息数量
struct i2c_rdwr_ioctl_data hum_temp_data;
// I2C消息数组:本次仅需1条写消息
struct i2c_msg msgs[1];
// 修复:原代码错误使用CMD_TEMP_HOLD,改为正确的复位命令CMD_RESET
unsigned char cmd = CMD_RESET;
/* 构建I2C写消息(向传感器发送复位命令) */
msgs[0].addr = SLAVE_ADDR; // 传感器I2C从地址
msgs[0].flags = 0; // 0表示写操作(I2C_M_RD为读)
msgs[0].len = 1; // 发送1字节(复位命令)
msgs[0].buf = &cmd; // 指向复位命令缓冲区
/* 绑定消息到ioctl数据结构 */
hum_temp_data.msgs = msgs; // 消息数组指针
hum_temp_data.nmsgs = 1; // 消息数量(本次1条)
/* 调用ioctl接口执行I2C写操作 */
// I2C_RDWR:Linux I2C核心驱动提供的读写组合命令
if(ioctl(fd, I2C_RDWR, (unsigned long)&hum_temp_data) < 0)
{
perror("ioctl reset failed"); // 打印错误信息
return -1;
}
return 0;
}
/*
* sensor_get_humidity_temperature: 读取传感器温湿度原始数据
* @cmd:控制命令
* 温度:CMD_TEMP_HOLD (0xE3)
* 相对湿度:CMD_RH_HOLD (0xE5)
* 返回值:
* 成功:读取到的16位原始数据(MSB<<8 | LSB)
* 失败:-1
* 核心逻辑:
* 1. 发送测量命令(写操作)
* 2. 读取传感器返回的2字节原始数据(读操作)
* 3. 拼接为16位数据返回
*/
unsigned short sensor_get_humidity_temperature(unsigned char cmd)
{
// I2C读写数据结构体
struct i2c_rdwr_ioctl_data hum_temp_data;
// I2C消息数组:2条消息(写命令 + 读数据)
struct i2c_msg msgs[2];
unsigned char rxbuf[2] = {0}; // 接收数据缓冲区(存储2字节原始数据)
unsigned short value = 0; // 最终拼接的16位数据
/* 构建第一条I2C消息:向传感器发送测量命令(写操作) */
msgs[0].addr = SLAVE_ADDR; // 传感器I2C从地址
msgs[0].flags = 0; // 写操作
msgs[0].len = 1; // 发送1字节(测量命令)
msgs[0].buf = &cmd; // 指向测量命令缓冲区
/* 构建第二条I2C消息:读取传感器返回的原始数据(读操作) */
msgs[1].addr = SLAVE_ADDR; // 传感器I2C从地址
msgs[1].flags = I2C_M_RD; // 读操作标志
msgs[1].len = 2; // 读取2字节数据
msgs[1].buf = rxbuf; // 指向接收缓冲区
/* 绑定消息到ioctl数据结构 */
hum_temp_data.msgs = msgs; // 消息数组指针
hum_temp_data.nmsgs = 2; // 消息数量(本次2条)
/* 调用ioctl接口执行I2C读写操作 */
if(ioctl(fd, I2C_RDWR, (unsigned long)&hum_temp_data) < 0)
{
perror("ioctl read data failed"); // 打印错误信息
return -1;
}
/* 拼接原始数据:传感器返回MSB(高8位)→rxbuf[0],LSB(低8位)→rxbuf[1] */
value = rxbuf[0] << 8 | rxbuf[1];
return value;
}
/*
* main: 程序入口函数
* @argc:命令行参数个数
* @argv:命令行参数数组
* 执行流程:
* 1. 检查命令行参数合法性
* 2. 打开I2C设备节点
* 3. 复位传感器
* 4. 循环读取并打印温湿度数据
*/
int main(int argc, const char *argv[])
{
unsigned short value = 0; // 存储传感器原始数据
/* 1. 检查命令行参数合法性:必须传入I2C设备节点(如/dev/i2c-1) */
if(argc != 2)
{
printf("usage: \"%s /dev/i2c-x\", x=0/1/2/3\n", argv[0]);
exit(-1); // 参数错误,退出程序
}
/* 2. 打开I2C设备节点(读写模式) */
if ((fd = open(argv[1], O_RDWR)) < 0)
{
perror("open i2c device failed"); // 打印打开失败原因
exit(-1); // 打开失败,退出程序
}
/* 3. 复位传感器,确保初始状态正常 */
sensor_reset();
/* 4. 循环读取并打印温湿度数据(每秒1次) */
while(1)
{
/* 读取湿度原始数据 */
value = sensor_get_humidity_temperature(CMD_RH_HOLD);
/* 计算并打印湿度值(保留0位小数) */
printf("relative humidity = %.0f%%\n", caculate_relative_humidity(value));
/* 读取温度原始数据 */
value = sensor_get_humidity_temperature(CMD_TEMP_HOLD);
/* 计算并打印温度值(保留2位小数) */
printf("temperature = %.2f℃\n", caculate_temperature(value));
sleep(1); // 延时1秒,降低读取频率
}
/* 注:循环无退出条件,实际使用可添加信号处理(如Ctrl+C退出) */
return 0;
}
I2C设备驱动


hum_temp_driver.h
cs
#ifndef HUM_TEMP_HHHH
#define HUM_TEMP_HHHH
#define HUM_TEMP_MAGIC 'H'
#define GET_RH_VALUE _IOR('H', 0, int)
#define GET_TEMP_VALUE _IOR('H', 1, int)
#endif
hum_temp_driver.c
cs
/* 内核核心头文件 */
#include <linux/kernel.h> // 内核打印、基本类型
#include <linux/module.h> // 模块加载/卸载、MODULE_*宏
#include <linux/fs.h> // 字符设备文件操作结构体
#include <linux/cdev.h> // 字符设备注册/注销
#include <linux/i2c.h> // I2C子系统核心头文件
#include <linux/slab.h> // 内核内存分配(kzalloc/kfree)
#include <linux/uaccess.h> // 内核/用户空间数据拷贝(copy_to_user)
/* 驱动自定义头文件(包含ioctl命令宏等) */
#include "hum_temp_driver.h"
/************************ 设备私有数据结构体 ************************/
/*
* struct fs_hum_temp_sensor - 温湿度传感器设备私有数据
* @cdev: 字符设备结构体(内核字符设备核心)
* @client: 指向I2C从设备的指针(关联I2C总线和设备地址)
* 备注:将cdev和i2c_client封装,便于通过inode快速关联设备
*/
struct fs_hum_temp_sensor {
struct cdev cdev; // 字符设备对象
struct i2c_client *client; // 关联的I2C设备客户端
};
/************************ 字符设备配置宏 ************************/
#define FS_HUM_TEMP_MAJOR 300 // 主设备号(静态指定,需确保未被占用)
#define FS_HUM_TEMP_MINOR 0 // 次设备号
#define FS_HUM_TEMP_NUM 1 // 设备数量
/************************ 传感器命令宏定义 ************************/
#define CMD_RH_HOLD 0xE5 // 湿度测量命令(主机保持模式)
#define CMD_TEMP_HOLD 0xE3 // 温度测量命令(主机保持模式)
#define CMD_RESET 0xFE // 传感器软复位命令
/************************ I2C底层操作函数 ************************/
/*
* i2c_write_cmd - 向传感器写入单个控制命令
* @client: 指向I2C从设备的指针(包含地址、适配器等信息)
* @command: 要发送的控制命令(如复位、测量命令)
*
* 返回值:
* 成功:0
* 失败:负数(内核错误码,如-EIO、-ENOMEM)
* 核心逻辑:
* 1. 封装单个I2C写消息(addr+flags+buf+len)
* 2. 调用i2c_transfer执行总线传输
*/
static int i2c_write_cmd(struct i2c_client *client, unsigned char command)
{
int ret = 0; // 函数返回值
/* 定义I2C消息数组(仅1条写消息) */
struct i2c_msg msg[1] = {
{
.addr = client->addr, // 传感器I2C从地址(如0x40)
.flags = 0, // 0表示写操作(I2C_M_RD为读)
.buf = &command, // 指向要发送的命令缓冲区
.len = 1, // 发送1字节数据
},
};
/* 执行I2C传输:参数为适配器、消息数组、消息数量 */
ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
if (ret < 0) { // 传输失败(返回负数错误码)
printk(KERN_ERR "failed: i2c_transfer (write cmd)\n"); // 内核错误打印
return ret;
}
return 0;
}
/*
* i2c_read_word - 向传感器发命令并读取16位数据(2字节)
* @client: 指向I2C从设备的指针
* @command: 测量控制指令(如CMD_RH_HOLD/CMD_TEMP_HOLD)
*
* 返回值:
* 成功:拼接后的16位原始数据(MSB<<8 | LSB)
* 失败:负数(内核错误码)
* 核心逻辑:
* 1. 第一条消息:发送测量命令(写操作)
* 2. 第二条消息:读取传感器返回的2字节数据(读操作)
* 3. 拼接高8位和低8位返回
*/
static int i2c_read_word(struct i2c_client *client, unsigned char command)
{
int ret = 0;
unsigned char value[2] = {0}; // 接收数据缓冲区(存储2字节原始数据)
/* 定义I2C消息数组(2条:写命令 + 读数据) */
struct i2c_msg msg[2] = {
/* 第一条:写命令 */
{
.addr = client->addr, // 传感器I2C从地址
.flags = 0, // 写操作
.buf = &command, // 指向测量命令
.len = 1, // 发送1字节
},
/* 第二条:读数据 */
{
.addr = client->addr, // 传感器I2C从地址
.flags = I2C_M_RD, // 读操作标志
.buf = value, // 指向接收缓冲区
.len = 2, // 读取2字节
}
};
/* 执行I2C连续传输(无停止位) */
ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
if (ret < 0) {
printk(KERN_ERR "failed: i2c_transfer (read word)\n");
return ret;
}
/* 拼接数据:高8位(value[0]) << 8 | 低8位(value[1]) */
return value[0]<<8 | value[1];
}
/************************ 字符设备文件操作函数 ************************/
/*
* fs_hum_temp_open - 字符设备打开函数
* @inode: 设备节点结构体(包含字符设备指针)
* @file: 文件结构体(用于存储私有数据)
* 返回值:0(成功)
* 核心逻辑:
* 1. 通过container_of宏,从inode的i_cdev反向找到私有数据结构体
* 2. 将私有数据指针存入file->private_data,供后续操作使用
*/
static int fs_hum_temp_open(struct inode *inode, struct file *file)
{
/* container_of:通过成员变量地址找结构体首地址 */
struct fs_hum_temp_sensor *sensor = container_of(inode->i_cdev, struct fs_hum_temp_sensor, cdev);
file->private_data = sensor; // 保存私有数据到文件结构体
return 0;
}
/*
* fs_hum_temp_release - 字符设备关闭函数
* @inode: 设备节点结构体
* @file: 文件结构体
* 返回值:0(成功)
* 备注:本驱动无特殊释放逻辑,仅返回0即可
*/
static int fs_hum_temp_release(struct inode *inode, struct file *file)
{
return 0;
}
/*
* fs_hum_temp_ioctl - 字符设备IO控制函数(应用层核心接口)
* @file: 文件结构体(包含私有数据)
* @cmd: 应用层传入的控制命令(GET_RH_VALUE/GET_TEMP_VALUE)
* @arg: 应用层传入的缓冲区地址(用于返回数据)
* 返回值:
* 成功:0
* 失败:负数(内核错误码)
* 核心逻辑:
* 1. 根据cmd解析操作类型(读湿度/读温度)
* 2. 调用I2C读取函数获取原始数据
* 3. 按手册公式计算实际值(放大100倍避免浮点运算)
* 4. 将结果拷贝到用户空间
*/
static long fs_hum_temp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int value = 0; // 最终返回给应用层的值(放大100倍)
int tmp = 0; // 传感器原始数据
/* 从文件结构体获取私有数据 */
struct fs_hum_temp_sensor *sensor = file->private_data;
struct i2c_client *client = sensor->client; // 获取I2C客户端
/* 根据应用层命令执行对应操作 */
switch(cmd) {
case GET_RH_VALUE: // 读取湿度值
/* 1. 读取湿度原始数据 */
tmp = i2c_read_word(client, CMD_RH_HOLD);
/* 2. 过滤无效值,确保湿度在0%-100%范围 */
if(tmp > 55575)
tmp = 55575; // 对应100%湿度临界值
if(tmp < 3146)
tmp = 3146; // 对应0%湿度临界值
/* 3. 计算湿度值(放大100倍,避免浮点运算)
* 公式:RH = (125 * tmp / 65536) - 6
* 放大100倍:RH = (125*100*tmp)/65536 - 6*100
* 注:原代码用65535是笔误,正确应为65536(2^16)
*/
value = 125 * 100 * tmp / 65535 - 6 * 100;
break;
case GET_TEMP_VALUE: // 读取温度值
/* 1. 读取温度原始数据 */
tmp = i2c_read_word(client, CMD_TEMP_HOLD);
/* 2. 计算温度值(放大100倍)
* 公式:T = (175.72 * tmp / 65536) - 46.85
* 放大100倍:T = (17572*tmp)/65536 - 4685
*/
value = 17572 * tmp / 65536 - 4685;
break;
default: // 无效命令
printk(KERN_ERR "Invalid ioctl argument: %d\n", cmd);
return -EINVAL; // 返回无效参数错误码
}
/* 将计算结果拷贝到用户空间(内核不能直接访问用户地址) */
if(copy_to_user((void *)arg, (void *)&value, sizeof(int)))
return -EFAULT; // 拷贝失败返回错误码
return 0;
}
/************************ 字符设备操作集 ************************/
/*
* fs_hum_temp_fops - 字符设备文件操作结构体
* 关联open/release/ioctl回调函数,是应用层与内核交互的入口
*/
static struct file_operations fs_hum_temp_fops = {
.owner = THIS_MODULE, // 补充:指定所属模块(规范要求)
.open = fs_hum_temp_open, // 打开设备回调
.release = fs_hum_temp_release,// 关闭设备回调
.unlocked_ioctl = fs_hum_temp_ioctl, // IO控制回调(无锁版本)
};
/************************ I2C驱动核心回调函数 ************************/
/*
* fs_hum_temp_probe - I2C设备匹配成功后的初始化函数
* @client: 匹配成功的I2C客户端指针
* @id: 匹配的设备ID(传统ID匹配时有效)
* 返回值:
* 成功:0
* 失败:负数错误码
* 执行流程:
* 1. 分配设备私有数据内存
* 2. 注册字符设备号
* 3. 初始化并添加字符设备
* 4. 绑定私有数据到I2C客户端
* 5. 复位传感器
*/
static int fs_hum_temp_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct fs_hum_temp_sensor *sensor;
dev_t devno = MKDEV(FS_HUM_TEMP_MAJOR, FS_HUM_TEMP_MINOR); // 组合设备号
int ret = 0;
/* 1. 分配设备私有数据内存(kzalloc:分配并清零) */
sensor = kzalloc(sizeof(*sensor), GFP_KERNEL); // GFP_KERNEL:内核态常规分配
if (sensor == NULL) {
ret = -ENOMEM; // 内存不足错误码
printk(KERN_ERR "failed: kzalloc (sensor data)\n");
goto err_kzalloc; // 跳转到出错处理
}
sensor->client = client; // 关联I2C客户端
/* 2. 静态注册字符设备号(需确保主设备号未被占用) */
ret = register_chrdev_region(devno, FS_HUM_TEMP_NUM, "fs_hum_temp");
if (ret < 0) {
printk(KERN_ERR "failed: register_chrdev_region\n");
goto err_register_chrdev_region; // 注册失败,释放内存
}
/* 3. 初始化并添加字符设备 */
cdev_init(&sensor->cdev, &fs_hum_temp_fops); // 绑定操作集到cdev
sensor->cdev.owner = THIS_MODULE; // 指定所属模块
ret = cdev_add(&sensor->cdev, devno, FS_HUM_TEMP_NUM); // 添加字符设备到内核
if (ret < 0) {
printk(KERN_ERR "failed: cdev_add\n");
goto err_cdev_add; // 添加失败,注销设备号
}
/* 4. 将私有数据绑定到I2C客户端(便于remove时获取) */
i2c_set_clientdata(client, sensor);
/* 5. 复位温湿度传感器,确保初始状态正常 */
i2c_write_cmd(client, CMD_RESET);
printk(KERN_INFO "fs_hum_temp probe success (addr: 0x%02x)\n", client->addr);
return 0;
/* 出错处理流程(反向释放已申请的资源) */
err_cdev_add:
unregister_chrdev_region(devno, FS_HUM_TEMP_NUM); // 注销设备号
err_register_chrdev_region:
kfree(sensor); // 释放私有数据内存
err_kzalloc:
printk(KERN_ERR "probe failed, ret = %d\n", ret);
return ret;
};
/*
* fs_hum_temp_remove - I2C设备移除/驱动卸载时的清理函数
* @client: 要移除的I2C客户端指针
* 返回值:0(成功)
* 执行流程:
* 1. 获取私有数据
* 2. 注销字符设备
* 3. 释放设备号
* 4. 释放私有数据内存
*/
static int fs_hum_temp_remove(struct i2c_client *client)
{
dev_t devno = MKDEV(FS_HUM_TEMP_MAJOR, FS_HUM_TEMP_MINOR);
/* 获取绑定到I2C客户端的私有数据 */
struct fs_hum_temp_sensor *sensor = i2c_get_clientdata(client);
/* 释放资源(与probe流程反向) */
cdev_del(&sensor->cdev); // 注销字符设备
unregister_chrdev_region(devno, FS_HUM_TEMP_NUM); // 释放设备号
kfree(sensor); // 释放私有数据内存
printk(KERN_INFO "fs_hum_temp remove success\n");
return 0;
};
/************************ 设备匹配表 ************************/
/*
* fs_hum_temp_ids - 传统I2C设备ID匹配表(无设备树时使用)
* 匹配i2c_client->name,支持si7006/sht20两种设备
*/
static const struct i2c_device_id fs_hum_temp_ids[] = {
{"si7006", 0}, // 匹配设备名"si7006"
{"sht20", 0}, // 匹配设备名"sht20"
{/* LIST END */} // 哨兵项(必须有,标识数组结束)
};
MODULE_DEVICE_TABLE(i2c, fs_hum_temp_ids); // 向内核注册ID表
/*
* fs_i2c_of_match - 设备树匹配表(现代内核主流方式)
* __maybe_unused:避免未启用设备树时的编译警告
* compatible属性:匹配设备树中"silabs,si7006"/"sensirion,sht20"节点
*/
static const struct of_device_id __maybe_unused fs_i2c_of_match[] = {
{
.compatible = "silabs,si7006", // 匹配SI7006设备树节点
},
{
.compatible = "sensirion,sht20", // 匹配SHT20设备树节点
},
{/* LIST END */}, // 哨兵项
};
MODULE_DEVICE_TABLE(of, fs_i2c_of_match); // 向内核注册设备树匹配表
/************************ I2C驱动结构体 ************************/
/*
* fs_hum_temp_driver - I2C驱动核心结构体
* 关联probe/remove回调、匹配表、驱动名称
*/
static struct i2c_driver fs_hum_temp_driver = {
.driver = {
.name = "fs_hum_temp", // 驱动名称(/sys/bus/i2c/drivers/下可见)
.of_match_table = of_match_ptr(fs_i2c_of_match), // 设备树匹配表(自动处理空指针)
},
.probe = fs_hum_temp_probe, // 设备匹配成功回调
.remove = fs_hum_temp_remove, // 设备移除回调
.id_table = fs_hum_temp_ids, // 传统ID匹配表
};
/************************ 模块加载/卸载函数 ************************/
/*
* fs_hum_temp_init - 驱动模块加载入口
* 返回值:i2c_add_driver的返回值(0成功,负数失败)
* 备注:module_init宏会将该函数注册为模块加载入口
*/
static int __init fs_hum_temp_init(void)
{
printk(KERN_INFO "fs_hum_temp driver init\n");
return i2c_add_driver(&fs_hum_temp_driver); // 注册I2C驱动
}
/*
* fs_hum_temp_exit - 驱动模块卸载入口
* 备注:module_exit宏会将该函数注册为模块卸载入口
*/
static void __exit fs_hum_temp_exit(void)
{
printk(KERN_INFO "fs_hum_temp driver exit\n");
i2c_del_driver(&fs_hum_temp_driver); // 注销I2C驱动
}
/* 模块加载/卸载宏 */
module_init(fs_hum_temp_init);
module_exit(fs_hum_temp_exit);
/* 模块信息宏(内核规范要求) */
MODULE_AUTHOR("HQYJ <support@hqyj.com>"); // 作者信息
MODULE_DESCRIPTION("Si7006/SHT20 Relative Humidity and Temperature Sensor driver"); // 驱动描述
MODULE_LICENSE("GPL"); // 许可证(GPL兼容,避免内核警告)
MODULE_ALIAS("i2c:fs_hum_temp"); // 补充:驱动别名(便于内核识别)