I2C总线驱动:裸机版、应用层的使用、二级外设驱动三种方法

一、I2C总线背景知识

SOC芯片平台的外设分为:

  1. 一级外设:外设控制器集成在SOC芯片内部
  2. 二级外设:外设控制器由另一块芯片负责,通过一些通讯总线与SOC芯片相连
    在这里插入图片描述
    Inter-Integrated Circuit: 字面意思是用于“集成电路之间”的通信总线,简写:IIC(或者I2C)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

i2c传输的要点就是: 传输一个字节 后面必然紧跟一个"响应"信号----应答信号.这个响应信号可能来自主机,或者是从机,具体是谁,就要看传输方向。
传输方向分两种情况(每种情况又有两种可能: A无应答和 B有应答):

1.主机->从机,主机对从机发一个字节之后,主机要读取从机的响应信号(主机读SDA线)

A) 主机读SDA为高电平,说明从机无应答(意味着从机接收完毕,主机发送停止信号)
B) 主机读SDA为低电平,说明从机有应答。(可继续发送下一个字节)

2.从机->主机, 主机读取从机一个字节之后,主机要向从机发送一个响应信号(主机写SDA线)

A) 主机写SDA为高电平,从机收到主机的无应答信号之后,从机停止传输,等待主机的停止信号。
​B) 主机写SDA为低电平,从机收到主机的应答信号之后,从机继续输出下一字节

二、Exynos4412 I2C收发实现之裸机版

I2CCON寄存器:控制寄存器

在这里插入图片描述

第7位:决定是否允许产生应答信号,无论发送还是接收前,需置1

第6位:传输时时钟线分频,一般选置1

第5位:决定是否开启发送或接收结束时发通知,无论发送还是接收前,需置1

第4位:接收或发送是否完毕可以通过检查此位是否为1,接收或发送完毕后需置0

I2CSTAT寄存器:状态寄存器
在这里插入图片描述

第6、7位:每次传输前需选择传输模式

第5位:置0产生将产生终止信号,传输前置1产生起始信号

第4位:使能数据输出,传输前需置1

I2CDS寄存器:数据寄存器,发送前被发送的数据存放处,接收后结果也从此处读取

2.1 发送

在这里插入图片描述
在这里插入图片描述

void iic_write (unsigned char slave_addr, unsigned char addr, unsigned char data)
{
    // 从设备寻址
    I2C5.I2CDS = slave_addr;
    I2C5.I2CCON = 1<<7 | 1<<6 | 1<<5; /* 启用 ACK 位, 预分频器: 512, 启用 RX/TX */

    I2C5.I2CSTAT = 0x3 << 6 | 1<<5 | 1<<4; /* 主传输模式, 启动, 启用 RX/TX */
    while(!(I2C5.I2CCON & (1<<4)));

    I2C5.I2CDS = addr;
    I2C5.I2CCON &= ~(1<<4); // 清除挂起位以恢复
    while(!(I2C5.I2CCON & (1<<4)));

    // 发送数据
    I2C5.I2CDS = data; // 数据
    I2C5.I2CCON &= ~(1<<4); // 清除挂起位以恢复
    while(!(I2C5.I2CCON & (1<<4)));

    I2C5.I2CSTAT = 0xD0; // 停止

    I2C5.I2CCON &= ~(1<<4); // 清除挂起位以恢复

    mydelay_ms(10);
}

2.2 接收

在这里插入图片描述在这里插入图片描述

void iic_read(unsigned char slave_addr, unsigned char addr, unsigned char *data)
{
    // 从设备寻址
    I2C5.I2CDS = slave_addr;

    I2C5.I2CCON = 1<<7 | 1<<6 | 1<<5; /* 启用 ACK 位, 预分频器: 512, 启用 RX/TX 中断使能 */
    I2C5.I2CSTAT = 0x3 << 6 | 1<<5 | 1<<4; /* 主传输模式, 启动, 启用 RX/TX */
    while(!(I2C5.I2CCON & (1<<4))); /* 对应位为1表示slave_addr传输完成,线路处于挂起状态 */

    I2C5.I2CDS = addr;
    I2C5.I2CCON &= ~(1<<4); // 清除挂起位以继续传输
    while(!(I2C5.I2CCON & (1<<4)));

    I2C5.I2CSTAT = 0xD0; // 停止  第5位写0,表示要求产生stop信号

    // 接收数据
    I2C5.I2CDS = slave_addr | 0x01; // 读取
    I2C5.I2CCON = 1<<7 | 1<<6 | 1<<5; /* 启用 ACK 位, 预分频器: 512, 启用 RX/TX 中断使能 */

    I2C5.I2CSTAT = 2<<6 | 1<<5 | 1<<4; /* 主接收模式, 启动, 启用 RX/TX, 0xB0 */
    while(!(I2C5.I2CCON & (1<<4)));

    I2C5.I2CCON &= ~((1<<7) | (1<<4)); /* 恢复操作 & 无 ACK */
    while(!(I2C5.I2CCON & (1<<4)));

    I2C5.I2CSTAT = 0x90; // 停止  第5位写0,表示要求产生stop信号
    I2C5.I2CCON &= ~(1<<4); /* 清除中断挂起位 */

    *data = I2C5.I2CDS;
    mydelay_ms(10);
}

三、Linux内核对I2C总线的支持

在这里插入图片描述

I2C设备驱动(driver驱动层):即挂接在I2C总线上的二级外设的驱动,也称客户(client)驱动,实现对二级外设的各种操作,二级外设的几乎所有操作全部依赖于对其自身内部寄存器的读写,对这些二级外设寄存器的读写又依赖于I2C总线的发送和接收

I2C总线驱动(访问抽象层、硬件实现控制层):即对I2C总线自身控制器的驱动,一般SOC芯片都会提供多个I2C总线控制器,每个I2C总线控制器提供一组I2C总线(SDA一根+SCL一根),每一组被称为一个I2C通道,Linux内核里将I2C总线控制器叫做适配器(adapter),适配器驱动主要工作就是提供通过本组I2C总线与二级外设进行数据传输的接口,每个二级外设驱动里必须能够获得其对应的adapter对象才能实现数据传输

I2C核心:承上启下,为I2C设备驱动和I2C总线驱动开发提供接口,为I2C设备驱动层提供管理多个i2c_driver、i2c_client对象的数据结构,为I2C总线驱动层提供多个i2c_algorithm、i2c_adapter对象的数据结构

四大核心对象之间的关系图

在这里插入图片描述

i2c二级外设驱动开发涉及到核心结构体及其相关接口函数:

struct i2c_board_info {
    char        type[I2C_NAME_SIZE];
    unsigned short  flags;
    unsigned short  addr;
    void        *platform_data;
    struct dev_archdata *archdata;
    struct device_node *of_node;
    int     irq;
};
/*用来协助创建i2c_client对象
重要成员
type:用来初始化i2c_client结构中的name成员
flags:用来初始化i2c_client结构中的flags成员
addr:用来初始化i2c_client结构中的addr成员
platform_data:用来初始化i2c_client结构中的.dev.platform_data成员
archdata:用来初始化i2c_client结构中的.dev.archdata成员
irq:用来初始化i2c_client结构中的irq成员

关键就是记住该结构和i2c_client结构成员的对应关系。在i2c子系统不直接创建i2c_client结构,只是提供struct i2c_board_info结构信息,让子系统动态创建,并且注册。
*/
struct i2c_client {
    unsigned short flags;
    unsigned short addr;
    char name[I2C_NAME_SIZE];
    struct i2c_adapter *adapter;
    struct i2c_driver *driver;
    struct device dev;
    int irq;
    struct list_head detected;
};
/*重要成员:
flags:地址长度,如是10位还是7位地址,默认是7位地址。如果是10位地址器件,则设置为I2C_CLIENT_TEN
addr:具体I2C器件如(at24c02),设备地址,低7位
name:设备名,用于和i2c_driver层匹配使用的,可以和平台模型中的平台设备层platform_driver中的name作用是一样的。
adapter:本设备所绑定的适配器结构(CPU有很多I2C适配器,类似单片机有串口1、串口2等等,在linux中每个适配器都用一个结构描述)
driver:指向匹配的i2c_driver结构,不需要自己填充,匹配上后内核会完成这个赋值操作
dev:内嵌的设备模型,可以使用其中的platform_data成员传递给任何数据给i2c_driver使用。
irq:设备需要使用到中断时,把中断编号传递给i2c_driver进行注册中断,如果没有就不需要填充。(有的I2C器件有中断引脚编号,与CPU相连)
*/

/* 获得/释放 i2c_adapter 路径:i2c-core.c linux-3.5\drivers\i2c */
/*功能:通过i2c总线编号获得内核中的i2c_adapter结构地址,然后用户可以使用这个结构地址就可以给i2c_client结构使用,从而实现i2c_client进行总线绑定,从而增加适配器引用计数。
返回值:
NULL:没有找到指定总线编号适配器结构
非NULL:指定nr的适配器结构内存地址*/
struct i2c_adapter *i2c_get_adapter(int nr);


