总线类设备驱动——IIC

news2024/11/19 9:31:24

目录

一、本章目标

二、IIC设备驱动

2.1  I2C协议简介

2.2 LinuxI2C驱动

2.3 I2C 设备驱动实例


一、本章目标

        一条总线可以将多个设备连接在一起,提高了系统的可扩展性能。这个互联的系统通常由三部分组成:总线控制器、物理总线(一组信号线) 和设备。总线控制器和设备通过总线连接在一起,总线控制器可以发起对总线上设备的访问操作。通常总线控制器有一个驱动程序,用于控制总线控制器来操作总线,从而来访问设备,这一类驱动通常在内核中都已实现了。连接在总线上的设备也可能有一段程序要运行,这段程序可能是不基于操作系统的裸机程序 (俗称固件),也可能是基于操作系统的一个驱动。最后还有一类驱动程序,它们通常调用总线控制器的驱动程序来完成对总线上具体设备的访问,这类驱动程序的叫法不一,这里统称为设备驱动(一定要和前面说到的运行在设备本身的固件或驱动区分开),这也是本章重点讨论的内容。本章依次讨论了最常见的I2C总线、SPI总线、USB总线和PCI总线的设备驱动。今天先来学习IIC。

二、IIC设备驱动


2.1  I2C协议简介


        I2C(Intel-Integrated Circuit)是由飞利浦(现在叫恩智浦)公司开发的一种慢速两线制总线协议。最初总线的速率定为 100KHz,经过发展,速率出现了 400KHz、3.4MHZ、1MHz和5MHz的不同等级,目前大多数器件都支持400KHz。不过速率是可变的,比如一个支持400KHz的器件,完全可以工作在299KHz这个速率上,只要不超过400KHz即可。I2C 总线为那些不需要经常访问、低带宽的设备提供了一种廉价的互联方式,被广泛地应用在嵌入式系统设备当中。不过 I2C 总线协议有商标上的一些限制,有些厂家为了避免这种限制,使用了另外一种总线协议 SMBus(System Management Bus)。SMBus 总线协议其实是I2C总线协议的一个子集,绝大多数I2C 设备都能够在SMBus 总线上工作现在的计算机主板通常使用 SMBus 总线,最常见的就是内存条上的 EEPROM 配置芯片.

        I2C 总线由 SCL (串行时钟线)和 SDA(串行数据线)两条线组成,其上连接有主机控制器(Master,在 Linux 驱动中称为 Adapter) 和从设备 (Slave,在 Linux 驱动中称为 Client)。所有的访问操作都是由主机控制器发起的,在同一条总线上可以有多个主机控制器,当多个主机控制器同时对设备发起访问时,由协议的冲突检测和仲裁机制来保证只有一个主机控制器对从设备进行访问。多个从设备是通过从设备的地址来区分的,地址分为7位地址和10 位地址两种,常见的是7位地址。下图是具有两个主机控制器的I2C总线连接图,总线控制器由微控制器来充当,从设备有门阵列、LCD 驱动器ADC和 EEPROM。


下面以图示方式来说明12C 的总线时序


开始位:当SCL为高电平时,SDA 由高电平变为低电平的期间,如图中最左边的STARTcondition 所标记的区域,这表示主机控制器要开始对从机发起访问了。
地址位:接下来的7个时钟周期,主机控制器将会发送从机的 7位地址(如果是 10位地址需要分两次发送),如图中ADDRESS 所标记的区域。
读/写位:在第8个时钟周期,如果 SDA 为高电平则表示接下来要读取从机的数据如果是低电平则表示主机要写数据到从机,如图 10.2中 R/W所标记的区域。
应答位:在第9个时钟周期由从机进行应答,低电平为 ACK,高电平为 NACK,如果从机响应,应该发ACK。

数据位:在接下来的若干个周期内,主机可以持续读取数据(如果读/写位为读),或号数据(如果读/写位为写),每次数据传输完成(如图 10.2中的 DATA 所标记的区域)也要进行应答,是读则由主机控制器来应答,是写则由从机来应答,只是在主机读完最后一个字节的数据后应该以 NACK 来应答。
停止位::当SCL 为高电平时,SDA 由低电平变为高电平的期间,如图10.2中最右边的STOP condition 所标记的区域,这表示主机控制器结束了对从机的访问

        I2C 从设备内部通常有若干个寄存器,每个寄存器都有一个地址,对这些从设备的访问通常是顺序访问的。比如上次从寄存器 2开始连续访问了 4 个寄存器,那么下次访问将从寄存器6开始。不过按照下图的时序,可以实现随机的读和写访问。


        随机写访问是在主机发送完从机地址和写标志位,从机应答后,主机继续写寄存器地址,如图 10.3 中的 RA 所示。当从机对写入的寄存器地址进行应答后,主机才写入真正的数据。对随机读访问则要复杂一些,在寄存器地址写入,从机应答后,主机需要再次发送开始位,然后发送从机地址和读标志位,之后才是读操作。


2.2 LinuxI2C驱动


I2C驱动层次结构如图10.5所示。


I2C主机驱动:I2C 主机控制器的驱动,一般由 SoC 芯片厂商负责设计实现,用于控制I2C主机控制器发出时序信号。
I2CCore:为上层提供统一的API接口和对其他模块进行注册和注销等管理等.

I2C 设备驱动:调用12C Core 提供的统一 API,根据I2C 设备的访问规范,控制I2主机控制器发出不同的时序信号,对12C 设备进行访问。该驱动称为内核层I2C 设备驱动

i2c-dev:将I2C主机控制器实现为一个字符设备,应用程序可以直接访问/dev/i2c-N来访问12C 主机控制器,从而对12C 设备发起访问,该应用程序称为应用层I2C 设备驱动

        I2C Core 为屏蔽不同的 I2C 主机控制器驱动提供了可能,可以使 I2C 设备驱动仅关心如何操作 I2C 设备,而不需要了解 12C 主机控制器的细节,从而使I2C 设备驱动可以独立的存在,适用于各种不同的硬件平台。
        I2C 驱动和我们之前接触到的平台总线设备驱动非常类似,都有总线、设备和驱动这三者。12C 的设备由 struct i2c_client 来进行描述,类似于 struct platform_device,但是我们较少自己来创建这个结构对象。通常,在一个嵌入式系统中,我们是知道一个 I2C设备的地址、驱动的名字和连接的主机控制器等信息的。对 I2C 设备的描述和向内核的注册操作可以通过类似于下面的代码来实现具体可以参考linux-3.14/arch/arm/mach-s3c24xx/mach-mini2440.c

