Linux驱动开发(使用I2C总线设备驱动模型编写AT24C02驱动程序)

news/2025/2/16 5:43:30/

文章目录

  • 前言
  • 一、I2C总线设备驱动模型
  • 二、设备树编写
  • 三、驱动程序编写
    • 1.提供i2c_driver结构体变量并且注册
    • 2.注册file_operations结构体
    • 3.操作AT24C02
  • 四、应用程序编写
  • 五、上机测试
  • 总结


前言

本篇文章将讲解如何使用I2C总线设备驱动模型编写AT24C02驱动程序。

一、I2C总线设备驱动模型

I2C设备模型驱动程序是一种新的I2C设备驱动模型,引入了设备树(Device Tree)这一机制,可以在I2C设备和相应的Linux设备节点之间建立关联。在I2C设备模型中,所有I2C设备节点共用一个I2C设备模型驱动程序,不需要为每个I2C设备节点编写独立的设备驱动程序。

下图来自百问网:
在这里插入图片描述
在i2c总线下分别有i2c_client和i2c_driver。i2c_client就是硬件设备(比如本篇文章用到的AT24C02),i2c_driver就是我们需要编写的驱动程序。

在这里插入图片描述
i2c_client由设备树提供。

i2c_driver是我们自己编写的驱动程序,里面提供了probe函数,当驱动和设备树中的compatible属性匹配后调用probe函数。

二、设备树编写

因为我使用的AT24C02是挂载在i2c1这根总线上的所有需要在i2c1这个节点下添加at24c02这个子节点。
reg属性代表的是AT24C02的设备地址。

&i2c1 {clock-frequency = <100000>;pinctrl-names = "default";pinctrl-0 = <&pinctrl_i2c1>;status = "okay";at24c02 {compatible = "my,at24c02";reg = <0x50>;};
};

三、驱动程序编写

1.提供i2c_driver结构体变量并且注册

这里和之前编写的驱动程序的思路都是一样的提供一个driver结构体变量并且将其注册。

这里和之前最大的不同就是需要在i2c_driver结构体变量中提供id_table成员。

在内核源码中发现缺少了probe函数或者缺少了id_table成员都是无法进行正确的匹配的。
在这里插入图片描述

static const struct of_device_id at24c02_of_match[] = {{.compatible = "my,at24c02"},{}
};static const struct i2c_device_id at24c02_ids[] = {{ "xxxxyyy",	(kernel_ulong_t)NULL },{ /* END OF LIST */ }
};static struct i2c_driver at24c02_drv = {.driver = {.name = "myat24c02",.of_match_table = at24c02_of_match,},.probe = at24c02_probe,.remove = at24c02_remove,.id_table = at24c02_ids,
};/* 2. 在入口函数注册platform_driver */
static int __init at24c02_init(void)
{int err;printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);return i2c_add_driver(&at24c02_drv);return err;
}/* 3. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数*     卸载platform_driver*/
static void __exit at24c02_exit(void)
{printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);i2c_del_driver(&at24c02_drv);
}/* 7. 其他完善:提供设备信息,自动创建设备节点                                     */module_init(at24c02_init);
module_exit(at24c02_exit);MODULE_LICENSE("GPL");

2.注册file_operations结构体

这里我们使用ioctl来操作AT24C02,ioctl既可以读又可以写,可以对read和write函数进行替换。

