https://blog.csdn.net/b7376811/article/details/100023485

当前的这个项目需要使用一个数字电位器,型号选的是isl95311,控制接口是I2C,折腾了两天,终于实现了这个电位器的驱动,今天记录一下这个过程,以备以后查阅。

1、首先在nuc972的设备文件中增加isl95311相关的设备信息,在内核中的路径为/arch/arm/mach-nuc970/dev.c,如下所示:

  1. static struct i2c_board_info __initdata nuc970_i2c_clients2[] =

  2. {

  3. {

  4. I2C_BOARD_INFO("tsc2007", 0x48),

  5. .platform_data = &tsc2007_info,

  6. /* irq number is run-time assigned */

  7. },

  8. #ifdef CONFIG_SENSOR_OV7725

  9. {I2C_BOARD_INFO("ov7725", 0x21),},

  10. #endif

  11. #ifdef CONFIG_SENSOR_OV5640

  12. {I2C_BOARD_INFO("ov5640", 0x3c),},

  13. #endif

  14. #ifdef CONFIG_SENSOR_NT99141

  15. {I2C_BOARD_INFO("nt99141", 0x2a),},

  16. #endif

  17. #ifdef CONFIG_SENSOR_NT99050

  18. {I2C_BOARD_INFO("nt99050", 0x21),},

  19. #endif

  20. {I2C_BOARD_INFO("lm75a", 0x4e),},

  21. {I2C_BOARD_INFO("ds1307", 0x68),},

  22. {I2C_BOARD_INFO("isl95311", 0x28),},

  23. };

  24. static struct i2c_gpio_platform_data i2c_gpio_adapter_data = {

  25. .sda_pin = NUC970_PB1,

  26. .scl_pin = NUC970_PB0,

  27. .udelay = 1,

  28. .timeout = 100,

  29. .sda_is_open_drain = 0, //not support open drain mode

  30. .scl_is_open_drain = 0, //not support open drain mode

  31. };

  32. static struct platform_device i2c_gpio = {

  33. .name = "i2c-gpio",

  34. .id = 2,

  35. .dev = {

  36. .platform_data = &i2c_gpio_adapter_data,

  37. },

  38. };

在结构体数组nuc972_i2c_clients2中添加一个名字为Isl95311,从设备地址为0x28,这个地址需要配合硬件上的A0与A1的设置,我这里设置的都是低电平,根据isl95311的技术手册,可以计算得到0x28的设备从地址。数组中其他的成员信息,如果不需要,可以屏蔽掉。

结构体i2c_gpio_adapter_data里面保存的是使用gpio模拟I2C时序的方式的I2C适配器需要的数据,因为在当前的内核配置中,使用的是gpio模拟的I2C总线,所以这里也用模拟的I2C,这里面包含了几个重要的信息:

sda_pin:数据线对应的GPIO口;

scl_pin:时钟线对应的GPIO口;

udelay:决定I2C频率的延时,频率=500khz/udelay;

timeout:请求超时时长;

sda_is_open_drain:sda对应的gpio是否支持开漏模式;

scl_is_open_drain:scl对应的gpio是否支持开漏模式;

注意:这两个非常重要,Isl95311的数据线和时钟线都是开漏模式,我刚开始没太弄明白,而且我的板子上也没有设计I2C的上拉电阻,就想当然的把这两项都设置成了1,虽然电平正常,但是就是不通,最后发现这两个选项必须配置为0,外置上拉电阻才行,目前没有搞明白是为什么,可能是这个选项的意思是nuc972的gpio口不支持外部从设备的开漏模式。

结构体i2c_gpio是当前gpio模拟的I2C在系统上注册成平台设备所需要的数据,因为片子的I2C设备不管是硬件I2C还是gpio模拟的I2C在系统上都是注册成平台设备的。

2、第二步是将上面的数据注册到系统中,其实很简单,就一句话:

i2c_register_board_info(2, nuc970_i2c_clients2, ARRAY_SIZE(nuc970_i2c_clients2));

该函数的第一个参数是I2C设备在系统中的编号,我这里用的是I2C2,第二个参数是注册的数据,第三个参数是数据的长度,到这里,isl95311驱动的设备信息部分就完成了。

