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

Linux驱动学习day18(I2C设备ap3216c驱动编写)

一、ap3216c驱动程序

下面这段读函数的代码主要是发送读信号给client并从client中读到信号。由于是char kernelbuf,所以这边读到16位的字节用两个buf来存,kernel_buf[0] = val & 0xff; 从读到的数据取出低8位字节放到这个位置。kernel_buf[1] = (val >> 8) & 0xff;从读到的数据取出高8位字节放到这个位置。

static ssize_t ap3216c_read (struct file *file, char __user *buf, size_t size, loff_t *off)
{printk(KERN_INFO "[ap3126c_drv] %s %s:%d\n" , __FILE__ , __FUNCTION__ , __LINE__);char kernel_buf[6];int val;if(size != 6){return -EINVAL;}val = i2c_smbus_read_word_data(ap3216c_client, 0xA);kernel_buf[0] = val & 0xff;kernel_buf[1] = (val >> 8) & 0xff;val = i2c_smbus_read_word_data(ap3216c_client, 0xC);kernel_buf[2] = val & 0xff;kernel_buf[3] = (val >> 8) & 0xff;val = i2c_smbus_read_word_data(ap3216c_client, 0xE);kernel_buf[4] = val & 0xff;kernel_buf[5] = (val >> 8) & 0xff;if(copy_to_user(buf, kernel_buf, size) != 0){return -1;} return size;
}
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/mod_devicetable.h>
#include <linux/log2.h>
#include <linux/fs.h>
#include <linux/bitops.h>
#include <linux/jiffies.h>
#include <linux/property.h>
#include <linux/acpi.h>
#include <linux/i2c.h>
#include <linux/nvmem-provider.h>
#include <linux/regmap.h>
#include <linux/platform_data/at24.h>
#include <linux/pm_runtime.h>
#include <linux/gpio/consumer.h>static int major;
static struct class *ap3216c_class;
static struct i2c_client *ap3216c_client;static const struct of_device_id ap3216c_of_match[] = {{ .compatible = "com_name,chip_name",		.data = NULL },{ /* END OF LIST */ },
};static const struct i2c_device_id ap3216c_ids[] = {{ "chip_name",	(kernel_ulong_t)NULL },{ /* END OF LIST */ }
};static int ap3216c_open (struct inode *inode, struct file *file)
{/* reset */i2c_smbus_write_byte_data(ap3216c_client, 0, 0x4);mdelay(20);/* enable */i2c_smbus_write_byte_data(ap3216c_client, 0, 0x3);return 0;
}static ssize_t ap3216c_read (struct file *file, char __user *buf, size_t size, loff_t *off)
{printk(KERN_INFO "[ap3126c_drv] %s %s:%d\n" , __FILE__ , __FUNCTION__ , __LINE__);char kernel_buf[6];int val;if(size != 6){return -EINVAL;}val = i2c_smbus_read_word_data(ap3216c_client, 0xA);kernel_buf[0] = val & 0xff;kernel_buf[1] = (val >> 8) & 0xff;val = i2c_smbus_read_word_data(ap3216c_client, 0xC);kernel_buf[2] = val & 0xff;kernel_buf[3] = (val >> 8) & 0xff;val = i2c_smbus_read_word_data(ap3216c_client, 0xE);kernel_buf[4] = val & 0xff;kernel_buf[5] = (val >> 8) & 0xff;if(copy_to_user(buf, kernel_buf, size) != 0){return -1;} return size;
}static struct file_operations i2c_example_ops = {.owner   = THIS_MODULE,.open    = ap3216c_open,.read    = ap3216c_read,
}static int ap3216c_probe(struct i2c_client *client)
{printk(KERN_INFO "[ap3126c_drv] %s %s:%d\n" , __FILE__ , __FUNCTION__ , __LINE__);ap3216c_client = client;/* register_chrdev */major = register_chrdev(0 , "ap3216c_drv" , &i2c_example_ops);ap3216c_class = class_create(THIS_MODULE, "ap3216c_class");device_create(ap3216c_class, NULL, MKDEV(major, 0), NULL, "ap3216c");return 0;
}static int ap3216c_remove(struct i2c_client *client)
{printk(KERN_INFO "[ap3126c_drv] %s %s:%d\n" , __FILE__ , __FUNCTION__ , __LINE__);device_destroy(ap3216c_class, MKDEV(major, 0));class_destroy(ap3216c_class);unregister_chrdev(major, "ap3216c_drv");return 0;
}static struct i2c_driver ap3216c_driver = {.driver = {.name = "ap3216c",.of_match_table = ap3216c_of_match,},.probe_new = ap3216c_probe,.remove = ap3216c_remove,.id_table = ap3216c_ids,
};static int __init ap3216c_init(void)
{printk(KERN_INFO "[ap3126c_drv] %s %s:%d\n" , __FILE__ , __FUNCTION__ , __LINE__);return i2c_add_driver(&ap3216c_driver);
}
module_init(ap3216c_init);static void __exit ap3216c_exit(void)
{printk(KERN_INFO "[ap3126c_drv] %s %s:%d\n" , __FILE__ , __FUNCTION__ , __LINE__);i2c_del_driver(&ap3216c_driver);
}
module_exit(ap3216c_exit);MODULE_LICENSE("GPL");

