STM32F105作为I2C从机如何接收小于缓冲区的不定长数据
问题根因
你遇到的HAL_I2C_ERROR_AF错误是STM32 HAL库I2C从机接收逻辑的正常触发:当调用HAL_I2C_Slave_Seq_Receive_IT时指定了接收长度为RX_BUFFER_SIZE,HAL底层会持续等待直到收到对应数量的字节;如果主机在收满指定字节前发送STOP信号,HAL会因为没收到预期数量的字节判定为通信异常,抛出AF错误。该场景下已收到的数据都是有效的,只需要做适配处理即可实现不定长数据接收。
解决方案
以下提供两种成熟的实现方案,可根据业务需求选择:
方案1:最小改动适配(错误回调处理)
不需要修改原有接收逻辑,只需要重写I2C错误回调,过滤STOP触发的AF错误即可:
- 重写
HAL_I2C_ErrorCallback函数,判定AF错误场景下手动计算实际接收长度,标记数据就绪后重启I2C监听
示例代码:void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *hi2c) { uint32_t error_code = HAL_I2C_GetError(hi2c); // 仅处理从机接收场景下的AF错误 if (error_code == HAL_I2C_ERROR_AF && hi2c->State == HAL_I2C_STATE_BUSY_RX) { // 计算实际接收到的字节数 uint16_t recv_len = RX_BUFFER_SIZE - hi2c->XferCount; if (__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_RXBNE)) { recv_len += 1; } // 保存接收长度,标记数据就绪 slave_recv_len = recv_len; slave_data_available = 1; // 清除错误标志 __HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_AF); // 重启从机监听准备下一次通信 if (HAL_I2C_EnableListen_IT(hi2c) != HAL_OK) { Error_Handler(); } } else { // 其他真实错误走原有错误处理逻辑 Error_Handler(); } } - 保留原有
HAL_I2C_SlaveRxCpltCallback逻辑,处理数据长度恰好等于RX_BUFFER_SIZE的正常接收场景,同样需要标记数据就绪、重启I2C监听。
方案2:逐字节接收(更灵活)
如果需要适配任意长度的不定长数据(只要不超过缓冲区大小),可以采用逐字节接收的逻辑,完全避免AF错误触发:
- 全局定义缓冲区偏移变量:
volatile uint16_t rx_offset = 0; volatile uint16_t slave_recv_len = 0; volatile uint8_t slave_data_available = 0; - 修改
HAL_I2C_AddrCallback中的接收调用,每次仅接收1字节:wr_AddrMatchCode = AddrMatchCode; rx_offset = 0; if (HAL_I2C_Slave_Seq_Receive_IT(&hi2c1, rx_buffer + rx_offset, 1, I2C_FIRST_FRAME) != HAL_OK) { Error_Handler(); } - 重写
HAL_I2C_SlaveRxCpltCallback,每收完1字节后继续接收下1字节,直到缓冲区满:void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c) { rx_offset++; if (rx_offset < RX_BUFFER_SIZE) { HAL_I2C_Slave_Seq_Receive_IT(hi2c, rx_buffer + rx_offset, 1, I2C_NEXT_FRAME); } } - 重写
HAL_I2C_ListenCpltCallback,STOP信号触发监听完成时标记数据就绪,重启监听:void HAL_I2C_ListenCpltCallback(I2C_HandleTypeDef *hi2c) { slave_recv_len = rx_offset; slave_data_available = 1; rx_offset = 0; HAL_I2C_EnableListen_IT(hi2c); }
注意事项
- 两种方案都需要在I2C初始化完成后调用一次
HAL_I2C_EnableListen_IT(&hi2c1)启动从机监听 - 涉及中断和主循环共享的全局变量需要加
volatile修饰,避免编译器优化导致逻辑异常
内容的提问来源于stack exchange,提问作者fadedbee
相关产品推荐
相关产品推荐

