采用stm32MP157AAA芯片,温度传感器 si7006 0x40
1、在内核空间不支持浮点数进行打印,所以需要将读取到的数据拷贝到用户空间,执行用户程序打印
2、在probe函数中
- 分步注册字符设备驱动
- 自动创建设备节点
3、在i2c驱动代码中,需要自己封装读写温湿度函数
4、使用ioctl通过判断命令码读取温湿度数据,将数据拷贝到用户空间
#define GET_SI7006_TEMP _IOR('a',1,int)
#define GET_SI7006_HUM _IOR('a',0,int)
5、将采集到的温湿度传感器的值,在用户空间进行打印
6、在remove函数中,执行释放相关操作
si7006
#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/fs.h>
#include <linux/device.h>
unsigned int major ; //定义一个变量存放主设备号值
struct device *dev;
struct class *cls;
struct i2c_client *client1;
#define GET_SI7006_TEMP _IOR('a',1,int)
#define GET_SI7006_HUM _IOR('a',0,int)
//函数返回值:读取到的内容,根据芯片手册可知,需要读取两个字节(高8位,低8位)
short read_hum_tem_value(unsigned char reg)
{
char r_buf[] = {reg}; //读取的命令码,从机地址
unsigned short value; //保存读到的温湿度数据
//消息结构体的封装
struct i2c_msg r_msg[] = {
[0] = {
.addr = client1->addr, //从机地址
.flags = 0, //写
.len = 1, //描述buf字节长度
.buf = r_buf, //寄存器地址
},
[1] = {
.addr =client1->addr,
.flags = 1,
.len = 2,
.buf = (__u8 *)&value,
},
};
//发送消息
int ret = i2c_transfer(client1->adapter,r_msg,ARRAY_SIZE(r_msg));
if(ret != ARRAY_SIZE(r_msg))
{
printk("消息传输失败\n");
return -EIO;
}
return value>>8|value<<8; //高8位和低8位进行拼接
}
//打开
int si7006_open(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
return 0;
};
//读取
long si7006_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
//读取温湿度
short hum,tem;
int ret;
switch ((cmd))
{
case GET_SI7006_HUM: //判断湿度命令码
hum = read_hum_tem_value(0xE5); //读取湿度值
ret = copy_to_user((void *)arg,(void*)&hum,2); //将内核空间数据拷贝到用户空间
if(ret)
{
printk("coyp_to_user err %d\n",__LINE__);
}
break;
case GET_SI7006_TEMP:
tem = read_hum_tem_value(0xE5);
ret = copy_to_user((void *)arg,&tem,2);
if(ret)
{
printk("coyp_to_user err %d\n",__LINE__);
}
break;
}
return 0;
}
//关闭
int si7006_close(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
return 0;
}
//定义操作方法结构体变量并复制
struct file_operations fops = {
.open = si7006_open,
.unlocked_ioctl = si7006_ioctl,
.release = si7006_close,
};
int si7006_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
client1 = client;
// 1、分步注册字符设备驱动
//1.分配对象,就是分配struct cdev结构体指针 cdev_alloc
major = register_chrdev(0,"si7006",&fops);
if(major < 0)
{
printk("字符设备驱动注册失败\n");
return major;
}
printk("字符设备驱动注册成功\n");
//(1)、
//2、自动创建设备节点
//(2)、注册字符设备驱动
//(2)、向上层提交目录信息 /sys/class查看向上层提交目录信息
cls = class_create(THIS_MODULE,"si7006");
if(IS_ERR(cls))
{
printk("class create is error\n");
return PTR_ERR(cls);
}
//(2)、向上层提交设备信息 /dev目录下查看创建设备节点名字
dev = device_create(cls,NULL,MKDEV(major,0),NULL,"si7006");
if(IS_ERR(dev))
{ //判断地址是否在4k空间
return -PTR_ERR(dev); //将错误码指针转换为错误码
}
//设备驱动层和总线驱动层匹配成功执行函数
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
return 0;
}
int si7006_remove(struct i2c_client *client)
{
//设备驱动层和总线驱动层分离执行函数
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
//释放函数
//设备信息注销
device_destroy(cls,MKDEV(major,0));
//设备节点注销
class_destroy(cls);
//驱动注销
unregister_chrdev(major,"si7006");
return 0;
}
//定义设备树匹配的表
const struct of_device_id oftable[] = {
{.compatible = "hqyj,si7006",},
{},
};
//分配i2c_driver对象
struct i2c_driver si7006 = {
.probe = si7006_probe,
.remove = si7006_remove,
.driver = {
.name = "si7006",
.of_match_table = oftable,
},
};
//一键注册i2c子系统宏
module_i2c_driver(si7006);
MODULE_LICENSE("GPL");
test.c 应用层写执行逻辑
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#define GET_SI7006_TEMP _IOR('a',1,int)
#define GET_SI7006_HUM _IOR('a',0,int)
int main(int argc, char const *argv[])
{
char buf[128]={0};
unsigned short hum,tem;
float hum1,tem1;
//打开设备文件
int fd =-1;
fd = open("/dev/si7006",O_RDWR);
if(fd < 0)
{
printf("打开设备文件失败\n");
exit(-1);
}
while(1)
{
//读取温湿度数据
//fd,命令码,读取数据
ioctl(fd,GET_SI7006_HUM,&hum);
ioctl(fd,GET_SI7006_TEMP,&tem);
//进行字节序转换
// hum = ntohs(hum);
// tem = ntohs(tem);
// 数据转换
hum1 = (float)hum*125/65536-6;
tem1 = (float)tem*175.72/65536-46.85;
printf("rhum = %0.2f rtem = %0.2f\n",hum1,tem1);
sleep(1);
}
close(fd);
return 0;
}