STM32 HAL库中如何用DMA循环模式执行I2C_Mem_Read_DMA读取MPU9250?
能否用HAL_I2C_Mem_Read_DMA实现MPU9250的DMA循环读取?
不能直接通过HAL_I2C_Mem_Read_DMA实现DMA循环读取,原因和解决方案如下:
问题根源
- HAL_I2C_Mem_Read_DMA的设计限制:这个函数本质是完成单次I2C内存读取事务——先通过TX DMA发送目标寄存器地址,再切换到RX DMA接收数据。事务完成后,HAL库会自动停止DMA通道并调用回调函数,即使你配置了DMA循环模式,也会被HAL的逻辑覆盖。
- 配置对象错误:你设置的是
hdma_i2c1_tx.Init.Mode = DMA_CIRCULAR;,但HAL_I2C_Mem_Read_DMA的接收阶段使用的是RX DMA通道,这个配置对接收过程的DMA模式没有影响。
可行解决方案
方案一:利用回调函数模拟循环读取
这是最贴合HAL库框架的做法,在每次读取完成的回调中重新启动DMA读取:
- 确保RX DMA通道配置为
DMA_NORMAL模式(不需要循环,靠回调触发下一次读取) - 实现
HAL_I2C_MemRxCpltCallback回调函数:
void HAL_I2C_MemRxCpltCallback(I2C_HandleTypeDef *hi2c) { if(hi2c->Instance == I2C1) { // 此处处理刚读取到的mpu9250_rawdata数据 // 重新启动DMA读取,实现循环 HAL_I2C_Mem_Read_DMA(&hi2c1, I2C_SLV_ADDR << 1, ACCEL_XOUT_H_ADDR, I2C_MEMADD_SIZE_8BIT, mpu9250_rawdata, 14); } }
方案二:手动操作寄存器实现真循环DMA读取
绕开HAL库的单次事务限制,直接控制I2C和DMA寄存器:
- 确保MPU9250的寄存器自动递增功能开启(默认开启,可通过
USER_CTRL寄存器确认) - 先手动发送一次目标寄存器地址(阻塞模式即可),告诉MPU9250后续读取的起始位置
- 配置RX DMA通道为
DMA_CIRCULAR模式,然后启动I2C DMA接收:
// 手动发送寄存器地址(阻塞模式,仅执行一次) HAL_I2C_Mem_Write(&hi2c1, I2C_SLV_ADDR << 1, ACCEL_XOUT_H_ADDR, I2C_MEMADD_SIZE_8BIT, NULL, 0, HAL_MAX_DELAY); // 重新配置RX DMA为循环模式 hdma_i2c1_rx.Init.Mode = DMA_CIRCULAR; HAL_DMA_Init(&hdma_i2c1_rx); // 启动循环DMA接收 HAL_I2C_Receive_DMA(&hi2c1, mpu9250_rawdata, 14);
这种方式下,DMA会持续循环接收14字节数据,每次都会从MPU9250的当前寄存器地址(自动递增到起始地址)开始读取,完全由硬件自动完成,不需要软件触发。
内容的提问来源于stack exchange,提问作者Frog
相关产品推荐
相关产品推荐