486 /*
487  * I2C devices
488  */
489 static struct at24_platform_data at24c08 = {
490     .byte_len   = SZ_8K / 8,
491     .page_size  = 16,
492 };
493 
494 static struct i2c_board_info mini2440_i2c_devs[] __initdata = {
495     {
496         I2C_BOARD_INFO("24c08", 0x50),
497         .platform_data = &at24c08,
498     },
499 };
......
676     i2c_register_board_info(0, mini2440_i2c_devs,
677                 ARRAY_SIZE(mini2440_i2c_devs));


        i2c_board_info 用于描述某些信息已知的 12C 设备,通常用I2C_BOARD_INFO宏来描述基本信息,包括驱动的名字和设备的地址。上面的代码定义了一个 i2c_board_info的数组 mini2440_i2c_devs,里面有一个设备,其对应的12C 设备驱动的名字是 24c08,设备地址为0x50。另外,platform_data 指定了其他的一些设备数据,如这片 EEPROM 的存储容量和页大小信息。最后使用 i2c_register_board_info 将 mini2440_i2c_devs 数组中的所有设备都注册到了 0号 I2C 总线上。


        但随着设备树的出现,上述方法基本被淘汰了,取而代之的是用设备树节点来进行I2C 设备的描述。Exynos4412 的 I2C 设备节点描述请参见内核文档 Documentation/devicetree/bindings/i2c/i2c-s3c2410.txt。在该文档中,给出了一个具体的实例,代码如下。

38     i2c@13870000 {                                                                                 
 39         compatible = "samsung,s3c2440-i2c";
 40         reg = <0x13870000 0x100>;
 41         interrupts = <345>;
 42         samsung,i2c-sda-delay = <100>;
 43         samsung,i2c-max-bus-freq = <100000>;
 44         /* Samsung GPIO variant begins here */
 45         gpios = <&gpd1 2 0 /* SDA */
 46              &gpd1 3 0 /* SCL */>;
 47         /* Samsung GPIO variant ends here */
 48         /* Pinctrl variant begins here */
 49         pinctrl-0 = <&i2c3_bus>;
 50         pinctrl-names = "default";
 51         /* Pinctrl variant ends here */
 52         #address-cells = <1>;
 53         #size-cells = <0>;
 54 
 55         wm8994@1a {
 56             compatible = "wlf,wm8994";
 57             reg = <0x1a>;
 58         };
 59     };


        i2c@13870000 是一个12C 主机控制器节点,里面的 compatible、reg、interrupts 等属性指定了其匹配的主机控制器驱动、I/O 内存的范围和使用的中断等信息。作为一个 12C设备驱动开发者来说,这些信息我们通常不关心。我们需要关心的是接在这个主机控制器上的 12C 设备的子节点如何来描述。上面例子中的子节点 wm8994@la 中的 compatible属性给出了匹配的驱动,而 reg 指定了 12C 设备的地址。也就是说,如果我们要编写一个I2C 设备的节点信息,则只需要在对应的 I2C 主机控制器节点中编写一个子节点,给出compatible 和reg 属性即可。对比一下,这其实和 I2c_board_info 所描述的信息基本一样只是形式不同而已。
        在内核的启动过程中,会自动将这些信息转换为一个 struct i2c_client 结构对象,当有匹配的驱动注册时,同样会像平台驱动一样,调用其 probe 函数。接下来我们就来看看I2C 驱动,核心的数据结构是 struct i2c_driver,其定义如下。
 

struct i2c_driver{
......
    int (*probe)(struct i2c_client *, const struct i2c_device_id *);\
    int (*remove)(struct i2c_client *);
    void (*shutdown)(struct i2c_client *);
    int (*suspend)(struct i2c_client *, pm_message_t mesg);
    int (*resume)(struct i2c_client *);
......
    struct device_driver driver;
    const struct i2c_device_id *id_table;
......
};


        上面省略了 I2C 设备驱动开发者较少关心的成员,不难发现,这和前面的 struct platform_driver 基本一样,所以在此就不再赘述。struct i2c_driver 相关的主要 API 罗列如下。

i2c_add_driver(driver)

void i2c_del_driver(struct i2c_driver *driver);

void i2c_set_clientdata(struct i2c_client *dev, void *data);

void *i2c_get_clientdata(const struct i2c_client *dev);

int i2c_check_functionality(struct i2c_adapter *adap, u32 func);


i2c add driver; 添加I2C 设备驱动。
i2c del driver: 删除 I2C 设备驱动。
i2c set clientdata: 将 data 保存在 struct i2c client 结构中
i2c get_clientdata: 从 struct i2c_client 中获取保存的数据
i2c_check_functionality: 查 12C 从设备所绑定的主机控制器是否支持 func 中指定的所有功能,常见的功能如下。

I2C_FUNC_10BIT_ADDR: 十位地址

I2C_PUNC_SMBUS_READ_BYTE_DATA:单字节随机读

I2C_EUNC_SMBUS_WRITE_BYTE_DATA: 单字节随机写

I2C_FUNC_SMBUS_READ_BLOCK_DATA: 多字节随机读

I2C_FUNC_SMBUS_WRITE_BLOCK_DATA:多字节随机写


简单的 I2C 数据收发相关的函数原型如下。
int i2c_master_send(const struct i2c_client *client, const char *buf, int count);

int i2c_master_recv(const struct 12c_client *client, char *buf, int count);

int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);

i2c_master_send:向12C 从设备写数据,I2C 设备的地址包含在 client 中,要写的数
缓冲区地址为 buf,写的字节数为 count,返回成功写入的字节数,负数表示失败。

i2c_master_recv: 从12C 从设备读数据,参数和返回值同写操作。

i2c_transfer: 可以将几个读写操作合并在一起执行的函数,adap 是执行读写操作的2C主机控制器,msgs 是消息数组,num 是消息的条数。消息的结构类型定义如下。

struct i2c_msg
{
    _u16 addr;
    _u16 flags;
    _ul6 len;
    _u8 *buf;
};


addr:I2C 从设备的地址。
flags: 操作的一些标志,常见的有 I2C_M_RD,表示本次是读操作。I2C_M_TEN,表示使用的是 10 位 I2C 设备地址。
len:本次要读或写的数据字节数。
buf:数据缓冲区指针。
如果要向设备先写后读,可以通过类似于下面的代码来实现。
 

msgs[0].len=1;

msgs[0].addr=0x48;

msgs[0].flags=0;

msgs[0].buf=txbuf;

msgs[0].buf[0]=0x0;
msgs[1].len=2;

msgs[1].addr=0x48;

msgs[1],flags=I2C_M_RD;

msgs[1].buf=rxbuf;

msgs[1].buf[0]=0;
msgs[1].buf[1]=0;

i2c_transfer(client->adapter, msgs,2);

        上面的代码构造了两条消息,两条消息都是要操作设备地址为 0x48 的 12C 备,第一条消息是向设备写 1个字节的数据,第二条消息是读两个字节的数据。

        i2c_transfer 不要求每条消息的地址都是一样的,该函数返回被成功执行的消息条数