3、第三步就是编写isl95311驱动的算法部分,直接将所有的代码都贴出来:

  1. #include <linux/module.h>

  2. #include <linux/init.h>

  3. #include <linux/slab.h>

  4. #include <linux/i2c.h>

  5. #include <linux/mutex.h>

  6. #include <linux/delay.h>

  7. #include <linux/miscdevice.h>

  8. #include <linux/fs.h>

  9. #include <linux/i2c-algo-bit.h>

  10. #include <asm/uaccess.h>

  11. #define ISL95311_DRV_NAME "isl95311"

  12. #define DRIVER_VERSION "1.0"

  13. #define ISL95311_NUM_CACHABLE_REGS 4

  14. struct isl95311_data {

  15. struct i2c_client *client;

  16. struct mutex lock;

  17. u8 reg_cache[ISL95311_NUM_CACHABLE_REGS];

  18. struct miscdevice i2c_misc;

  19. };

  20. struct isl95311_data *isl95311_data_dev;

  21. /*

  22. * register access helpers

  23. */

  24. static int __isl95311_i2c_recv(struct i2c_client *client, char *buf, size_t count)

  25. {

  26. return 0;

  27. }

  28. static int __isl95311_i2c_send(struct i2c_client *client, char *buf, size_t count)

  29. {

  30. int ret = 0;

  31. //i2c_adapter描述控制器,包含编号、算法等描述

  32. struct i2c_adapter *adapter = client->adapter;

  33. struct i2c_msg msg;

  34. msg.addr = client->addr;

  35. msg.buf = (char *)buf;

  36. msg.flags = 0;//0是写,1是读

  37. msg.len = count;

  38. ret = i2c_transfer(adapter, &msg, 1);

  39. return ret==1?count:ret;

  40. }

  41. /*

  42. * I2C layer

  43. */

  44. static int isl95311_open(struct inode *inode, struct file *filp)

  45. {

  46. //printk("-------%s--------\n", __FUNCTION__);

  47. return 0;

  48. }

  49. static int isl95311_close(struct inode *inode, struct file *filp)

  50. {

  51. //printk("------%s------\n", __FUNCTION__);

  52. return 0;

  53. }

  54. static int isl95311_read(struct file *filp, char __user *buf, size_t count, loff_t *fops)

  55. {

  56. //printk("------%s------\n", __FUNCTION__);

  57. return 0;

  58. }

  59. static int isl95311_write(struct file *filp, char __user *buf, size_t count, loff_t *fpos)

  60. {

  61. //printk("------%s------\n", __FUNCTION__);

  62. ssize_t ret;

  63. //动态分配一个空间

  64. char *temp = kzalloc(count, GFP_KERNEL);

  65. //从用户空间读取得到的数据

  66. ret = copy_from_user(temp, buf, count);

  67. if (ret > 0) {

  68. printk("copy_from_user error!\n");

  69. ret = -EFAULT;

  70. goto err_free;

  71. }

  72. //将数据写入到硬件设备

  73. ret = __isl95311_i2c_send(isl95311_data_dev->client, temp, count);

  74. if (ret < 0) {

  75. printk("isl95311_i2c_send error\n");

  76. goto err_free;

  77. }

  78. kfree(temp);

  79. return 0;

  80. err_free:

  81. kfree(temp);

  82. return ret;

  83. }

  84. const struct file_operations isl95311_i2c_fops = {

  85. .open = isl95311_open,

  86. .read = isl95311_read,

  87. .write = isl95311_write,

  88. .release = isl95311_close,

  89. };

  90. static int isl95311_probe(struct i2c_client *client,

  91. const struct i2c_device_id *id)

  92. {

  93. struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);

  94. int err = 0;

  95. if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))

  96. return -EIO;

  97. isl95311_data_dev = kzalloc(sizeof(struct isl95311_data), GFP_KERNEL);

  98. if (!isl95311_data_dev)

  99. return -ENOMEM;

  100. isl95311_data_dev->client = client;

  101. i2c_set_clientdata(client, isl95311_data_dev);

  102. mutex_init(&isl95311_data_dev->lock);

  103. isl95311_data_dev->i2c_misc.fops = &isl95311_i2c_fops;

  104. isl95311_data_dev->i2c_misc.minor = 199;

  105. isl95311_data_dev->i2c_misc.name = "isl95311";

  106. misc_register(&isl95311_data_dev->i2c_misc);

  107. dev_info(&client->dev, "driver version %s enabled\n", DRIVER_VERSION);

  108. return 0;

  109. }

  110. static int isl95311_remove(struct i2c_client *client)

  111. {

  112. misc_deregister(&isl95311_data_dev->i2c_misc);

  113. kfree(i2c_get_clientdata(client));

  114. return 0;

  115. }

  116. static const struct i2c_device_id isl95311_id[] = {

  117. { "isl95311", 0 },

  118. {}

  119. };

  120. MODULE_DEVICE_TABLE(i2c, isl95311_id);

  121. static struct i2c_driver isl95311_driver = {

  122. .driver = {

  123. .name = ISL95311_DRV_NAME,

  124. .owner = THIS_MODULE,

  125. },

  126. .probe = isl95311_probe,

  127. .remove = isl95311_remove,

  128. .id_table = isl95311_id,

  129. };

  130. module_i2c_driver(isl95311_driver);

  131. MODULE_AUTHOR("jakey <632233021@qq.com>");

  132. MODULE_DESCRIPTION("ISL95311 driver");

  133. MODULE_LICENSE("GPL v2");

  134. MODULE_VERSION(DRIVER_VERSION);