/*减少引用计数:当使用·i2c_get_adapter·后,需要使用该函数减少引用计数。(如果你的适配器驱动不需要卸载,可以不使用)*/
void i2c_put_adapter(struct i2c_adapter *adap);

/*
功能:根据参数adap,info,addr,addr_list动态创建i2c_client并且进行注册
参数:
adap:i2c_client所依附的适配器结构地址
info:i2c_client基本信息
addt_list: i2c_client的地址(地址定义形式是固定的,一般是定义一个数组,数组必须以I2C_CLIENT_END结束,示例:unsigned short ft5x0x_i2c[]={0x38,I2C_CLIENT_END};
probe:回调函数指针,当创建好i2c_client后,会调用该函数,一般没有什么特殊需求传递NULL。
返回值:
非NULL:创建成功,返回创建好的i2c_client结构地址
NULL:创建失败
*/
struct i2c_client * i2c_new_probed_device
(
 struct i2c_adapter *adap,
 struct i2c_board_info *info,
 unsigned short const *addr_list,
 int (*probe)(struct i2c_adapter *, unsigned short addr)
);
/*示例:
struct i2c_adapter *ad;
struct i2c_board_info info={""};

unsigned short addr_list[]={0x38,0x39,I2C_CLIENT_END};

//假设设备挂在i2c-2总线上
ad=i2c_get_adapter(2);

//自己填充board_info 
strcpy(inf.type,"xxxxx");
info.flags=0;
//动态创建i2c_client并且注册
i2c_new_probed_device(ad,&info,addr_list,NULL);

i2c_put_adapter(ad);
*/

/*注销*/
void i2c_unregister_device(struct i2c_client *pclt)


 struct i2c_client * i2c_new_device
 (
     struct i2c_adapter *padap,
     struct i2c_board_info const *pinfo
 );
/*示例:
struct i2c_adapter *ad;
struct i2c_board_info info={
	I2C_BOARD_INFO(name,二级外设地址)
};
//假设设备挂在i2c-2总线上
ad=i2c_get_adapter(2);

//动态创建i2c_client并且注册
i2c_new_device(ad,&info);

i2c_put_adapter(ad);
*/
struct i2c_driver {
    unsigned int class;

    /* 标准驱动模型接口 */
    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 *);
	void (*alert)(struct i2c_client *, unsigned int data);

    /* 类似ioctl的命令,可用于执行特定功能 */
    int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);

    struct device_driver driver;
    const struct i2c_device_id *id_table;

    /* 用于自动设备创建的设备检测回调 */
    int (*detect)(struct i2c_client *, struct i2c_board_info *);
    const unsigned short *address_list;
    struct list_head clients;
};
/* 重要成员:
probe:在i2c_client与i2c_driver匹配后执行该函数
remove:在取消i2c_client与i2c_driver匹配绑定后执行该函数
driver:这个成员类型在平台设备驱动层中也有,而且使用其中的name成员来实现平台设备匹配,但是i2c子系统中不使用其中的name进行匹配,这也是i2c设备驱动模型和平台设备模型匹配方法的一点区别
id_table:用来实现i2c_client与i2c_driver匹配绑定,当i2c_client中的name成员和i2c_driver中id_table中name成员相同的时候,就匹配上了。

补充:i2c_client与i2c_driver匹配问题
- i2c_client中的name成员和i2c_driver中id_table中name成员相同的时候
- i2c_client指定的信息在物理上真实存放对应的硬件,并且工作是正常的才会绑定上,并执行其中的probe接口函数这第二点要求和平台模型匹配有区别,平台模型不要求设备层指定信息在物理上真实存在就能匹配
*/

/* 功能:向内核注册一个i2c_driver对象
返回值:0成功,负数 失败*/
#define i2c_add_driver(driver)     i2c_register_driver(THIS_MODULE, driver)
int i2c_register_driver(struct module *owner, struct i2c_driver *driver);

/* 功能:从内核注销一个i2c_driver对象
返回值:无 */
void i2c_del_driver(struct i2c_driver *driver);

struct i2c_msg {
    __u16 addr; /* slave address            */
    __u16 flags;
#define I2C_M_TEN       0x0010  /* this is a ten bit chip address */
#define I2C_M_RD        0x0001  /* read data, from slave to master */
    __u16 len;      /* msg length               */
    __u8 *buf;      /* pointer to msg data          */
};
/* 重要成员:
addr:要读写的二级外设地址
flags:表示地址的长度,读写功能。如果是10位地址必须设置I2C_M_TEN,如果是读操作必须设置有I2C_M_RD······,可以使用或运算合成。
buf:要读写的数据指针。写操作:数据源 读操作:指定存放数据的缓存区
len:读写数据的数据长度
*/

/*i2c收发一体化函数,收还是发由参数msgs的成员flags决定*/
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
/*
功能:根据msgs进行手法控制
参数:
adap:使用哪一个适配器发送信息,一般是取i2c_client结构中的adapter指针作为参数
msgs:具体发送消息指针,一般情况下是一个数组
num:表示前一个参数msgs数组有多少个消息要发送的
返回值:
负数:失败
> 0 表示成功发送i2c_msg数量
*/

/*I2C读取数据函数*/
int i2c_master_recv(const struct i2c_client *client, char *buf, int count)
/*功能:实现标准的I2C读时序,数据可以是N个数据,这个函数调用时候默认已经包含发送从机地址+读方向这一环节了
参数:
client:设备结构
buf:读取数据存放缓冲区
count:读取数据大小 不大于64k
返回值:
失败:负数
成功:成功读取的字节数
*/
    
/*I2C发送数据函数*/
int i2c_master_send(const struct i2c_client *client, const char *buf, int count)
/*功能:实现标准的I2C写时序,数据可以是N个数据,这个函数调用时候默认已经包含发送从机地址+写方向这一环节了
参数:
client:设备结构地址
buf:发送数据存放缓冲区
count:发送数据大小 不大于64k
返回值:
失败:负数
成功:成功发送的字节数
*/

四、MPU6050

三轴角速度+三轴加速度+温度传感器

在这里插入图片描述

#define SMPLRT_DIV  0x19 //陀螺仪采样率,典型值:0x07(125Hz)
#define CONFIG   0x1A //低通滤波频率,典型值:0x06(5Hz)
#define GYRO_CONFIG  0x1B //陀螺仪自检及测量范围,典型值:0xF8(不自检,+/-2000deg/s)
#define ACCEL_CONFIG 0x1C //加速计自检、测量范围,典型值:0x19(不自检,+/-G)
#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(正常启用)

五、应用层直接使用I2C通道

5.1 预备工作:

5.1.1 exynos4412平台每个i2c通道的信息是通过设备树提供的,因此需要首先在exynos4412-fs4412.dts中增加5通道的节点:

在这里插入图片描述

不要忘记:

  1. 回内核源码顶层目录执行:make dtbs
  2. 将新生成的dtb拷贝到/tftpboot

5.1.2 i2c总线驱动层提供了一个字符设备驱动,以便于应用层可以直接通过它去使用i2c总线通讯去操作二级外设,但需要

内核编译时添加此字符设备驱动代码(i2c-dev.c),因此需要修改make menuconfig的配置:

在这里插入图片描述

不要忘记:

  1. 回内核源码顶层目录执行:make uImage
  2. 将新生成的uImage拷贝到/tftpboot

5.2 应用层直接使用i2c总线的代码实现

缺点:

  1. 需要应用程序开发人员查阅原理图和芯片手册,增加了他们的开发负担
  2. 开发出的应用程序缺乏可移植性
5.2.1 调用read、write实现接收、发送

mpu6050.h

#ifndef MPU_6050_H
#define MPU_6050_H

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


int init_mpu6050(int fd);
int read_accelx(int fd);
int read_accely(int fd);
int read_accelz(int fd);
int read_temp(int fd);
int read_gyrox(int fd);
int read_gyroy(int fd);
int read_gyroz(int fd);


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

#define	SMPLRT_DIV		0x19	//陀螺仪采样率,典型值:0x07(125Hz)
#define	CONFIG			0x1A	//低通滤波频率,典型值:0x06(5Hz)
#define	GYRO_CONFIG		0x1B	//陀螺仪自检及测量范围,典型值:0x18(不自检,2000deg/s)
#define	ACCEL_CONFIG	0x1C	//加速计自检、测量范围及高通滤波频率,典型值:0x18(不自检,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	WHO_AM_I		0x75	//IIC地址寄存器(默认数值0x68,只读)
#define	SlaveAddress	0x68	//MPU6050-I2C地址

#define I2C_SLAVE	0x0703	/* Use this slave address */
#define I2C_TENBIT	0x0704	/* 0 for 7 bit addrs, != 0 for 10 bit */

#endif

mpu6050_op_rw.c

#include "mpu6050.h"