/* 定义自己的file_operations结构体                                              */
static struct file_operations at24c02_fops = {.owner	 = THIS_MODULE,.unlocked_ioctl    = at24c02_chrdev_ioctl,
};static int at24c02_probe(struct i2c_client *client, const struct i2c_device_id *id)
{printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);at24c02_client = client;/* 注册file_operations 	*/major = register_chrdev(0, "100ask_at24c02", &at24c02_fops);  /* /dev/at24c02 */at24c02_class = class_create(THIS_MODULE, "100ask_at24c02_class");if (IS_ERR(at24c02_class)) {printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);unregister_chrdev(major, "100ask_at24c02");return PTR_ERR(at24c02_class);}device_create(at24c02_class, NULL, MKDEV(major, 0), NULL, "100ask_at24c02"); /* /dev/100ask_at24c02 */return 0;
}static int at24c02_remove(struct i2c_client *client)
{printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);device_destroy(at24c02_class, MKDEV(major, 0));class_destroy(at24c02_class);unregister_chrdev(major, "100ask_at24c02");return 0;
}

3.操作AT24C02

根据AT24C02的数据手册我们可以清楚的知道如何去读写AT24C02。

写AT24C02时序:
在这里插入图片描述
写AT24C02时需要发送设备地址和需要写入的数据,写入数据的地址,只需要构造一个msg消息即可。

读AT24C02时序:
在这里插入图片描述

读AT24C02时首先需要写入设备地址,再去读取指定要读取数据的地址。 然后再发起一次操作,指定设备地址,指定读取数据保存的地址。一共需要构造两个msg消息。

#define IOC_AT24C02_READ  100
#define IOC_AT24C02_WRITE 101/* 主设备号                                                                 */
static int major = 0;
static struct class *at24c02_class;
struct i2c_client *at24c02_client;static long at24c02_chrdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{unsigned char addr;unsigned char data;unsigned int ker_buf[2];unsigned int *usr_buf = (unsigned int *)arg;unsigned char byte_buf[2];struct i2c_msg msgs[2];copy_from_user(ker_buf, usr_buf, 8);addr = ker_buf[0];switch (cmd){case IOC_AT24C02_READ:{/* 读AT24C02 */msgs[0].addr  = at24c02_client->addr;msgs[0].flags = 0; /* 写 */msgs[0].len   = 1;msgs[0].buf   = &addr;msgs[1].addr  = at24c02_client->addr;msgs[1].flags = I2C_M_RD; /* 读 */msgs[1].len   = 1;msgs[1].buf   = &data;i2c_transfer(at24c02_client->adapter, msgs, 2);ker_buf[1] = data;copy_to_user(usr_buf, ker_buf, 8);break;}case IOC_AT24C02_WRITE:{/* 写AT24C02 */byte_buf[0] = addr;byte_buf[1] = ker_buf[1];msgs[0].addr  = at24c02_client->addr;msgs[0].flags = 0; /* 写 */msgs[0].len   = 2;msgs[0].buf   = byte_buf;i2c_transfer(at24c02_client->adapter, msgs, 1);mdelay(20);break;}}return 0;}

