电子说
在电子设计中,当我们遇到需要连接多个具有相同 I2C 地址的设备时,常常会陷入困境。不过,Adafruit TCA9548A 1-to-8 I2C 多路复用器的出现,为我们提供了一个完美的解决方案。
文件下载:2717.pdf
在使用 I2C 传感器时,我们可能会遇到这样的问题:某个 I2C 传感器芯片具有固定的 I2C 地址,而 I2C 协议不允许在同一 SDA/SCL 引脚上连接两个具有相同地址的设备。这时,TCA9548A 多路复用器就派上用场了。它就像一个门卫,能够根据我们的指令将命令传输到选定的 I2C 引脚组。
该多路复用器本身的 I2C 地址默认为 0x70,但可以在 0x70 到 0x77 之间进行调整。我们只需向该端口写入一个包含所需多路复用输出编号的单字节,后续的 I2C 数据包就会被发送到该端口。理论上,我们可以在 0x70 - 0x77 每个地址上使用 8 个这样的多路复用器,从而控制 64 个具有相同 I2C 地址的设备。
有 8 组 SDx 和 SCx 引脚,从 SD0/SC0 到 SD7/SC7。这些是多路复用引脚,每组都是一个完全独立的 I2C 总线集。因此,只要每个设备都连接到一个 I2C 总线上,我们就可以连接 8 个具有相同地址的 I2C 设备。需要注意的是,这些引脚没有安装上拉电阻,如果使用的芯片或扩展板没有 I2C 上拉电阻,一定要添加。而且,Vin 可以是 3.3V,而这些引脚可以上拉到 5V(即它们支持 5V)。
如果需要,将排针裁剪到合适的长度。将其插入面包板,长引脚朝下,这样更便于焊接。
将扩展板放在排针上,使短引脚穿过扩展板的焊盘。
确保焊接所有引脚,以实现可靠的电气连接。如果需要焊接技巧,可以查看相关的焊接指南。
TCA9548A 多路复用器有一个 I2C 地址(默认 0x70),我们可以向它发送命令,告诉它要与哪个 I2C 多路复用输出进行通信,然后就可以对目标板进行寻址。
#define TCAADDR 0x70
void tcaselect(uint8_t i) {
if (i > 7) return;
Wire.beginTransmission(TCAADDR);
Wire.write(1 < < i);
Wire.endTransmission();
}
我们可以调用 tcaselect(0) 到 tcaselect(7) 来设置多路复用器。如果碰巧有 I2C 地址为 0x70 的 I2C 设备,需要将 TCA9548 扩展板上的一个地址引脚短接到 Vin,以避免冲突。
假设我们要连接两个 HMC5883 扩展板,这些磁力计的固定地址为 0x1E,不能在一个 I2C 总线上连接两个。接线时,将 TCA9548 扩展板的 Vin 连接到 5V(对于 3V 逻辑的 Arduino/微控制器,使用 3.3V),GND 连接到地,SCL 连接到 I2C 时钟,SDA 连接到 I2C 数据。然后将每个传感器扩展板连接到 Vin、地,并使用其中一个 SCn/SDn 多路复用总线。
#include "Wire.h"
#define TCAADDR 0x70
void tcaselect(uint8_t i) {
if (i > 7) return;
Wire.beginTransmission(TCAADDR);
Wire.write(1 < < i);
Wire.endTransmission();
}
void setup() {
while (!Serial);
delay(1000);
Wire.begin();
Serial.begin(115200);
Serial.println("nTCAScanner ready!");
for (uint8_t t = 0; t < 8; t++) {
tcaselect(t);
Serial.print("TCA Port #");
Serial.println(t);
for (uint8_t addr = 0; addr <= 127; addr++) {
if (addr == TCAADDR) continue;
Wire.beginTransmission(addr);
if (!Wire.endTransmission()) {
Serial.print("Found I2C 0x");
Serial.println(addr, HEX);
}
}
Serial.println("ndone");
}
}
void loop() {}
运行这个脚本可以检测连接的设备。
#include < Wire.h >
#include < Adafruit_Sensor.h >
#include < Adafruit_HMC5883_U.h >
#define TCAADDR 0x70
Adafruit_HMC5883_Unified mag1 = Adafruit_HMC5883_Unified(1);
Adafruit_HMC5883_Unified mag2 = Adafruit_HMC5883_Unified(2);
void displaySensorDetails(Adafruit_HMC5883_Unified *mag) {
sensor_t sensor;
mag- >getSensor(&sensor);
Serial.println("------------------------------------");
Serial.print("Sensor: ");
Serial.println(sensor.name);
Serial.print("Driver Ver: ");
Serial.println(sensor.version);
Serial.print("Unique ID: ");
Serial.println(sensor.sensor_id);
Serial.print("Max Value: ");
Serial.print(sensor.max_value);
Serial.println(" uT");
Serial.print("Min Value: ");
Serial.print(sensor.min_value);
Serial.println(" uT");
Serial.print("Resolution: ");
Serial.print(sensor.resolution);
Serial.println(" uT");
Serial.println("------------------------------------");
delay(500);
}
void tcaselect(uint8_t i) {
if (i > 7) return;
Wire.beginTransmission(TCAADDR);
Wire.write(1 < < i);
Wire.endTransmission();
}
void setup(void) {
Serial.begin(9600);
Serial.println("HMC5883 Magnetometer Test");
Serial.println("");
tcaselect(2);
if (!mag1.begin()) {
Serial.println("Ooops, no HMC5883 detected ... Check your wiring!");
while (1);
}
tcaselect(6);
if (!mag2.begin()) {
Serial.println("Ooops, no HMC5883 detected ... Check your wiring!");
while (1);
}
tcaselect(2);
displaySensorDetails(&mag1);
tcaselect(6);
displaySensorDetails(&mag2);
}
void loop(void) {
sensors_event_t event;
tcaselect(2);
mag1.getEvent(&event);
Serial.print("Sensor #1 - ");
Serial.print("X: ");
Serial.print(event.magnetic.x);
Serial.print(" ");
Serial.print("Y: ");
Serial.print(event.magnetic.y);
Serial.print(" ");
Serial.print("Z: ");
Serial.print(event.magnetic.z);
Serial.println(" uT");
tcaselect(6);
mag2.getEvent(&event);
Serial.print("Sensor #2 - ");
Serial.print("X: ");
Serial.print(event.magnetic.x);
Serial.print(" ");
Serial.print("Y: ");
Serial.print(event.magnetic.y);
Serial.print(" ");
Serial.print("Z: ");
Serial.print(event.magnetic.z);
Serial.println(" uT");
delay(500);
}
通过添加 tcaselect() 函数,我们可以与两个传感器进行通信。
由于 TCA9548 是可寻址的,我们可以在总线上使用多个多路复用器。有 8 个可能的地址,这意味着我们可以控制多达 64 个独立的 I2C 总线。为了避免不同多路复用器上具有相同地址的设备之间发生冲突,可以使用以下代码禁用多路复用器上的所有通道:
Wire.beginTransmission(TCAADDR1);
Wire.write(0); // no channel selected
Wire.endTransmission();
需要安装 Adafruit_Blinka 库,该库为 Python 提供 CircuitPython 支持。这可能还需要在平台上启用 I2C 并验证是否运行 Python 3。由于每个平台略有不同,并且 Linux 经常变化,请访问 CircuitPython on Linux 指南来准备好计算机。安装完成后,从命令行运行以下命令:
sudo pip3 install adafruit-circuitpython-tca9548a
如果默认 Python 是版本 3,可能需要运行 pip 而不是 pip3。请确保不要在 Python 2.x 上使用 CircuitPython,因为它不支持。
使用 CircuitPython 时,需要先将 TCA9548A 库及其依赖项安装到 CIRCUITPY 驱动器的 lib 文件夹中,然后用示例脚本更新 code.py。可以点击下载项目捆绑包按钮,下载必要的库和 code.py 文件的压缩包,解压后将整个 lib 文件夹和 code.py 文件复制到 CIRCUITPY 驱动器。
在计算机上使用 pip3 安装库后,将示例代码复制或下载到计算机上,运行以下命令:
python3 code.py
import board
import adafruit_tca9548a
i2c = board.I2C()
tca = adafruit_tca9548a.TCA9548A(i2c)
for channel in range(8):
if tca[channel].try_lock():
print("Channel {}:".format(channel), end="")
addresses = tca[channel].scan()
print([hex(address) for address in addresses if address != 0x70])
tca[channel].unlock()
这个简单的测试会对 TCA9548A 的所有八个端口进行 I2C 扫描,如果有设备连接,会将 I2C 地址打印到 REPL 中。
import time
import board
import adafruit_tsl2591
import adafruit_tca9548a
i2c = board.I2C()
tca = adafruit_tca9548a.TCA9548A(i2c)
tsl1 = adafruit_tsl2591.TSL2591(tca[0])
tsl2 = adafruit_tsl2591.TSL2591(tca[1])
while True:
print(tsl1.lux, tsl2.lux)
time.sleep(0.1)
在这个多传感器示例中,TCA9548A 作为 I2C 多路复用器与两个 TSL2591 光传感器一起使用。循环中,每 0.1 秒将两个光传感器的读数打印到 REPL 中。
可以下载 TCA9548A 的数据手册、Adafruit Fritzing 库中的 Fritzing 对象以及 GitHub 上的 EagleCAD PCB 文件,还能查看原理图和制造图纸。
总之,Adafruit TCA9548A 1-to-8 I2C 多路复用器为解决 I2C 设备地址冲突问题提供了一个简单而有效的解决方案。无论是使用 Arduino 还是 CircuitPython/Python,都能轻松实现多个相同地址 I2C 设备的连接和通信。大家在实际应用中有没有遇到过类似的地址冲突问题呢?又是如何解决的呢?欢迎在评论区分享交流。
全部0条评论
快来发表一下你的评论吧 !