当前位置: 首页 > article >正文

LINUX驱动学习之IIC驱动-----以AP3216C为例

目录

1.编写设备树

2.添加新的IIC设备

3.编写AP3216C的驱动程序如下

4.对IIC驱动函数详细解读

1.SMBUS

1)向I2C设备中读出或者写入一个字节(8位)/两个字节(16位)的数据

2)读取或者写入指定长度的值,一般使用后面两个

3)i2c_master_send ()和 i2c_master_recv()


AP3216C不做具体的介绍,本文主要介绍LInux是如何驱动IIC设备的

传感器具体的介绍可以参考:详解AP3216C(三合一sensor: 光照、距离、照射强度)驱动开发-CSDN博客

1.编写设备树

首先设置 I2C1 引脚的复用功能和电气属性,找到 pinctrl_i2c1 节点:

已经设置好了,不需要编写

2.添加新的IIC设备

找到IIC子节点补充描述

    ap3216c@1e{
        compatible = "my_ap3216c";
        reg = <0x1e>;
    };

切换到内核目录下,重新编译(make dtbs)即可,将编译好的dtb文件移动到开发板的boot目录下面即可。重新启动开发板即可。

3.编写AP3216C的驱动程序如下

#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/uaccess.h>
#include <linux/mod_devicetable.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/delay.h>

static int major;
static struct class *ap3216c_class;
static struct i2c_client *ap3216c_client;

static int ap3216c_open(struct inode *inode, struct file *filp) {
  /*reset: write 0x4 to reg 0*/
  
  i2c_smbus_write_byte_data(ap3216c_client, 0, 0x4);
  /*delay for reset*/
  mdelay(15);

  /*enable:write 0x3 to reg 0*/
  i2c_smbus_write_byte_data(ap3216c_client, 0, 0x3);
  return 0;
}

static ssize_t ap3216c_read(struct file *filp, char __user *buf, size_t size,
                            loff_t *offset) {
  int var, err;
  char data[6];

  if (size != 6) {
    return -EINVAL;
  }

  // read IR
  var = i2c_smbus_read_word_data(ap3216c_client, 0xa);
    //将读取到的16位数据var拆分为两个8位的字节,并分别存储到data[0]与data[1]中
    //第一个代码,将var右移8位,取出高8位,(8-15),然后与0xff进行按位与操作,确保结果是一个8位的值
    //第二个代码,取出var的低8位(0-7),并且存储到data[1]中。
  data[0] = (var >> 8) & 0xff;
  data[1] = var & 0xff;

  // read light
  var = i2c_smbus_read_word_data(ap3216c_client, 0xc);
  data[2] = (var >> 8) & 0xff;
  data[3] = var & 0xff;

  // read distance
  var = i2c_smbus_read_word_data(ap3216c_client, 0xe);
  data[4] = (var >> 8) & 0xff;
  data[5] = var & 0xff;

  err = copy_to_user(buf, data, size);

  return 0;
}

static struct file_operations ap3216c_fops = {
    .owner = THIS_MODULE,
    .open = ap3216c_open,
    .read = ap3216c_read,
};
// 用于和设备书进行匹配
static const struct of_device_id ap3216c_dt_match[] = {
    {
        .compatible = "my_ap3216c",
    },
    {},
};
static const struct i2c_device_id ap3216c_i2c_id[] = {
    {
        "ap3216c",
    },
    {}
};

static int ap3216c_i2c_probe(struct i2c_client* client,const struct i2c_device_id *i2c_id){
  struct device *result;
  ap3216c_client = client;

  printk("====%s====\n", __FUNCTION__);

  /*register chrdev*/
  major = register_chrdev(0, "ap3216c", &ap3216c_fops);

  ap3216c_class = class_create(THIS_MODULE, "ap3216c_class");
  if (IS_ERR(ap3216c_class)) {
    printk("ap3216c class_create failed!\n");
    unregister_chrdev(major, "ap3216c");
    return PTR_ERR(ap3216c_class);
  }

  result = device_create(ap3216c_class, NULL, MKDEV(major, 0), NULL, "ap3216c");
  if (IS_ERR(result)) {
    printk("ap3216c device_create failed\n");
    class_destroy(ap3216c_class);
    unregister_chrdev(major, "ap3216c");
    return -EINVAL;
  }

  dev_info(&client->dev, "=====ap3216c initialized successfully====\n");
  return 0;
}

