IIC子系统之读取温湿度
- IIC总线协议
- 主机读取一个字节
- 主机发送一个字节
- 设备树编写
- IIC设备驱动层API
- 编写程序读取温湿度
- 应用层
- 驱动
- 读取温湿度函数解析
- 头文件
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