赞
踩
(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.分配并初始化对象 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(变量名);
#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");
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"; //控制器没有使能 };
参考: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>; //从机地址 }; };
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; //消息的首地址
};
有多少起始位就有多少消息,消息的长度是以字节表示的。
写的消息:
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, }, };
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
功能:发送消息
参数:
@adap:控制器驱动对象的指针
@msgs:消息的首地址
@num:消息的个数
返回值:成功返回值等于num,否则就是失败
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; }
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; }
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;
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。