为负表示失败。还有一些适合于 SMBus 的更简单的 API,如下所示。

s32 i2c_smbus_read_byte_data (struct i2c_client *client, u8 command);
s32 i2c_smbus_write_byte_data(struct i2c_client *client, u8 command, u8 value);
s32 i2c_smbus_read_block_data(struct i2c_client *client, u8 command, u8 *values);
s32 i2c_smbus_write_block_data(struct i2c_elient *client, u8 command, u8 length,const u8 *values);

        上面的函数为单字节或多字节的随机读写函数,command 是 SMBus 中的命令,通常是 i2C 设备内部的寄存器地址。返回的是实际读写的字节数,为负表示失败。其他参数都很好理解,这里不再详细说明。
        我们前面谈到,如果把 12C 主机控制器实现为一个字符设备,应用程序可以通过对应的设备来直接访问 12C 主机控制器,从而产生相应的时序来访问 12C 从设备,这样应用程序叫应用层 i2C 设备驱动。不过这需要配置内核,以确保这一功能被选中,配置如下。


内核配置并重新编译后,目标板使用新的内核镜像,系统启动后,可以看到如下的
设备。


使能的主机控制器节点越多,设备文件就会越多。
应用层也有相关的 API,最主要的如下。

ioctl(file,I2C_SLAVE,long addr)
ioctl(file,I2C FUNCS, unsigned long *funcs)
ioctl(file,I2C_RDWR, struct i2c_rdwr_ioctl_data *msgset)
__s32 i2c_smbus_read_byte_data(int file,__u8 command)
__s32 i2c_smbus_write_byte_data(int file,__u8 command,__u8 value);
__s32 i2c_smbus_read_block_data(int file,__u8 command,__u8 *values);
__s32 i2c_smbus_write_block_data(int file, __u8 command, __u8 length,__u8 *values);


I2C_SLAVE: 设置要访问的从机设备地址。
I2C_FUNCS: 获取主机控制器的功能。
I2C_RDWR:读写 12C 设备,读写操作由 msgset 来指定,其类型定义如下。

struct i2c_rdwr_ioctl_data 
{
    struct i2c_msg *msgs;
    int nmsgs;
}


        其中的 struct i2c_msg 我们在前面已经见过了。针对前面的内核层 i2C 设备驱动的例子而言,应用层 I2C 设备驱动代码如下。
 

i2c_data.nmsgs=2;
i2c_data.msgs[0].len=1;
i2c_data.msgs[0].addr=0x48;
12c_data.msgs[0].flags=0;
i2c_data.msgs[0].buf=txbuf;
i2c_data.msgs[0].buf[0]=0x0;
i2c_data.msgs[1].len=2;
i2c_data.msgs[1].addr=0x48;
i2c_data.msgs[1].flags=I2C_M_RD;
i2c_data.msgs[1].buf=rxbuf;
i2c_data.msgs[1].buf[0]=0;
i2c_data.msgs[1].buf[1]=0;
ioctl(fd,I2C_RDWR,(unsigned long)&i2c_data);


i2c_smbus_read_byte_data 之类的函数和前面内核层中的含义一样,此处不再细述.


2.3 I2C 设备驱动实例


        在 FS4412 目标板上有一个集陀螺仪、三轴加速度传感器和温度传感器于一体的器件MPU6050,相关的原理图如图所示。


        MPU6050 连接在 Exynos4412 编号为 5 的 i2C 主机控制器上,MPU6050 设备的地址为 0x68,根据前面的知识并查阅 Exynos4412 的用户手册,可以得出相应的设备节点代码。

i2c@138B0000{
    samsung,i2c-sda-delay = <100>;
    samsung,i2c-max-bus-freq = <20000>;
    pinctr1-0 = <&i2c5_bus>;
    pinctrl-names = "default";
    status = "okay";
    mpu6050@68 {
        compatible="fs4412,mpu6050";
        reg = <0x68>;
    };
};


        重新编译设备树给目标板使用,系统启动后,将会多出一个 i2c-5 的字符设备,

正常应该只有一个i2c-0设备的,上面那个是已经配好的所以有个i2c-5
MPU6050 的寄存器非常多,但是只是简单获取一些传感数据,关注如图几个寄存器就可以了。



/****************MPU6050内部常用寄存器地址****************/

#define	SMPLRT_DIV		0x19	//陀螺仪采样率,典型值:0x07(125Hz)
#define	CONFIG			0x1A	//低通滤波频率,典型值:0x06(5Hz)
#define	GYRO_CONFIG		0x1B	//陀螺仪自检及测量范围,典型值:0x18(不自检,2000°/s)
#define	ACCEL_CONFIG	0x1C	//加速计自检及测量范围及高通滤波频率,典型值:0x0(不自检,2G,5Hz)
#define	ACCEL_XOUT_H	0x3B
#define	ACCEL_XOUT_L	0x3C
#define	ACCEL_YOUT_H	0x3D
#define	ACCEL_YOUT_L	0x3E
#define	ACCEL_ZOUT_H	0x3F
#define	ACCEL_ZOUT_L	0x40
#define	TEMP_OUT_H		0x41
#define	TEMP_OUT_L		0x42
#define	GYRO_XOUT_H		0x43
#define	GYRO_XOUT_L		0x44
#define	GYRO_YOUT_H		0x45
#define	GYRO_YOUT_L		0x46
#define	GYRO_ZOUT_H		0x47
#define	GYRO_ZOUT_L		0x48
#define	PWR_MGMT_1		0x6B	//电源管理,典型值:0x00(正常启用)
#define	SlaveAddress	0x68	//MPU6050-I2C地址

SMPLRT_DIV: 采样时钟分频值,相应的公式为:采样率 = 陀螺仪输出频率 /(1 + SMPLRT_DIV)。
CONFIG: EXT_SYNC_SET 是外部同步的配置,不需要设为0 即可。DLPF_CFG是数字低通滤波器的配置,它将决定陀螺仪和三轴加速度传感器的带宽和延时值,最大可配置的值为 6。
GYRO_CONFIG: 陀螺配置寄存器,FS_SEL 用于选择陀螺仪的输出范围,设置值和范围如图 所示


