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

【IMX6ULL驱动开发学习】10.Linux I2C驱动实战:AT24C02驱动设计流程

前情回顾:【IMX6ULL驱动开发学习】09.Linux之I2C框架简介和驱动程序模板_阿龙还在写代码的博客-CSDN博客

目录

一、修改设备树(设备树用来指定引脚资源)

二、编写驱动

2.1 i2c_drv_read

2.2 i2c_drv_write

2.3 完整驱动程序

三、上机测试

一、修改设备树(设备树用来指定引脚资源)

  • 放在哪个I2C控制器下面
  • AT24C02的I2C设备地址(查看芯片手册 0x50)
  • compatible属性:用来寻址驱动程序

修改设备树:在内核目录下 vi  arch/arm/boot/dts/100ask_imx6ull-14x14.dts 

&i2c1 {clock-frequency = <100000>;pinctrl-names = "default";pinctrl-0 = <&pinctrl_i2c1>;status = "okay";at24c02 { compatible = "100ask,i2cdev";reg = <0x50>;//设备地址 查看芯片手册};  
};

at24c02节点会转换成i2c_client,i2c_client里有个adpater,即i2c_client.adpater。i2c_client.adpater将指向一个i2c控制器,i2c控制器与&i2c1节点对应,即&i2c1就是一个i2c控制器,内核里对&i2c1这个i2c控制器已经编写好了驱动程序。

二、编写驱动

在I2C驱动框架的基础上对i2c_drv_read和i2c_drv_write函数修改

2.1 i2c_drv_read

实现读AT24C02的0地址数据需要完成一下步骤:

  • 初始化i2c_msg

  • 发起一次写操作: 把0发给AT24C02, 表示要从0地址读数据

  • 发起一次读操作: 得到数据

/* 实现对应的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; //设备树里记录了设备地址:0x50msgs[0].flags = 0;              //flags为0:写操作 msgs[0].buf   = kern_buf;		//取得数据kern_buf[0]   = 0; 				//buf里第一个字节表示设备存储空间地址,把地址数据0发给设备msgs[0].len   = 1;				//发送长度为1,即 发送一个字节数据0给设备msgs[1].addr  = g_client->addr;msgs[1].flags = I2C_M_RD;  		//读操作msgs[1].buf   = kern_buf;		//读到的数据保存在kern_buf里msgs[1].len   = size;			//读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;
}

2.2 i2c_drv_write

AT24C02一页只有8个字节内存,故写入多数据需要循环写入

实现AT24C02的0地址起的读数据需要完成一下步骤:

  • 初始化i2c_msg

  • 发起一次写操作:把地址发给AT24C02, 表示要从该地址读数据

  • 地址偏移并继续写

//一页只有8个字节 循环写
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的地址,从0开始写,buf里第一个字节表示设备存储空间地址msgs[0].len   = len+1;addr += len;err = i2c_transfer(g_client->adapter, msgs, 1);mdelay(20);//写一页需要时间约10ms,烧写一页数据需等待}//kfree(kern_buf);return size;    
}

2.3 完整驱动程序

i2c_drv.c

#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; //设备树里记录了设备地址:0x50msgs[0].flags = 0;              //flags为0:写操作 msgs[0].buf   = kern_buf;		//取得数据kern_buf[0]   = 0; 				//buf里第一个字节表示设备存储空间地址,把地址数据0发给设备msgs[0].len   = 1;				//发送长度为1,即 发送一个字节数据0给设备msgs[1].addr  = g_client->addr;msgs[1].flags = I2C_M_RD;  		//读操作msgs[1].buf   = kern_buf;		//读到的数据保存在kern_buf里msgs[1].len   = size;			//读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;
}//一页只有8个字节 循环写
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的地址,从0开始写,buf里第一个字节表示设备存储空间地址msgs[0].len   = len+1;addr += len;err = i2c_transfer(g_client->adapter, msgs, 1);mdelay(20);//写一页需要时间约10ms,烧写一页数据需等待}//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");

三、上机测试

  • 在/home/book/100ask_imx6ull-sdk/Linux-4.9.88目录下重新编译设备树:make dtbs
  • 复制到单板上,如下:
PC:
cp arch/arm/boot/dts/100ask_imx6ull-14x14.dtb ~/nfs_rootfs/开发板:
mount -t nfs -o nolock,vers=3 192.168.5.11:/home/book/nfs_rootfs /mnt
cp /mnt/100ask_imx6ull-14x14.dtb  /boot
reboot
  • 进入系统固件目录下查看cd /sys/firmware/devicetree/base/

  • 在系统 总线 i2c 设备下面查看是否有这个设备

 

 有这个设备,但没有对应驱动程序

  • 挂载网络文件系统:mount -t nfs -o nolock,vers=3 192.168.5.11:/home/book/nfs_rootfs /mnt
  • 装载驱动程序:insmod i2c_drv.ko
  • 查看对应设备节点:ls /dev/myi2c -l  

  • 查看用法、测试驱动程序 

 

 成功读出数据!

 

 

 

相关文章:

【IMX6ULL驱动开发学习】10.Linux I2C驱动实战:AT24C02驱动设计流程

前情回顾&#xff1a;【IMX6ULL驱动开发学习】09.Linux之I2C框架简介和驱动程序模板_阿龙还在写代码的博客-CSDN博客 目录 一、修改设备树&#xff08;设备树用来指定引脚资源&#xff09; 二、编写驱动 2.1 i2c_drv_read 2.2 i2c_drv_write 2.3 完整驱动程序 三、上机测…...

【C++】详解声明和定义

2023年8月28日&#xff0c;周一下午 研究了一个下午才彻底弄明白... 写到晚上才写完这篇博客。 目录 声明和定义的根本区别结构体的声明和定义声明结构体 定义结构体类的声明和定义函数的定义和声明声明函数 定义函数变量声明和定义声明变量定义变量 声明和定义的根本区别 …...

掌握C/C++协程编程,轻松驾驭并发编程世界

一、引言 协程的定义和背景 协程&#xff08;Coroutine&#xff09;&#xff0c;又称为微线程或者轻量级线程&#xff0c;是一种用户态的、可在单个线程中并发执行的程序组件。协程可以看作是一个更轻量级的线程&#xff0c;由程序员主动控制调度。它们拥有自己的寄存器上下文…...

MyBatis-Plus的分页配置类

文章目录 package com.itheima.reggie.config;import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import org.springframework.context.annotation.Bean; imp…...

排序算法-选择排序(Java)

选择排序 选择排序 &#xff08;selection sort&#xff09;的工作原理非常直接&#xff1a;开启一个循环&#xff0c;每轮从未排序区间选择最小的元素&#xff0c;将其放到已排序区间的末尾。 算法原理 排序数组&#xff1a;&#xff08;2 4 3 1 5 2&#xff09; &#xf…...

SpringBoot 怎么返回html界面

方法一&#xff1a; &#xff08;1&#xff09;html文件要放在resource下的static目录下&#xff08;没有static 自己就创建一个文件夹&#xff09; &#xff08;2&#xff09;在application.yml 中配置视图解析器 spring:mvc:view:prefix: /suffix: .html &#xff08;3&a…...

watch computed 和 method

在Vue中&#xff0c;watch computed 和 method有啥区别&#xff0c;有啥作用&#xff0c;适用于何种情景并代码举例 在Vue中&#xff0c;watch、computed和methods是三种不同的属性&#xff0c;用于处理不同的场景和需求。 watch&#xff1a;watch用于监听数据的变化并执行相…...

数据结构,线性表有哪些

线性表是一种常见的数据结构&#xff0c;它的特点是数据元素之间存在一对一的线性关系。根据线性表的存储方式和实现方式&#xff0c;线性表主要有以下几种&#xff1a; 1. 顺序表&#xff08;Sequential List&#xff09;: - 通常使用数组实现。 - 元素在内存中是连续…...

服务间通过Feign相互调用报错,参数是MultiparFile、参数是POJO报错

目录 1.Feign传文件报错,Feign不支持上传文件需要借助外面的依赖才可以实现上传 2.服务之间通过Feign相互调用传递DTO(实体对象)报错 1.Feign传文件报错,Feign不支持上传文件需要借助外面的依赖才可以实现上传 具体报错内容: FileUploadException: the request was reje…...

Flutter系列文章-Flutter应用优化

当涉及到优化 Flutter 应用时&#xff0c;考虑性能、UI 渲染和内存管理是至关重要的。在本篇文章中&#xff0c;我们将通过实例深入讨论这些主题&#xff0c;展示如何通过优化技巧改进你的 Flutter 应用。 代码性能优化 1. 使用 const 构造函数 在构建小部件时&#xff0c;尽…...

opencv案例03 -基于OpenCV实现二维码生成,发现,定位,识别

1.二维码的生成 废话不多说&#xff0c;直接上代码 # 生成二维码 import qrcode# 二维码包含的示例数据 data "B0018" # 生成的二维码图片名称 filename "qrcode.png" # 生成二维码 img qrcode.make(data) # 保存成图片输出 img.save(filename)img.sh…...

叠螺式污泥脱水机的要点及价格分析

诸城市鑫淼环保小编带大家了解一下叠螺式污泥脱水机的要点及价格分析 设备工作步骤 叠螺脱水机在工作时分为3个步骤&#xff0c;分别是稀释、脱水、自洗濯&#xff1a; 1、稀释&#xff1a;当螺旋推进轴迁移转变时&#xff0c;设在推进轴核心的多重固活叠片挪动&#xff0c;在重…...

Visual Studio中Linux开发头文件intellisense问题的解决办法

文章目录 前言个人环境 SSH到WSL复制文件后记 前言 最近在用我心爱的Visual Studio配合WSL2做一些Linux开发&#xff0c;但是有一个问题&#xff0c;就是当我#include <sys/socket.h>&#xff0c;会提示找不到文件 我尝试了各种姿势&#xff0c;包括修改CMakeSettings.…...

如何以CPU方式启动Stable Diffusion WebUI?

默认情况下Stable Diffusion WebUI采用GPU模式运行&#xff0c;但是稍微运行起来就知道至少需要4G的显存&#xff0c;2G显存虽然能够通过带--lowvram运行起来&#xff0c;但是能够炼出来的图基本都是512x512的&#xff0c;不能够炼大图&#xff0c;如果你刚好和我一样家境贫寒&…...

HTTP 框架修炼之道 | 青训营

Powered by:NEFU AB-IN 文章目录 HTTP 框架修炼之道 | 青训营 走进 HTTP 协议HTTP 框架的设计与实现应用层中间件层路由设计协议层 传输层&#xff08;网络层&#xff09;1. BIO&#xff08;Blocking I/O&#xff09;:2. NIO&#xff08;Non-blocking I/O&#xff09;:区别&…...

【活动】怎么维护电脑?

无论是学习还是工作&#xff0c;电脑都是IT人必不可少的重要武器&#xff0c;一台好电脑除了自身配置要经得起考验&#xff0c;后期主人对它的维护也是决定它寿命的重要因素&#xff01; 其实这是个伪命题&#xff0c;就像穿衣一样&#xff0c;有的人喜欢干净整洁&#xff0c;…...

Linux安装Docker

文章目录 先决条件开始安装1.卸载旧版本2.安装依赖3.切换数据源4.安装Docker5.启动服务6.查看版本7.查看端口8.测试拉取镜像 同系列文章 先决条件 Linux内核版本高于3.1 开始安装 1.卸载旧版本 yum remove docker \docker-client \docker-client-latest \docker-common \doc…...

极氪汽车的云资源治理细探

作者&#xff1a;极氪汽车吴超 前言 2021 年&#xff0c;极氪 001 迅速崭露头角&#xff0c;仅用 110 天便创下了首款车型交付量“最快破万”的纪录。2022 年 11 月&#xff0c;极氪 009 在短短 76 天内便率先完成了首批交付&#xff0c;刷新了中国豪华纯电品牌交付速度的纪录…...

设计模式之抽象工厂

文章目录 一、介绍二、基本组件三、演示案例1. 定义抽象工厂2. 定义抽象产品3. 定义具体工厂4. 定义具体产品5. 代码演示6. 代码改造 四、总结 一、介绍 抽象工厂模式(Abstract Factory Pattern)属于创建型设计模式。用于解决比工厂方法设计模式更加复杂的问题。 复杂到哪里了…...

问道管理:数字经济概念走势强劲,竞业达、久其软件等涨停,观想科技等大涨

信创、智慧政务等数字经济概念22日盘中走势微弱&#xff0c;截至发稿&#xff0c;观想科技、慧博云通涨超15%&#xff0c;竞业达、中远海科、久其软件等涨停&#xff0c;云赛智联、延华智能、汇纳科技涨约9%&#xff0c;天玑科技、安硕信息、思特奇、零点稀有涨逾7%。 音讯面上…...

Spring Boot 实现流式响应(兼容 2.7.x)

在实际开发中&#xff0c;我们可能会遇到一些流式数据处理的场景&#xff0c;比如接收来自上游接口的 Server-Sent Events&#xff08;SSE&#xff09; 或 流式 JSON 内容&#xff0c;并将其原样中转给前端页面或客户端。这种情况下&#xff0c;传统的 RestTemplate 缓存机制会…...

QMC5883L的驱动

简介 本篇文章的代码已经上传到了github上面&#xff0c;开源代码 作为一个电子罗盘模块&#xff0c;我们可以通过I2C从中获取偏航角yaw&#xff0c;相对于六轴陀螺仪的yaw&#xff0c;qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...

学校招生小程序源码介绍

基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码&#xff0c;专为学校招生场景量身打造&#xff0c;功能实用且操作便捷。 从技术架构来看&#xff0c;ThinkPHP提供稳定可靠的后台服务&#xff0c;FastAdmin加速开发流程&#xff0c;UniApp则保障小程序在多端有良好的兼…...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1

每日一言 生活的美好&#xff0c;总是藏在那些你咬牙坚持的日子里。 硬件&#xff1a;OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写&#xff0c;"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...

工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配

AI3D视觉的工业赋能者 迁移科技成立于2017年&#xff0c;作为行业领先的3D工业相机及视觉系统供应商&#xff0c;累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成&#xff0c;通过稳定、易用、高回报的AI3D视觉系统&#xff0c;为汽车、新能源、金属制造等行…...

html-<abbr> 缩写或首字母缩略词

定义与作用 <abbr> 标签用于表示缩写或首字母缩略词&#xff0c;它可以帮助用户更好地理解缩写的含义&#xff0c;尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时&#xff0c;会显示一个提示框。 示例&#x…...

七、数据库的完整性

七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...

深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用

文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么&#xff1f;1.1.2 感知机的工作原理 1.2 感知机的简单应用&#xff1a;基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...

LLMs 系列实操科普(1)

写在前面&#xff1a; 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容&#xff0c;原视频时长 ~130 分钟&#xff0c;以实操演示主流的一些 LLMs 的使用&#xff0c;由于涉及到实操&#xff0c;实际上并不适合以文字整理&#xff0c;但还是决定尽量整理一份笔…...

作为测试我们应该关注redis哪些方面

1、功能测试 数据结构操作&#xff1a;验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化&#xff1a;测试aof和aof持久化机制&#xff0c;确保数据在开启后正确恢复。 事务&#xff1a;检查事务的原子性和回滚机制。 发布订阅&#xff1a;确保消息正确传递。 2、性…...