我用的单片机型号是STM32G030C8T6,环境使用CUBEMX生成的代码,采用硬件I2C方式。
问题:
我想用I2C+DMA的方式去读写AT24C02,往里面写入0~0xff,然后读取出来。但是写入不了,调试的时候发现设备状态一直是繁忙,不会返回应答。
/* USER CODE BEGIN PV */
#define PAGE_SIZE_24CXX 8
#define PAGE_NUM_24CXX 32
#define ADDR_24CXX 0xA0
#define I2C_24CXX hi2c1
#define ADD_SIZE_24CXX I2C_MEMADD_SIZE_8BIT
int i;
uint8_t WriteBuffer[PAGE_SIZE_24CXX*PAGE_NUM_24CXX] = {0};
uint8_t ReadBuffer[PAGE_SIZE_24CXX*PAGE_NUM_24CXX] = {0};
/* USER CODE END PV */
/* USER CODE BEGIN 0 */
HAL_StatusTypeDef I2C_WaitUntilDeviceReady(I2C_HandleTypeDef *hi2c,uint16_t DevAddress,uint32_t Timeout){
uint32_t tickstart = HAL_GetTick(); //获取当前时间
while(HAL_GetTick()-tickstart <= Timeout){ //如果没有超时就一直尝试
if(HAL_I2C_IsDeviceReady(hi2c, DevAddress,1, Timeout) == HAL_OK){ //如果准备好了就return
return HAL_OK;
}
}
return HAL_TIMEOUT; //否则return一个错误
}
/* USER CODE END 0 */
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_USART1_UART_Init();
MX_I2C1_Init();
/* USER CODE BEGIN 2 */
int e;
for(i=0; i < PAGE_SIZE_24CXX*PAGE_NUM_24CXX; i++) {WriteBuffer[i] = i;} //初始化要写入的数组
uint32_t TickStart=HAL_GetTick(); //获取当前时间戳
for (int j=0; j < PAGE_NUM_24CXX; j++)//页写入
{
I2C_WaitUntilDeviceReady(&I2C_24CXX, ADDR_24CXX, 10);//自己写的等待函数
e = HAL_I2C_Mem_Write_DMA(&I2C_24CXX, ADDR_24CXX, PAGE_SIZE_24CXX*j, \
ADD_SIZE_24CXX, WriteBuffer+PAGE_SIZE_24CXX*j, PAGE_SIZE_24CXX);//向每一页都写入数据
if (e == HAL_OK) {
printf("第%d页已写入,time=%lu ms\r\n", j,HAL_GetTick());//输出页写时间
} else {
printf("EEPROM写入出错 ,j=%d,error=%d\r\n", j, e);
}
}
printf("%d次页写共用时%lu ms\r\n",PAGE_NUM_24CXX, HAL_GetTick() - TickStart);//输出总用时
I2C_WaitUntilDeviceReady(&I2C_24CXX, ADDR_24CXX, 20);
e = HAL_I2C_Mem_Read_DMA(&I2C_24CXX, ADDR_24CXX, 0, ADD_SIZE_24CXX,\
ReadBuffer, PAGE_SIZE_24CXX*PAGE_NUM_24CXX);//读取数据
if(e==HAL_ERROR)
printf("EEPROM读取出错 %d\r\n",e);
else
for(i=0; i<PAGE_NUM_24CXX; i++){
for(int j=0; j<PAGE_SIZE_24CXX; j++){
printf("%2x ", ReadBuffer[i*8+j]);
}
printf("\r\n");
}
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
但是我不用DMA的方式,直接用轮询就没问题,代码也就只改了写入和读取的函数。
int e;
for(i=0; i < PAGE_SIZE_24CXX*PAGE_NUM_24CXX; i++) {WriteBuffer[i] = i;} //初始化要写入的数组
uint32_t TickStart=HAL_GetTick(); //获取当前时间戳
for (int j=0; j < PAGE_NUM_24CXX; j++)//页写入
{
I2C_WaitUntilDeviceReady(&I2C_24CXX, ADDR_24CXX, 10);//自己写的等待函数
e = HAL_I2C_Mem_Write(&I2C_24CXX, ADDR_24CXX, PAGE_SIZE_24CXX*j, \
ADD_SIZE_24CXX, WriteBuffer+PAGE_SIZE_24CXX*j, PAGE_SIZE_24CXX, 1000);//向每一页都写入数据
if (e == HAL_OK) {
printf("第%d页已写入,time=%lu ms\r\n", j,HAL_GetTick());//输出页写时间
} else {
printf("EEPROM写入出错 ,j=%d,error=%d\r\n", j, e);
}
}
printf("%d次页写共用时%lu ms\r\n",PAGE_NUM_24CXX, HAL_GetTick() - TickStart);//输出总用时
I2C_WaitUntilDeviceReady(&I2C_24CXX, ADDR_24CXX, 20);
e = HAL_I2C_Mem_Read(&I2C_24CXX, ADDR_24CXX, 0, ADD_SIZE_24CXX,\
ReadBuffer, PAGE_SIZE_24CXX*PAGE_NUM_24CXX, 1000);//读取数据
if(e==HAL_ERROR)
printf("EEPROM读取出错 %d\r\n",e);
else
for(i=0; i<PAGE_NUM_24CXX; i++){
for(int j=0; j<PAGE_SIZE_24CXX; j++){
printf("%2x ", ReadBuffer[i*8+j]);
}
printf("\r\n");
}
参考gpt:
结合自己分析给你如下建议:
检查你的DMA通道是否正确配置,是否与I2C匹配。你可以参考1中的例子(https://blog.csdn.net/qq_45396672/article/details/117253441) ,看看他是如何配置DMA通道的。
检查你的I2C初始化函数是否正确设置了DMA模式。你需要在HAL_I2C_Init函数中设置hdma_i2c_tx和hdma_i2c_rx两个成员,指向你的DMA句柄。你可以参考2中的例子(https://cloud.tencent.com/developer/article/2030809) ,看看他是如何设置I2C初始化函数的。
检查你的I2C读写函数是否正确使用了DMA模式。你需要使用HAL_I2C_Mem_Read_DMA和HAL_I2C_Mem_Write_DMA两个函数,而不是HAL_I2C_Mem_Read和HAL_I2C_Mem_Write。你可以参考3中的例子 (https://zhuanlan.zhihu.com/p/624105226) ,看看他是如何使用I2C读写函数的。