完整代码:

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/mod_devicetable.h>
#include <linux/log2.h>
#include <linux/bitops.h>
#include <linux/jiffies.h>
#include <linux/of.h>
#include <linux/acpi.h>
#include <linux/i2c.h>
#include <asm/uaccess.h>#define IOC_AT24C02_READ  100
#define IOC_AT24C02_WRITE 101/* 主设备号                                                                 */
static int major = 0;
static struct class *at24c02_class;
struct i2c_client *at24c02_client;static long at24c02_chrdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{unsigned char addr;unsigned char data;unsigned int ker_buf[2];unsigned int *usr_buf = (unsigned int *)arg;unsigned char byte_buf[2];struct i2c_msg msgs[2];copy_from_user(ker_buf, usr_buf, 8);addr = ker_buf[0];switch (cmd){case IOC_AT24C02_READ:{/* 读AT24C02 */msgs[0].addr  = at24c02_client->addr;msgs[0].flags = 0; /* 写 */msgs[0].len   = 1;msgs[0].buf   = &addr;msgs[1].addr  = at24c02_client->addr;msgs[1].flags = I2C_M_RD; /* 读 */msgs[1].len   = 1;msgs[1].buf   = &data;i2c_transfer(at24c02_client->adapter, msgs, 2);ker_buf[1] = data;copy_to_user(usr_buf, ker_buf, 8);break;}case IOC_AT24C02_WRITE:{/* 写AT24C02 */byte_buf[0] = addr;byte_buf[1] = ker_buf[1];msgs[0].addr  = at24c02_client->addr;msgs[0].flags = 0; /* 写 */msgs[0].len   = 2;msgs[0].buf   = byte_buf;i2c_transfer(at24c02_client->adapter, msgs, 1);mdelay(20);break;}}return 0;}/* 定义自己的file_operations结构体                                              */
static struct file_operations at24c02_fops = {.owner	 = THIS_MODULE,.unlocked_ioctl    = at24c02_chrdev_ioctl,
};static int at24c02_probe(struct i2c_client *client, const struct i2c_device_id *id)
{printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);at24c02_client = client;/* 注册file_operations 	*/major = register_chrdev(0, "100ask_at24c02", &at24c02_fops);  /* /dev/at24c02 */at24c02_class = class_create(THIS_MODULE, "100ask_at24c02_class");if (IS_ERR(at24c02_class)) {printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);unregister_chrdev(major, "100ask_at24c02");return PTR_ERR(at24c02_class);}device_create(at24c02_class, NULL, MKDEV(major, 0), NULL, "100ask_at24c02"); /* /dev/100ask_at24c02 */return 0;
}static int at24c02_remove(struct i2c_client *client)
{printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);device_destroy(at24c02_class, MKDEV(major, 0));class_destroy(at24c02_class);unregister_chrdev(major, "100ask_at24c02");return 0;
}static const struct of_device_id at24c02_of_match[] = {{.compatible = "my,at24c02"},{}
};static const struct i2c_device_id at24c02_ids[] = {{ "xxxxyyy",	(kernel_ulong_t)NULL },{ /* END OF LIST */ }
};static struct i2c_driver at24c02_drv = {.driver = {.name = "myat24c02",.of_match_table = at24c02_of_match,},.probe = at24c02_probe,.remove = at24c02_remove,.id_table = at24c02_ids,
};/* 2. 在入口函数注册platform_driver */
static int __init at24c02_init(void)
{int err;printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);return i2c_add_driver(&at24c02_drv);return err;
}/* 3. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数*     卸载platform_driver*/
static void __exit at24c02_exit(void)
{printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);i2c_del_driver(&at24c02_drv);
}/* 7. 其他完善:提供设备信息,自动创建设备节点                                     */module_init(at24c02_init);
module_exit(at24c02_exit);MODULE_LICENSE("GPL");

四、应用程序编写

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>#define IOC_AT24C02_READ  100
#define IOC_AT24C02_WRITE 101/** at24c02_test /dev/myat24c02 r 10* at24c02_test /dev/myat24c02 w 10 123*/int main(int argc, char **argv)
{int fd;int buf[2];if ((argc != 4) && (argc != 5)){printf("Usage: %s <dev> r <addr>\n", argv[0]);printf("       %s <dev> w <addr> <val>\n", argv[0]);return -1;}fd = open(argv[1], O_RDWR);if (fd < 0){printf(" can not open %s\n", argv[1]);return -1;}if (argv[2][0] == 'r'){buf[0] = strtoul(argv[3], NULL, 0);ioctl(fd, IOC_AT24C02_READ, buf);printf("Read addr 0x%x, get data 0x%x\n", buf[0], buf[1]);}else{buf[0] = strtoul(argv[3], NULL, 0);buf[1] = strtoul(argv[4], NULL, 0);ioctl(fd, IOC_AT24C02_WRITE, buf);}return 0;
}

五、上机测试

装载驱动后进入/sys/bus/i2c/devices目录下找到我们自己编写的驱动程序:
在这里插入图片描述
进入0-0050目录使用cat命令查看具体信息:
在这里插入图片描述
根据信息可以得知驱动程序装载成功。

