# 引言 在典型的 I2C 通信中,从机只能被动响应主机请求,若需上报状态,主机必须周期性轮询,造成总线拥堵和响应延迟。AVR 系列单片机(如 ATmega328P)的 TWI(Two-Wire Interface)模块提供了硬件地址匹配功能,可扩展出“从机主动上报”模式。本文将深入剖析其原理,并给出在 Arduino 环境下的完整实现。 # TWI 硬件地址匹配原理 AVR 的 TWI 模块在硬件层面持续监听总线,并将接收到的地址字节与自身地址寄存器(TWAR)比较。当匹配成功时,硬件自动应答 ACK,并触发 TWI 中断(TWINT 置位)。关键点在于: - **地址掩码寄存器(TWAMR)**:允许屏蔽地址位,使从机能响应多个地址。例如,设置 TWAMR=0xFE,则只匹配最低位,可响应两个地址。 - **通用调用地址(0x00)**:若启用,可被所有从机响应。 利用这些特性,我们可以设计一个“事件触发地址”:主机平时不轮询,但当从机需要上报时,它先通过 GPIO 拉低 SCL 或使用专用信号线通知主机,然后主机发送一个特定地址(如 0x55),从机通过地址匹配中断唤醒,并立即接管总线发送数据。但更优雅的做法是:从机直接利用 TWI 的“多主机”能力,在事件发生时主动发起传输。 # 免轮询方案设计 我们的目标是:从机在内部事件(如传感器阈值)发生时,主动向主机发送数据,而无需主机轮询。实现思路如下: 1. **初始化 TWI 为从机模式**,并设置一个“唤醒地址”(如 0x30)。 2. **启用 TWI 地址匹配中断**,但平时不响应任何地址(通过 TWAR 设置为不匹配地址)。 3. **当事件发生时**,从机临时修改 TWAR 为唤醒地址,并拉低 SCL 线(模拟总线占用)或直接发送 START 条件(多主机模式)。 4. **主机检测到总线活动**,发送唤醒地址,从机匹配后进入中断,此时从机切换为主机模式,发送数据。 但更简洁的免轮询方案是:从机在事件发生时,主动发送 START 并抢占总线(多主机模式),直接向主机地址发送数据。这需要从机支持多主机仲裁。AVR TWI 硬件支持多主机,但需谨慎处理总线冲突。 # 配置步骤 ## 1. TWI 从机模式初始化 ```c #include #include void twi_slave_init(uint8_t addr) { TWAR = addr << 1; // 设置从机地址(7位左移一位) TWCR = (1< #include #include #define EVENT_PIN 2 #define WAKE_ADDR 0x55 #define SELF_ADDR 0x30 volatile bool event_occurred = false; volatile bool wake_received = false; void setup() { pinMode(EVENT_PIN, OUTPUT); digitalWrite(EVENT_PIN, HIGH); // 空闲高电平 // 初始化 TWI 从机,地址设为不匹配 TWAR = 0x00; // 不响应任何地址 TWCR = (1<