二、I2C_Adapter驱动框架

2.1、核心结构体

在Linux中,总是使用一个结构体来描述一个对象,在I2C中也是如此,使用i2c_adapter结构体描述一个i2c总线(i2c适配器、i2c控制器)。

2.1.1 i2c_adapter结构体

2.1.2 i2c_algorithm结构体

master_xfer:这是最重要的函数,实现了一般的I2C传输,用来传输一个或者多个i2c_msg。

2.2 驱动框架

分配、设置、注册 i2c_adapter 结构体。一切都和字符设备驱动框架一样,只不过完善的是不同的结构体,有了设备树之后方便许多了。

2.2.1 设备树中添加一个i2c结点

2.2.2 写驱动程序注册platform_driver,并且在probe中分配、设置、注册 i2c_adapter 结构体

/* 不确认adapter中的nr成员使用,nr = -1系统会自动分配 */
i2c_add_adapter /* 确定使用的数字 */
i2c_add_numbered_adapter

2.2.3 完善i2c_adapter结构体中的重要成员

static int i2c_bus_virtual_probe(struct platform_device *pdev)
{printk("%s %s %d\n" , __FILE__ , __FUNCTION__ , __LINE__);/* register set i2c_adapter  */g_adap = kzalloc(sizeof(*g_adap), GFP_KERNEL);g_adap->owner = THIS_MODULE;g_adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;g_adap->nr = -1;snprintf(g_adap->name, sizeof(g_adap->name), "i2c_virtual");g_adap->algo = &i2c_bus_virtual_algorithm;i2c_add_adapter(g_adap);return 0;
}

2.2.4 i2c_algorithm结构体中实现master_xfer函数

这里主要有三种情况,下面列出各个情况使用i2ctool的命令:

1、写错地址:

i2cset -y -f 7 0x55 1 0x56

由于这个驱动程序中只有一个0x50的虚拟设备,所以地址不是0x50会直接返回错误号。

2、写数据到virtual_eeporm中

i2cset -y -f 7 0x50 1 0x56

该命令的意思是,使用i2c 7 控制器,往0x50设备中的1地址写入0x56。 

3、从virtual_eeporm中读取数据

i2cget -y -f 7 0x50 1

读数据比较复杂,模拟eeporm来读写数据,当收到这个指令时,其实会收到两条msg。第一条msg是写信号,写入要读取的地址。第二条信号是读信号,读之前写的地址中的数据。

msgs[0].addr  = 0x50;
msgs[0].flags = 0;  // 写
msgs[0].len   = 1;
msgs[0].buf   = [ 0x03 ];  // 要读取的寄存器地址msgs[1].addr  = 0x50;
msgs[1].flags = I2C_M_RD;  // 读
msgs[1].len   = 1;
msgs[1].buf   = [ ]  // 将读取到的数据存入这里
static int eeporm_cur_addr = 0;
static char eeporm_buffer[512];static eeporm_emulate_xfer(struct i2c_adapter *adap , struct i2c_msg *msgs)
{int i;if(msgs->flags & I2C_M_RD){msgs->buf[i] = eeporm_buffer[eeporm_cur_addr++];if(eeporm_cur_addr == 512)eeporm_cur_addr = 0;}else{eeporm_cur_addr = msgs->buf[0];for(i = 1 ; i < msgs->len ; i++){eeporm_buffer[eeporm_cur_addr++] = msgs->buf[i];if(eeporm_cur_addr == 512)eeporm_cur_addr = 0;}}return 0;
}static int i2c_bus_virtual_master_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
{int i;int j;while (i < num){if(msgs.addr != 0x50){i = -EIO;return i;}/* 两条指令 i2cget -y -f 7 0x50 1*/if( (i + 1 < num) && !(msgs[i].flags == I2C_M_RD)&& (msgs[i + 1].flags == I2C_M_RD)){eeporm_cur_addr = msgs[i][0];for(j = 0 ; j < msgs[i + 1]->len ; i++){msgs[i+1].buf[j] = eeporm_buffer[eeporm_cur_addr++];if(eeporm_cur_addr == 512)eeporm_cur_addr = 0;}i += 2;}else{/* 只有一条指令的时候 */eeporm_emulate_xfer(adap , &msg[i]);i++;}}return 0;
}
static int i2c_bus_virtual_master_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num){int i;for(i = 0 ; i < num ; i++){if (msgs[i].addr == 0x50) {   eeporm_emulate_xfer(adap , &msgs[i]);}else{i = -EIO;break;}}return i;}

