STM32执行阶段能否修改I2C从设备地址?如何实现?
动态修改STM32 I2C从地址的可行方案(Nucleo-F207GZ适用)
直接修改OwnAddress1后调用HAL_I2C_Init或先DeInit再Init无效的核心原因是:HAL_I2C_Init在从模式下不会重新配置地址寄存器,而DeInit可能重置过多外设状态导致后续初始化异常。正确的做法是直接操作I2C地址寄存器,步骤如下:
操作步骤
停止当前I2C从模式操作
如果使用中断监听模式,先关闭监听中断:HAL_I2C_DisableListen_IT(&hi2cX);若启用了DMA,需同步停止DMA传输:
HAL_DMA_Abort(&hdma_i2cX_rx); HAL_DMA_Abort(&hdma_i2cX_tx);禁用I2C外设
修改地址寄存器前必须先禁用外设,否则硬件不允许写入:__HAL_I2C_DISABLE(&hi2cX);更新句柄中的地址配置
根据地址类型(7位/10位)修改OwnAddress1,比如7位地址需左移1位(匹配HAL库的寄存器配置规则):hi2cX.Init.OwnAddress1 = (new_7bit_addr << 1); hi2cX.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; // 明确地址模式重新配置I2C地址寄存器
调用HAL库专门的地址配置函数,直接更新硬件寄存器:HAL_I2C_SetOwnAddressConfig(&hi2cX, &hi2cX.Init);重新启用I2C外设
__HAL_I2C_ENABLE(&hi2cX);恢复从模式操作
重新开启监听中断或DMA传输,恢复设备的从模式响应能力:HAL_I2C_EnableListen_IT(&hi2cX); // 若用DMA,重新启动接收/传输 HAL_I2C_Slave_Receive_DMA(&hi2cX, rx_buf, buf_len);
代码示例(中断模式下处理地址修改指令)
假设在I2C从接收完成回调中处理地址修改指令:
uint8_t rx_buf[2]; // rx_buf[0]为触发指令,rx_buf[1]为新7位地址 uint8_t tx_buf[1]; // 用于发送修改确认 void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c) { if(hi2c->Instance == I2C1) { // 匹配特定修改地址指令 if(rx_buf[0] == 0xAA) { uint8_t new_addr = rx_buf[1]; // 停止当前监听 HAL_I2C_DisableListen_IT(hi2c); // 禁用I2C外设 __HAL_I2C_DISABLE(hi2c); // 更新地址配置 hi2c->Init.OwnAddress1 = (new_addr << 1); hi2c->Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; // 重新配置地址寄存器 HAL_I2C_SetOwnAddressConfig(hi2c, &hi2c->Init); // 重新启用外设并恢复监听 __HAL_I2C_ENABLE(hi2c); HAL_I2C_EnableListen_IT(hi2c); // 发送修改成功确认 tx_buf[0] = 0x01; HAL_I2C_Slave_Transmit_IT(hi2c, tx_buf, 1); } else { // 处理其他指令,重新开启接收 HAL_I2C_Slave_Receive_IT(hi2c, rx_buf, 2); } } }
注意事项
- 确保修改地址时I2C总线处于空闲状态,避免总线冲突导致修改失败。
- 新地址不能与总线上其他设备地址重复,否则会引发通信异常。
- 若使用10位地址,需将
AddressingMode改为I2C_ADDRESSINGMODE_10BIT,OwnAddress1直接填写10位地址值。
内容的提问来源于stack exchange,提问作者grybouilli
相关产品推荐
相关产品推荐