// 从MPU6050读取数据的函数
static int read_data_from_mpu6050(int fd, unsigned char reg, unsigned char *pdata)
{
    int ret = 0;
    unsigned char buf[1] = {reg};  // 准备要写入的寄存器地址

    // 写入要读取的寄存器地址
    ret = write(fd, buf, 1);
    if (ret != 1)
    {
        printf("write reg failed, in read_data_from_mpu6050\n");
        return -1;  // 写入失败,返回错误
    }

    buf[0] = 0;
    // 读取数据
    ret = read(fd, buf, 1);
    if (ret != 1)
    {
        printf("read data failed, in read_data_from_mpu6050\n");
        return -1;  // 读取失败,返回错误
    }

    *pdata = buf[0];  // 将读取的数据存储到 pdata 中
    return 0;  // 成功读取数据,返回0
}

// 向MPU6050写入数据的函数
static int write_data_to_mpu6050(int fd, unsigned char reg, unsigned char data)
{
    unsigned char buf[2] = {reg, data};  // 准备要写入的寄存器地址和数据
    int ret = 0;

    // 写入数据
    ret = write(fd, buf, 2);
    if (ret != 2)
    {
        printf("write data failed, in write_data_to_mpu6050\n");
        return -1;  // 写入失败,返回错误
    }

    return 0;  // 成功写入数据,返回0
}

// 初始化MPU6050的函数
int init_mpu6050(int fd)
{
    int ret = 0;

    // 使用ioctl配置I2C设备,将 I2C 设备配置为使用 7 位地址模式
    ret = ioctl(fd, I2C_TENBIT, 0);
    if (ret < 0)
    {
        printf("ioctl I2C_TENBIT failed, in init_mpu6050\n");
        return -1;  // 配置失败,返回错误
    }

	//设置I2C从设备地址,将 I2C 设备的从设备地址设置为 0x68
    ret = ioctl(fd, I2C_SLAVE, 0x68);
    if (ret < 0)
    {
        printf("ioctl I2C_TENBIT failed, in init_mpu6050\n");
        return -1;  // 配置失败,返回错误
    }

    // 向MPU6050写入初始化数据,设置各个寄存器的值
    ret = write_data_to_mpu6050(fd, PWR_MGMT_1, 0x00);
    ret += write_data_to_mpu6050(fd, SMPLRT_DIV, 0x07);
    ret += write_data_to_mpu6050(fd, ACCEL_CONFIG, 0x19);
    ret += write_data_to_mpu6050(fd, GYRO_CONFIG, 0xF8);
    if (ret < 0)
    {
        printf("write init data to mpu6050 failed, in init_mpu6050\n");
        return -1;  // 写入初始化数据失败,返回错误
    }

    return 0;  // 初始化成功,返回0
}

// 读取MPU6050加速度计X轴数据的函数
int read_accelx(int fd)
{
    unsigned short val = 0;
    unsigned char d = 0;
    int ret = 0;

    // 从MPU6050读取低位数据
    ret = read_data_from_mpu6050(fd, ACCEL_XOUT_L, &d);
    val = d;

    // 从MPU6050读取高位数据
    ret = read_data_from_mpu6050(fd, ACCEL_XOUT_H, &d);
    val |= d << 8;

    if (ret < 0)
    {
        printf("read accel x value failed, in read_accelx\n");
        return -1;  // 读取失败,返回错误
    }
    else
    {
        return val;  // 返回读取到的加速度计X轴数据
    }
}
int read_accely(int fd)
{
	unsigned short val = 0;
	unsigned char d = 0;
	int ret = 0;

	ret = read_data_from_mpu6050(fd,ACCEL_YOUT_L,&d);
	val = d;

	ret = read_data_from_mpu6050(fd,ACCEL_YOUT_H,&d);
	val |= d << 8;

	if(ret < 0)
	{
		printf("read accel y value failed,in read_accely\n");
		return -1;
	}
	else
	{
		return val;
	}
}

int read_accelz(int fd)
{
	unsigned short val = 0;
	unsigned char d = 0;
	int ret = 0;

	ret = read_data_from_mpu6050(fd,ACCEL_ZOUT_L,&d);
	val = d;

	ret = read_data_from_mpu6050(fd,ACCEL_ZOUT_H,&d);
	val |= d << 8;

	if(ret < 0)
	{
		printf("read accel z value failed,in read_accelz\n");
		return -1;
	}
	else
	{
		return val;
	}
}

int read_temp(int fd)
{
	unsigned short val = 0;
	unsigned char d = 0;
	int ret = 0;

	ret = read_data_from_mpu6050(fd,TEMP_OUT_L,&d);
	val = d;

	ret = read_data_from_mpu6050(fd,TEMP_OUT_H,&d);
	val |= d << 8;

	if(ret < 0)
	{
		printf("read temp value failed,in read_temp\n");
		return -1;
	}
	else
	{
		return val;
	}
}

int read_gyrox(int fd)
{
	unsigned short val = 0;
	unsigned char d = 0;
	int ret = 0;

	ret = read_data_from_mpu6050(fd,GYRO_XOUT_L,&d);
	val = d;

	ret = read_data_from_mpu6050(fd,GYRO_XOUT_H,&d);
	val |= d << 8;

	if(ret < 0)
	{
		printf("read gyro x value failed,in read_gyrox\n");
		return -1;
	}
	else
	{
		return val;
	}
}

int read_gyroy(int fd)
{
	unsigned short val = 0;
	unsigned char d = 0;
	int ret = 0;

	ret = read_data_from_mpu6050(fd,GYRO_YOUT_L,&d);
	val = d;

	ret = read_data_from_mpu6050(fd,GYRO_YOUT_H,&d);
	val |= d << 8;

	if(ret < 0)
	{
		printf("read gyro y value failed,in read_gyroy\n");
		return -1;
	}
	else
	{
		return val;
	}
}

int read_gyroz(int fd)
{
	unsigned short val = 0;
	unsigned char d = 0;
	int ret = 0;

	ret = read_data_from_mpu6050(fd,GYRO_ZOUT_L,&d);
	val = d;

	ret = read_data_from_mpu6050(fd,GYRO_ZOUT_H,&d);
	val |= d << 8;

	if(ret < 0)
	{
		printf("read gyro z value failed,in read_gyroz\n");
		return -1;
	}
	else
	{
		return val;
	}
}

main.c

#include "mpu6050.h"

int main(int argc, char *argv[])
{
	int fd = -1;

	if(argc < 2) {
		printf("Argument is too few\n");
		return 0;
	}

	/* open */
	fd = open(argv[1], O_RDWR);
	if(fd < 0) {
		printf("open %s failed\n", argv[1]);
		return -1;
	}

	/* init mpu6050 */
	init_mpu6050(fd);

	while(1) {

		sleep(2);
		/* read and printf data from mpu6050 */
		printf("Accel-X : 0x%x\n", read_accelx(fd));
		printf("Accel-Y : 0x%x\n", read_accely(fd));
		printf("Accel-Z : 0x%x\n", read_accelz(fd));
		printf("Temp : 0x%x\n", read_temp(fd));
		printf("Gyrox-X : 0x%x\n", read_gyrox(fd));
		printf("Gyroy-X : 0x%x\n", read_gyroy(fd));
		printf("Gyroz-X : 0x%x\n", read_gyroz(fd));
	}

	/* close */
	close(fd);
	fd = -1;
	return 0;
}
5.2.2 调用ioctl实现接收、发送

mpu6050.h

#ifndef MPU_6050_H
#define MPU_6050_H

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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


int init_mpu6050(int fd);
int read_accelx(int fd);
int read_accely(int fd);
int read_accelz(int fd);
int read_temp(int fd);
int read_gyrox(int fd);
int read_gyroy(int fd);
int read_gyroz(int fd);

#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

#define I2C_SLAVE	0x0703	/* 使用此从设备地址 */
#define I2C_TENBIT	0x0704	/* 0 表示 7 位地址,非 0 表示 10 位地址 */
#define I2C_RDWR	0x0707	/* 组合的读/写传输(只有一个 STOP) */

struct i2c_msg {
	unsigned short addr;	/* 从设备地址 */
	unsigned short flags;
#define I2C_M_TEN		0x0010	/* 这是十位地址的芯片地址 */
#define I2C_M_RD		0x0001	/* 读取数据,从从设备传输到主设备 */
	unsigned short len;		/* 消息长度 */
	unsigned char *buf;		/* 指向消息数据的指针 */
};

/* 此结构在 I2C_RDWR ioctl 调用中使用 */
struct i2c_rdwr_ioctl_data {
	struct i2c_msg *msgs;	/* 指向 i2c_msg 的指针 */
	unsigned int nmsgs;	/* i2c_msg 的数量 */
};
#endif

mpu6050_op_ioctl.c

#include "mpu6050.h"

static int read_data_from_mpu6050(int fd,unsigned char slave,unsigned char reg,unsigned char *pdata)
{
	struct i2c_rdwr_ioctl_data work = {NULL};
	struct i2c_msg msgs[2] = {{0}};
	unsigned char buf1[1] = {reg};
	unsigned char buf2[1] = {0};
	int ret = 0;

	work.msgs = msgs;
	work.nmsgs = 2;

	msgs[0].addr = slave;
	msgs[0].flags = 0;
	msgs[0].buf = buf1;
	msgs[0].len = 1;

	msgs[1].addr = slave;
	msgs[1].flags = I2C_M_RD;
	msgs[1].buf = buf2;
	msgs[1].len = 1;

	ret = ioctl(fd,I2C_RDWR,&work);
	if(ret < 0)
	{
		printf("ioctl I2C_RDWR failed,in read_data_from_mpu6050\n");
		return -1;
	}
	else
	{
		*pdata = buf2[0];
		return 0;
	}
}