static int ap3216c_i2c_remove(struct i2c_client *client) {
  printk("======%s=====\n", __FUNCTION__);
  device_destroy(ap3216c_class, MKDEV(major, 0));
  class_destroy(ap3216c_class);
  unregister_chrdev(major, "ap3216c");
  return 0;
}

static struct i2c_driver ap3216c_i2c_driver = {
    .driver =
        {
            .owner = THIS_MODULE,
            .name = "ap3216c",
            .of_match_table = ap3216c_dt_match,
        },
    .probe = ap3216c_i2c_probe,
    .remove = ap3216c_i2c_remove,
    .id_table = ap3216c_i2c_id,
};

static int __init ap3216c_i2c_init(void) {
  int ret;
  printk("====%s====\n", __FUNCTION__);
  ret = i2c_add_driver(&ap3216c_i2c_driver);
  if (ret != 0) {
    pr_err("Failed to register ap3216c I2C driver: %d \n", ret);
  }
    return 0;
}

static void __exit ap3216c_i2c_exit(void) {
  printk("====%s====\n", __FUNCTION__);
  i2c_del_driver(&ap3216c_i2c_driver);
}

module_init(ap3216c_i2c_init);
module_exit(ap3216c_i2c_exit);
MODULE_LICENSE("GPL");

4.对IIC驱动函数详细解读

1.SMBUS

SMBUS常用的函数集有:

s32 i2c_smbus_read_byte_data(const struct i2c_client *client, u8 command);
s32 i2c_smbus_write_byte_data(const struct i2c_client *client,u8 command, u8 value);

s32 i2c_smbus_read_word_data(const struct i2c_client *client, u8 command);
s32 i2c_smbus_write_word_data(const struct i2c_client *client,u8 command, u16 value);

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

s32 i2c_smbus_read_i2c_block_data(const struct i2c_client *client,u8 command, u8 length, u8 *values);
s32 i2c_smbus_write_i2c_block_data(const struct i2c_client *client,u8 command, u8 length, const u8 *values);
1)向I2C设备中读出或者写入一个字节(8位)/两个字节(16位)的数据
/* client:client是指i2c slave 设备,使用时有时需要初始化其i2c地址
*  command :读写的寄存器地址
*  value:写入的数据,为8位
*/
s32 i2c_smbus_read_byte_data(const struct i2c_client *client, u8 command);
s32 i2c_smbus_write_byte_data(const struct i2c_client *client,u8 command, u8 value);

s32 i2c_smbus_read_word_data(const struct i2c_client *client, u8 command);
/*value:写入的数据,为16位*/
s32 i2c_smbus_write_word_data(const struct i2c_client *client,u8 command, u16 value);

比如这个函数中,open函数中打开设备后做了下面的事情

  /*reset: write 0x4 to reg 0*/
  i2c_smbus_write_byte_data(ap3216c_client, 0, 0x4);
  /*enable:write 0x3 to reg 0*/
  i2c_smbus_write_byte_data(ap3216c_client, 0, 0x3);

在read函数中,读取

  var = i2c_smbus_read_word_data(ap3216c_client, 0xa);
  data[0] = (var >> 8) & 0xff;
  data[1] = var & 0xff;

  // read light
  var = i2c_smbus_read_word_data(ap3216c_client, 0xc);
  data[2] = (var >> 8) & 0xff;
  data[3] = var & 0xff;

  // read distance
  var = i2c_smbus_read_word_data(ap3216c_client, 0xe);
  data[4] = (var >> 8) & 0xff;
  data[5] = var & 0xff;
2)读取或者写入指定长度的值,一般使用后面两个
/* client:client是指i2c slave 设备,使用时有时需要初始化其i2c地址
*  command :读写的寄存器地址
*  length  :读取或者写入长度
*  value   :写入的数据,值的类型为u8
*/
s32 i2c_smbus_read_block_data(const struct i2c_client *client,u8 command, u8 *values);
s32 i2c_smbus_write_block_data(const struct i2c_client *client,u8 command, u8 length, const u8 *values);

s32 i2c_smbus_read_i2c_block_data(const struct i2c_client *client,u8 command, u8 length, u8 *values);
s32 i2c_smbus_write_i2c_block_data(const struct i2c_client *client,u8 command, u8 length, const u8 *values);

举例:

