【IIC子系统之读取温湿度】

IIC总线协议

1.I2C总线是PHLIPS公司在八十年代初推出的一种串行的半双工同步总线,主要用于连接整体电路。
1)同一个板子两个芯片之间的通信,使用IIC总线 SOC(stm32mp157aaa) <-----IIC----->温湿度传感器si7006
2)同一个板子两个设备之间的通信,使用UART总线 PC<-----UART----->Target
2.I2C总线为两线制,只有两根双向信号线。一根是数据线SDA,另一根是时钟线SCL
3.I2C硬件结构简单,接口连接方便,成本较低。因此在各个领域得到了广泛的应用
4.IIC总线传输速率
低速:100k
中速:400k
高速:3.4M
5.iic总线需要外接两个上拉电阻,这两个上拉电阻的作用:在IIC总线处于空闲状态时,是高电平状态。

主机读取一个字节

在这里插入图片描述

主机发送一个字节

在这里插入图片描述

设备树编写

&i2c1 {
    
    
    pinctrl-names = "default", "sleep";
    pinctrl-0 = <&i2c1_pins_b>;
    pinctrl-1 = <&i2c1_sleep_pins_b>;
    i2c-scl-rising-time-ns = <100>;
    i2c-scl-falling-time-ns = <7>;
    status = "okay";
    /delete-property/dmas;
    /delete-property/dma-names;

    si7006@40 {
    
    
        compatible = "hqyj,si7006";
        reg = <0x40>;        
    }
};

这是一个 I2C 设备在设备树中的描述,其中包含一个名为 &i2c1 的 I2C 控制器节点和一个名为 si7006@40 的子节点。

以下是每个属性的解释:

pinctrl-names:定义了该节点使用的引脚控制器名称,本例中定义了两个控制器,分别为默认和睡眠模式。
pinctrl-0 和 pinctrl-1:定义了节点在两种不同模式下使用的引脚配置。
i2c-scl-rising-time-ns 和 i2c-scl-falling-time-ns:定义了时序参数,用于在 I2C 通信过程中控制时钟线的上升和下降时间。
status:指示该节点当前状态,“okay” 表示该节点被启用。
/delete-property/dmas 和 /delete-property/dma-names:从节点中删除了 dmas 和 dma-names 属性。
子节点 si7006@40 的属性如下:

compatible:表示该设备与哪种驱动程序兼容,本例中为 “hqyj,si7006”。
reg:设备地址,本例中为 0x40。

IIC设备驱动层API

#include<linux/i2c.h>
1.对象结构体
struct i2c_driver {
    
    
    //与设备匹配成功执行
    int (*probe)(struct i2c_client *client, const struct i2c_device_id *id);
    //设备分离时执行
    int (*remove)(struct i2c_client *client);
    //设置名字匹配和设备树匹配
    struct device_driver driver;
    //设置id_table匹配
    const struct i2c_device_id *id_table;
};
struct device_driver {
    
    
    const char      *name;
    const struct of_device_id  *of_match_table;
    };
2.给对象分配空间并且初始化
int i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    
    
    return 0;
}
int i2c_remove(struct i2c_client *client)
{
    
    
    return 0;
}
struct i2c_driver i2c_drv={
    
    
    .probe=i2c_probe,
    .remove=i2c_remove,
    .driver={
    
    
        .name="si7006",
        .of_match_table=设备树匹配表名,   
    },
};
3.注册
  #define i2c_add_driver(struct i2c_driver *driver) \
    i2c_register_driver(THIS_MODULE, driver)
4.注销
    void i2c_del_driver(struct i2c_driver *driver)
5.一键注册宏
#define module_i2c_driver(__i2c_driver) \
    module_driver(__i2c_driver, i2c_add_driver, \
            i2c_del_driver)