ACCEL_CONFIG: 三轴加速度传感器配置寄存器,AFS_SEL 用于三轴加速度传感器的输出范围,设置值和范围如图所示。


        ACCEL_xxx、TEMP_xxx、GYRO xxx:传感器输出值,三者的换算关系如下。

        三轴加速度 = 采样值 /n, n 根据 FS_SEL 的值 0、1、2、3 分别对应为 16384、8192、4096、2048。
        温度 = 采样值 /340 +36.53。

        陀螺仪角速度 = 采样值 /n,n 根据 AFS_SEL 的值 0、1、2、3 分别对应为 131、65.5、32.8、16.4。
        PWR_MGMT_1:电源管理寄存器 1。DEVICE_RESET 用于复位整个芯片。SLEEP是休眠控制,为1表示休眠。CYCLE 是循环模式控制,如果 SLEEP 为0,CYCLE为1,那么传感器将会定期醒来进行采样。TEMP_ DIS 为1禁止温度传感器,CLKSEL 为0选择内部的 8MHz 时钟。
        根据以上对寄存器的介绍,可以写出如下的应用层 I2C 设备驱动代码

/*
    i2c-dev.h - i2c-bus driver, char device interface

    Copyright (C) 1995-97 Simon G. Vogl
    Copyright (C) 1998-99 Frodo Looijaard <frodol@dds.nl>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
    MA 02110-1301 USA.
*/

/* $Id: i2c-dev.h 5361 2008-10-19 09:47:02Z khali $ */

#ifndef LIB_I2CDEV_H
#define LIB_I2CDEV_H

#include <linux/types.h>
#include <sys/ioctl.h>


/* -- i2c.h -- */


/*
 * I2C Message - used for pure i2c transaction, also from /dev interface
 */
struct i2c_msg {
	__u16 addr;	/* slave address			*/
	unsigned short flags;		
#define I2C_M_TEN	0x10	/* we have a ten bit chip address	*/
#define I2C_M_RD	0x01
#define I2C_M_NOSTART	0x4000
#define I2C_M_REV_DIR_ADDR	0x2000
#define I2C_M_IGNORE_NAK	0x1000
#define I2C_M_NO_RD_ACK		0x0800
	short len;		/* msg length				*/
	char *buf;		/* pointer to msg data			*/
};

/* To determine what functionality is present */

#define I2C_FUNC_I2C			0x00000001
#define I2C_FUNC_10BIT_ADDR		0x00000002
#define I2C_FUNC_PROTOCOL_MANGLING	0x00000004 /* I2C_M_{REV_DIR_ADDR,NOSTART,..} */
#define I2C_FUNC_SMBUS_PEC		0x00000008
#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL	0x00008000 /* SMBus 2.0 */
#define I2C_FUNC_SMBUS_QUICK		0x00010000 
#define I2C_FUNC_SMBUS_READ_BYTE	0x00020000 
#define I2C_FUNC_SMBUS_WRITE_BYTE	0x00040000 
#define I2C_FUNC_SMBUS_READ_BYTE_DATA	0x00080000 
#define I2C_FUNC_SMBUS_WRITE_BYTE_DATA	0x00100000 
#define I2C_FUNC_SMBUS_READ_WORD_DATA	0x00200000 
#define I2C_FUNC_SMBUS_WRITE_WORD_DATA	0x00400000 
#define I2C_FUNC_SMBUS_PROC_CALL	0x00800000 
#define I2C_FUNC_SMBUS_READ_BLOCK_DATA	0x01000000 
#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000 
#define I2C_FUNC_SMBUS_READ_I2C_BLOCK	0x04000000 /* I2C-like block xfer  */
#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK	0x08000000 /* w/ 1-byte reg. addr. */

#define I2C_FUNC_SMBUS_BYTE (I2C_FUNC_SMBUS_READ_BYTE | \
                             I2C_FUNC_SMBUS_WRITE_BYTE)
#define I2C_FUNC_SMBUS_BYTE_DATA (I2C_FUNC_SMBUS_READ_BYTE_DATA | \
                                  I2C_FUNC_SMBUS_WRITE_BYTE_DATA)
#define I2C_FUNC_SMBUS_WORD_DATA (I2C_FUNC_SMBUS_READ_WORD_DATA | \
                                  I2C_FUNC_SMBUS_WRITE_WORD_DATA)
#define I2C_FUNC_SMBUS_BLOCK_DATA (I2C_FUNC_SMBUS_READ_BLOCK_DATA | \
                                   I2C_FUNC_SMBUS_WRITE_BLOCK_DATA)
#define I2C_FUNC_SMBUS_I2C_BLOCK (I2C_FUNC_SMBUS_READ_I2C_BLOCK | \
                                  I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)

/* Old name, for compatibility */
#define I2C_FUNC_SMBUS_HWPEC_CALC	I2C_FUNC_SMBUS_PEC

/* 
 * Data for SMBus Messages 
 */
#define I2C_SMBUS_BLOCK_MAX	32	/* As specified in SMBus standard */	
#define I2C_SMBUS_I2C_BLOCK_MAX	32	/* Not specified but we use same structure */
union i2c_smbus_data {
	__u8 byte;
	__u16 word;
	__u8 block[I2C_SMBUS_BLOCK_MAX + 2]; /* block[0] is used for length */
	                                            /* and one more for PEC */
};

/* smbus_access read or write markers */
#define I2C_SMBUS_READ	1
#define I2C_SMBUS_WRITE	0

/* SMBus transaction types (size parameter in the above functions) 
   Note: these no longer correspond to the (arbitrary) PIIX4 internal codes! */
#define I2C_SMBUS_QUICK		    0
#define I2C_SMBUS_BYTE		    1
#define I2C_SMBUS_BYTE_DATA	    2 
#define I2C_SMBUS_WORD_DATA	    3
#define I2C_SMBUS_PROC_CALL	    4
#define I2C_SMBUS_BLOCK_DATA	    5
#define I2C_SMBUS_I2C_BLOCK_BROKEN  6
#define I2C_SMBUS_BLOCK_PROC_CALL   7		/* SMBus 2.0 */
#define I2C_SMBUS_I2C_BLOCK_DATA    8


/* ----- commands for the ioctl like i2c_command call:
 * note that additional calls are defined in the algorithm and hw 
 *	dependent layers - these can be listed here, or see the 
 *	corresponding header files.
 */
				/* -> bit-adapter specific ioctls	*/
#define I2C_RETRIES	0x0701	/* number of times a device address      */
				/* should be polled when not            */
                                /* acknowledging 			*/
#define I2C_TIMEOUT	0x0702	/* set timeout - call with int 		*/


/* this is for i2c-dev.c	*/
#define I2C_SLAVE	0x0703	/* Change slave address			*/
				/* Attn.: Slave address is 7 or 10 bits */
#define I2C_SLAVE_FORCE	0x0706	/* Change slave address			*/
				/* Attn.: Slave address is 7 or 10 bits */
				/* This changes the address, even if it */
				/* is already taken!			*/
#define I2C_TENBIT	0x0704	/* 0 for 7 bit addrs, != 0 for 10 bit	*/

#define I2C_FUNCS	0x0705	/* Get the adapter functionality */
#define I2C_RDWR	0x0707	/* Combined R/W transfer (one stop only)*/
#define I2C_PEC		0x0708	/* != 0 for SMBus PEC                   */

