文章目录
1.i2c总线
1.1i2c总线驱动硬件知识复习
(1)i2c几根线?
scl:时钟线
sda:数据线
(2)几种信号?
start:起始信号:scl为高电平的时候,sda从高到低的跳变
stop:结束信号:scl为高电平的时候,sda从低到高的跳变
ack:在第九个时钟周期的时候,sda总线是低电平就代表应答
(master--->slave slave ---->master)
No ack:在第九个时钟周期的时候,sda总线持续高电平
(3)i2c读写时序
写时序:
\*\*start+ (从机地址 7bit + 0(写) 1bit) + ack + 寄存器地址(8bit|16bit)+ack \*\*
**+data(8bit|16bit) + ack + stop**
读时序:
**start+ (从机地址 7bit + 0(写) 1bit) + ack + 寄存器地址(8bit|16bit)+ack **
**start+ (从机地址 7bit + 1(读) 1bit) + ack + data(8bit 从机给主机) +NO ack+stop**
(4)i2c总线的特点
i2c是一个半双工,同步的,串行,具备应答机制的总线协议。
(5)i2c总线速率(控制器)
100K 低速 400K 全速 3.4M 高速
1.2i2c总线驱动框架结构
1.3i2c总线驱动的API
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;
//const struct i2c_device_id *id_table;
}
struct device_driver {
const char *name;
const struct of_device_id *of_match_table;
}
//i2c的匹配方式只有两种:1.idtable,2.设备树
2.注册、注销
#define i2c_add_driver(driver) i2c_register_driver(THIS_MODULE, driver) //注册
void i2c_del_driver(struct i2c_driver *driver); //注销
3.一键注册的函数
module_i2c_driver(变量名);
1.4i2c总线驱动的实例
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
int myi2c_probe(struct i2c_client* client,
const struct i2c_device_id* id)
{
printk("%s:%d\n", __func__, __LINE__);
return 0;
}
int myi2c_remove(struct i2c_client* client)
{
printk("%s:%d\n", __func__, __LINE__);
return 0;
}
struct of_device_id oftable[] = {
{.compatible = "hqyj,myi2c",},
{}
};
MODULE_DEVICE_TABLE(of,oftable);
struct i2c_driver myi2c = {
.probe = myi2c_probe,
.remove = myi2c_remove,
.driver = {
.name = "myi2c123456",
.of_match_table = oftable,
}
};
module_i2c_driver(myi2c);
MODULE_LICENSE("GPL");
2.i2c总线驱动
2.1si7006(sht20)设备树的编写
2.1.1画出硬件连接图
2.1.2找到控制器的设备树
stm32mp151.dtsi
i2c1: i2c@40012000 {
compatible = "st,stm32mp15-i2c"; //和控制器匹配的名字
reg = <0x40012000 0x400>; //控制器的地址
interrupt-names = "event", "error";
interrupts-extended = <&exti 21 IRQ_TYPE_LEVEL_HIGH>,
<&intc GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&rcc I2C1_K>;
resets = <&rcc I2C1_R>;
#address-cells = <1>;
#size-cells = <0>; //修饰子节点 reg成员个数
dmas = <&dmamux1 33 0x400 0x80000001>,
<&dmamux1 34 0x400 0x80000001>;
dma-names = "rx", "tx";
power-domains = <&pd_core>;
st,syscfg-fmp = <&syscfg 0x4 0x1>;
wakeup-source;
i2c-analog-filter;
status = "disabled"; //控制器没有使能
};
2.1.3编写自己的设备树
参考:linux-5.10.61/Documentation/devicetree/bindings/i2c
&i2c1{
pinctrl-names = "default", "sleep";
pinctrl-0 = <&i2c1_pins_b>; //管脚复用,工作状态
pinctrl-1 = <&i2c1_sleep_pins_b>; //管脚复用,休眠状态
i2c-scl-rising-time-ns = <100>; //scl上升沿和下降沿的时间
i2c-scl-falling-time-ns = <7>;
status = "okay"; //使能控制器
/delete-property/dmas; //删除dma属性
/delete-property/dma-names;
si7006@40{
compatible = "hqyj,si7006"; //和设备驱动匹配的名字
reg = <0x40>; //从机地址
};
};
2.2i2c中相关的结构体
i2c_client结构体:当驱动进入到probe函数之前,内核会创建i2c_client,
这个i2c_client是用来记录数据的。比如i2c_adapter(控制器驱动的对象)就
被存放到这个结构体中。后面在发消息的时候会用到这个i2c_adapter
https://www.totalphase.com/support/articles/200349176-7-bit-8-bit-and-10-bit-I2C-Slave-Addressing
struct i2c_client {
unsigned short flags; //i2c的标志位 I2C_M_TEN
unsigned short addr; //从机地址
char name[I2C_NAME_SIZE]; //设备的名
struct i2c_adapter *adapter;//控制器的对象
};
i2c_msg结构体:i2c设备驱动和控制器驱动传输数据的时候是通过i2c_msg
结构体完成的。
//消息结构体
struct i2c_msg {
__u16 addr; //从机地址
__u16 flags; //读写标志位 0 写 1读
__u16 len; //消息的长度
__u8 *buf; //消息的首地址
};
2.3封装数据发送数据的过程
2.3.1消息的封装过程
有多少起始位就有多少消息,消息的长度是以字节表示的。
写的消息:
start+(slave addr 7bit 0 1bit)+ack+reg+ack+data+ack+stop
(slave addr 7bit 0 1bit)+reg+data
char wbuf[] = {reg,data};
struct i2c_msg w_msg = {
.addr = client->addr,
.flags = 0,
.len = 2,
.buf = wbuf,
};
读的消息:
start+(slave addr 7bit 0 1bit)+ack+reg+ack+
start+(slave addr 7bit 1 1bit)+ack+data+NO ack+stop
char rbuf[] = {reg};
unsigned char val;
struct i2c_msg r_msg[] = {
[0] = {
.addr = client->addr,
.flags = 0,
.len = 1,
.buf = rbuf,
},
[1] = {
.addr = client->addr,
.flags = 1,
.len = 1,
.buf = &val,
},
};
2.3.2消息的发送过程
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
功能:发送消息
参数:
@adap:控制器驱动对象的指针
@msgs:消息的首地址
@num:消息的个数
返回值:成功返回值等于num,否则就是失败
2.4读取si7006串号和固件号实例
si7006.h
#ifndef __SI7006_H__
#define __SI7006_H__
#define GET_SERIAL _IOR('j',0,int)
#define GET_FIRWARE _IOR('j',1,int)
#define SERIAL_ADDR 0xfcc9
#define FIRWARE_ADDR 0x84b8
#endif
si7006.c
#include "si7006.h"
#include <linux/fs.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#define CNAME "si7006"
struct i2c_client* gclient;
int major;
const int count = 1;
struct class* cls;
struct device* dev;
int i2c_read_serial_firware(unsigned short reg)
{
int ret;
unsigned char val;
char r_buf[] = { reg >> 8, reg & 0xff };
// 1.封装消息结构体
struct i2c_msg r_msg[] = {
[0] = {
.addr = gclient->addr,
.flags = 0,
.len = 2,
.buf = r_buf,
},
[1] = {
.addr = gclient->addr,
.flags = 1,
.len = 1,
.buf = &val,
},
};
// 2.发送消息
ret = i2c_transfer(gclient->adapter, r_msg, ARRAY_SIZE(r_msg));
if (ret != ARRAY_SIZE(r_msg)) {
printk("i2c read error\n");
return -EAGAIN;
}
return val;
}
int si7006_open(struct inode* inode, struct file* file)
{
return 0;
}
long si7006_ioctl(struct file* filp,
unsigned int cmd, unsigned long arg)
{
int data, ret;
switch (cmd) {
case GET_SERIAL:
data = i2c_read_serial_firware(SERIAL_ADDR);
if (data < 0) {
printk("i2c read serial error\n");
return data;
}
ret = copy_to_user((void*)arg, &data, sizeof(int));
if (ret) {
printk("copy data serial to user error\n");
return -EIO;
}
break;
case GET_FIRWARE:
data = i2c_read_serial_firware(FIRWARE_ADDR);
if (data < 0) {
printk("i2c read firware error\n");
return data;
}
ret = copy_to_user((void*)arg, &data, sizeof(int));
if (ret) {
printk("copy data firware to user error\n");
return -EIO;
}
break;
}
return 0;
}
int si7006_close(struct inode* inode, struct file* file)
{
return 0;
}
static 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)
{
gclient = client;
printk("%s:%d\n", __func__, __LINE__);
// 1.注册字符设备驱动
major = register_chrdev(0, CNAME, &fops);
if (major < 0) {
printk("register chrdev error\n");
return -EAGAIN;
}
// 2.创建设备节点
cls = class_create(THIS_MODULE, CNAME);
if (IS_ERR(cls)) {
printk("class create error\n");
return PTR_ERR(cls);
}
dev = device_create(cls, NULL, MKDEV(major, 0), NULL, CNAME);
if (IS_ERR(dev)) {
printk("device create error\n");
return PTR_ERR(dev);
}
return 0;
}
int si7006_remove(struct i2c_client* client)
{
printk("%s:%d\n", __func__, __LINE__);
device_destroy(cls, MKDEV(major, 0));
class_destroy(cls);
unregister_chrdev(major, CNAME);
return 0;
}
struct of_device_id oftable[] = {
{
.compatible = "hqyj,si7006",
},
{}
};
MODULE_DEVICE_TABLE(of, oftable);
struct i2c_driver si7006 = {
.probe = si7006_probe,
.remove = si7006_remove,
.driver = {
.name = "si7006123456",
.of_match_table = oftable,
}
};
module_i2c_driver(si7006);
MODULE_LICENSE("GPL");
test.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include "si7006.h"
int main(int argc, const char *argv[])
{
int fd,serial,firware;
if((fd = open("/dev/si7006",O_RDWR)) < 0){
perror("open error");
exit(EXIT_FAILURE);
}
ioctl(fd,GET_SERIAL,&serial);
ioctl(fd,GET_FIRWARE,&firware);
printf("serial = %#x,firware = %#x\n",serial,firware);
close(fd);
return 0;
}
2.5读取si7006温湿度实例
si7006.h
#ifndef __SI7006_H__
#define __SI7006_H__
#define GET_SERIAL _IOR('j',0,int)
#define GET_FIRWARE _IOR('j',1,int)
#define GET_TMP _IOR('j',2,int)
#define GET_HUM _IOR('j',3,int)
#define SERIAL_ADDR 0xfcc9
#define FIRWARE_ADDR 0x84b8
#define TMP_ADDR 0xe3
#define HUM_ADDR 0xe5
#endif
si7006.c
#include "si7006.h"
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/uaccess.h>
#define CNAME "si7006"
struct i2c_client* gclient;
int major;
const int count = 1;
struct class* cls;
struct device* dev;
int i2c_read_serial_firware(unsigned short reg)
{
int ret;
unsigned char val;
char r_buf[] = { reg >> 8, reg & 0xff };
// 1.封装消息结构体
struct i2c_msg r_msg[] = {
[0] = {
.addr = gclient->addr,
.flags = 0,
.len = 2,
.buf = r_buf,
},
[1] = {
.addr = gclient->addr,
.flags = 1,
.len = 1,
.buf = &val,
},
};
// 2.发送消息
ret = i2c_transfer(gclient->adapter, r_msg, ARRAY_SIZE(r_msg));
if (ret != ARRAY_SIZE(r_msg)) {
printk("i2c read error\n");
return -EAGAIN;
}
return val;
}
int i2c_read_tmp_hum(unsigned char reg)
{
int ret;
unsigned short val;
char r_buf[] = { reg };
// 1.封装消息结构体
struct i2c_msg r_msg[] = {
[0] = {
.addr = gclient->addr,
.flags = 0,
.len = 1,
.buf = r_buf,
},
[1] = {
.addr = gclient->addr,
.flags = 1,
.len = 2,
.buf = (__u8*)&val,
},
};
// 2.发送消息
ret = i2c_transfer(gclient->adapter, r_msg, ARRAY_SIZE(r_msg));
if (ret != ARRAY_SIZE(r_msg)) {
printk("i2c read error\n");
return -EAGAIN;
}
return (val >> 8 | val << 8);
}
int si7006_open(struct inode* inode, struct file* file)
{
return 0;
}
long si7006_ioctl(struct file* filp,
unsigned int cmd, unsigned long arg)
{
int data, ret;
switch (cmd) {
case GET_SERIAL:
data = i2c_read_serial_firware(SERIAL_ADDR);
if (data < 0) {
printk("i2c read serial error\n");
return data;
}
ret = copy_to_user((void*)arg, &data, sizeof(int));
if (ret) {
printk("copy data serial to user error\n");
return -EIO;
}
break;
case GET_FIRWARE:
data = i2c_read_serial_firware(FIRWARE_ADDR);
if (data < 0) {
printk("i2c read firware error\n");
return data;
}
ret = copy_to_user((void*)arg, &data, sizeof(int));
if (ret) {
printk("copy data firware to user error\n");
return -EIO;
}
break;
case GET_TMP:
data = i2c_read_tmp_hum(TMP_ADDR);
if (data < 0) {
printk("i2c read tmp error\n");
return data;
}
data &= 0xffff;
ret = copy_to_user((void*)arg, &data, sizeof(int));
if (ret) {
printk("copy data tmp to user error\n");
return -EIO;
}
break;
case GET_HUM:
data = i2c_read_tmp_hum(HUM_ADDR);
if (data < 0) {
printk("i2c read hum error\n");
return data;
}
data &= 0xffff;
ret = copy_to_user((void*)arg, &data, sizeof(int));
if (ret) {
printk("copy data hum to user error\n");
return -EIO;
}
break;
}
return 0;
}
int si7006_close(struct inode* inode, struct file* file)
{
return 0;
}
static 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)
{
gclient = client;
printk("%s:%d\n", __func__, __LINE__);
// 1.注册字符设备驱动
major = register_chrdev(0, CNAME, &fops);
if (major < 0) {
printk("register chrdev error\n");
return -EAGAIN;
}
// 2.创建设备节点
cls = class_create(THIS_MODULE, CNAME);
if (IS_ERR(cls)) {
printk("class create error\n");
return PTR_ERR(cls);
}
dev = device_create(cls, NULL, MKDEV(major, 0), NULL, CNAME);
if (IS_ERR(dev)) {
printk("device create error\n");
return PTR_ERR(dev);
}
return 0;
}
int si7006_remove(struct i2c_client* client)
{
printk("%s:%d\n", __func__, __LINE__);
device_destroy(cls, MKDEV(major, 0));
class_destroy(cls);
unregister_chrdev(major, CNAME);
return 0;
}
struct of_device_id oftable[] = {
{
.compatible = "hqyj,si7006",
},
{}
};
MODULE_DEVICE_TABLE(of, oftable);
struct i2c_driver si7006 = {
.probe = si7006_probe,
.remove = si7006_remove,
.driver = {
.name = "si7006123456",
.of_match_table = oftable,
}
};
module_i2c_driver(si7006);
MODULE_LICENSE("GPL");
test.c
#include "si7006.h"
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, const char* argv[])
{
int fd, serial, firware, tmpcode, humcode;
float tmp, hum;
if ((fd = open("/dev/si7006", O_RDWR)) < 0) {
perror("open error");
exit(EXIT_FAILURE);
}
ioctl(fd, GET_SERIAL, &serial);
ioctl(fd, GET_FIRWARE, &firware);
printf("serial = %#x,firware = %#x\n", serial, firware);
while (1) {
ioctl(fd, GET_TMP, &tmpcode);
ioctl(fd, GET_HUM, &humcode);
hum = 125.0 * humcode / 65536 - 6;
tmp = 175.72 * tmpcode / 65536 - 46.85;
printf("tmp=%.2f,hum=%.2f\n",tmp,hum);
sleep(1);
}
close(fd);
return 0;
}