我这里没有用到isl95311的读,只用到了向isl95311写数据,所以只实现了写数据的具体操作方法,其他都只是一个空壳。这个驱动相对比较简单,这里简单的记录说明一下:

驱动一般是从下往上看,最后的几行都是固定格式,其中isl95311id这个结构体数组中的第一个元素的第一个字符串是该驱动的名字,这个名字要和设备文件里面的名字要完全一致,要不然匹配不上,驱动会加载不成功。先看一下这个probe函数,很简单,就是申请一个自定义的和isl95311相关的数据结构空间,并将其中的某些数据填充,我这里是将isl95311注册成了一个杂散类设备,这样可以产生一个字符设备驱动的操作节点,杂散类设备的名字可以随便定,不需要一定和设备信息里面的名字一致。remove函数正好和probe函数相反,注意,操作顺序要相反,比如在probe函数中先申请了空间,后注册设备,在remove函数中要先反注册设备,再释放空间。

最后看一次实现isl95311这个数字电位器写操作的方法,即isl95311_write函数,首先是需要将用户空间的数据拷贝到内核空间,这是内核空间与用户空间交互数据的唯一接口,其次是将这些数据封装成I2C规定的数据包,也就是结构体i2c_msg的这种格式,在这里分析一下这个数据格式,这个结构体主要包括了四个字段:

addr:I2C从机地址,7位或者是10位,在这里,isl95311是7位地址

buf:读或者写数据存放的位置

flags:读或写标志位,0是写,1是读

len:需要读写的数据长度

还有就是调用了一个非常重要的函数i2c_transfer,这个函数是I2C核心层的数据传输函数之一,具体的不做分析,可以翻看内核源码,这里只说明使用方法,该函数的第一个参数是I2C适配器,简单的说就是代表I2C1还是I2C2的相关内容数据,第二个参数是存放要发送的I2C消息缓存的地址,第三个参数是该次发送数据的个数。现在具体的说一下这个函数的工作过程:

(1)、I2C适配器向总线发送设备从机地址,等待ACK;

(2)、收到ACK后,向总线发送从设备片内寄存器地址,等待ACK;

(3)、收到ACK后,向总线发送将要写入片内寄存器的数据,并等待ACK;

(4)、收到ACK后,本次操作就完成了;

以上的每次写操作,如果失败了,默认都是重复3次。

4、好了,到这里驱动部分就完成了,将上面的代码编译成模块,安装到系统,会在/dev的路径下,产生一个名字为isl95311的设备节点,在应用层就可以使用通用的open函数和write函数进行打开和写操作了!

5、有一点需要特别强调一下,硬件电路上,nuc972的gpio不支持开漏模式,所以外部必须对isl95311的时钟线和数据线进行上拉处理,我在这一点儿卡了一天时间,切记切记!