static int write_data_to_mpu6050(int fd,unsigned char slave,unsigned char reg,unsigned char data)
{
	struct i2c_rdwr_ioctl_data work = {NULL};
	struct i2c_msg msg = {0};
	unsigned char buf[2] = {reg,data};
	int ret = 0;

	work.msgs = &msg;
	work.nmsgs = 1;

	msg.addr = slave;
	msg.flags = 0;
	msg.buf = buf;
	msg.len = 2;

	ret = ioctl(fd,I2C_RDWR,&work);
	if(ret < 0)
	{
		printf("ioctl I2C_RDWR failed,in write_data_to_mpu6050\n");
		return -1;
	}
	else
	{
		return 0;
	}
}

int init_mpu6050(int fd)
{
	int ret = 0;

	ret = ioctl(fd,I2C_TENBIT,0);
	if(ret < 0)
	{
		printf("ioctl I2C_TENBIT failed,in init_mpu6050\n");
		return -1;
	}

	ret = ioctl(fd,I2C_SLAVE,0x68);
	if(ret < 0)
	{
		printf("ioctl I2C_TENBIT failed,in init_mpu6050\n");
		return -1;
	}

	ret = write_data_to_mpu6050(fd,0x68,PWR_MGMT_1,0x00);
	ret += write_data_to_mpu6050(fd,0x68,SMPLRT_DIV,0x07);
	ret += write_data_to_mpu6050(fd,0x68,ACCEL_CONFIG,0x19);
	ret += write_data_to_mpu6050(fd,0x68,GYRO_CONFIG,0xF8);
	if(ret < 0)
	{
		printf("write init data to mpu6050 failed,in init_mpu6050\n");
		return -1;
	}

	return 0;
}

int read_accelx(int fd)
{
	unsigned short val = 0;
	unsigned char d = 0;
	int ret = 0;

	ret = read_data_from_mpu6050(fd,0x68,ACCEL_XOUT_L,&d);
	val = d;

	ret = read_data_from_mpu6050(fd,0x68,ACCEL_XOUT_H,&d);
	val |= d << 8;

	if(ret < 0)
	{
		printf("read accel x value failed,in read_accelx\n");
		return -1;
	}
	else
	{
		return val;
	}
}

int read_accely(int fd)
{
	unsigned short val = 0;
	unsigned char d = 0;
	int ret = 0;

	ret = read_data_from_mpu6050(fd,0x68,ACCEL_YOUT_L,&d);
	val = d;

	ret = read_data_from_mpu6050(fd,0x68,ACCEL_YOUT_H,&d);
	val |= d << 8;

	if(ret < 0)
	{
		printf("read accel y value failed,in read_accely\n");
		return -1;
	}
	else
	{
		return val;
	}
}

int read_accelz(int fd)
{
	unsigned short val = 0;
	unsigned char d = 0;
	int ret = 0;

	ret = read_data_from_mpu6050(fd,0x68,ACCEL_ZOUT_L,&d);
	val = d;

	ret = read_data_from_mpu6050(fd,0x68,ACCEL_ZOUT_H,&d);
	val |= d << 8;

	if(ret < 0)
	{
		printf("read accel z value failed,in read_accelz\n");
		return -1;
	}
	else
	{
		return val;
	}
}

int read_temp(int fd)
{
	unsigned short val = 0;
	unsigned char d = 0;
	int ret = 0;

	ret = read_data_from_mpu6050(fd,0x68,TEMP_OUT_L,&d);
	val = d;

	ret = read_data_from_mpu6050(fd,0x68,TEMP_OUT_H,&d);
	val |= d << 8;

	if(ret < 0)
	{
		printf("read temp value failed,in read_temp\n");
		return -1;
	}
	else
	{
		return val;
	}
}

int read_gyrox(int fd)
{
	unsigned short val = 0;
	unsigned char d = 0;
	int ret = 0;

	ret = read_data_from_mpu6050(fd,0x68,GYRO_XOUT_L,&d);
	val = d;

	ret = read_data_from_mpu6050(fd,0x68,GYRO_XOUT_H,&d);
	val |= d << 8;

	if(ret < 0)
	{
		printf("read gyro x value failed,in read_gyrox\n");
		return -1;
	}
	else
	{
		return val;
	}
}

int read_gyroy(int fd)
{
	unsigned short val = 0;
	unsigned char d = 0;
	int ret = 0;

	ret = read_data_from_mpu6050(fd,0x68,GYRO_YOUT_L,&d);
	val = d;

	ret = read_data_from_mpu6050(fd,0x68,GYRO_YOUT_H,&d);
	val |= d << 8;

	if(ret < 0)
	{
		printf("read gyro y value failed,in read_gyroy\n");
		return -1;
	}
	else
	{
		return val;
	}
}

int read_gyroz(int fd)
{
	unsigned short val = 0;
	unsigned char d = 0;
	int ret = 0;

	ret = read_data_from_mpu6050(fd,0x68,GYRO_ZOUT_L,&d);
	val = d;

	ret = read_data_from_mpu6050(fd,0x68,GYRO_ZOUT_H,&d);
	val |= d << 8;

	if(ret < 0)
	{
		printf("read gyro z value failed,in read_gyroz\n");
		return -1;
	}
	else
	{
		return val;
	}
}

main.c

#include "mpu6050.h"

int main(int argc,char *argv[])
{
	int fd = -1;
	if(argc < 2)
	{
		printf("Argument is too few\n");
		return 1;
	}

	/*open*/
	fd = open(argv[1],O_RDWR);
	if(fd < 0)
	{
		printf("open %s failed\n",argv[1]);
		return 2;
	}

	/*init mpu6050*/
	init_mpu6050(fd);

	while(1)
	{
		sleep(2);
		/*read and print data from 6050*/
		printf("Accel-X:0x%x\n",read_accelx(fd));
		printf("Accel-Y:0x%x\n",read_accely(fd));
		printf("Accel-Z:0x%x\n",read_accelz(fd));
		printf("Temp:0x%x\n",read_temp(fd));
		printf("GYRO-X:0x%x\n",read_gyrox(fd));
		printf("GYRO-Y:0x%x\n",read_gyroy(fd));
		printf("GYRO-z:0x%x\n",read_gyroz(fd));
		printf("\n");
	}


	/*close*/
	close(fd);
	fd = -1;
	return 0;
}

六、I2C总线二级外设驱动开发方法

  1. 查阅原理图以便得知二级外设挂在哪条I2C总线上、二级外设的身份标识(二级外设自身的地址)

  2. 参照platform样式搭建二级外设驱动框架

  3. 查询二级外设芯片手册以便得知驱动需要用到的寄存器地址

    注意:

    1. 此处寄存器是指二级外设内部的寄存器,每个寄存器在芯片手册里有个对应编号(也被称为地址),但不是内存地址,特别提醒此寄存器不是SOC芯片内部参与内存统一编址的寄存器,更不是ARM核-CPU的寄存器
    2. 通过调用i2c_tranfer函数完成与相应寄存器的数据交互
  4. 参照字符驱动完成其余代码编写

  5. 创建对应的i2c_client对象

    linux-3.14\Documentation\i2c\instantiating-devices

    匹配方式:

    1. 名称匹配

    2. 设备树匹配

    3. ACPI匹配

      Advanced Configuration and Power Management Interface 高级配置和电源管理接口

      PC机平台采用的一种硬件配置接口

i2c二级外设驱动框架:

//其它struct file_operations函数实现原理同硬编驱动

static int mpu6050_probe(struct i2c_client *pclt,const struct i2c_device_id *pid)
{
    //做硬编驱动模块入口函数的活
}

static int mpu6050_remove(struct i2c_client *pclt)
{
    //做硬编驱动模块出口函数的活
}

/*名称匹配时定义struct i2c_device_id数组*/
static struct i2c_device_id mpu6050_ids = 
{
    {"mpu6050",0},
    //.....
    {}
};

/*设备树匹配时定义struct of_device_id数组*/
static struct of_device_id mpu6050_dts =
{
    {.compatible = "invensense,mpu6050"},
    //....
    {}
};

/*通过定义struct i2c_driver类型的全局变量来创建i2c_driver对象,同时对其主要成员进行初始化*/
struct i2c_driver mpu6050_driver = 
{
	.driver = {
        .name = "mpu6050",
        .owner = THIS_MODULE,
        .of_match_table = mpu6050_dts,
    },
    .probe = mpu6050_probe,
    .remove = mpu6050_remove,
    .id_table = mpu6050_ids,
};

/*以下其实是个宏,展开后相当于实现了模块入口函数和模块出口函数*/
module_i2c_driver(mpu6050_driver);

MODULE_LICENSE("GPL");

