019——IIC模块驱动开发(基于EEPROM【AT24C02】和I.MX6uLL)
目录
一、 IIC基础知识
二、Linux中的IIC(韦东山老师的学习笔记)
1. I2C驱动程序的层次
2. I2C总线-设备-驱动模型
2.1 i2c_driver
2.2 i2c_client
三、 AT24C02 介绍
四、 AT24C02驱动开发
实验
驱动程序
应用程序
一、 IIC基础知识
总线类设备驱动——IIC_iic设备驱动-CSDN博客
Exynos_4412——IIC总线概述_.若使用iic总线让从机给主机发送一个字节的数据0xa2,画出scl和sda上的时序图-CSDN博客
STM32——IIC总线(MPU6050应用)_mpu6050例程-CSDN博客
写过好多次啦不打算重复写了。
二、Linux中的IIC(韦东山老师的学习笔记)
参考资料:
-
Linux内核文档:
-
Documentation\i2c\instantiating-devices.rst -
Documentation\i2c\writing-clients.rst
-
-
Linux内核驱动程序示例:
-
drivers/eeprom/at24.c
-
1. I2C驱动程序的层次

I2C Core就是I2C核心层,它的作用:
-
提供统一的访问函数,比如i2c_transfer、i2c_smbus_xfer等
-
实现
I2C总线-设备-驱动模型,管理:I2C设备(i2c_client)、I2C设备驱动(i2c_driver)、I2C控制器(i2c_adapter)
2. I2C总线-设备-驱动模型