2.2.5 完整代码

#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <linux/platform_data/i2c-gpio.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/gpio/consumer.h>
#include <linux/of.h>static struct i2c_adapter *g_adap;
static char eeporm_buffer[512];
static int eeporm_cur_addr = 0;static const struct of_device_id i2c_bus_virtual_dt_ids[] = {{ .compatible = "xupt,i2c_virtual", },{ /* sentinel */ }
};static int eeporm_emulate_xfer(struct i2c_adapter *adap , struct i2c_msg *msgs)
{int i;if(msgs->flags & I2C_M_RD){for(i = 0 ; i < msgs->len ; i++){msgs->buf[i] = eeporm_buffer[eeporm_cur_addr++];if(eeporm_cur_addr == 512)eeporm_cur_addr = 0;}}else{eeporm_cur_addr = msgs->buf[0];for(i = 1 ; i < msgs->len ; i++){eeporm_buffer[eeporm_cur_addr++] = msgs->buf[i];if(eeporm_cur_addr == 512)eeporm_cur_addr = 0;}}return 0;
}// static int i2c_bus_virtual_master_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
// {
//     int i;
//     for(i = 0 ; i < num ; i++)
//     {
//         if (msgs[i].addr == 0x50) 
//         {   
//             eeporm_emulate_xfer(adap , &msgs[i]);
//         }
//         else
//         {
//             i = -EIO;
//             break;
//         }
//     }//     return i;
// }
static int i2c_bus_virtual_master_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
{int i = 0;int j;printk(KERN_INFO"num of msgs = %d" , num);while (i < num) {if (msgs[i].addr != 0x50)return -EIO;if ((i + 1 < num) &&!(msgs[i].flags & I2C_M_RD) &&(msgs[i+1].flags & I2C_M_RD)) {eeporm_cur_addr = msgs[i].buf[0];for (j = 0; j < msgs[i+1].len; j++) {msgs[i+1].buf[j] = eeporm_buffer[eeporm_cur_addr++];if (eeporm_cur_addr == 512)eeporm_cur_addr = 0;}i += 2;  }else {eeporm_emulate_xfer(adap, &msgs[i]);i++;}}return num;
}static u32 i2c_bus_virtual_functionality(struct i2c_adapter *adap)
{return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |I2C_FUNC_PROTOCOL_MANGLING | I2C_FUNC_NOSTART;
}static const struct i2c_algorithm i2c_bus_virtual_algorithm = {.master_xfer	= i2c_bus_virtual_master_xfer,.functionality	= i2c_bus_virtual_functionality,
};static int i2c_bus_virtual_probe(struct platform_device *pdev)
{printk("%s %s %d\n" , __FILE__ , __FUNCTION__ , __LINE__);/* register set i2c_adapter  */g_adap = kzalloc(sizeof(*g_adap), GFP_KERNEL);g_adap->owner = THIS_MODULE;g_adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;g_adap->nr = -1;snprintf(g_adap->name, sizeof(g_adap->name), "i2c_virtual");g_adap->algo = &i2c_bus_virtual_algorithm;i2c_add_adapter(g_adap);return 0;
}static int i2c_bus_virtual_remove(struct platform_device *pdev)
{printk("%s %s %d\n" , __FILE__ , __FUNCTION__ , __LINE__);i2c_del_adapter(g_adap);kfree(g_adap);return 0;
}static struct platform_driver i2c_bus_virtual_driver = {.driver		= {.name	= "xupt,i2c_virtual",.of_match_table	= of_match_ptr(i2c_bus_virtual_dt_ids),},.probe		= i2c_bus_virtual_probe,.remove		= i2c_bus_virtual_remove,
};static int __init i2c_bus_virtual_init(void)
{int ret;printk("%s %s %d\n" , __FILE__ , __FUNCTION__ , __LINE__);ret = platform_driver_register(&i2c_bus_virtual_driver);if (ret)printk(KERN_ERR "i2c_bus_virtual_driver: probe failed: %d\n", ret);return ret;
}
module_init(i2c_bus_virtual_init);static void __exit i2c_bus_virtual_exit(void)
{printk("%s %s %d\n" , __FILE__ , __FUNCTION__ , __LINE__);platform_driver_unregister(&i2c_bus_virtual_driver);
}
module_exit(i2c_bus_virtual_exit);MODULE_LICENSE("GPL");