在nuc972上实现I2C接口数字电位器isl95311的驱动相关推荐

  1. Linux的I2C 设备驱动 -- mini2440 上i2c接口触摸屏驱动

    本篇记录在友善之臂 mini2440 平台上挂载I2C接口触摸屏的驱动开发过程. 内核版本linux-2.6.32.2, 平台是ARM9 S3C2440+I2C接口的触摸屏 如上篇 Linux的I2C ...

  2. 2022年全球市场数字电位器IC总体规模、主要生产商、主要地区、产品和应用细分研究报告

    本文研究全球市场.主要地区和主要国家数字电位器IC的销量.销售收入等,同时也重点分析全球范围内主要厂商(品牌)竞争态势,数字电位器IC销量.价格.收入和市场份额等. 针对过去五年(2017-2021) ...

  3. TPL0401x-10-Q1 具有I2C接口的128抽头单通道数字电位计

    TI的TPL0401x-10-Q1 是具有 128 个抽头位置的单通道线性抽头数字电位计,车规非功能安全芯片. 1.有一个10kΩ端到端电阻,20%误差,低电平端子内部连接至 GND. 2.可使用 I ...

  4. 外设驱动库开发笔记3:AD527x系列数字电位器驱动

    在一些时候我们需要使用精度更高的数字电位器来实现我们的应用.我们经常使用AD527x系列数字电位器来实现这类应用.在通常情况下,AD527x系列数字电位器完全能够满足要求.为了减少重复工作,在这里我们 ...

  5. 数字电位器程控可调电阻IC

    一.前言 数字电位器又叫可编程电阻器,是一种替代传统机械电位器的新型CMOS数字.模拟混合信号处理集成电路,不需要搭建复杂的电路环境即可简单的通过CPU数字通讯实现电路调节,数字电位器也不能完全替代传 ...

  6. UART SPI I2C 接口介绍 转载

    UART SPI I2C 接口介绍@TOC 做单片机开发时UART,SPI和I2C都是我们最经常使用到的硬件接口,我收集了相关的具体材料对这三种接口进行了详细的解释. UART UART是一种通用串行 ...

  7. 数字编码电位器c语言,数字电位器x9c103应用电路

    描述 数字电位器x9c103应用电路(一) 基础部分 数字电位器X9c103模块是一款宽电压(3V-5V)供电,10K满量程电位器. 电位器中间抽头在0-10k间滑动共100级(电位器滑动端典型阻抗4 ...

  8. 利用数字电位器简化LCD面板的VCOM调节

    http://bbs.dzsc.com/space/viewspacepost.aspx?postid=78550 任何薄膜晶体管液晶显示器面板都至少需要一个适当调节的VCOM信号,以便为面板(pan ...

  9. i2c hid 触摸板不能用_I2C 总线协议初探 - STM32 I2C 接口外设学习笔记

    I2C(Inter-Integrated Circuit)总线是由 PHILIPS(飞利浦) 公司开发的两线式串行总线,用于连接微控制器及其外围设备.是微电子通信控制领域广泛采用的一种总线标准.它是同 ...

最新文章

  1. 也议MySQL中隐式转换
  2. oracle的全文索引
  3. mysql 单例模式好处_PHP单例模式的优点分析
  4. USBASP的ISP上位机软件AVR_fighter
  5. mysql数据库服务器设置访问权限
  6. linux把svs文件分割,freeebsd,pkg_add,svsup,make改服务器的设定
  7. 乱码问题产生的原因与解决方案---UTF-8
  8. java什么叫声明异常_java – 声明异常而不是处理它的重点是什么
  9. 驾校学员驾考成绩管理系统
  10. 为 WE 打 Call!
  11. ImageView和ImageButton的区别
  12. 追剪电子凸轮算法(图)
  13. 微信 php 地图定位,微信公众号定位地图位置写入数据库,再显示地图的方法
  14. AndroidManifest基本定义
  15. python网盘搜索引擎_打造一个蓝奏云网盘搜索引擎
  16. 输出递归因数分解php,[学习笔记] Miller-Rabin质数测试 Pollard-Rho质因数分解
  17. Armbian魔百盒折腾记3(傻妞 机器人 pushplus推送)
  18. 【数据安全】数据脱敏解决方案
  19. 用JAVA算养鸡大户王大喜_养鸡大户王大喜,用百钱买百鸡,公鸡每只5元整,3元一只是母鸡,小小鸡崽价钱低,1元正好买三只,公鸡母鸡和小鸡,请你算算各...
  20. 【小程序】腾讯云服务配置小程序流程

热门文章

  1. Java基于JSP校园二手闲置商品交易系统
  2. autojs之浩然ocr-不安装插件就能用ocr
  3. 机床电气电路综合实训考核鉴定,典型机床电气控制,QY-JCDQ07
  4. swordfish.py
  5. Hadoop,Zookeeper这些名字都是怎么来的呢
  6. SSM毕设项目 - 基于SSM的企业公寓宿舍后勤管理系统(含源码+论文)
  7. 交友盲盒小程序版本 全开源版本kxdao首发(已更新)
  8. 小米云网站服务器错误代码,小米健康云开放平台iOS SDK使用指南
  9. github上漂亮的android loading动画
  10. java socket一直得不到返回值