如果是使用STM32CbueMX生成 I2C 的代码,使用 I2C 读写 过程会出现HAL_ERROR。
这个时候一般有两种选择,
方式一:是选择IO模拟I2C;
方式二:是排查代码问题解决硬件I2C。
使用DMA的时候有时候DMA启动失败,往往这是时候我们就方式使用DMA了,转而使用更低效率的方式发接数据。
方式一:问度娘。
方式二:
像这种情况,很多人说ST的I2C有bug,可能由于专利问题。但是我觉得吧,那么多人在用ST的MCU,怎么可能会。
原因就是初始化流程的问题。
首先要想到的是看ST的官方源码是怎么使用硬件I2C的,和STM32CbueMX生成的代码比对一下。
下面是STM32CbueMX生成的I2C初始化代码
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_I2C1_Init();
MX_DMA_Init();
while (1)
{
}
}
static void MX_I2C1_Init(void)
{
hi2c1.Instance = I2C1;
hi2c1.Init.ClockSpeed = 100000;
hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(&hi2c1) != HAL_OK)
{
Error_Handler();
}
if (HAL_I2CEx_ConfigAnalogFilter(&hi2c1, I2C_ANALOGFILTER_ENABLE) != HAL_OK)
{
Error_Handler();
}
if (HAL_I2CEx_ConfigDigitalFilter(&hi2c1, 0) != HAL_OK)
{
Error_Handler();
}
}
static void MX_DMA_Init(void)
{
__HAL_RCC_DMA1_CLK_ENABLE();
HAL_NVIC_SetPriority(DMA1_Stream0_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream0_IRQn);
HAL_NVIC_SetPriority(DMA1_Stream6_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream6_IRQn);
}
void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(hi2c->Instance==I2C1)
{
__HAL_RCC_GPIOB_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF4_I2C1;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
__HAL_RCC_I2C1_CLK_ENABLE();
hdma_i2c1_rx.Instance = DMA1_Stream0;
hdma_i2c1_rx.Init.Channel = DMA_CHANNEL_1;
hdma_i2c1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_i2c1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_i2c1_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_i2c1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_i2c1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_i2c1_rx.Init.Mode = DMA_NORMAL;
hdma_i2c1_rx.Init.Priority = DMA_PRIORITY_LOW;
hdma_i2c1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_i2c1_rx) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(hi2c,hdmarx,hdma_i2c1_rx);
hdma_i2c1_tx.Instance = DMA1_Stream6;
hdma_i2c1_tx.Init.Channel = DMA_CHANNEL_1;
hdma_i2c1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_i2c1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_i2c1_tx.Init.MemInc = DMA_MINC_ENABLE;
hdma_i2c1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_i2c1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_i2c1_tx.Init.Mode = DMA_NORMAL;
hdma_i2c1_tx.Init.Priority = DMA_PRIORITY_LOW;
hdma_i2c1_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_i2c1_tx) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(hi2c,hdmatx,hdma_i2c1_tx);
}
}
MX_I2C1_Init抽象层初始化函数执行的时候会调用HAL_I2C_MspInit硬件层初始化函数。
上面代码初始化流程:
1、初始化填充I2C结构体
2、初始化I2C引脚,配置成内部开漏。
3、使能I2C时钟。
4、初始化I2C的DMA,关联DMA。
5、使能DMA时钟,初始化及使能中断。
看一下HAL库的I2C驱动stm32f4xx_hal_i2c.c,如何教我们使用I2C的。
看到图上的初始化流程:
#)声明一个I2C_HandleTypeDef句柄结构,例如:
I2C_HandleTypeDef hi2c;
(#)通过实现@ref HAL_I2C_MspInit()API来初始化I2C低级资源:
(##)启用I2Cx接口时钟
(##)I2C引脚配置
(+++)启用I2C GPIO的时钟
(+++)将I2C引脚配置为备用功能漏极开路
(##)NVIC配置,如果您需要使用中断过程
(+++)配置I2Cx中断优先级
(+++)启用NVIC I2C IRQ通道
(##)DMA配置(如果需要使用DMA进程)
(+++)为传输或接收流声明DMA_HandleTypeDef句柄结构
(+++)使用以下命令启用DMAx接口时钟
(+++)配置DMA句柄参数
(+++)配置DMA Tx或Rx流
(+++)将初始化的DMA句柄与hi2c DMA Tx或Rx句柄相关联
(+++)配置优先级并启用NVIC以进行传输完成中断DMA Tx或Rx流
对比发现:STM32CubeMX的I2C初始化与HAL的初始化流程不一样,我之前也遇到过HAL_ERROR的问题,最后我按HAL库的流程初始化就没问题了。做法就是用STM32CubeMX生成代码后,先I2C的时钟和DMA的时钟先打开。
__HAL_RCC_I2C1_CLK_ENABLE();
__HAL_RCC_DMA1_CLK_ENABLE();
int main(void)
{
HAL_Init();
SystemClock_Config();
/* USER CODE BEGIN SysInit */
__HAL_RCC_I2C1_CLK_ENABLE();
__HAL_RCC_DMA1_CLK_ENABLE();
/* USER CODE END SysInit */
MX_GPIO_Init();
MX_I2C1_Init();
MX_DMA_Init();
while (1)
{
}
}
全篇完。
本人博客仅仅代表我个人见解方便记录成长笔记。
若有与 看官老爷见解有冲突,我坚信看官老爷见解是对的,我的是错的。
感谢~!
来源:CSDN
作者:sudaroot
链接:https://blog.csdn.net/sudaroot/article/details/103730871