三、使用GPIO模拟I2C控制器

reg本意是用来表示寄存器register的地址信息:<起始地址><大小>后来也可以用来分辨设备,
比如对于12C设备,可以用reg来表示它的设备地址。

3.1 设置引脚为GPIO功能

3.2 GPIO设为输出、开极或者开漏

如果没有在设备树中指定,系统的i2c-gpio.c驱动代码中会帮忙设置其flags。

3.3 要有上拉电阻

3.4 设备树

i2c_gpio{compatible = "i2c-gpio";gpios = <&gpio4 20 0&gpio4 21 0>;i2c-gpio,delay-us = <5>;  /* ~100KHz */#address-cells = <1>;#size-cells    = <0>;
}

四、I2C控制器内部结构抽象 

GPIO 模拟 I2C 在数据多、中断多或需要高性能时非常不适合,因此大多数芯片都内置 I2C 控制器用于高效通信。

像之前的使用GPIO来模拟I2C,在传输过程中不允许有中断产生,整个过程如果有很多数据就会导致占用系统资源过多,系统变得缓慢。所以一般的芯片中都会有I2C控制器,发送数据的时候,会直接发送到发送寄存器,然后再发送到移位寄存器上给SDA发送,这时候发送数据函数就可以休眠,系统就会去做其他的事情,发送数据结束之后会产生中断。

4.1 I2C控制器操作方法

4.1.1 使能时钟、设置时钟

4.1.2 发送数据

把数据写入tx_register,等待中断发生。中断发生后,判断状态:是否发生错误、是否得到回应信号(ACK)。把下一个数据写入txregister,等待中断:如此循环。

4.1.3 接收数据

设置controller_register,进入接收模式,启动接收,等待中断发生。中断发生后,判断状态,读取rxregister得到数据 如此循环。

http://www.dtcms.com/a/268249.html

相关文章:

  • Mybatis----留言板
  • python实战项目81:ZeoDB多线程数据爬取程序(最新稳定好用)
  • Node中Unexpected end of form 错误
  • 【大模型入门】访问GPT_API实战案例
  • 从LLM和MCP的协同过程看如何做优化
  • webUI平替应用,安装简单,功能齐全
  • 基于Java+springboot 的车险理赔信息管理系统
  • 基于udev规则固定相机名称
  • 计算机网络:(七)网络层(上)网络层中重要的概念与网际协议 IP
  • 深度学习图像分类数据集—濒危动物识别分类
  • 如何将 Java 项目打包为可执行 JAR 文件
  • Git使用教程
  • 软考(软件设计师)进程管理—进程基本概念,信号量与PV操作
  • centos7.9安装ffmpeg6.1和NASM、Yasm、x264、x265、fdk-aac、lame、opus解码器
  • 1.8 提示词优化
  • Tuning Language Models by Proxy
  • HBuilder提示”未检测到联盟快应用开发者工具”的问题无法发布快应用的解决方案-优雅草卓伊凡
  • 【第七章】全球卫星导航定位技术
  • 缺陷追踪流程
  • Vue+Openlayers加载OSM、加载天地图
  • Modbus_TCP_V5 新功能
  • 【机器学习深度学习】模型微调时的4大基础评估指标(1)
  • [netty5: WebSocketServerHandshaker WebSocketServerHandshakerFactory]-源码分析
  • 机器学习绪论
  • LeetCode 100题(1)(10题)
  • 线性代数--AI数学基础复习
  • 暑假算法日记第二天
  • DTW模版匹配:弹性对齐的时间序列相似度度量算法
  • 基于联合国国家指标 2025数据(UN Countries Metrics 2025: HDI, GDP, POP, AREA)的综合可视化分析
  • PDF转换工具,即开即用