七、I2C总线二级外设驱动开发之名称匹配

这种匹配方式需要自己创建i2c_client对象

创建i2c_client对象有三种方式:

1. i2c_register_board_info

1. 当开发板上电内核跑起来的时候,肯定是架构相关的程序首先运行,也就是mach-xxx.c
2. mach-xxx.c文件里首先会定义i2c_board_info的结构体数组,在mach-xxx.c的初始化函数里调用 
   i2c_register_board_info函数把i2c_board_inifo链接进内核的i2c_board_list链表当中去
3. 在驱动i2c目录下和开发板板对应的驱动文件i2c-xxx.c里,创建i2c_adapter对象
4. 这种方式严重依赖平台,缺乏灵活性,基本会被遗弃

2. i2c_new_device:明确二级外设地址的情况下可用

i2c二级外设client框架:

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

static struct i2c_board_info mpu6050_info = 
{
	I2C_BOARD_INFO("mpu6050",二级外设地址)   
};

static struct i2c_client *mpu6050_client;
static int __init mpu6050_dev_init(void)
{
    struct i2c_adapter *padp = NULL;
    padp = i2c_get_adapter(i2c通道编号);
    mpu6050_client = i2c_new_device(padp,&mpu6050_info);
    i2c_put_adapter(padp);
    return 0;
}
module_init(mpu6050_dev_init);

static void __exit mpu6050_dev_exit(void)
{
    i2c_unregister_device(mpu6050_client);
}
module_exit(mpu6050_dev_exit);
MODULE_LICENSE("GPL");

完整代码

mpu6050.h

#ifndef MPU_6050_H
#define MPU_6050_H

struct accel_data
{
	unsigned short x;
	unsigned short y;
	unsigned short z;
};
struct gyro_data
{
	unsigned short x;
	unsigned short y;
	unsigned short z;
};

union mpu6050_data
{
	struct accel_data accel;
	struct gyro_data gyro;
	unsigned short temp;
};

#define MPU6050_MAGIC 'K'

#define GET_ACCEL _IOR(MPU6050_MAGIC,0,union mpu6050_data)
#define GET_GYRO _IOR(MPU6050_MAGIC,1,union mpu6050_data)
#define GET_TEMP _IOR(MPU6050_MAGIC,2,union mpu6050_data)

#endif

mpu6050_client.c

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


static struct i2c_board_info mpu6050_info = 
{
	I2C_BOARD_INFO("mpu6050",0x68)
};

static struct i2c_client *gpmpu6050_client = NULL;

static int __init mpu6050_client_init(void)
{
	struct i2c_adapter *padp = NULL;

	padp = i2c_get_adapter(5);
	gpmpu6050_client = i2c_new_device(padp,&mpu6050_info);
	i2c_put_adapter(padp);
	return 0;
}

static void  mpu6050_client_exit(void)
{
	i2c_unregister_device(gpmpu6050_client);
}

module_init(mpu6050_client_init);
module_exit(mpu6050_client_exit);
MODULE_LICENSE("GPL");

mpu6050_drv.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/i2c.h>
#include <linux/cdev.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/io.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>

#include "mpu6050.h"

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

#define	SMPLRT_DIV		0x19	//陀螺仪采样率,典型值:0x07(125Hz)
#define	CONFIG			0x1A	//低通滤波频率,典型值:0x06(5Hz)
#define	GYRO_CONFIG		0x1B	//陀螺仪自检及测量范围,典型值:0x18(不自检,2000deg/s)
#define	ACCEL_CONFIG	0x1C	//加速计自检、测量范围及高通滤波频率,典型值:0x18(不自检,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	WHO_AM_I		0x75	//IIC地址寄存器(默认数值0x68,只读)
#define	SlaveAddress	0x68	//MPU6050-I2C地址


#define PWR_MGMT_1  0x6B

int major = 11;
int minor = 0;
int mpu6050_num  = 1;

struct mpu6050_dev
{
	struct cdev mydev;
	struct i2c_client *pclt;

};

struct mpu6050_dev *pgmydev = NULL;

int mpu6050_read_byte(struct i2c_client *pclt,unsigned char reg)
{
	int ret = 0;
	char txbuf[1] = {reg};
	char rxbuf[1] = {0};

	struct i2c_msg msg[2] = 
	{
		{pclt->addr,0,1,txbuf},
		{pclt->addr,I2C_M_RD,1,rxbuf}
	};

	ret = i2c_transfer(pclt->adapter,msg,ARRAY_SIZE(msg));
	if(ret < 0)
	{
		printk("ret = %d,in mpu6050_read_byte\n",ret);
		return ret;
	}

	return rxbuf[0];
}


int mpu6050_write_byte(struct i2c_client *pclt,unsigned char reg,unsigned char val)
{
	int ret = 0;
	char txbuf[2] = {reg,val};

	struct i2c_msg msg[1] = 
	{
		{pclt->addr,0,2,txbuf},
	};

	ret = i2c_transfer(pclt->adapter,msg,ARRAY_SIZE(msg));
	if(ret < 0)
	{
		printk("ret = %d,in mpu6050_write_byte\n",ret);
		return ret;
	}

	return 0;
}


int mpu6050_open(struct inode *pnode,struct file *pfile)
{
	pfile->private_data =(void *) (container_of(pnode->i_cdev,struct mpu6050_dev,mydev));
	
	return 0;
}

int mpu6050_close(struct inode *pnode,struct file *pfile)
{
	return 0;
}


long mpu6050_ioctl(struct file *pfile,unsigned int cmd,unsigned long arg)
{
	struct mpu6050_dev *pmydev = (struct mpu6050_dev *)pfile->private_data;
	union mpu6050_data data;

	switch(cmd)
	{
		case GET_ACCEL:
			data.accel.x = mpu6050_read_byte(pmydev->pclt,ACCEL_XOUT_L);
			data.accel.x = mpu6050_read_byte(pmydev->pclt,ACCEL_XOUT_H) << 8;
			
			data.accel.y = mpu6050_read_byte(pmydev->pclt,ACCEL_YOUT_L);
			data.accel.y = mpu6050_read_byte(pmydev->pclt,ACCEL_YOUT_H) << 8;

			data.accel.z = mpu6050_read_byte(pmydev->pclt,ACCEL_ZOUT_L);
			data.accel.z = mpu6050_read_byte(pmydev->pclt,ACCEL_ZOUT_H) << 8;
			break;
		case GET_GYRO:
			data.gyro.x = mpu6050_read_byte(pmydev->pclt,GYRO_XOUT_L);
			data.gyro.x = mpu6050_read_byte(pmydev->pclt,GYRO_XOUT_H) << 8;
			
			data.gyro.y = mpu6050_read_byte(pmydev->pclt,GYRO_YOUT_L);
			data.gyro.y = mpu6050_read_byte(pmydev->pclt,GYRO_YOUT_H) << 8;

			data.gyro.z = mpu6050_read_byte(pmydev->pclt,GYRO_ZOUT_L);
			data.gyro.z = mpu6050_read_byte(pmydev->pclt,GYRO_ZOUT_H) << 8;
			break;
		case GET_TEMP:
			data.temp = mpu6050_read_byte(pmydev->pclt,TEMP_OUT_L);
			data.temp = mpu6050_read_byte(pmydev->pclt,TEMP_OUT_H) << 8;
			break;
		default:
			return -EINVAL;
	}

	if(copy_to_user((void *)arg,&data,sizeof(data)))
	{
		return -EFAULT;
	}

	return sizeof(data);
}

void init_mpu6050(struct i2c_client *pclt)
{
	mpu6050_write_byte(pclt,PWR_MGMT_1,0x00);
	mpu6050_write_byte(pclt,SMPLRT_DIV,0x07);
	mpu6050_write_byte(pclt,CONFIG,0x06);
	mpu6050_write_byte(pclt,GYRO_CONFIG,0xF8);
	mpu6050_write_byte(pclt,ACCEL_CONFIG,0x19);
}

struct file_operations myops = {
	.owner = THIS_MODULE,
	.open = mpu6050_open,
	.release = mpu6050_close,
	.unlocked_ioctl = mpu6050_ioctl,
};

static int mpu6050_probe(struct i2c_client *pclt,const struct i2c_device_id *pid)
{
	int ret = 0;
	dev_t devno = MKDEV(major,minor);

	/*申请设备号*/
	ret = register_chrdev_region(devno,mpu6050_num,"mpu6050");
	if(ret)
	{
		ret = alloc_chrdev_region(&devno,minor,mpu6050_num,"mpu6050");
		if(ret)
		{
			printk("get devno failed\n");
			return -1;
		}
		major = MAJOR(devno);//容易遗漏,注意
	}

	pgmydev = (struct mpu6050_dev *)kmalloc(sizeof(struct mpu6050_dev),GFP_KERNEL);
	if(NULL == pgmydev)
	{
		unregister_chrdev_region(devno,mpu6050_num);
		printk("kmalloc failed\n");
		return -1;
	}
	memset(pgmydev,0,sizeof(struct mpu6050_dev));

	pgmydev->pclt = pclt;

	/*给struct cdev对象指定操作函数集*/	
	cdev_init(&pgmydev->mydev,&myops);

	/*将struct cdev对象添加到内核对应的数据结构里*/
	pgmydev->mydev.owner = THIS_MODULE;
	cdev_add(&pgmydev->mydev,devno,mpu6050_num);

	init_mpu6050(pgmydev->pclt);

	return 0;
}