#define module_driver(__driver, __register, __unregister, ...) \
    static int __init __driver##_init(void) \
    {
      
       \
        return __register(&(__driver) , ##__VA_ARGS__); \
    } \
    module_init(__driver##_init); \
    static void __exit __driver##_exit(void) \
    {
      
       \
        __unregister(&(__driver) , ##__VA_ARGS__); \
    } \
    module_exit(__driver##_exit);

编写程序读取温湿度

应用层

#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include<string.h>
#include <sys/ioctl.h>  
#include <arpa/inet.h>
#include"iic.h"
int main(int argc, const char *argv[])
{
    
    
    int tem, hum;
    float tem1, hum1;
    int fd = open("/dev/si7006", O_RDWR);
    if (fd < 0)
    {
    
    
        printf("设备文件打开失败\n");
        return -1;
    }
    while (1)
    {
    
    
        ioctl(fd, GET_HUM, &hum);
        ioctl(fd, GET_TEM, &tem);
        // 大小端转换
        hum = ntohs(hum);
        tem = ntohs(tem);
        hum1 = 125.0 * hum / 65536 - 6;
        tem1 = 175.72 * tem / 65536 - 46.85;
        printf("温度:%lf   湿度: %lf \n", tem1, hum1);
        sleep(1);
    }

    return 0;
}

驱动

#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/device.h>
#include "iic.h"

int major;
struct class *cls;
struct device *dev;
struct i2c_client *client1;
int i2c_read_hum_tem(unsigned char reg)
{
    
    
    int ret;
    char r_buf[] = {
    
    reg};
    short value;
    struct i2c_msg r_msg[] =
        {
    
    
            [0] = {
    
    
                .addr = client1->addr,
                .flags = 0,
                .len = 1,
                .buf = r_buf,
            },
            [1] = {
    
    
                .addr = client1->addr,
                .flags = 1,
                .len = 2,
                .buf = (char *)&value,
            },
        };
    ret = i2c_transfer(client1->adapter, r_msg, ARRAY_SIZE(r_msg));
    if (ret != ARRAY_SIZE(r_msg))
    {
    
    
        printk("消息传送失败\n");
        return ret;
    }
    return value;
}

int si7006_open(struct inode *inode, struct file *file)
{
    
    
    return 0;
}
long si7006_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    
    
    int hum, tem;
    int ret;
    switch (cmd)
    {
    
    
    case GET_HUM:
        hum = i2c_read_hum_tem(0xe5);
        ret = copy_to_user((void *)arg, &hum, sizeof(int));
        if (ret)
        {
    
    
            printk("copy to user err\n");
            return -1;
        }
        break;
    case GET_TEM:
        tem = i2c_read_hum_tem(0xe3);
        ret = copy_to_user((void *)arg, &tem, sizeof(int));
        if (ret)
        {
    
    
            printk("copy to user err\n");
            return -1;
        }
        break;
    }
    return 0;
}
int si7006_close(struct inode *inode, struct file *file)
{
    
    
    return 0;
}

struct file_operations fops = {
    
    
    .open = si7006_open,
    .unlocked_ioctl = si7006_ioctl,
    .release = si7006_close,
};

int i2c_probe(struct i2c_client * client, const struct i2c_device_id *id)
{
    
    
    client1 = client;
    major = register_chrdev(0, "si7006", &fops);
    if (major < 0)
    {
    
    
        printk("字符设备驱动注册失败\n");
        return major;
    }
    printk("字符设备驱动注册成功\n");
    cls = class_create(THIS_MODULE, "si7006");
    if (IS_ERR(cls))
    {
    
    
        printk("向上提交目录失败\n");
        return PTR_ERR(cls);
    }
    printk("向上提交目录成功\n");
    dev = device_create(cls, NULL, MKDEV(major, 0), NULL, "si7006");
    if (IS_ERR(dev))
    {
    
    
        printk("向上提交设备失败\n");
        return PTR_ERR(dev);
    }
    printk("向上提交设备成功\n");
    return 0;
}
int i2c_remove(struct i2c_client *client)
{
    
    
    device_destroy(cls, MKDEV(major, 0));
    class_destroy(cls);
    unregister_chrdev(major, "si7006");
    return 0;
}

struct of_device_id oftable[] = {
    
    
    {
    
    
        .compatible = "hqyj,si7006",
    },
    {
    
    },
};
struct i2c_driver i2c_drv = {
    
    
    .probe = i2c_probe,
    .remove = i2c_remove,
    .driver = {
    
    
        .name = "si7006",
        .of_match_table = oftable,
    },
};
MODULE_DEVICE_TABLE(of, oftable);
module_i2c_driver(i2c_drv);
MODULE_LICENSE("GPL");

读取温湿度函数解析

int i2c_read_hum_tem(unsigned char reg)
{
    
    
    int ret;
    char r_buf[] = {
    
    reg};
    short value;

定义了一个名为 i2c_read_hum_tem 的函数,该函数接受一个 unsigned char 类型的参数 reg,表示要读取的寄存器地址。r_buf 是一个 char 类型的数组,用于存储要读取的寄存器地址。value 是一个 short 类型的变量,用于存储读取到的温湿度数据。

struct i2c_msg r_msg[] =
    {
    
    
        [0] = {
    
    
            .addr = client1->addr,
            .flags = 0,
            .len = 1,
            .buf = r_buf,
        },
        [1] = {
    
    
            .addr = client1->addr,
            .flags = 1,
            .len = 2,
            .buf = (char *)&value,
        },
    };

定义了一个名为 r_msg 的结构体数组,用于定义 I2C 传输中的读操作。该结构体数组包含两个元素,其中第一个元素是写操作,用于向设备发送要读取的寄存器地址,第二个元素是读操作,用于从设备中读取温湿度数据。client1->addr 表示要访问的 I2C 设备的地址,.flags 表示传输标志位,0 表示写操作,1 表示读操作,.len 表示数据长度,buf 表示数据缓冲区。

ret = i2c_transfer(client1->adapter, r_msg, ARRAY_SIZE(r_msg));
if (ret != ARRAY_SIZE(r_msg))
{
    
    
    printk("消息传送失败\n");
    return ret;
}

使用 i2c_transfer 函数将读写操作传输到 I2C 总线上,client1->adapter 表示要使用的 I2C 总线控制器。如果传输失败,将会输出一条错误信息并返回传输结果。如果传输成功,则返回读取到的温湿度数据。

    return value;
}

返回读取到的温湿度数据。

头文件

#ifndef __IIC_H__
#define __IIC_H__
#define GET_HUM _IOR('m',1,int)
#define GET_TEM _IOR('m',0,int)

#endif

猜你喜欢

转载自blog.csdn.net/a1379292747/article/details/129070155