STM32 可以同时支持Master以及Slave mode。
通常我们I2C如果作为slave设备的时候,如果主机是Linux系统,我们往往先通过i2ctools进行设备的扫描,检测下设备是否存在。命令如下:
#i2cdetect -y -r i2c-id
如果能正确的发现设备,比如我们发现设备为0x17。这时候我们就可以通过0x17跟i2c slave进行通信了。比如我们可以通过以下命令查看这个设备的全部寄存器值。
#i2cdump -y -f 1 0x17
那么进一步思考,我们就会想为什么我们知道slave address是0x17,或者说为什么能通过0x17跟设备进行通信呢?
下面我们就来一点一点的揭开谜底。
我们随便打开一个介绍I2C的文档都是这么描述的,在I2C开始之后,先发送7bit的数据,然后一个读写位,如果设备slave address匹配成功,则会在第九个clock接收到ACK。
也就是说,在上面我们在Linux系统下其实只传递了前面7bit的数据,并没有添加读写位。而这个读写位是我们在调用读、写函数的时候,函数完成了对这个数据位的填充(此处需要注意,在单片机中,没有完成对这个第7位数据的填充功能,因此需要自己将这个slave address转换成函数能够正确传递的值,一般为slave address<<1)。
而当我们传递了slave address之后,数据到底是怎么被slave设备识别并且回复一个ACK的呢?
首先我们看一下STM32 I2C部分寄存器的定义:
这里我们需要对照源码来看一下这个地方的赋值情况。
hi2c1.Instance = I2C1;
hi2c1.Init.Timing = 0x40000A0B;
hi2c1.Init.OwnAddress1 = 46;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_ENABLE;
hi2c1.Init.OwnAddress2 = 208;
hi2c1.Init.OwnAddress2Masks = I2C_OA2_MASK02;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
可以看到我们虽然知道设备的地址是0x17,但是我们看到 hi2c1.Init.OwnAddress1 = 46;却是0x17<<1。
我们做一个对比,我们设置是10bit地址,看一下初始化代码部分是:
hi2c1.Instance = I2C1;
hi2c1.Init.Timing = 0x40000A0B;
hi2c1.Init.OwnAddress1 = 23;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_10BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
从上面的对比我们看的出来,首先在选择10bit的时候必须不能支持dual address了,另外地址就占用了最低位。
综上,我们就知道了为什么我们在使用7bit地址的时候一些疑问。
另外我们需要注意一下 I2C协议和SMBUS协议的不同,从而在实现协议的过程中,能够同时兼容两种协议模式。