韦东山嵌入式Liunx入门应用开发三(含课后作业、代码详解)

本文详细介绍了I2C协议及其在AT24C02EEPROM访问中的应用,包括SMBus协议的REPEATEDSTARTCondition和重要结构体如i2c_adapter、i2c_client和i2c_msg。还涵盖了使用I2CTools操作I2C设备和直接通过APP访问EEPROM的方法。
摘要由CSDN通过智能技术生成


本人学习完韦老师的视频,因此来复习巩固,写以笔记记之。
韦老师的课比较难,第一遍不知道在说什么,但是坚持看完一遍,再来复习,基本上就水到渠成了。
看完视频复习的同学观看最佳!
基于 IMX6ULL-PRO
参考视频 Linux快速入门到精通视频
参考资料:01_嵌入式Linux应用开发完全手册V5.1_IMX6ULL_Pro开发板.pdf

一、I2C 应用编程

1-1 I2C协议

I2C硬件框架如下图所示
在这里插入图片描述
在一个芯片内部,有一个或多个 I2C 控制器;在一个I2C控制器上,可以连接一个或多个I2C设备;I2C 总线只需要 2 条线:时钟线 SCL 、数据线 SDA;在 I2C 总线的SCL 、 SDA 线上,都有上拉电阻。

以I2C 接口的存储设备 AT24C02 为例:
① APP:
提出要求:把字符串**“www.100ask.net”** 写入AT24C02地址16开始的地方
② AT24C02 驱动:
◼ 它知道 AT24C02 要求的地址、数据格式
◼ 它知道发出什么信号才能让 AT24C02 执行擦除、烧写工作
◼ 它知道怎么判断数据是否烧写成功
◼ 它构造好一系列的数据,发给 I2C 控制器
③ I2C 控制器驱动:
◼ 它根据 I2C 协议发出各类信号: I2C 设备地址、 I2C 存储地址、数据
◼ 它根据 I2C 协议判断
IIC总线协议
I2C协议中数据传输的单位是字节,也就是8位。但是要用到9个时钟;前面8个时钟用来传输8数据,第9个时钟用来传输回应信号。传输时,先传输最高位(MSB)。
主芯片通过一根 SDA 线既可以把数据发给从设备,也可以从SDA 上读取数据,连接SDA线的引脚里面必然有两个引脚(发送引脚和接收引脚)。

在这里插入图片描述
在这里插入图片描述
① 当某一个芯片不想影响 SDA 线时,那就不驱动这个三极管
② 想让SDA 输出高电平,双方都不驱动三极管 (SDA 通过上拉电阻变为高电平)不然就会变为高阻态。
③ 想让SDA 输出低电平,就驱动三极管

ACK应答信号为低电平的原因?
数据传输,当主设备发送完8位以后,第9位为ACK应答信号,此时主设备不驱动三极管,而要发应答信号时,从设备驱动三极管,即当A=1;B=0时,此时输出为SDA为低电平。
为何SCL 也要使用上拉电阻?
在第 9 个时钟之后,如果有某一方需要更多的时间来处理数据,它可以一直驱动三极管把 SCL 拉低。当SCL为低电平时候,大家都不应该使用 IIC 总线,只有当SCL从低电平变为高电平的时候,IIC总线才能被使用。当它就绪后,就可以不再驱动三极管,这是上拉电阻把SCL 变为高电平,其他设备就可以继续使用I2C总线了。

1-2 SMBus协议

SMBus: System Management Bus,系统管理总线。
SMBus 为系统和电源管理这样的任务提供了一条控制总线,使用 SMBus 的系统,设备之间发送和接收消息都是通过 SMBus ,而不是使用单独的控制线,这样可以节省设备的管脚数。
SMBus 是基于I2C协议的,SMBus 要求更严格,SMBus是I2C协议的子集。

(1) REPEATED START Condition( 重复发出S信号)

在写和读设备之间,可以不发出P信号,而是直接发出Sr信号进行读写设备的切换。
在这里插入图片描述

(2) SMBus Receive Byte

在这里插入图片描述

1-3 I2C系统的重要结构体
(1) i2c_adapter结构体

使用一句话概括I2C 传输: APP通过I2C ControllerI2C Device传输数据。
首先确定使用哪个I2C Controller?使用 i2c_adapter可以表示I2C Controller,中的i2c_algorithm结构体中有主设备的传输函数来进行发送数据。
在这里插入图片描述在这里插入图片描述

int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,int num);

I2C主设备传输函数。该函数用于在总线上执行一系列 I2C 消息传输操作,返回成功处理的消息数量,或者在出错时返回负值。

int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,unsigned short flags, 
		char read_write,u8 command, int size, union i2c_smbus_data *data);

该函数用于在总线上执行 SMBus 消息传输操作,根据给定的参数执行读取或写入操作,并返回操作结果。

(2) i2c_client结构体

I2C总线上的设备使用i2c_client 结构体来表示一个I2C Device。一个I2C Device ,一定有设备地址。
在这里插入图片描述