/* 其中client中会保存slave的I2C地址
 * 0x03为寄存器地址
 * write_buf是一个u8类型的数组
 * 4为长度
*/
static int write_reg(struct i2c_client *client, int addr,u8* data, int len)
{
	return  i2c_smbus_write_i2c_block_data(client,addr,len,data);
}

void func()
{
    u8 write_buf[4];
	write_buf[0]=0x01;
	write_buf[1]=0x02;
	write_buf[2]=0x05;
	write_buf[3]=0x04
	write_reg(client,0x03,write_buf,4);
}

/* client中保存slave的I2C地址
*  0x20为寄存器地址
*  read_buf是一个u8类型的数组
*  63为读取的长度
*/
static int read_register(struct i2c_client *client, int addr,u8* u8_val, int len)
{
	return i2c_smbus_read_i2c_block_data(client,addr,len,u8_val);
}
void func()
{
	u8 read_buf[80];
	read_register(client, 0x20, read_buf ,80);
}
3)i2c_master_send ()和 i2c_master_recv()

在设备驱动中通常调用 i2c-core 定义的接口 i2c_master_send() 和 i2c_master_recv() 来发送或接收一次数据。

/*
* client:I2C从机
* buf   :要发送的buf指针
* count :发送的数据长度
*/
int i2c_master_send(struct i2c_client *client,const char *buf ,int count) 
{ 
    int ret; 
    struct i2c_adapter *adap=client->adapter;  // 获取adapter信息 
    struct i2c_msg msg;                        // 定义一个临时的数据包 
 
    msg.addr = client->addr;                   // 将从机地址写入数据包 
    msg.flags = client->flags & I2C_M_TEN;     // 将从机标志并入数据包 
    msg.len = count;                           // 将此次发送的数据字节数写入数据包 
    msg.buf = (char *)buf;                     // 将发送数据指针写入数据包 
 
    ret = i2c_transfer(adap, &msg, 1);         // 调用平台接口发送数据 
 
    // If everything went ok (eg: 1 msg transmitted), return bytes number transmitted, else error code. 
    return (ret == 1) ? count : ret;           // 如果发送成功就返回字节数 
} 
EXPORT_SYMBOL(i2c_master_send); 

/*
* client:I2C从机
* buf   :要接收的buf指针
* count :发送的数据长度
*/
int i2c_master_recv(struct i2c_client *client, char *buf ,int count) 
{ 
    struct i2c_adapter *adap=client->adapter;  // 获取adapter信息 
    struct i2c_msg msg;                        // 定义一个临时的数据包 
    int ret; 
 
    msg.addr = client->addr;                   // 将从机地址写入数据包 
    msg.flags = client->flags & I2C_M_TEN;     // 将从机标志并入数据包 
    msg.flags |= I2C_M_RD;                     // 将此次通信的标志并入数据包 
    msg.len = count;                           // 将此次接收的数据字节数写入数据包 
    msg.buf = buf; 
 
    ret = i2c_transfer(adap, &msg, 1);         // 调用平台接口接收数据 
 
    /* If everything went ok , return number of bytes transmitted, else error code. */ 
    return (ret == 1) ? count : ret;           // 如果接收成功就返回字节数 
} 
EXPORT_SYMBOL(i2c_master_recv); 

例如:

写寄存器:

//写8位寄存器
static inline int xxx_reg_write(struct data *pdata,int index,unsigned char reg, unsigned char val)
{
	unsigned char u8_buf[2] = { 0 };
	unsigned int buf_len = 2;
	int retry, timeout = 5;

	u8_buf[0] = reg;
	u8_buf[1] = val; 
    
	pdata->client->addr = ADDR + index;
	for (retry = 0; retry < timeout; retry++) 
    {
		if (i2c_master_send(pdata->client, u8_buf, buf_len) < 0) 
        {
			printk("%s:write reg error: reg=0x%x, val=0x%x, retry = %d.\n", __func__, reg, val, retry);
			msleep(5);
			continue;
		}
		else
        {      
			printk("%s:write reg ok: reg=0x%x, val=0x%x, retry = %d.\n", __func__, reg, val, retry);
            break;
        }
	}
	return 0;
}