#define I2C_SMBUS	0x0720	/* SMBus-level access */

/* -- i2c.h -- */


/* Note: 10-bit addresses are NOT supported! */

/* This is the structure as used in the I2C_SMBUS ioctl call */
struct i2c_smbus_ioctl_data {
	char read_write;
	__u8 command;
	int size;
	union i2c_smbus_data *data;
};

/* This is the structure as used in the I2C_RDWR ioctl call */
struct i2c_rdwr_ioctl_data {
	struct i2c_msg *msgs;	/* pointers to i2c_msgs */
	int nmsgs;		/* number of i2c_msgs */
};


static inline __s32 i2c_smbus_access(int file, char read_write, __u8 command, 
                                     int size, union i2c_smbus_data *data)
{
	struct i2c_smbus_ioctl_data args;

	args.read_write = read_write;
	args.command = command;
	args.size = size;
	args.data = data;
	return ioctl(file,I2C_SMBUS,&args);
}


static inline __s32 i2c_smbus_write_quick(int file, __u8 value)
{
	return i2c_smbus_access(file,value,0,I2C_SMBUS_QUICK,NULL);
}
	
static inline __s32 i2c_smbus_read_byte(int file)
{
	union i2c_smbus_data data;
	if (i2c_smbus_access(file,I2C_SMBUS_READ,0,I2C_SMBUS_BYTE,&data))
		return -1;
	else
		return 0x0FF & data.byte;
}

static inline __s32 i2c_smbus_write_byte(int file, __u8 value)
{
	return i2c_smbus_access(file,I2C_SMBUS_WRITE,value,
	                        I2C_SMBUS_BYTE,NULL);
}

static inline __s32 i2c_smbus_read_byte_data(int file, __u8 command)
{
	union i2c_smbus_data data;
	if (i2c_smbus_access(file,I2C_SMBUS_READ,command,
	                     I2C_SMBUS_BYTE_DATA,&data))
		return -1;
	else
		return 0x0FF & data.byte;
}

static inline __s32 i2c_smbus_write_byte_data(int file, __u8 command, 
                                              __u8 value)
{
	union i2c_smbus_data data;
	data.byte = value;
	return i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
	                        I2C_SMBUS_BYTE_DATA, &data);
}

static inline __s32 i2c_smbus_read_word_data(int file, __u8 command)
{
	union i2c_smbus_data data;
	if (i2c_smbus_access(file,I2C_SMBUS_READ,command,
	                     I2C_SMBUS_WORD_DATA,&data))
		return -1;
	else
		return 0x0FFFF & data.word;
}

static inline __s32 i2c_smbus_write_word_data(int file, __u8 command, 
                                              __u16 value)
{
	union i2c_smbus_data data;
	data.word = value;
	return i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
	                        I2C_SMBUS_WORD_DATA, &data);
}

static inline __s32 i2c_smbus_process_call(int file, __u8 command, __u16 value)
{
	union i2c_smbus_data data;
	data.word = value;
	if (i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
	                     I2C_SMBUS_PROC_CALL,&data))
		return -1;
	else
		return 0x0FFFF & data.word;
}


/* Returns the number of read bytes */
static inline __s32 i2c_smbus_read_block_data(int file, __u8 command, 
                                              __u8 *values)
{
	union i2c_smbus_data data;
	int i;
	if (i2c_smbus_access(file,I2C_SMBUS_READ,command,
	                     I2C_SMBUS_BLOCK_DATA,&data))
		return -1;
	else {
		for (i = 1; i <= data.block[0]; i++)
			values[i-1] = data.block[i];
		return data.block[0];
	}
}

static inline __s32 i2c_smbus_write_block_data(int file, __u8 command, 
                                               __u8 length, __u8 *values)
{
	union i2c_smbus_data data;
	int i;
	if (length > 32)
		length = 32;
	for (i = 1; i <= length; i++)
		data.block[i] = values[i-1];
	data.block[0] = length;
	return i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
	                        I2C_SMBUS_BLOCK_DATA, &data);
}

/* Returns the number of read bytes */
/* Until kernel 2.6.22, the length is hardcoded to 32 bytes. If you
   ask for less than 32 bytes, your code will only work with kernels
   2.6.23 and later. */
static inline __s32 i2c_smbus_read_i2c_block_data(int file, __u8 command,
                                                  __u8 length, __u8 *values)
{
	union i2c_smbus_data data;
	int i;

	if (length > 32)
		length = 32;
	data.block[0] = length;
	if (i2c_smbus_access(file,I2C_SMBUS_READ,command,
	                     length == 32 ? I2C_SMBUS_I2C_BLOCK_BROKEN :
	                      I2C_SMBUS_I2C_BLOCK_DATA,&data))
		return -1;
	else {
		for (i = 1; i <= data.block[0]; i++)
			values[i-1] = data.block[i];
		return data.block[0];
	}
}

static inline __s32 i2c_smbus_write_i2c_block_data(int file, __u8 command,
                                               __u8 length, __u8 *values)
{
	union i2c_smbus_data data;
	int i;
	if (length > 32)
		length = 32;
	for (i = 1; i <= length; i++)
		data.block[i] = values[i-1];
	data.block[0] = length;
	return i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
	                        I2C_SMBUS_I2C_BLOCK_BROKEN, &data);
}

/* Returns the number of read bytes */
static inline __s32 i2c_smbus_block_process_call(int file, __u8 command,
                                                 __u8 length, __u8 *values)
{
	union i2c_smbus_data data;
	int i;
	if (length > 32)
		length = 32;
	for (i = 1; i <= length; i++)
		data.block[i] = values[i-1];
	data.block[0] = length;
	if (i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
	                     I2C_SMBUS_BLOCK_PROC_CALL,&data))
		return -1;
	else {
		for (i = 1; i <= data.block[0]; i++)
			values[i-1] = data.block[i];
		return data.block[0];
	}
}


#endif /* LIB_I2CDEV_H */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>

#include "i2c-dev.h"

#define	SMPLRT_DIV	0x19
#define	CONFIG		0x1A
#define	GYRO_CONFIG	0x1B
#define	ACCEL_CONFIG	0x1C
#define	ACCEL_XOUT_H	0x3B
#define	ACCEL_XOUT_L	0x3C
#define	ACCEL_YOUT_H	0x3D
#define	ACCEL_YOUT_L	0x3E
#define	ACCEL_ZOUT_H	0x3F
#define	ACCEL_ZOUT_L	0x40
#define	TEMP_OUT_H	0x41
#define	TEMP_OUT_L	0x42
#define	GYRO_XOUT_H	0x43
#define	GYRO_XOUT_L	0x44
#define	GYRO_YOUT_H	0x45
#define	GYRO_YOUT_L	0x46
#define	GYRO_ZOUT_H	0x47
#define	GYRO_ZOUT_L	0x48
#define	PWR_MGMT_1	0x6B