(3) i2c_msg结构体

传输的数据用i2c_msg来表示
在这里插入图片描述
举例:设备地址为0x50 的 EEPROM ,要读取它里面存储地址为 0x10 的一个字节,
应该构造几个 i2c_msg ?要构造 2 个 i2c_msg

  1. 第一个 i2c_msg表示写操作,把要访问的存储地址 0x10发给设备
  2. 第二个 i2c_msg表示读操作
u8 d ata_addr = 0x10;
i8 data;
struct i2c_msg msgs[2];
msgs[0].addr = 0x50;
msgs[0].flags = 0;
msgs[0].len = 1;
msgs[0].buf = &data_addr;

msgs[1].addr = 0x50;
msgs[1].flags = I2C_M_RD;
msgs[1].len = 1;
msgs[1].buf = &data;

总结:
在这里插入图片描述

1-4 使用I2C Tools访问I2C设备

这部分看视频即可,视频中的源码是自己需要下载I2C Tools的源码才能找到。
使用I2C Tools 操作传感器 AP3216C(板子的右下角),AP3216C的设备地址是 0x1E
AP3216C是红外、光强、距离三合一的传感器,以读出光强、距离值为例,步骤如下:
① 复位:往寄存器0 写入 0x4
② 使能:往寄存器0 写入 0x3
③ 读光强:读寄存器0xC 、 0xD得到2字节的光强
④ 读距离:读寄存器0xE 、 0xF得到2字节的距离值

检测总线bus0上是否有I2C设备

i2cdetect -y 0

在这里插入图片描述

查询板子上有多少条I2C总线

i2cdetect -l

在这里插入图片描述

(1) 使用 SMBus 协议

在这里插入图片描述
复位:使用 SMBus 协议,在0号总线上的设备0x1e的0x0地址写入强制0x4

i2cset -f -y 0 0x1e 0 0x4

使能:在0号总线上的设备0x1e的0x0地址写入强制0x3

i2cset -f -y 0 0x1e 0 0x3

读光强:读寄存器 0xC 、0xD得到2字节的光强

i2cget -f -y 0 0x1e 0xc w

读距离:读寄存器 0xE 、0xF得到 2 字节的距离值

i2cget -f -y 0 0x1e 0xe w
(2) 使用 I2C 协议

在这里插入图片描述
使用下面指令,依次完成上述操作

i2ctransfer -f -y 0 w2@0x1e 0 0x4 
i2ctransfer -f -y 0 w2@0x1e 0 0x3 
i2ctransfer -f -y 0 w1@0x1e 0xc r2 
i2ctransfer -f -y 0 w1@0x1e 0xe r2
(3) I2C Tools访问I2C设备的2种方式

I2C Tools 可以通过SMBus来访问 I2C 设备,也可以使用一般的I2C协议来访问 I2C 设备。
① 怎么指定 I2C控制器?
i2c dev.c 为每个I2C控制器 (I2C Bus 、I2C Adapter) 都生成一个设备节点: /dev/i2c 0 、/dev/i2c 1等等
open 某个 /dev/i2c X 节点,就是去访问该 I2C 控制器下的设备;
② 怎么指定 I2C设备?
通过ioctl 指定 I2C 设备的地址
③ 怎么传输数据?
一般的 I2C 方式: ioctl(file, I2C_RDWR, &rdwr)
SMBus 方式: ioctl(file, I2C_SMBUS, &args)

1-5 编写APP直接访问EEPROM

根据实验和外设做实验即可,注意的地方有,
① 首先EEPROM白板对齐扩展板,防止插反。
② 插入对应的I2C插排,别插到SPI去了
③ 输出i2c检测指令,查看0x50地址位于那条总线上,如下图所示我的就是位于bus0总线上(与视频就不同)

i2cdetect -y 0
i2cdetect -y 1

在这里插入图片描述
代码解析

/* ./at24c02 <i2c_bus_number> w "100ask.taobao.com"
 * ./at24c02 <i2c_bus_number> r
 */

int main(int argc, char **argv)
{
	unsigned char dev_addr = 0x50;  /*i2c设备地址 片外地址*/
	unsigned char mem_addr = 0;		/*存储地址 从0地址开始 片内地址*/
	unsigned char buf[32];

	int file;
	char filename[20];

	int ret;
	struct timespec req;
	
	if (argc != 3 && argc != 4) {
		printf("Usage:\n");
		printf("write eeprom: %s <i2c_bus_number> w string\n", argv[0]);
		printf("read  eeprom: %s <i2c_bus_number> r\n", argv[0]);
		return -1;
	}

	/*argv[1]代表是第二个字符串      	   argv[1][0]代表第二个字符串中的第一个字符*/
	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]*/ 
		unsigned char *str = argv[3]; 

		req.tv_sec  = 0;
		req.tv_nsec = 20000000; /* 20ms */
		
		while (*str)  /*写入字符串*/
		{
			// mem_addr, *str
			// mem_addr++, 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;
}

实验结果
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值