• 嵌入式Linux驱动开发(I2C专题)(四)


    编写APP直接访问EEPROM

    参考资料

    • Linux驱动程序: drivers/i2c/i2c-dev.c
    • I2C-Tools-4.2: https://mirrors.edge.kernel.org/pub/software/utils/i2c-tools/
    • AT24cxx.pdf

    1. 硬件连接

    • STM32MP157的I2C模块连接方法
      在这里插入图片描述
    • IMX6ULL的I2C模块连接方法
      在这里插入图片描述

    2. AT24C02访问方法

    2.1 设备地址

    从芯片手册上可以知道,AT24C02的设备地址跟它的A2、A1、A0引脚有关:
    在这里插入图片描述

    打开I2C模块的原理图(这2个文件是一样的):

    • STM32MP157\开发板配套资料\原理图\04_Extend_modules(外设模块)\eeprom.zip\i2c_eeprom_module_v1.0.pdf
    • IMX6ULL\开发板配套资料\原理图\Extend_modules\eeprom.zip\i2c_eeprom_module_v1.0.pdf
    • 如下:
      在这里插入图片描述
      从原理图可知,A2A1A0都是0,所以AT24C02的设备地址是:0b1010000,即0x50。
    2.2 写数据

    在这里插入图片描述

    2.3 读数据

    可以读1个字节,也可以连续读出多个字节。
    连续读多个字节时,芯片内部的地址会自动累加。
    当地址到达存储空间最后一个地址时,会从0开始。
    在这里插入图片描述

    3. 使用I2C-Tools的函数编程

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include "i2cbusses.h"
    #include 
    
    int main(int argc, char **argv)
    {
    	unsigned char dev_addr = 0x50;		//设备地址
    	unsigned char mem_addr = 0;			//设备内部存储地址
    	unsigned char buf[32];
    
    	int file;
    	char filename[20];
    	unsigned char *str;
    
    	int ret;
    
    	struct timespec req;
    	
    	if (argc != 3 && argc != 4)
    	{
    		printf("Usage:\n");
    		printf("write eeprom: %s  w string\n", argv[0]);
    		printf("read  eeprom: %s  r\n", argv[0]);
    		return -1;
    	}
    
    	file = open_i2c_dev(argv[1][0]-'0', filename, sizeof(filename), 0);
    	if (file < 0)
    	{
    		printf("can't open %s\n", filename);
    		return -1;
    	}
    
    	if (set_slave_addr(file, dev_addr, 1))		//分配设备地址
    	{
    		printf("can't set_slave_addr\n");
    		return -1;
    	}
    
    	if (argv[2][0] == 'w')			//写操作
    	{
    		// write str: argv[3]
    		str = argv[3];
    
    		req.tv_sec  = 0;
    		req.tv_nsec = 20000000; /* 20ms */
    		
    		while (*str)
    		{
    			ret = i2c_smbus_write_byte_data(file, mem_addr, *str);
    			if (ret)
    			{
    				printf("i2c_smbus_write_byte_data err\n");
    				return -1;
    			}
    			// wait tWR(10ms)
    			nanosleep(&req, NULL);
    			
    			mem_addr++;
    			str++;
    		}
    		ret = i2c_smbus_write_byte_data(file, mem_addr, 0); // string end char
    		if (ret)
    		{
    			printf("i2c_smbus_write_byte_data err\n");
    			return -1;
    		}
    	}
    	else		//读操作
    	{
    		// read
    		ret = i2c_smbus_read_i2c_block_data(file, mem_addr, sizeof(buf), buf);
    		if (ret < 0)
    		{
    			printf("i2c_smbus_read_i2c_block_data err\n");
    			return -1;
    		}
    		
    		buf[31] = '\0';
    		printf("get data: %s\n", buf);
    	}
    	
    	return 0;
    	
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93

    4. 编译

    4.1 在Ubuntu设置交叉编译工具链
    • STM32MP157

      export ARCH=arm
      export CROSS_COMPILE=arm-buildroot-linux-gnueabihf-
      export PATH=$PATH:/home/book/100ask_stm32mp157_pro-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin
      
      • 1
      • 2
      • 3
    • IMX6ULL

      export ARCH=arm
      export CROSS_COMPILE=arm-linux-gnueabihf-
      export PATH=$PATH:/home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin
      
      • 1
      • 2
      • 3
    4.2 使用I2C-Tools的源码

    在这里插入图片描述

    4.3 编译

    为IMX6ULL编译时,有如下错误:
    在这里插入图片描述
    这是因为IMX6ULL的工具链自带的include目录中,没有smbus.h。

    需要我们自己提供这个头文件,解决方法:

    • 提供头文件:
      在这里插入图片描述

    • 修改Makefile指定头文件目录

      all:
      	$(CROSS_COMPILE)gcc -I ./include -o at24c02_test at24c02_test.c i2cbusses.c smbus.c
      	
      
      • 1
      • 2
      • 3

    4.4 上机测试

    以下命令在开发板中执行。

    • 挂载NFS

      • vmware使用NAT(假设windowsIP为192.168.1.100)

        [root@100ask:~]# mount -t nfs -o nolock,vers=3,port=2049,mountport=9999 
        192.168.1.100:/home/book/nfs_rootfs /mnt
        
        • 1
        • 2
      • vmware使用桥接,或者不使用vmware而是直接使用服务器:假设Ubuntu IP为192.168.1.137

        [root@100ask:~]#  mount -t nfs -o nolock,vers=3 192.168.1.137:/home/book/nfs_rootfs /mnt
        
        • 1
    • 复制、执行程序

    [root@100ask:~]# cp /mnt/at24c02_test   /bin
    [root@100ask:~]# at24c02_test 0 w www.100ask.net
    [root@100ask:~]# at24c02_test 0 r
    get data: www.100ask.net
    
    • 1
    • 2
    • 3
    • 4

    五、通用驱动i2c-dev分析

    参考资料:

    • Linux驱动程序: drivers/i2c/i2c-dev.c
    • I2C-Tools-4.2: https://mirrors.edge.kernel.org/pub/software/utils/i2c-tools/
    • AT24cxx.pdf

    1. 回顾字符设备驱动程序

    在这里插入图片描述
    怎么编写字符设备驱动程序?

    • 确定主设备号
    • 创建file_operations结构体
      • 在里面填充drv_open/drv_read/drv_ioctl等函数
    • 注册file_operations结构体
      • register_chrdev(major, &fops, name)
    • 谁调用register_chrdev?在入口函数调用
    • 有入口自然就有出口
      • 在出口函数unregister_chrdev
    • 辅助函数(帮助系统自动创建设备节点)
      • class_create
      • device_create

    2. i2c-dev.c注册过程分析

    2.1 register_chrdev的内部实现

    在这里插入图片描述

    2.2 i2c-dev驱动的注册过程

    在这里插入图片描述

    3. file_operations函数分析

    i2c-dev.c的核心:

    static const struct file_operations i2cdev_fops = {
    	.owner		= THIS_MODULE,
    	.llseek		= no_llseek,
    	.read		= i2cdev_read,
    	.write		= i2cdev_write,
    	.unlocked_ioctl	= i2cdev_ioctl,
    	.compat_ioctl	= compat_i2cdev_ioctl,
    	.open		= i2cdev_open,
    	.release	= i2cdev_release,
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    主要的系统调用:open, ioctl:
    在这里插入图片描述
    要理解这些接口,记住一句话:APP通过I2C Controller与I2C Device传输数据。

    3.1 i2cdev_open

    在这里插入图片描述

    3.2 i2cdev_ioctl: I2C_SLAVE/I2C_SLAVE_FORCE

    在这里插入图片描述

    3.3 i2cdev_ioctl: I2C_RDWR

    在这里插入图片描述

    3.4 i2cdev_ioctl: I2C_SMBUS

    在这里插入图片描述

    3.5 总结

    在这里插入图片描述

  • 相关阅读:
    kernel简单学习(CTF-wiki)
    如何使用yum 安装php7.2
    数据安全防护:云访问安全代理(CASB)
    9.14小米笔试C++
    服务器常用端口号及作用
    JavaScript do-while循环
    华为云IOT平台设备获取api调用笔记
    一篇文章让你没有爬不到的shipin,只有顶不住的shipin
    行为型模式 - 访问者模式Visitor
    详解js跨页面传参以及API的解释
  • 原文地址:https://blog.csdn.net/afddasfa/article/details/132868893