static int mpu6050_remove(struct i2c_client *pclt)
{
	dev_t devno = MKDEV(major,minor);


	cdev_del(&pgmydev->mydev);

	unregister_chrdev_region(devno,mpu6050_num);

	kfree(pgmydev);
	pgmydev = NULL;

	return 0;
}

struct i2c_device_id mpu6050_ids[] = 
{
	{"mpu6050",0},
	{}
};

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

#if 0
	int __init mpu6050_driver_init(void)
	{
		i2c_add_driver(&mpu6050_driver);
	}
	
	void __exit mpu6050_driver_exit(void)
	{
		i2c_del_driver(&mpu6050_driver);
	}
	module_init(mpu6050_driver_init);
	module_exit(mpu6050_driver_exit);
#else
	module_i2c_driver(mpu6050_driver);
#endif

MODULE_LICENSE("GPL");

testapp.c

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


#include <stdio.h>

#include "mpu6050.h"

int main(int argc,char *argv[])
{
	int fd = -1;
	union mpu6050_data data;

	if(argc < 2)
	{
		printf("The argument is too few\n");
		return 1;
	}

	fd = open(argv[1],O_RDONLY);
	if(fd < 0)
	{
		printf("open %s failed \n",argv[1]);
		return 2;
	}

	while(1)
	{
		sleep(2);

		ioctl(fd,GET_ACCEL,&data);
		printf("Accel-x=0x%x\n",data.accel.x);
		printf("Accel-y=0x%x\n",data.accel.y);
		printf("Accel-z=0x%x\n",data.accel.z);

		ioctl(fd,GET_GYRO,&data);
		printf("Gyro-x=0x%x\n",data.gyro.x);
		printf("Gyro-y=0x%x\n",data.gyro.y);
		printf("Gyro-z=0x%x\n",data.gyro.z);

		ioctl(fd,GET_TEMP,&data);
		printf("Temp=0x%x\n",data.temp);

		printf("\n");
	}


	close(fd);
	fd = -1;
	return 0;
}

输出结果:
在这里插入图片描述

3. i2c_new_probed_device:不明确二级外设地址

i2c二级外设client框架:不明确二级外设地址,但是知道是可能几个值之一的情况下可用

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

static const unsigned short addr_list[] = 
{
	0x68,
    //.....
    I2C_CLIENT_END
};

static struct i2c_client *mpu6050_client;
static int __init mpu6050_dev_init(void)
{
    struct i2c_adapter *padp = NULL;
    struct i2c_board_info mpu6050_info = {""};
    
    strcpy(mpu6050_info.type,"mpu6050");
    
    padp = i2c_get_adapter(i2c通道编号);
    mpu6050_client = i2c_new_probed_device(padp,&mpu6050_info,addr_list,NULL);
    i2c_put_adapter(padp);
    if(mpu6050_client != NULL)
    {
        return 0;
    }
    else
    {
    	return -ENODEV;
    }
}
module_init(mpu6050_dev_init);

static void __exit mpu6050_dev_exit(void)
{
    i2c_unregister_device(mpu6050_client);
}
module_exit(mpu6050_dev_exit);
MODULE_LICENSE("GPL");

完整代码

mpu6050.h

#ifndef MPU_6050_H
#define MPU_6050_H

struct accel_data
{
	unsigned short x;
	unsigned short y;
	unsigned short z;
};
struct gyro_data
{
	unsigned short x;
	unsigned short y;
	unsigned short z;
};

union mpu6050_data
{
	struct accel_data accel;
	struct gyro_data gyro;
	unsigned short temp;
};

#define MPU6050_MAGIC 'K'

#define GET_ACCEL _IOR(MPU6050_MAGIC,0,union mpu6050_data)
#define GET_GYRO _IOR(MPU6050_MAGIC,1,union mpu6050_data)
#define GET_TEMP _IOR(MPU6050_MAGIC,2,union mpu6050_data)

#endif

mpu6050_client_probed.c

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


static unsigned short mpu6050_addr_list[] = 
{
	0x68,
	0x69,
	I2C_CLIENT_END
};

static struct i2c_client *gpmpu6050_client = NULL;

static int __init mpu6050_client_init(void)
{
	struct i2c_adapter *padp = NULL;
	struct i2c_board_info mpu6050_info = {""};


	strcpy(mpu6050_info.type,"mpu6050");
	padp = i2c_get_adapter(5);
	gpmpu6050_client = i2c_new_probed_device(padp,&mpu6050_info,mpu6050_addr_list,NULL);
	i2c_put_adapter(padp);

	if(gpmpu6050_client != NULL)
	{
		return 0;
	}
	else
	{
		return -ENODEV;
	}
}

static void  mpu6050_client_exit(void)
{
	i2c_unregister_device(gpmpu6050_client);
}

module_init(mpu6050_client_init);
module_exit(mpu6050_client_exit);
MODULE_LICENSE("GPL");

mpu6050_drv.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/i2c.h>
#include <linux/cdev.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/io.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>

#include "mpu6050.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

int major = 11;
int minor = 0;
int mpu6050_num  = 1;

struct mpu6050_dev
{
	struct cdev mydev;
	struct i2c_client *pclt;

};

struct mpu6050_dev *pgmydev = NULL;

int mpu6050_read_byte(struct i2c_client *pclt,unsigned char reg)
{
	int ret = 0;
	char txbuf[1] = {reg};
	char rxbuf[1] = {0};

	struct i2c_msg msg[2] = 
	{
		{pclt->addr,0,1,txbuf},
		{pclt->addr,I2C_M_RD,1,rxbuf}
	};

	ret = i2c_transfer(pclt->adapter,msg,ARRAY_SIZE(msg));
	if(ret < 0)
	{
		printk("ret = %d,in mpu6050_read_byte\n",ret);
		return ret;
	}

	return rxbuf[0];
}


int mpu6050_write_byte(struct i2c_client *pclt,unsigned char reg,unsigned char val)
{
	int ret = 0;
	char txbuf[2] = {reg,val};

	struct i2c_msg msg[1] = 
	{
		{pclt->addr,0,2,txbuf},
	};

	ret = i2c_transfer(pclt->adapter,msg,ARRAY_SIZE(msg));
	if(ret < 0)
	{
		printk("ret = %d,in mpu6050_write_byte\n",ret);
		return ret;
	}

	return 0;
}


int mpu6050_open(struct inode *pnode,struct file *pfile)
{
	pfile->private_data =(void *) (container_of(pnode->i_cdev,struct mpu6050_dev,mydev));
	
	return 0;
}

int mpu6050_close(struct inode *pnode,struct file *pfile)
{
	return 0;
}


long mpu6050_ioctl(struct file *pfile,unsigned int cmd,unsigned long arg)
{
	struct mpu6050_dev *pmydev = (struct mpu6050_dev *)pfile->private_data;
	union mpu6050_data data;

	switch(cmd)
	{
		case GET_ACCEL:
			data.accel.x = mpu6050_read_byte(pmydev->pclt,ACCEL_XOUT_L);
			data.accel.x = mpu6050_read_byte(pmydev->pclt,ACCEL_XOUT_H) << 8;
			
			data.accel.y = mpu6050_read_byte(pmydev->pclt,ACCEL_YOUT_L);
			data.accel.y = mpu6050_read_byte(pmydev->pclt,ACCEL_YOUT_H) << 8;

			data.accel.z = mpu6050_read_byte(pmydev->pclt,ACCEL_ZOUT_L);
			data.accel.z = mpu6050_read_byte(pmydev->pclt,ACCEL_ZOUT_H) << 8;
			break;
		case GET_GYRO:
			data.gyro.x = mpu6050_read_byte(pmydev->pclt,GYRO_XOUT_L);
			data.gyro.x = mpu6050_read_byte(pmydev->pclt,GYRO_XOUT_H) << 8;
			
			data.gyro.y = mpu6050_read_byte(pmydev->pclt,GYRO_YOUT_L);
			data.gyro.y = mpu6050_read_byte(pmydev->pclt,GYRO_YOUT_H) << 8;

			data.gyro.z = mpu6050_read_byte(pmydev->pclt,GYRO_ZOUT_L);
			data.gyro.z = mpu6050_read_byte(pmydev->pclt,GYRO_ZOUT_H) << 8;
			break;
		case GET_TEMP:
			data.temp = mpu6050_read_byte(pmydev->pclt,TEMP_OUT_L);
			data.temp = mpu6050_read_byte(pmydev->pclt,TEMP_OUT_H) << 8;
			break;
		default:
			return -EINVAL;
	}

	if(copy_to_user((void *)arg,&data,sizeof(data)))
	{
		return -EFAULT;
	}

	return sizeof(data);
}