进行at24c02的读写操作:
在这里插入图片描述
读写测试通过。

总结

本篇文章主要讲解了i2C总线设备驱动模型编写AT24C02驱动程序,这里大家主要需要掌握的就是i2C总线设备驱动这个模型,只要掌握好了这个模型那么剩下的就是裸机的操作了。


http://www.ppmy.cn/news/252289.html

相关文章

PyGame游戏编程

Python非常受欢迎的一个原因是它的应用领域非常广泛&#xff0c;其中就包括游戏开发。而是用Python进行游戏开发的首选模块就是PyGame。 1. 初识Pygame PyGame是跨平台Python模块&#xff0c;专为电子游戏设计&#xff0c;包含图像、声音等&#xff0c;创建在SDL&#xff08;…

软件过程与管理——民宿管理系统的项目实践报告(文档+ppt+图表源文件)

目录 一、题目分析与设计 二、评分标准 三、文档目录 四、文档下载 一、题目分析与设计 1、团队组织建设 同学们以3-5人为一组&#xff0c;最多5人一组&#xff0c;每组选择一个具体的软件项目&#xff0c;如现进行的个人毕业设计题目等为主题&#xff0c;要求项目的工作…

【25JavaScript 调试】JavaScript调试技巧:掌握console.log、断点调试和浏览器开发者工具,轻松解决代码中的错误和异常情况

JavaScript 调试 调试是在开发过程中解决问题和排除错误的关键技能。在 JavaScript 中&#xff0c;我们可以使用多种工具和技术来帮助我们进行调试&#xff0c;以快速定位和修复代码中的问题。 使用 console.log() 输出调试信息 console.log() 是调试中最常用的方法之一。它…

DALL·E 2:艺术性的人工智能

DALLE 2 是 OpenAI 公司最新推出的人工智能工具&#xff0c;它具有创造性和艺术性。DALLE 2 可以生成令人惊奇的图像&#xff0c;并且它可以理解文本&#xff0c;根据文本生成图像。 DALLE 2 使用了深度学习技术&#xff0c;可以识别复杂的图像结构和图形。它可以根据文本描述生…

DALL-E如何使用

DALL-E是由OpenAI开发的一个人工智能模型,可以根据文字描述生成图像。它是基于深度学习的模型,在运行时需要大量的计算资源。 使用OpenAI API使用DALL-E的示例代码如下: import requests from requests.structures import CaseInsensitiveDictimport jsonQUERY_URL = &quo…

刷爆 AI 圈!基于 Transformer 的 DALL-E 代码刚刚开源了

点击上方“视学算法”&#xff0c;选择加"星标"或“置顶” 重磅干货&#xff0c;第一时间送达 转自 | AI科技评论 OpenAI在1月5日公布DALL-E模型以来&#xff0c;人们都惊艳于模型的语言想象力是如此丰富和细致。如今&#xff0c;我们终于等到了论文的公布&#xff0…

AI-多模态-文本->图像-2021:DALL-E模型【OpenAI】

Dall-e&#xff1a;从拟物文字到图片的创造 人类不断地从五种感官接收和整合信息&#xff0c;通过视觉、听觉、触觉、嗅觉和味觉等生物信息来理解文字和图片。然而文字和图片属于符号&#xff0c;Dall-e模型在理解符号的含义时并不能通过生物信息的传递。通过将对自然语言的理…

用AI智能(DALL.E2)搞一个Logo和绘制坤坤的脚

浅谈ChatGPT和DALL.E2 ChatGPT 最近有个叫chatgpt的玩意老火了&#xff0c;不仅在科技圈刮起一阵骚乱&#xff0c;同时在金融圈也闹的沸沸扬扬&#xff0c;很多板块个股纷纷水涨船高 chatgpt是美国人工智能研究所实验室OpenAi开发的一种全新的聊天机器人模型&#xff0c;当然我…