void swap_int16(short *val)
{
	*val = (*val << 8) | (*val >> 8);
}

int main(int argc, char *argv[])
{
	int fd;
	int ret;
	short accelx, accely, accelz;
	short temp;
	short gyrox, gyroy, gyroz;
	unsigned char *p;

	fd = open("/dev/i2c-5", O_RDWR);
	if (fd == -1)
		goto fail;

	if (ioctl(fd, I2C_SLAVE, 0x68) < 0)
		goto fail;

	i2c_smbus_write_byte_data(fd, PWR_MGMT_1, 0x80);
	usleep(200000);
	i2c_smbus_write_byte_data(fd, PWR_MGMT_1, 0x40);
	i2c_smbus_write_byte_data(fd, PWR_MGMT_1, 0x00);

	i2c_smbus_write_byte_data(fd, SMPLRT_DIV,   0x7);
	i2c_smbus_write_byte_data(fd, CONFIG,       0x6);
	i2c_smbus_write_byte_data(fd, GYRO_CONFIG,  0x3 << 3);
	i2c_smbus_write_byte_data(fd, ACCEL_CONFIG, 0x3 << 3);

	while (1) {
		accelx = i2c_smbus_read_word_data(fd, ACCEL_XOUT_H);
		swap_int16(&accelx);
		accely = i2c_smbus_read_byte_data(fd, ACCEL_YOUT_H);
		swap_int16(&accely);
		accelz = i2c_smbus_read_byte_data(fd, ACCEL_ZOUT_H);
		swap_int16(&accelz);

		printf("accelx: %.2f\n", accelx / 2048.0);
		printf("accely: %.2f\n", accely / 2048.0);
		printf("accelz: %.2f\n", accelz / 2048.0);

		temp = i2c_smbus_read_word_data(fd, TEMP_OUT_H);
		swap_int16(&temp);
		printf("temp: %.2f\n", temp / 340.0 + 36.53);

		gyrox = i2c_smbus_read_word_data(fd, GYRO_XOUT_H);
		swap_int16(&gyrox);
		gyroy = i2c_smbus_read_byte_data(fd, GYRO_YOUT_H);
		swap_int16(&gyroy);
		gyroz = i2c_smbus_read_byte_data(fd, GYRO_ZOUT_H);
		swap_int16(&gyroz);

		printf("gyrox: %.2f\n", gyrox / 16.4);
		printf("gyroy: %.2f\n", gyroy / 16.4);
		printf("gyroz: %.2f\n", gyroz / 16.4);

		sleep(1);
	}

fail:
	perror("i2c test");
	exit(EXIT_FAILURE);
}


        代码第 9 行包含了一个 i2c-dev.h 头文件,该文件中包含了 i2c_smbus_read_word_data等函数的定义。代码第 11 行至第 29 行是相关存器地址的宏定文。第45行开了/dev/i2c-5,因为根据前面的分析,MPU6050 是连接在 5号主机控制器上的。代码第 9行将 i2C 设备的地址设置为 0x68,也就是 MPU6050 的设备地址。代码第 52行复位整个芯片,然后休眠了 200ms,等待芯片复位完成,再将 SLEEP 位先置1后置0,确保其不处于 SLEEP 模式。代码第 57 行设置采样时钟分频值为7,第 8 行设置 DLPF_CFG 的值为6,那么采样率为 1KHz /(1 +7)= 125Hz。代码第 59 行和第 60行分别设置陀螺仪的的输出范围为土2000 和三轴加速度的输出范围为+16。代码第 60行至第 90行则读出采祥值.然后根据前面的换算关系进行换算并打印输出。
编译和测试的命令如下。



内核层 I2C 设备驱动代码如下

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

#include <linux/fs.h>
#include <linux/cdev.h>

#include <linux/slab.h>
#include <linux/ioctl.h>
#include <linux/uaccess.h>

#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/i2c.h>
#include <linux/delay.h>

#include "mpu6050.h"

#define FSRTC_MAJOR	256
#define FSRTC_MINOR	9
#define FSRTC_DEV_NAME	"mpu6050"

#define SMPLRT_DIV	0x19
#define CONFIG		0x1A
#define GYRO_CONFIG	0x1B
#define ACCEL_CONFIG	0x1C
#define ACCEL_XOUT_H	0x3B
#define ACCEL_XOUT_L	0x3C
#define ACCEL_YOUT_H	0x3D
#define ACCEL_YOUT_L	0x3E
#define ACCEL_ZOUT_H	0x3F
#define ACCEL_ZOUT_L	0x40
#define TEMP_OUT_H	0x41
#define TEMP_OUT_L	0x42
#define GYRO_XOUT_H	0x43
#define GYRO_XOUT_L	0x44
#define GYRO_YOUT_H	0x45
#define GYRO_YOUT_L	0x46
#define GYRO_ZOUT_H	0x47
#define GYRO_ZOUT_L	0x48
#define PWR_MGMT_1	0x6B

struct mpu6050_dev {
	struct i2c_client *client;
	atomic_t available;
	struct cdev cdev;
};

static int mpu6050_open(struct inode *inode, struct file *filp)
{
	struct mpu6050_dev *mpu6050 = container_of(inode->i_cdev, struct mpu6050_dev, cdev);

	filp->private_data = mpu6050;
	if (atomic_dec_and_test(&mpu6050->available))
		return 0;
	else {
		atomic_inc(&mpu6050->available);
		return -EBUSY;
	}
}

static int mpu6050_release(struct inode *inode, struct file *filp)
{
	struct mpu6050_dev *mpu6050 = filp->private_data;

	atomic_inc(&mpu6050->available);
	return 0;
}

static long mpu6050_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	struct mpu6050_dev *mpu6050 = filp->private_data;
	struct atg_val val;

	if (_IOC_TYPE(cmd) != MPU6050_MAGIC)
		return -ENOTTY;

	switch (cmd) {
	case MPU6050_GET_VAL:
		val.accelx = i2c_smbus_read_word_data(mpu6050->client, ACCEL_XOUT_H);
		val.accely = i2c_smbus_read_word_data(mpu6050->client, ACCEL_YOUT_H);
		val.accelz = i2c_smbus_read_word_data(mpu6050->client, ACCEL_ZOUT_H);
		val.temp   = i2c_smbus_read_word_data(mpu6050->client, TEMP_OUT_H);
		val.gyrox  = i2c_smbus_read_word_data(mpu6050->client, GYRO_XOUT_H);
		val.gyroy  = i2c_smbus_read_word_data(mpu6050->client, GYRO_YOUT_H);
		val.gyroz  = i2c_smbus_read_word_data(mpu6050->client, GYRO_ZOUT_H);
		val.accelx = be16_to_cpu(val.accelx);
		val.accely = be16_to_cpu(val.accely);
		val.accelz = be16_to_cpu(val.accelz);
		val.temp   = be16_to_cpu(val.temp);
		val.gyrox  = be16_to_cpu(val.gyrox);
		val.gyroy  = be16_to_cpu(val.gyroy);
		val.gyroz  = be16_to_cpu(val.gyroz);
		if (copy_to_user((struct atg_val __user *)arg, &val, sizeof(struct atg_val)))
			return -EFAULT;
		break;
	default:
		return -ENOTTY;
	}

	return 0;
}

