STM32 HAL库I2C的使用以AT24C02为例

背景

I2C是一种很常用的通信电气协议,至于I2C的具体时序我们不作探讨。使用IO模拟I2C也可以很好地实现通信效果,不过芯片上拥有硬件I2C,我们也可以使用硬件I2C来达到减少CPU的占用情况。
STM32的硬件I2C在之前非HAL库的时候,很多人都说STM32的硬件I2C存在问题,不过实际用起来,并没有发现特别大的问题。只是使用过程确实需要很多地方需要注意。
基于HAL库函数支持。可以使用很少的代码就可以实现I2C的通信。

硬件要求

硬件I2C的IO管脚配置为AF_OD模式,因此使用I2C时必须外加上拉电阻,阻值可以选择4.7k。

初始化

使用cubemx配置好对应的参数,可自动生成初始代码MX_I2C1_Init(),大致流程如下
1.声明I2C的初始化结构体I2C_HandleTypeDef,并赋值
2.调用HAL_I2C_MspInit函数
3.配置好相就应的通信速度,地址模式等参数
4.如果使中断或者DMA时也在初始化参数中配置好

官方提供的方式

HAL的I2C库,官方提供了很多API,大简化我们使用I2C的功能。事实上对于具体硬件I2C的操作,需要对寄存器有一定的了解,并且操作流程要看很多资料。
直接调用I2C库提供的API确实可以很方便地实现我们需要的功能,而忽略复杂的底层寄存器的操作。
接下来以AT24C02为例提供几种调用示例,当时除此之外还有很多其他的函数跟操作可以使用。

一、轮询模式 Polling mode IO MEM

使用HAL_I2C_Mem_Write与HAL_I2C_Mem_Read这两个函数就可以实现对AT24C02的读写

        static uint8_t data = 0;
	static uint8_t addr = 0;
	addr++;
	if(addr >= 0x66)
	{
          addr = 0;
	}
	HAL_I2C_Mem_Write(&hi2c1, 0xa0, addr, I2C_MEMADD_SIZE_8BIT, &addr, 1, 10);
	HAL_Delay(10);
	HAL_I2C_Mem_Read(&hi2c1, 0xa0, addr, I2C_MEMADD_SIZE_8BIT, &data, 1, 10);
        printf("addr : 0x%02x, data : 0x%02x\n", addr, data);

二、中断模式 Interrupt mode IO MEM

如果为减少CPU的占用时间,可以使用中断模式来实现I2C的读写。相当于处于等待时间时,将CPU的使用权交出,在中断实现操作细节的状态转换。
对于一些嵌入式操作系统或者是经常使用I2C且不想占太多的时间,可以选择这种方式,不过这种方式下需要用户自己确认IO是否完成,可以通过回调函数来判断。
同时在使用前需要对I2C的中断进行配置,否则不能正常工作

volatile uint8_t readCplt = 0;
volatile uint8_t writeCplt = 0;
{
        static uint8_t data = 0;
	static uint8_t addr = 0;
	addr++;
	if(addr >= 0x66)
	{
          addr = 0;
	}
        writeCplt = 0;
	HAL_I2C_Mem_Write_IT(&hi2c1, 0xa0, addr, I2C_MEMADD_SIZE_8BIT, &addr, 1);
	while(writeCplt == 0);
	HAL_Delay(20);
	readCplt = 0;
	HAL_I2C_Mem_Read_IT(&hi2c1, 0xa0, addr, I2C_MEMADD_SIZE_8BIT, &data, 1);
	while(readCplt == 0);

	printf("addr : 0x%02x, data : 0x%02x\n", addr, data);
}
void HAL_I2C_MemRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	readCplt = 1;
}
void HAL_I2C_MemTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	writeCplt = 1;
}

三、DMA模式 DMA mode IO MEM

使用DMA模式前,需要对DMA进行配置,这种模式下,也是要使用一定的时间来进行等待I2C的操作(I2C进行设置地址及读数据前操作,接收数据的时候,直接使用DMA记录)。
操作完成后,会在回调函数进行确认,跟中断的实现过程差不多。
这样操作也是可以省掉一部分CPU时间。

volatile uint8_t readCplt = 0;
volatile uint8_t writeCplt = 0;
{
        static uint8_t data = 0;
	static uint8_t addr = 0;
	addr++;
	if(addr >= 0x66)
	{
          addr = 0;
	}
        writeCplt = 0;
	HAL_I2C_Mem_Write_DMA(&hi2c1, 0xa0, addr, I2C_MEMADD_SIZE_8BIT, &addr, 1);
	while(writeCplt == 0);
	HAL_Delay(20);
	readCplt = 0;
	HAL_I2C_Mem_Read_DMA(&hi2c1, 0xa0, addr, I2C_MEMADD_SIZE_8BIT, &data, 1);
	while(readCplt == 0);

	printf("addr : 0x%02x, data : 0x%02x\n", addr, data);
}
void HAL_I2C_MemRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	readCplt = 1;
}
void HAL_I2C_MemTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	writeCplt = 1;
}

四、其他方式

当然HAL的I2C库还提供了很多其他的API函数,对AT24C02来说,读操作可以将前面写地址,与后面读操作分开来操作,并用相应的API来完成。
可以通过深入了解HAL的I2C库实现更多的功能。

总结

STM32使用HAL库来调用I2C对用户来说减少了很多硬件复杂的操作。用户可以很快地上手,实现想要做的功能,大大地加快开发速度。
为用户多提供了一种操作I2C的实现方式。

posted @ 2022-06-28 15:10  cau_par  阅读(3037)  评论(0编辑  收藏  举报