#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#define SHT30_ADDR 0x44 // SHT30 I2C地址
#define DEV_NAME "sht30" // 设备名
#define DEV_COUNT 1 // 设备数量
// 命令定义
#define SHT30_SOFT_RESET 0x30A2 // 软复位命令
#define SHT30_READ_DATA 0x2400 // 读取数据命令
// 设备结构体
struct sht30_dev {
struct i2c_client *client; // I2C客户端
struct cdev cdev; // 字符设备
dev_t devno; // 设备号
struct class *class; // 设备类
};
static struct sht30_dev *sht30_device;
// SHT30发送命令
static int sht30_send_cmd(struct i2c_client *client, uint16_t cmd) {
uint8_t buf[2] = {cmd >> 8, cmd & 0xFF}; // 命令分高8位和低8位
struct i2c_msg msg = {
.addr = client->addr,
.flags = 0, // 写操作
.len = 2,
.buf = buf
};
return i2c_transfer(client->adapter, &msg, 1); // 发送1条消息
}
// 读取温湿度数据
static int sht30_read_data(struct i2c_client *client, float *temp, float *hum) {
uint8_t data[6]; // 存储接收的6字节数据
struct i2c_msg msg = {
.addr = client->addr,
.flags = I2C_M_RD, // 读操作
.len = 6,
.buf = data
};
int ret;
// 发送读取命令
ret = sht30_send_cmd(client, SHT30_READ_DATA);
if (ret != 1) { // i2c_transfer成功返回消息数
dev_err(&client->dev, "send read cmd failed: %d\n", ret);
return -EIO;
}
// 等待传感器准备数据(约10ms)
msleep(10);
// 读取数据
ret = i2c_transfer(client->adapter, &msg, 1);
if (ret != 1) {
dev_err(&client->dev, "read data failed: %d\n", ret);
return -EIO;
}
// 解析温度(前2字节)
uint16_t temp_raw = (data[0] << 8) | data[1];
*temp = (temp_raw / 65535.0f) * 175.0f - 45.0f;
// 解析湿度(中间2字节,跳过第3字节校验位)
uint16_t hum_raw = (data[3] << 8) | data[4];
*hum = (hum_raw / 65535.0f) * 100.0f;
return 0;
}
// 字符设备read方法
static ssize_t sht30_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) {
struct sht30_dev *dev = filp->private_data;
float temp, hum;
char kernel_buf[32]; // 存储格式化后的字符串
int ret;
// 读取传感器数据
ret = sht30_read_data(dev->client, &temp, &hum);
if (ret) {
return ret;
}
// 格式化数据(温度+湿度)
snprintf(kernel_buf, sizeof(kernel_buf), "%.2f,%.2f\n", temp, hum);
// 拷贝到用户空间
if (copy_to_user(buf, kernel_buf, strlen(kernel_buf)) != 0) {
return -EFAULT;
}
return strlen(kernel_buf);
}
// 文件操作结构体
static const struct file_operations sht30_fops = {
.owner = THIS_MODULE,
.read = sht30_read,
};
// 设备探测函数
static int sht30_probe(struct i2c_client *client, const struct i2c_device_id *id) {
int ret;
// 分配设备结构体内存
sht30_device = devm_kzalloc(&client->dev, sizeof(struct sht30_dev), GFP_KERNEL);
if (!sht30_device) {
return -ENOMEM;
}
// 初始化设备结构体
sht30_device->client = client;
i2c_set_clientdata(client, sht30_device);
// 申请设备号
ret = alloc_chrdev_region(&sht30_device->devno, 0, DEV_COUNT, DEV_NAME);
if (ret) {
dev_err(&client->dev, "alloc chrdev failed: %d\n", ret);
return ret;
}
// 初始化字符设备
cdev_init(&sht30_device->cdev, &sht30_fops);
sht30_device->cdev.owner = THIS_MODULE;
ret = cdev_add(&sht30_device->cdev, sht30_device->devno, DEV_COUNT);
if (ret) {
dev_err(&client->dev, "cdev add failed: %d\n", ret);
unregister_chrdev_region(sht30_device->devno, DEV_COUNT);
return ret;
}
// 创建设备类
sht30_device->class = class_create(THIS_MODULE, DEV_NAME);
if (IS_ERR(sht30_device->class)) {
dev_err(&client->dev, "class create failed\n");
cdev_del(&sht30_device->cdev);
unregister_chrdev_region(sht30_device->devno, DEV_COUNT);
return PTR_ERR(sht30_device->class);
}
// 创建设备节点
device_create(sht30_device->class, &client->dev, sht30_device->devno, NULL, DEV_NAME);
// 传感器软复位
ret = sht30_send_cmd(client, SHT30_SOFT_RESET);
if (ret != 1) {
dev_err(&client->dev, "soft reset failed: %d\n", ret);
// 此处简化处理,实际应清理资源
}
msleep(10); // 复位后等待稳定
dev_info(&client->dev, "sht30 probe success\n");
return 0;
}
// 设备移除函数
static int sht30_remove(struct i2c_client *client) {
struct sht30_dev *dev = i2c_get_clientdata(client);
// 销毁设备节点
device_destroy(dev->class, dev->devno);
// 销毁设备类
class_destroy(dev->class);
// 移除字符设备
cdev_del(&dev->cdev);
// 释放设备号
unregister_chrdev_region(dev->devno, DEV_COUNT);
dev_info(&client->dev, "sht30 remove success\n");
return 0;
}
// 支持的设备ID表
static const struct i2c_device_id sht30_id[] = {
{DEV_NAME, 0},
{}
};
MODULE_DEVICE_TABLE(i2c, sht30_id);
// I2C驱动结构体
static struct i2c_driver sht30_driver = {
.driver = {
.name = DEV_NAME,
.owner = THIS_MODULE,
},
.probe = sht30_probe,
.remove = sht30_remove,
.id_table = sht30_id,
};
module_i2c_driver(sht30_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("牛马程序员");
MODULE_DESCRIPTION("SHT30 I2C Temperature and Humidity Sensor Driver");