static struct file_operations mpu6050_ops = {
	.owner = THIS_MODULE,
	.open = mpu6050_open,
	.release = mpu6050_release,
	.unlocked_ioctl = mpu6050_ioctl,
};

static int mpu6050_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	int ret;
	dev_t dev;
	struct mpu6050_dev *mpu6050;

	dev = MKDEV(FSRTC_MAJOR, FSRTC_MINOR);
	ret = register_chrdev_region(dev, 1, FSRTC_DEV_NAME);
	if (ret)
		goto reg_err;

	mpu6050 = kzalloc(sizeof(struct mpu6050_dev), GFP_KERNEL);
	if (!mpu6050) {
		ret = -ENOMEM;
		goto mem_err;
	}
	i2c_set_clientdata(client, mpu6050);
	mpu6050->client = client;

	cdev_init(&mpu6050->cdev, &mpu6050_ops);
	mpu6050->cdev.owner = THIS_MODULE;
	ret = cdev_add(&mpu6050->cdev, dev, 1);
	if (ret)
		goto add_err;

	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
		ret = -ENOSYS;
		goto fun_err;
	}

	i2c_smbus_write_byte_data(client, PWR_MGMT_1, 0x80);
	msleep(200);
	i2c_smbus_write_byte_data(client, PWR_MGMT_1, 0x40);
	i2c_smbus_write_byte_data(client, PWR_MGMT_1, 0x00);

	i2c_smbus_write_byte_data(client, SMPLRT_DIV,   0x7);
	i2c_smbus_write_byte_data(client, CONFIG,       0x6);
	i2c_smbus_write_byte_data(client, GYRO_CONFIG,  0x3 << 3); 
	i2c_smbus_write_byte_data(client, ACCEL_CONFIG, 0x3 << 3);

	atomic_set(&mpu6050->available, 1);

	return 0;

fun_err:
	cdev_del(&mpu6050->cdev);
add_err:
	kfree(mpu6050);
mem_err:
	unregister_chrdev_region(dev, 1);
reg_err:
	return ret;
}

static int mpu6050_remove(struct i2c_client *client)
{
	dev_t dev;
	struct mpu6050_dev *mpu6050 = i2c_get_clientdata(client);

	dev = MKDEV(FSRTC_MAJOR, FSRTC_MINOR);

	cdev_del(&mpu6050->cdev);
	kfree(mpu6050);
	unregister_chrdev_region(dev, 1);
	return 0;
}

static const struct i2c_device_id mpu6050_id[] = {
	{"mpu6050", 0},
	{}
};

MODULE_DEVICE_TABLE(i2c, mpu6050_id);

static struct i2c_driver mpu6050_driver = {
	.probe          =       mpu6050_probe,
	.remove         =       mpu6050_remove,
	.id_table       =       mpu6050_id,
	.driver = {
		.owner  =       THIS_MODULE,
		.name   =       "mpu6050",
	},
};