2.1 i2c_driver
i2c_driver表明能支持哪些设备:
-
使用of_match_table来判断
-
设备树中,某个I2C控制器节点下可以创建I2C设备的节点
-
如果I2C设备节点的compatible属性跟of_match_table的某项兼容,则匹配成功
-
-
i2c_client.name跟某个of_match_table[i].compatible值相同,则匹配成功
-
-
使用id_table来判断
-
i2c_client.name跟某个id_table[i].name值相同,则匹配成功
-
i2c_driver跟i2c_client匹配成功后,就调用i2c_driver.probe函数。
2.2 i2c_client
i2c_client表示一个I2C设备,创建i2c_client的方法有4种:
方法1
-
通过I2C bus number来创建
int i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len);
-
通过设备树来创建
i2c1: i2c@400a0000 {/* ... master properties skipped ... */clock-frequency = <100000>;flash@50 {compatible = "atmel,24c256";reg = <0x50>;};pca9532: gpio@60 {compatible = "nxp,pca9532";gpio-controller;#gpio-cells = <2>;reg = <0x60>;};};
方法2
有时候无法知道该设备挂载哪个I2C bus下,无法知道它对应的I2C bus number。 但是可以通过其他方法知道对应的i2c_adapter结构体。 可以使用下面两个函数来创建i2c_client:
-
i2c_new_device
static struct i2c_board_info sfe4001_hwmon_info = {I2C_BOARD_INFO("max6647", 0x4e),};int sfe4001_init(struct efx_nic *efx){(...)efx->board_info.hwmon_client =i2c_new_device(&efx->i2c_adap, &sfe4001_hwmon_info);(...)}
i2c_new_probed_device
static const unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END };static int usb_hcd_nxp_probe(struct platform_device *pdev){(...)struct i2c_adapter *i2c_adap;struct i2c_board_info i2c_info;(...)i2c_adap = i2c_get_adapter(2);memset(&i2c_info, 0, sizeof(struct i2c_board_info));strscpy(i2c_info.type, "isp1301_nxp", sizeof(i2c_info.type));isp1301_i2c_client = i2c_new_probed_device(i2c_adap, &i2c_info,normal_i2c, NULL);i2c_put_adapter(i2c_adap);(...)}
差别:
-
i2c_new_device:会创建i2c_client,即使该设备并不存在
-
i2c_new_probed_device:
-
它成功的话,会创建i2c_client,并且表示这个设备肯定存在
-
I2C设备的地址可能发生变化,比如AT24C02的引脚A2A1A0电平不一样时,设备地址就不一样
-
可以罗列出可能的地址
-
i2c_new_probed_device使用这些地址判断设备是否存在
-
-
方法3(不推荐):由i2c_driver.detect函数来判断是否有对应的I2C设备并生成i2c_client
-
方法4:通过用户空间(user-space)生成 调试时、或者不方便通过代码明确地生成i2c_client时,可以通过用户空间来生成。
// 创建一个i2c_client, .name = "eeprom", .addr=0x50, .adapter是i2c-3# echo eeprom 0x50 > /sys/bus/i2c/devices/i2c-3/new_device// 删除一个i2c_client# echo 0x50 > /sys/bus/i2c/devices/i2c-3/delete_device
三、 AT24C02 介绍
AT24C02 是基于 I2C 总线的存储器件,由于接口方便,体积小,数据掉电不丢失等特点,在仪器仪表及工业自动化控制中得到大量的应用。百问网提供的 EEPROM 模块使用的就是 AT24C02,使用 8 位地址,存储容量为 2K bit,即 2048bit = 256*8bit = 256 Byte, 其中它被分为 32 页,每页 8Byte。

基于 I2C 接口的设备操作,我们主要关心的是 I2C 数据格式。 AT24C02 作为 EEPROM 存储设备,显然我们关心的是:怎么读写某个地址,即怎么发地址、怎么读写数据。 查阅《AT24CXX.pdf》手册,要写入一个字节,可如下操作:先发出设备地址, 再发出 WORD 地址(即存储地址),再发出数据。

反之,要读 AT24C02,如下图:涉及 2 次 I2C 传输。 先发出设备地址, WORD地址;再次发出设备地址,读到数据。


四、 AT24C02驱动开发
实验
先添加设备树




这是我们的设备树目录

可以用find找到我们刚添加的设备目录


咱们用的这个小工具有个配置项可以设置一下很方便


reg这个可能是特殊的加密文件需要用hexdump去看



虽然我们是iic-1但是内核是从0开始的所以这个就是我们的设备目录

插入模块后我们的驱动就出来了,没写的时候读出来是一堆乱码


验证一下它的非易失性

没毛病
驱动程序
#include "asm/uaccess.h"
#include "linux/delay.h"
#include "linux/i2c.h"
#include <linux/module.h>
#include <linux/poll.h>#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/timer.h>/* 主设备号 */
static int major = 0;
static struct class *my_i2c_class;static struct i2c_client *g_client;static DECLARE_WAIT_QUEUE_HEAD(gpio_wait);
struct fasync_struct *i2c_fasync;/* 实现对应的open/read/write等函数,填入file_operations结构体 */
static ssize_t i2c_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{int err;unsigned char *kern_buf;struct i2c_msg msgs[2];/* 从0读取size字节 */kern_buf = kmalloc(size, GFP_KERNEL);/* 初始化i2c_msg * 1. 发起一次写操作: 把0发给AT24C02, 表示要从0地址读数据* 2. 发起一次读操作: 得到数据*/msgs[0].addr = g_client->addr;msgs[0].flags = 0; /* 写操作 */msgs[0].buf = kern_buf;kern_buf[0] = 0; /* 把数据0发给设备 */msgs[0].len = 1;msgs[1].addr = g_client->addr;msgs[1].flags = I2C_M_RD; /* 写操作 */msgs[1].buf = kern_buf;msgs[1].len = size;err = i2c_transfer(g_client->adapter, msgs, 2);/* copy_to_user */err = copy_to_user(buf, kern_buf, size);kfree(kern_buf);return size;
}static ssize_t i2c_drv_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{int err;unsigned char kern_buf[9];struct i2c_msg msgs[1];int len;unsigned char addr = 0;/* 把size字节的数据写入地址0 *///kern_buf = kmalloc(size+1, GFP_KERNEL);while (size > 0){if (size > 8)len = 8;elselen = size;size -= len;/* copy_from_user */err = copy_from_user(kern_buf+1, buf, len);buf += len;/* 初始化i2c_msg * 1. 发起一次写操作: 把0发给AT24C02, 表示要从0地址读数据* 2. 发起一次读操作: 得到数据*/msgs[0].addr = g_client->addr;msgs[0].flags = 0; /* 写操作 */msgs[0].buf = kern_buf;kern_buf[0] = addr; /* 写AT24C02的地址 */msgs[0].len = len+1;addr += len;err = i2c_transfer(g_client->adapter, msgs, 1);mdelay(20);}//kfree(kern_buf);return size;
}static unsigned int i2c_drv_poll(struct file *fp, poll_table * wait)
{//printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);poll_wait(fp, &gpio_wait, wait);//return is_key_buf_empty() ? 0 : POLLIN | POLLRDNORM;return 0;
}static int i2c_drv_fasync(int fd, struct file *file, int on)
{if (fasync_helper(fd, file, on, &i2c_fasync) >= 0)return 0;elsereturn -EIO;
}/* 定义自己的file_operations结构体 */
static struct file_operations i2c_drv_fops = {.owner = THIS_MODULE,.read = i2c_drv_read,.write = i2c_drv_write,.poll = i2c_drv_poll,.fasync = i2c_drv_fasync,
};static int i2c_drv_probe(struct i2c_client *client,const struct i2c_device_id *id)
{// struct device_node *np = client->dev.of_node;// struct i2c_adapter *adapter = client->adapter;/* 记录client */g_client = client;/* 注册字符设备 *//* 注册file_operations */major = register_chrdev(0, "100ask_i2c", &i2c_drv_fops); /* /dev/gpio_desc */my_i2c_class = class_create(THIS_MODULE, "100ask_i2c_class");if (IS_ERR(my_i2c_class)) {printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);unregister_chrdev(major, "100ask_i2c");return PTR_ERR(my_i2c_class);}device_create(my_i2c_class, NULL, MKDEV(major, 0), NULL, "myi2c"); /* /dev/myi2c */return 0;
}static int i2c_drv_remove(struct i2c_client *client)
{/* 反注册字符设备 */device_destroy(my_i2c_class, MKDEV(major, 0));class_destroy(my_i2c_class);unregister_chrdev(major, "100ask_i2c");return 0;
}static const struct of_device_id myi2c_dt_match[] = {{ .compatible = "100ask,i2cdev" },{},
};static const struct i2c_device_id at24c02_ids[] = {{ "xxxxyyy", (kernel_ulong_t)NULL },{ /* END OF LIST */ }
};
static struct i2c_driver my_i2c_driver = {.driver = {.name = "100ask_i2c_drv",.owner = THIS_MODULE,.of_match_table = myi2c_dt_match,},.probe = i2c_drv_probe,.remove = i2c_drv_remove,.id_table = at24c02_ids,
};static int __init i2c_drv_init(void)
{/* 注册i2c_driver */return i2c_add_driver(&my_i2c_driver);
}static void __exit i2c_drv_exit(void)
{/* 反注册i2c_driver */i2c_del_driver(&my_i2c_driver);
}/* 7. 其他完善:提供设备信息,自动创建设备节点 */module_init(i2c_drv_init);
module_exit(i2c_drv_exit);MODULE_LICENSE("GPL");
应用程序
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <poll.h>
#include <signal.h>static int fd;/** ./i2c_test /dev/myi2c string * ./i2c_test /dev/myi2c**/
int main(int argc, char **argv)
{int ret;char buf[100];/* 1. 判断参数 */if (argc < 2) {printf("Usage:\n", argv[0]);printf(" %s <dev>, read at24c02\n", argv[0]);printf(" %s <dev> <string>, write at24c02\n", argv[0]);return -1;}/* 2. 打开文件 */fd = open(argv[1], O_RDWR | O_NONBLOCK);if (fd == -1){printf("can not open file %s\n", argv[1]);return -1;}if (argc == 3){ret = write(fd, argv[2], strlen(argv[2]) + 1);}else{ret = read(fd, buf, 100);printf("read: %s\n", buf);}close(fd);return 0;
}
相关文章:
019——IIC模块驱动开发(基于EEPROM【AT24C02】和I.MX6uLL)
目录 一、 IIC基础知识 二、Linux中的IIC(韦东山老师的学习笔记) 1. I2C驱动程序的层次 2. I2C总线-设备-驱动模型 2.1 i2c_driver 2.2 i2c_client 三、 AT24C02 介绍 四、 AT24C02驱动开发 实验 驱动程序 应用程序 一、 IIC基础知识 总线类…...
【开发篇】十三、JVM基础参数设置与垃圾回收器的选择
文章目录 1、-Xmx 和 –Xms2、-XX:MaxMetaspaceSize 和 –XX:MetaspaceSize3、-Xss4、不建议改的参数5、其他参数6、选择GC回收器的调试思路7、CMS的并发模式失败现象的解决8、调优案例 GC问题解决方式: 优化JVM基础参数,避免频繁Full GC减少对象的产生…...
多维 HighCharts
1:showHighChart.html <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><!-- js脚本都是官方的,后两个是highchart脚本 --><script type"text/javascript" src"jquery1.7.1.mi…...
单细胞RNA测序(scRNA-seq)cellranger count的细胞定量和aggr整合
单细胞RNA测序(scRNA-seq)基础知识可查看以下文章: 单细胞RNA测序(scRNA-seq)工作流程入门 单细胞RNA测序(scRNA-seq)细胞分离与扩增 单细胞RNA测序(scRNA-seq)SRA数据下载及fastq-dumq数据拆分 单细胞RNA测序(scRNA-seq)Cellranger流程入门和数据质控 细胞定量…...
Unity URP 2021 Release-Notes
🌈Unity URP 2021 Release-Notes 本文信息收集来自自动搜集工具👈 版本更新内容2021.3.32URP: Vulkan URP will use MSAA samples count fallback from player settings. Prior to this x2 fallback would have been to upgrade to x4.(UUM-741)2021.3.…...
最新IntelliJ IDEA 2024.1 安装和快速配置教程
IntelliJ IDEA 2024.1 最新版如何快速入门体验?IntelliJ IDEA 2024.1 安装和配置教程 图文解说版 文章目录 IntelliJ IDEA 2024.1 最新版如何快速入门体验?IntelliJ IDEA 2024.1 安装和配置教程 图文解说版前言 第一步: IntelliJ IDEA 2024.1安装教程第 0 步&…...
24应届生求职中QAQ
有没有大佬给个机会帮忙内推一下啊,找工作太难了QAQ。 最近一直在BOOS上找工作,但是结果不太理想,一直没有找到满意的工作,有没有大佬帮忙内推一下,有的话请私信我QAQ。...
centos7离线安装postgresql13
**一.**安装存储库RPM yum install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm二.使用yumdownloader下载安装包 mkdir pg_yum cd pg_yum yumdownloader --resolve postgresql13-server**三.**上传rpm包到安…...
【JavaSE】搞定String类
前言 本篇会细致讲解String类的常见用法,让小伙伴们搞定String类~ 欢迎关注个人主页:逸狼 创造不易,可以点点赞吗~ 如有错误,欢迎指出~ 目录 前言 常用的三种字符串构造 字符串长度length 字符串比较 比较 比较字符串的内容equals…...
数字乡村创新实践探索农业现代化与农村治理现代化新路径:科技赋能农村全面振兴与农民幸福生活
目录 引言 一、数字乡村与农业现代化 1、智慧农业技术的应用 2、农业产业链的数字化转型 二、数字乡村与农村治理现代化 1、农村信息化水平的提升 2、农村治理模式的创新 三、科技赋能农村全面振兴与农民幸福生活 1、提升农业生产效益与农民收入 2、促进农村产业结构…...
【从零开始手搓12306项目】四、12306是如何成为全球最忙碌的网站之一?
4.1 12306有多忙碌 一天的请求量大概1600亿,平均180万/秒(二八理论:20%的事件产生80%的请求),不适合二八理论,因为12306分时间放票 平均一年售出30亿张,高峰期日售票能力达到了2000万张 高峰期…...
WebKit简介及工作流程
文章目录 一、WebKit简介二、WebKit结构三、Webkit工作流程四、WebKit常见问题五、WebKit优点六、相关链接 一、WebKit简介 WebKit是一个开源的浏览器引擎,它的起源可以追溯到2001年,当时苹果公司推出了其首款基于Unix的操作系统Mac OS X。在2002年&…...
软考-系统集成项目管理中级--进度管理(输入输出很重要!!!本章占分较高,着重复习)
本章历年考题分值统计(16年11月及以后按新教材考的) 本章重点常考知识点汇总清单(学握部分可直接理解记忆) 12、参数估算:参数估算是一种基于历史数据和项目参数,使用某种算法来计算成本或持续时间的估算技术。参数估算是指利用历史数据之间的统计关系和…...
AndroidAutomotive模块介绍(一)整体介绍
前言 Android Automotive 是一个基本 Android 平台,可运行 IVI 系统中预安装的 Android 应用以及可选的第二方和第三方 Android 应用。 本系列文档将会系统的介绍 Android Automotive 的功能、架构、逻辑等。模块逻辑将从 应用api接口、系统服务、底层服务&#x…...
【开发问题记录】Nacos修改服务实例权重时报错
问题记录 一、问题描述1.1 产生原因1.2 产生问题 二、问题解决2.1 docker部署的nacos解决方案2.1.1 进入nacos容器2.1.2 查看当前目录2.1.3 进入data文件夹2.1.4 删除protocol文件2.2 本地部署的nacos 一、问题描述 1.1 产生原因 在运行项目时,在本地启动了一个服务…...
高级IO和5种IO模型
目录 1. 高级IO1.1 IO的基本概念1.2 OS如何得知外设当中有数据可读取1.3 OS如何处理从网卡中读取到的数据包1.4 IO的步骤 2. 五种IO模型2.1 利用钓鱼来理解2.2 阻塞IO2.3 非阻塞IO2.4 信号驱动IO2.5 IO多路转接2.6 异步IO 3. 高级IO的概念3.1 同步通信 VS 异步通信3.2 阻塞 VS …...
OpenHarmony轻量系统开发【7】驱动之I2C显示OLED屏幕
7.1实验效果 Hispark WiFi开发套件又提供一个oled屏幕,但是鸿蒙源码中没有这个屏幕的驱动,我们需要自己去移植。 以下是移植效果: 接口:I2C 使用引脚:HI_IO_NAME_GPIO_13 、 HI_IO_NAME_GPIO_14 7.2代码 这里我直…...
C#:循环中断
任务描述 实现九九乘法表,按照编程要求,使用break跳出循环 测试说明 测试过程: 平台将编译用户补全代码,并根据程序的输出判断程序是否正确。 以下是测试样例: 测试输入: 预期输出: we found…...
34. UE5 RPG实现鼠标点击移动
在前面,我们实现过使用键盘按键wasd去实现控制角色的移动,现在,我们实现了InputAction按键触发,后面,实现一下通过鼠标点击地面实现角色移动。 我们将实现两种效果的切换,如果你点击地面快速松开࿰…...
《二》Qt Creator工具介绍与使用
一、关于界面 点击文件--->新建文件或项目会出现如下图: 我们选择第一个 点击下一步下一步: 继续下一步直到结束: 二,具体文件介绍 我们点击pro查看以下 QT core gui第1行 表示使用qt的core和gui库,如果以后…...
MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...
ESP32读取DHT11温湿度数据
芯片:ESP32 环境:Arduino 一、安装DHT11传感器库 红框的库,别安装错了 二、代码 注意,DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...
【JavaWeb】Docker项目部署
引言 之前学习了Linux操作系统的常见命令,在Linux上安装软件,以及如何在Linux上部署一个单体项目,大多数同学都会有相同的感受,那就是麻烦。 核心体现在三点: 命令太多了,记不住 软件安装包名字复杂&…...
优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...
Docker 本地安装 mysql 数据库
Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker ;并安装。 基础操作不再赘述。 打开 macOS 终端,开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...
【Go语言基础【12】】指针:声明、取地址、解引用
文章目录 零、概述:指针 vs. 引用(类比其他语言)一、指针基础概念二、指针声明与初始化三、指针操作符1. &:取地址(拿到内存地址)2. *:解引用(拿到值) 四、空指针&am…...
Go 并发编程基础:通道(Channel)的使用
在 Go 中,Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式,用于在多个 Goroutine 之间传递数据,从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...
嵌入式常见 CPU 架构
架构类型架构厂商芯片厂商典型芯片特点与应用场景PICRISC (8/16 位)MicrochipMicrochipPIC16F877A、PIC18F4550简化指令集,单周期执行;低功耗、CIP 独立外设;用于家电、小电机控制、安防面板等嵌入式场景8051CISC (8 位)Intel(原始…...
【iOS】 Block再学习
iOS Block再学习 文章目录 iOS Block再学习前言Block的三种类型__ NSGlobalBlock____ NSMallocBlock____ NSStackBlock__小结 Block底层分析Block的结构捕获自由变量捕获全局(静态)变量捕获静态变量__block修饰符forwarding指针 Block的copy时机block作为函数返回值将block赋给…...