//写16位寄存器
static inline int xxx_reg_write(struct data *pdata,int index,unsigned short reg, unsigned char val)
{
	unsigned char u8_buf[3] = { 0 };
	unsigned int buf_len = 3;
	int retry, timeout = 5;

	u8_buf[0] = (reg >> 8) & 0xFF;//寄存器地址高位
	u8_buf[1] = reg & 0xFF;  //寄存器地址低位
	u8_buf[2] = val; //要发送的数据

	pdata->client->addr = ADDR + index;
	for (retry = 0; retry < timeout; retry++) 
    {
		if (i2c_master_send(pdata->client, u8_buf, buf_len) < 0) 
        {
			printk("%s:write reg error: reg=0x%x, val=0x%x, retry = %d.\n", __func__, reg, val, retry);
			msleep(5);
			continue;
		}
		else
        {      
			printk("%s:write reg ok: reg=0x%x, val=0x%x, retry = %d.\n", __func__, reg, val, retry);
            break;
        }
	}
	return 0;
}

读寄存器:

//8位读
static inline int xxx_reg_read(struct data *pdata, int index, unsigned short reg)
{
	unsigned char u8_buf[2] = { 0 };
	unsigned int buf_len = 1;
	int retry, timeout = 5;
	unsigned char u8_val = 0;

	u8_buf[0] = reg;


	pdata->client->addr = ADDR + index;
	for (retry = 0; retry < timeout; retry++) 
    {
		if (i2c_master_send(pdata->client, u8_buf, buf_len) < 0) 
        {
			printk("%s:read reg error on send: reg=0x%x, retry = %d.\n", __func__, reg, retry);
			msleep(5);
			continue;
		}
		if (i2c_master_recv(pdata->client, &u8_val, 1) != 1) {
			printk("%s:read reg error on recv: reg=0x%x, retry = %d.\n", __func__, reg, retry);
			msleep(5);
			continue;
		}
		break;
	}

	if (retry >= timeout) {
		printk("%s:read reg error: reg=0x%x.\n", __func__, reg);
		return -1;
	}

	return u8_val;
}

//16位读
static inline int xxx_reg_read(struct data *pdata, int index, unsigned short reg)
{
	unsigned char u8_buf[2] = { 0 };
	unsigned int buf_len = 2;
	int retry, timeout = 5;
	unsigned char u8_val = 0;

	u8_buf[0] = (reg >> 8) & 0xFF;//寄存器地址高位
	u8_buf[1] = reg & 0xFF;//寄存器地址低位

	pdata->client->addr = ADDR + index;
	for (retry = 0; retry < timeout; retry++) 
    {
		if (i2c_master_send(pdata->client, u8_buf, buf_len) < 0) 
        {
			printk("%s:read reg error on send: reg=0x%x, retry = %d.\n", __func__, reg, retry);
			msleep(5);
			continue;
		}
		if (i2c_master_recv(pdata->client, &u8_val, 1) != 1) {
			printk("%s:read reg error on recv: reg=0x%x, retry = %d.\n", __func__, reg, retry);
			msleep(5);
			continue;
		}
		break;
	}

	if (retry >= timeout) {
		printk("%s:read reg error: reg=0x%x.\n", __func__, reg);
		return -1;
	}

	return u8_val;
}


http://www.kler.cn/a/589362.html

相关文章:

  • ZED X系列双目3D相机的耐用性与创新设计解析
  • 基于python+django+vue.js开发的停车管理系统运行-期末作业
  • 基于Python的tkinter开发的一个工具,解析图片文件名并将数据自动化导出为Excel文件
  • Spring 原生启动过程
  • Jenkins 快讯
  • A - 整数的简单问题/A - A Simple Problem with Integers
  • Linux centos7误删/boot拯救方法
  • 【系统架构设计师】操作系统 - 文件管理 ③ ( 树形目录结构 | 文件属性 | 绝对路径 与 相对路径 )
  • 大型智慧城市、交通枢纽、跨区域联网监控视频平台使用LiveGBS国标GB28181平台如何实现分布式集群及负载均衡
  • 线性表的顺序表示
  • Mock接口编写教程-axios-mock-adapter(React)
  • react18 核心知识点杂记1
  • 需要使用新应用以打开此ms-gamingoverlay链接怎么解决
  • list及其模拟实现
  • 如何管理需求变更
  • Elasticsearch:为推理端点配置分块设置
  • 深入理解蒸馏、Function Call、React、Prompt 与 Agent
  • Redis安装与基础配置:单节点离线部署与配置解析
  • 量子计算助力智能制造:未来工业的加速器
  • 【测试篇】关于allpairs实现正交测试用例保姆级讲解,以及常见的错误问题