module_i2c_driver(mpu6050_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("name <e-mail>");
MODULE_DESCRIPTION("MPU6050 driver");
#ifndef _MPU6050_H
#define _MPU6050_H

struct atg_val {
	short accelx;
	short accely;
	short accelz;
	short temp;
	short gyrox;
	short gyroy;
	short gyroz;
};

#define MPU6050_MAGIC	'm'

#define MPU6050_GET_VAL	_IOR(MPU6050_MAGIC, 0, struct atg_val)

#endif


        代码第 43 行至第 47 行定义了 struct mpu6050_dev 结构类型,成员 client 用于保存匹配的 client 对象。代码第 178 行至第 195 行是对 i2C 驱动的定义和注册相关的操作。代127 行将分配得到的 struct mpu050_dev 结构对象地址存入 client 中,方便之后从cliem中获取。代码第 136 行使用 i2c_check_functionality 验证 i2C 机控制器是否具有一个的随机读写能力。代码第 141 行至第 149 是对 MPU6050 的初始化,和前面应用层的 i2C没备驱动一样。mpu6050_ioctl 函数中读取寄存器的值,将大端转换成小端后复制给用户测试用的应用层代码比较简单以下是编译和测试的命令。
 

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdio.h>

#include "mpu6050.h"


int main(int argc, char *argv[])
{
	int fd;
	struct atg_val val;


	fd = open("/dev/mpu6050", O_RDWR);

	while (2) {
		ioctl(fd, MPU6050_GET_VAL, &val);

		printf("accelx: %.2f\n", val.accelx / 2048.0);
		printf("accely: %.2f\n", val.accely / 2048.0);
		printf("accelz: %.2f\n", val.accelz / 2048.0);
		printf("temp: %.2f\n", val.temp / 340.0 + 36.53); 
		printf("gyrox: %.2f\n", val.gyrox / 16.4);
		printf("gyroy: %.2f\n", val.gyroy / 16.4);
		printf("gyroz: %.2f\n", val.gyroz / 16.4);


		sleep(1);
	}
}

_____________________________________________________________________________

十分抱歉这篇文章晚了一个月,前段时间受感情影响导致工作和生活以及学习都不顺利,直接摆烂了两个星期,想想十分对不起老师和喜欢我文章的读者们,后面不会了,那个小太阳又回来了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1155234.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

视频监控平台EasyCVR分组接口出现“pending”报错,该如何解决?

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台可拓展性强、视频能力灵活&#xff0c;能对外分发RTMP、RTSP、…

直线导轨的误差匹配度如何?

直线导轨的误差匹配度是评估导轨之间配合精度的重要指标&#xff0c;导轨之间的配合精度越高&#xff0c;误差匹配度就会越好&#xff0c;反之则越差。 在直线导轨的生产和加工过程中&#xff0c;每个导轨都会产生一定误差&#xff0c;例如平行误差、垂直误差、轨面平整度、滑块…

什么是跨域问题?如何解决?

跨域问题指的是不同站点之间,使用 ajax 无法相互调用的问题。跨域问题本质是浏览器的一种保护机制,它的初衷是为了保证用户的安全,防止恶意网站窃取数据。但这个保护机制也带来了新的问题,它的问题是给不同站点之间的正常调用,也带来的阻碍,那怎么解决这个问题呢?接下来…

2023软件测试八股文最新版(含答案+文档)

一、Web 自动化测试 1、Selenium 中 hidden 或者是 display &#xff1d; none 的元素是否可以定位到&#xff1f; 不能&#xff0c;可以写 JavaScript 将标签中的 hidden 先改为 0&#xff0c;再定位元素 2、Selenium 中如何保证操作元素的成功率&#xff1f;也就是说如何保…

连续分析:提高应用效率和成本效益的关键

作者&#xff1a;John Knoepfle 最近&#xff0c;Elastic Universal Profiling 已经正式发布。 它是我们可观察性解决方案的一部分&#xff0c;允许用户在生产环境中进行整个系统的连续分析。 如果你不熟悉连续分析&#xff0c;你可能想知道通用分析是什么以及为什么你应该关心…

【1++的Linux】之信号(一)

&#x1f44d;作者主页&#xff1a;进击的1 &#x1f929; 专栏链接&#xff1a;【1的Linux】 文章目录 一&#xff0c;关于信号二&#xff0c;深剖信号的产生1. 键盘组合建产生信号2.核心转储3. 系统调用接口产生信号4. 由软件条件产生信号5. 硬件异常产生信号 一&#xff0c;…

TSINGSEE青犀AI视频识别技术+危化安全生产智慧监管方案

一、背景分析 石油与化学工业生产过程复杂多样&#xff0c;涉及的物料易燃易爆、有毒有害&#xff0c;生产条件多高温高压、低温负压&#xff0c;现场危险化学品存储量大、危险源集中&#xff0c;重特大安全事故多发。打造基于工业互联网的安全生产新型能力&#xff0c;提高危…

storage数据存储问题,不能存undefined

这篇文章分享一下自己使用sessionStorage遇到的一个小问题&#xff0c;以后遇到要避坑。 需求是easyui表格的单元格编辑&#xff0c;点击保存的时候会结束当前行的编辑&#xff0c;然后修改editingId&#xff08;当前编辑行记录的ID&#xff09;。 待解决问题 如图&#xff0c…

操作系统的内存管理之虚拟空间

操作系统的内存管理&#xff0c;主要分为三个方面。 第一&#xff0c;物理内存的管理&#xff0c;相当于会议室管理员管理会议室。 第二&#xff0c;虚拟地址的管理&#xff0c;也即在项目组的视角&#xff0c;会议室的虚拟地址应该如何组织。 第三&#xff0c;虚拟地址和物…

vcomp140.dll丢失是什么意思,vcomp140.dll丢失这几个方法都能修复好

vcomp140.dll是什么&#xff1f; vcomp140.dll是一个动态链接库&#xff08;Dynamic Link Library&#xff09;&#xff0c;它主要用于支持Microsoft Visual C 2015编程语言的运行。这个文件包含了编译器相关的函数和资源&#xff0c;对于使用Visual C 2015开发的程序和游戏来…

Android图形系统之HWComposer、ComposerHal、ComposerImpl、Composer、Hwc2::Composer实例总结(十四)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药. 更多原创,欢迎关注:Android…

一款Nacos漏洞自动化工具

1、参考GitHub - charonlight/NacosExploitGUI: Nacos漏洞综合利用GUI工具&#xff0c;集成了默认口令漏洞、SQL注入漏洞、身份认证绕过漏洞、反序列化漏洞的检测及其利用 0x01 前言 ​ 本工具已经集成Nacos常见漏洞的检测及其利用&#xff0c;工具为GUI版本&#xff0c;简单…

[迁移学习]DA-DETR基于信息融合的自适应检测模型

原文标题为&#xff1a;DA-DETR: Domain Adaptive Detection Transformer with Information Fusion&#xff1b;发表于CVPR2023 一、概述 本文所描述的模型基于DETR&#xff0c;DETR网络是一种基于Transformer的目标检测网络&#xff0c;详细原理可以参见往期文章&#xff1a;…

k8s 资源预留

KUBERNETES资源管理之–资源预留 Kubernetes 的节点可以按照 Capacity 调度。node节点本身除了运行不少驱动 OS 和 Kubernetes 的系统守护进程&#xff0c;默认情况下 pod 能够使用节点全部可用容量&#xff0c; 除非为这些系统守护进程留出资源&#xff0c;否则它们将与 pod 争…

创造产业链协同优势后,凌雄科技在DaaS行业转动成长飞轮

企业服务领域&#xff0c;一直存在一种共识&#xff1a;做好很难&#xff0c;但一旦服务模式跑通了&#xff0c;得到了市场的认可&#xff0c;要滚起雪球就会事半功倍。 重资产、重运营的DaaS&#xff08;设备及服务&#xff09;赛道&#xff0c;是个非常典型的细分领域。在这…

泡泡玛特首度跨界超跑品牌兰博基尼汽车,以潮流基因探索时空边界

近期&#xff0c;泡泡玛特携手兰博基尼汽车&#xff0c;于上海国际赛车场进行了一场玩味十足的赛道体验。25位兰博基尼车主&#xff0c;及多位汽车领域知名媒体人、kol到场参与。兰博基尼跑车巡游、专业车手驾驶的兰博基尼涂装赛车试乘、MEGA SPACE MOLLY 1000%/400%兰博基尼汽…

深入理解计算机系统CS213学习笔记

Lecture 01 1. 计算机表示数字 int 整数运算可能会出现错误&#xff0c;超过32位时会出现溢出。 float 浮点数不适用结合律&#xff0c;因为浮点数表示的精度有限。 根其原因&#xff0c;是用有限的位数表示无限的数字空间。 2.利用分层的存储系统&#xff0c;使程序运行更…

第三届字节跳动奖学金官宣开奖,13位优秀科研学子每人获10万奖学金

最近&#xff0c;第三届字节跳动奖学金正式公布了获奖者名单。 经过字节跳动技术专家团队层层评审&#xff0c;本届字节跳动奖学金共有来自北京大学、复旦大学、清华大学、上海交通大学、香港科技大学、浙江大学、中国科学技术大学&#xff08;按拼音首字母排序&#xff09;的 …

MyBatis-Plus返回getOne返回null疑惑

getOne返回null 问题描述分析过程总结 问题描述 在数据库建了一张表主要包括两个字段master_id和slave_id;主要的额外字段max_lots 默认值是null&#xff1b;当调用getOne进行查询是&#xff0c;返回是null 分析过程 总结

MFC String类的初始化学习

之前写过CString的用法&#xff1b; VC CString 编程实例图解_bcbobo21cn, cstring-CSDN博客 下面单独看一下CString的各种初始化方式&#xff1b; void CTest2View::OnDraw(CDC* pDC) {CTest2Doc* pDoc GetDocument();ASSERT_VALID(pDoc);// TODO: add draw code for nati…