void init_mpu6050(struct i2c_client *pclt)
{
	mpu6050_write_byte(pclt,PWR_MGMT_1,0x00);
	mpu6050_write_byte(pclt,SMPLRT_DIV,0x07);
	mpu6050_write_byte(pclt,CONFIG,0x06);
	mpu6050_write_byte(pclt,GYRO_CONFIG,0xF8);
	mpu6050_write_byte(pclt,ACCEL_CONFIG,0x19);
}

struct file_operations myops = {
	.owner = THIS_MODULE,
	.open = mpu6050_open,
	.release = mpu6050_close,
	.unlocked_ioctl = mpu6050_ioctl,
};

static int mpu6050_probe(struct i2c_client *pclt,const struct i2c_device_id *pid)
{
	int ret = 0;
	dev_t devno = MKDEV(major,minor);

	/*申请设备号*/
	ret = register_chrdev_region(devno,mpu6050_num,"mpu6050");
	if(ret)
	{
		ret = alloc_chrdev_region(&devno,minor,mpu6050_num,"mpu6050");
		if(ret)
		{
			printk("get devno failed\n");
			return -1;
		}
		major = MAJOR(devno);//容易遗漏,注意
	}

	pgmydev = (struct mpu6050_dev *)kmalloc(sizeof(struct mpu6050_dev),GFP_KERNEL);
	if(NULL == pgmydev)
	{
		unregister_chrdev_region(devno,mpu6050_num);
		printk("kmalloc failed\n");
		return -1;
	}
	memset(pgmydev,0,sizeof(struct mpu6050_dev));

	pgmydev->pclt = pclt;

	/*给struct cdev对象指定操作函数集*/	
	cdev_init(&pgmydev->mydev,&myops);

	/*将struct cdev对象添加到内核对应的数据结构里*/
	pgmydev->mydev.owner = THIS_MODULE;
	cdev_add(&pgmydev->mydev,devno,mpu6050_num);

	init_mpu6050(pgmydev->pclt);

	return 0;
}

static int mpu6050_remove(struct i2c_client *pclt)
{
	dev_t devno = MKDEV(major,minor);


	cdev_del(&pgmydev->mydev);

	unregister_chrdev_region(devno,mpu6050_num);

	kfree(pgmydev);
	pgmydev = NULL;

	return 0;
}

struct i2c_device_id mpu6050_ids[] = 
{
	{"mpu6050",0},
	{}
};

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

#if 0
	int __init mpu6050_driver_init(void)
	{
		i2c_add_driver(&mpu6050_driver);
	}
	
	void __exit mpu6050_driver_exit(void)
	{
		i2c_del_driver(&mpu6050_driver);
	}
	module_init(mpu6050_driver_init);
	module_exit(mpu6050_driver_exit);
#else
	module_i2c_driver(mpu6050_driver);
#endif

MODULE_LICENSE("GPL");

testapp.c

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


#include <stdio.h>

#include "mpu6050.h"

int main(int argc,char *argv[])
{
	int fd = -1;
	union mpu6050_data data;

	if(argc < 2)
	{
		printf("The argument is too few\n");
		return 1;
	}

	fd = open(argv[1],O_RDONLY);
	if(fd < 0)
	{
		printf("open %s failed \n",argv[1]);
		return 2;
	}

	while(1)
	{
		sleep(2);

		ioctl(fd,GET_ACCEL,&data);
		printf("Accel-x=0x%x\n",data.accel.x);
		printf("Accel-y=0x%x\n",data.accel.y);
		printf("Accel-z=0x%x\n",data.accel.z);

		ioctl(fd,GET_GYRO,&data);
		printf("Gyro-x=0x%x\n",data.gyro.x);
		printf("Gyro-y=0x%x\n",data.gyro.y);
		printf("Gyro-z=0x%x\n",data.gyro.z);

		ioctl(fd,GET_TEMP,&data);
		printf("Temp=0x%x\n",data.temp);

		printf("\n");
	}


	close(fd);
	fd = -1;
	return 0;
}

输出结果:
在这里插入图片描述

八、I2C总线二级外设驱动开发之设备树匹配

在这里插入图片描述

完整代码

mpu6050.h

#ifndef MPU_6050_H
#define MPU_6050_H

struct accel_data
{
	unsigned short x;
	unsigned short y;
	unsigned short z;
};
struct gyro_data
{
	unsigned short x;
	unsigned short y;
	unsigned short z;
};

union mpu6050_data
{
	struct accel_data accel;
	struct gyro_data gyro;
	unsigned short temp;
};

#define MPU6050_MAGIC 'K'

#define GET_ACCEL _IOR(MPU6050_MAGIC,0,union mpu6050_data)
#define GET_GYRO _IOR(MPU6050_MAGIC,1,union mpu6050_data)
#define GET_TEMP _IOR(MPU6050_MAGIC,2,union mpu6050_data)


#endif

mpu6050_drv.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/i2c.h>
#include <linux/cdev.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/io.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>

#include "mpu6050.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

int major = 11;
int minor = 0;
int mpu6050_num  = 1;

struct mpu6050_dev
{
	struct cdev mydev;
	struct i2c_client *pclt;

};

struct mpu6050_dev *pgmydev = NULL;

int mpu6050_read_byte(struct i2c_client *pclt,unsigned char reg)
{
	int ret = 0;
	char txbuf[1] = {reg};
	char rxbuf[1] = {0};

	struct i2c_msg msg[2] = 
	{
		{pclt->addr,0,1,txbuf},
		{pclt->addr,I2C_M_RD,1,rxbuf}
	};

	ret = i2c_transfer(pclt->adapter,msg,ARRAY_SIZE(msg));
	if(ret < 0)
	{
		printk("ret = %d,in mpu6050_read_byte\n",ret);
		return ret;
	}

	return rxbuf[0];
}


int mpu6050_write_byte(struct i2c_client *pclt,unsigned char reg,unsigned char val)
{
	int ret = 0;
	char txbuf[2] = {reg,val};

	struct i2c_msg msg[1] = 
	{
		{pclt->addr,0,2,txbuf},
	};

	ret = i2c_transfer(pclt->adapter,msg,ARRAY_SIZE(msg));
	if(ret < 0)
	{
		printk("ret = %d,in mpu6050_write_byte\n",ret);
		return ret;
	}

	return 0;
}


int mpu6050_open(struct inode *pnode,struct file *pfile)
{
	pfile->private_data =(void *) (container_of(pnode->i_cdev,struct mpu6050_dev,mydev));
	
	return 0;
}

int mpu6050_close(struct inode *pnode,struct file *pfile)
{
	return 0;
}


long mpu6050_ioctl(struct file *pfile,unsigned int cmd,unsigned long arg)
{
	struct mpu6050_dev *pmydev = (struct mpu6050_dev *)pfile->private_data;
	union mpu6050_data data;

	switch(cmd)
	{
		case GET_ACCEL:
			data.accel.x = mpu6050_read_byte(pmydev->pclt,ACCEL_XOUT_L);
			data.accel.x = mpu6050_read_byte(pmydev->pclt,ACCEL_XOUT_H) << 8;
			
			data.accel.y = mpu6050_read_byte(pmydev->pclt,ACCEL_YOUT_L);
			data.accel.y = mpu6050_read_byte(pmydev->pclt,ACCEL_YOUT_H) << 8;

			data.accel.z = mpu6050_read_byte(pmydev->pclt,ACCEL_ZOUT_L);
			data.accel.z = mpu6050_read_byte(pmydev->pclt,ACCEL_ZOUT_H) << 8;
			break;
		case GET_GYRO:
			data.gyro.x = mpu6050_read_byte(pmydev->pclt,GYRO_XOUT_L);
			data.gyro.x = mpu6050_read_byte(pmydev->pclt,GYRO_XOUT_H) << 8;
			
			data.gyro.y = mpu6050_read_byte(pmydev->pclt,GYRO_YOUT_L);
			data.gyro.y = mpu6050_read_byte(pmydev->pclt,GYRO_YOUT_H) << 8;

			data.gyro.z = mpu6050_read_byte(pmydev->pclt,GYRO_ZOUT_L);
			data.gyro.z = mpu6050_read_byte(pmydev->pclt,GYRO_ZOUT_H) << 8;
			break;
		case GET_TEMP:
			data.temp = mpu6050_read_byte(pmydev->pclt,TEMP_OUT_L);
			data.temp = mpu6050_read_byte(pmydev->pclt,TEMP_OUT_H) << 8;
			break;
		default:
			return -EINVAL;
	}

	if(copy_to_user((void *)arg,&data,sizeof(data)))
	{
		return -EFAULT;
	}

	return sizeof(data);
}

void init_mpu6050(struct i2c_client *pclt)
{
	mpu6050_write_byte(pclt,PWR_MGMT_1,0x00);
	mpu6050_write_byte(pclt,SMPLRT_DIV,0x07);
	mpu6050_write_byte(pclt,CONFIG,0x06);
	mpu6050_write_byte(pclt,GYRO_CONFIG,0xF8);
	mpu6050_write_byte(pclt,ACCEL_CONFIG,0x19);
}

struct file_operations myops = {
	.owner = THIS_MODULE,
	.open = mpu6050_open,
	.release = mpu6050_close,
	.unlocked_ioctl = mpu6050_ioctl,
};

static int mpu6050_probe(struct i2c_client *pclt,const struct i2c_device_id *pid)
{
	int ret = 0;
	dev_t devno = MKDEV(major,minor);

	/*申请设备号*/
	ret = register_chrdev_region(devno,mpu6050_num,"mpu6050");
	if(ret)
	{
		ret = alloc_chrdev_region(&devno,minor,mpu6050_num,"mpu6050");
		if(ret)
		{
			printk("get devno failed\n");
			return -1;
		}
		major = MAJOR(devno);//容易遗漏,注意
	}

	pgmydev = (struct mpu6050_dev *)kmalloc(sizeof(struct mpu6050_dev),GFP_KERNEL);
	if(NULL == pgmydev)
	{
		unregister_chrdev_region(devno,mpu6050_num);
		printk("kmalloc failed\n");
		return -1;
	}
	memset(pgmydev,0,sizeof(struct mpu6050_dev));

	pgmydev->pclt = pclt;

	/*给struct cdev对象指定操作函数集*/	
	cdev_init(&pgmydev->mydev,&myops);

	/*将struct cdev对象添加到内核对应的数据结构里*/
	pgmydev->mydev.owner = THIS_MODULE;
	cdev_add(&pgmydev->mydev,devno,mpu6050_num);

	init_mpu6050(pgmydev->pclt);

	return 0;
}

static int mpu6050_remove(struct i2c_client *pclt)
{
	dev_t devno = MKDEV(major,minor);


	cdev_del(&pgmydev->mydev);

	unregister_chrdev_region(devno,mpu6050_num);

	kfree(pgmydev);
	pgmydev = NULL;

	return 0;
}

struct of_device_id mpu6050_dt[] = 
{
	{.compatible = "invensense,mpu6050"},
	{}
};


struct i2c_device_id mpu6050_ids[] = 
{
	{"mpu6050",0},
	{}
};


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

#if 0
int __init mpu6050_driver_init(void)
{
	i2c_add_driver(&mpu6050_driver);
}

void __exit mpu6050_driver_exit(void)
{
	i2c_del_driver(&mpu6050_driver);
}
module_init(mpu6050_driver_init);
module_exit(mpu6050_driver_exit);
#else
module_i2c_driver(mpu6050_driver);
#endif

MODULE_LICENSE("GPL");

testapp.c

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


#include <stdio.h>

#include "mpu6050.h"

int main(int argc,char *argv[])
{
	int fd = -1;
	union mpu6050_data data;

	if(argc < 2)
	{
		printf("The argument is too few\n");
		return 1;
	}

	fd = open(argv[1],O_RDONLY);
	if(fd < 0)
	{
		printf("open %s failed \n",argv[1]);
		return 2;
	}

	while(1)
	{
		sleep(2);

		ioctl(fd,GET_ACCEL,&data);
		printf("Accel-x=0x%x\n",data.accel.x);
		printf("Accel-y=0x%x\n",data.accel.y);
		printf("Accel-z=0x%x\n",data.accel.z);

		ioctl(fd,GET_GYRO,&data);
		printf("Gyro-x=0x%x\n",data.gyro.x);
		printf("Gyro-y=0x%x\n",data.gyro.y);
		printf("Gyro-z=0x%x\n",data.gyro.z);

		ioctl(fd,GET_TEMP,&data);
		printf("Temp=0x%x\n",data.temp);

		printf("\n");
	}


	close(fd);
	fd = -1;
	return 0;
}

输出结果:
在这里插入图片描述

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1.EtherCAT为目前最快速之工业网络技术,优点为国际标准安全协议、高效率低成本、精确同步、灵活拓朴结构、冗馀回复功能。 2.因应工业4.0智慧制造智慧工厂等工业自动化市场需求的增加,InfineonMCUXMC4800集成提供EtherCATSlave工业乙太网路控制技术,实现工业自动化控制、智慧生产制造机器人、远端总线监控等应用。 3.InfineonXMC4800/XMC4300为EtherCAT协会会员产品,客户可基于XMC4800开发EtherCATslave远端AIO/DIO模组、EtherCATP电源、EtherCATslave马达运动控制器、机械手臂……等工控产品。 4.展示板XMC4800集成MCU和EtherCATslaveASIC,应用于EtherCATSlave产品。 5.EtherCATMasterPC使用开源工具TwinCAT3监控Slave。 6.8通道ISOFACE数位输入接口可监视8个设备节点,8通道ISOFACE数位输出接口可控制8个设备节点,目前广泛应用于工厂自动化和楼宇自动化产品。 场景应用图产品实体图展示板照片方案方块图核心技术优势一、芯片优势: 1. XMC4800 MCU based on ARM Cortex-M4 at 144 MHz, 2 MB flash and 352 KB SRAM. 2. XMC4800 integrated Beckhoff ET1100 EtherCAT ASIC, interface capable of 100 Mbit/s transfer rates with 2 MII ports. 3. 通讯单元:Ethernet, USB, CAN bus, UART, SPI, IIC, IIS, external memory, SD/MMC card. 4. 外设单元:ADC 12-bit, DAC 12-bit, POSIF and DSD for motor control. 5. PWM CCU4/CCU8 support capture, compare, timer, counter, mosfet dead time. 二、EtherCAT slave demo: 1. 软体工具说明: (1) XMC_ESC excel:定义 Isoface 8ch DI/8ch DO位址和输出输入格式。 (2) Beckhoff Slave Stack Code Tool:简称SSC Tool为代码产生器,产生EtherCAT SSC code and ESI file. (3) DAVE4:Infineon XMC IDE. (4) TwinCAT2/TwinCAT3:EtherCAT master monitor. 2. XMC_ESC 定义 EtherCAT slave设备位址和输出输入格式。 3. SSC tool代码产生器,产生SSC code, ESI file和ETG产品资讯。 4. XMC_ESC.c 撰写展示板对应位址和通道之程式。 5. DAVE4撰写XMC4800 and EtherCAT slave 应用层,打通EtherCAT slave控制。 6. TwinCAT3为EtherCAT monitor,模拟控制工厂远端设备节点。 方案规格1. Infineon XMC4800集成支持工业乙太网路从站自动化控制应用。 2. EtherCAT slave controller support 1 MII port input, 1MII port output. 3. 24V 8通道数位输入模组,具备电源隔离与故障保护,可监控工厂设备输入讯号。 4. 24V 8通道数位输出模组,具备电源隔离与故障保护,可远端控制工厂设备输出讯号。 5. OPTIGA Trust E 适用于工业自动化系统,增强系统安全性。 6. 展示板通讯接口,整合工业远端控制:EtherCAT slave, Ethernet, CAN bus, micro USB, external SDRAM, external FRAM. 7. IDE DAVE4 example code for basic EtherCAT physical layer evaluation. 8. Beckhoff slave stack code tool for EtherCAT software support. 9. Third-party driver tools for full EtherCAT application and charge support. 10. TwinCAT 3 support EtherCAT master. 本方案来源“大大通”
0.96寸OLED 1315驱动怎么用HAL 首先,0.96寸OLED 1315作为一种显示设备,可以通过使用HAL(硬件抽象层)来实现驱动。HAL是用于抽象硬件设备和操作系统之间的接口,将底层硬件操作和上层应用程序分离,使不同的硬件设备能够在不同的操作系统上运行。 在使用HAL驱动0.96寸OLED 1315之前,需先了解该驱动器的工作原理和通信接口。通常,OLED 1315驱动器可以通过SPI(串行外设接口)或I2C(串行总线接口)进行通信。 使用HAL驱动0.96寸OLED 1315的步骤如下: 1. 确定硬件接口:首先,需要确定OLED 1315驱动使用的是SPI还是I2C接口。这将决定后续使用的HAL接口类型。 2. 配置HAL:根据OLED 1315驱动器的通信接口,选择相应的HAL库和函数进行配置。对于SPI接口,可以使用HAL的SPI库函数;对于I2C接口,可以使用HAL的I2C库函数。 3. 初始化:调用HAL的初始化函数,根据硬件接口和特定的初始化参数,初始化OLED 1315驱动器。 4. 写入数据:通过调用HAL提供的写入函数,将需要显示的数据写入OLED 1315驱动器。可以根据需要,编写显示文本、图像或其他内容的函数。 5. 控制显示:通过HAL的控制函数,控制OLED 1315驱动器的显示状态。例如,启动或关闭显示、调整亮度或对比度等。 6. 清除:如果需要清除屏幕上显示的内容,可以使用HAL提供的函数清空OLED 1315驱动器的显示缓冲区。 以上是使用HAL驱动0.96寸OLED 1315的基本步骤。具体的代码实现可能会依赖于使用的开发平台和HAL库的具体实现。在编写代码时,可以参考HAL库提供的函数和示例代码,以及OLED 1315驱动器的数据手册。对于特定的问题,还可以参考相关的技术资料或开发者社区,以获取更详细的帮助和指导。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值