前言:
本系列教程将HAL库与STM32CubeMX结合在一起讲解,使您可以更快速的学会各个模块的使用
在之前的标准库中,STM32的硬件IIC非常复杂,更重要的是它并不稳定,所以都不推荐使用。
但是在我们的HAL库中,对硬件IIC做了全新的优化,使得之前软件IIC几百行代码,在HAL库中,只需要寥寥几行就可以完成 那么这篇文章将带你去感受下它的优异之处
这可能是目前关于STM32CubeMX的硬件iic 讲的最全面和详细的一篇文章之一了
所用工具:
1、芯片: STM32F103ZET6
2、STM32CubeMx软件
3、IDE: MDK-Keil软件
4、STM32F1xx/STM32F4xxHAL库
5、IIC: 使用硬件IIC1
知识概括:
通过本篇博客您将学到:
IIC的基本原理
STM32CubeMX创建IIC例程
HAL库IIC函数库
AT24C02 芯片原理
《IIC原理超详细讲解—值得一看》。
如果对IIC还不是太了解的朋友请移步到这篇文章中
IIC起始信号和终止信号:
起始信号:SCL保持高电平,SDA由高电平变为低电平后,延时(>4.7us),SCL变为低电平。
停止信号:SCL保持高电平。SDA由低电平变为高电平。
【STM32】HAL库 STM32CubeMX教程四—UART串口通信详解
5 时钟源设置
我的是 外部晶振为8MHz
1选择外部时钟HSE 8MHz
2PLL锁相环倍频9倍
3系统时钟来源选择为PLL
4设置APB1分频器为 /2
5 使能CSS监视时钟
32的时钟树框图 如果不懂的话请看《【STM32】系统时钟RCC详解(超详细,超全面)》
6 项目文件设置
1 设置项目名称
2 设置存储路径
3 选择所用IDE
7创建工程文件
然后点击GENERATE CODE 创建工程
配置下载工具
新建的工程所有配置都是默认的 我们需要自行选择下载模式,勾选上下载后复位运行
IIC HAL库代码部分
在i2c.c文件中可以看到IIC初始化函数。在stm32f1xx_hal_i2c.h头文件中可以看到I2C的操作函数。分别对应轮询,中断和DMA三种控制方式
上面的函数看起来多,但是只是发送和接收的方式改变了,函数的参数和本质功能并没有改变
比方说IIC发送函数 还是发送函数,只不过有普通发送,DMA传输,中断 的几种发送模式
这里我们仅介绍下普通发送,其他的只是改下函数名即可
IIC写函数
HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);
功能:IIC写数据
参数:
*hi2c 设置使用的是那个IIC 例:&hi2c2
DevAddress 写入的地址 设置写入数据的地址 例 0xA0
*pData 需要写入的数据
Size 要发送的字节数
Timeout 最大传输时间,超过传输时间将自动退出传输函数
IIC读函数
HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);
功能:IIC读一个字节
参数:
*hi2c: 设置使用的是那个IIC 例:&hi2c2
DevAddress: 写入的地址 设置写入数据的地址 例 0xA0
*pDat:a 存储读取到的数据
Size: 发送的字节数
Timeout: 最大读取时间,超过时间将自动退出读取函数
举例:
HAL_I2C_Master_Transmit(&hi2c1,0xA1,(uint8_t*)TxData,2,1000) ;;
发送两个字节数据
IIC写数据函数
HAL_I2C_Mem_Write(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout);
/* 第1个参数为I2C操作句柄
第2个参数为从机设备地址
第3个参数为从机寄存器地址
第4个参数为从机寄存器地址长度
第5个参数为发送的数据的起始地址
第6个参数为传输数据的大小
第7个参数为操作超时时间 */
功能: IIC写多个数据 该函数适用于IIC外设里面还有子地址寄存器的设备,比方说E2PROM,除了设备地址,每个存储字节都有其对应的地址
参数:
*hi2c: I2C设备号指针,设置使用的是那个IIC 例:&hi2c2
DevAddress: 从设备地址 从设备的IIC地址 例E2PROM的设备地址 0xA0
MemAddress: 从机寄存器地址 ,每写入一个字节数据,地址就会自动+1
MemAddSize: 从机寄存器地址字节长度 8位或16位
写入数据的字节类型 8位还是16位
I2C_MEMADD_SIZE_8BIT
I2C_MEMADD_SIZE_16BIT
在stm32f1xx_hal_i2c.h中有定义
*pData: 需要写入的的数据的起始地址
Size: 传输数据的大小 多少个字节
Timeout: 最大读取时间,超过时间将自动退出函数
使用HAL_I2C_Mem_Write等于先使用HAL_I2C_Master_Transmit传输第一个寄存器地址,再用HAL_I2C_Master_Transmit传输写入第一个寄存器的数据。可以传输多个数据
void Single_WriteI2C(uint8_t REG_Address,uint8_t REG_data)
{
uint8_t TxData[2] = {REG_Address,REG_data};
while(HAL_I2C_Master_Transmit(&hi2c1,I2C1_WRITE_ADDRESS,(uint8_t*)TxData,2,1000) != HAL_OK)
{
if (HAL_I2C_GetError(&hi2c1) != HAL_I2C_ERROR_AF)
{
Error_Handler();
}
}
}
在传输过程,寄存器地址和源数据地址是会自加的。
至于读函数也是如此,因此用HAL_I2C_Mem_Write和HAL_I2C_Mem_Read,来写读指定设备的指定寄存器数据是十分方便的,让设计过程省了好多步骤。
举例:
8位:
HAL_I2C_Mem_Write(&hi2c2, ADDR, i, I2C_MEMADD_SIZE_8BIT,&(I2C_Buffer_Write[i]),8, 1000);
HAL_I2C_Mem_Read(&hi2c2, ADDR, i, I2C_MEMADD_SIZE_8BIT,&(I2C_Buffer_Write[i]),8, 1000);
16位:
HAL_I2C_Mem_Write(&hi2c2, ADDR, i, I2C_MEMADD_SIZE_16BIT,&(I2C_Buffer_Write[i]),8, 1000);
HAL_I2C_Mem_Read(&hi2c2, ADDR, i, I2C_MEMADD_SIZE_16BIT,&(I2C_Buffer_Write[i]),8, 1000);
如果只往某个外设中写数据,则用Master_Transmit。 如果是外设里面还有子地址,例如我们的E2PROM,有设备地址,还有每个数据的寄存器存储地址。则用Mem_Write。
Mem_Write是2个地址,Master_Transmit只有从机地址
硬件IIC读取AT24C02
在mian.c文件前面声明,AT24C02 写地址和读地址 ,定义写数据数组,和读数据数组
/* USER CODE BEGIN PV */
#include #define ADDR_24LCxx_Write 0xA0 #define ADDR_24LCxx_Read 0xA1 #define BufferSize 256 uint8_t WriteBuffer[BufferSize],ReadBuffer[BufferSize]; uint16_t i; /* USER CODE END PV */ 重新定义printf函数 在 stm32f4xx_hal.c中包含#include #include "stm32f4xx_hal.h" #include extern UART_HandleTypeDef huart1; //声明串口 在 stm32f4xx_hal.c 中重写fget和fput函数 /** * 函数功能: 重定向c库函数printf到DEBUG_USARTx * 输入参数: 无 * 返 回 值: 无 * 说 明:无 */ int fputc(int ch, FILE *f) { HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff); return ch; } /** * 函数功能: 重定向c库函数getchar,scanf到DEBUG_USARTx * 输入参数: 无 * 返 回 值: 无 * 说 明:无 */ int fgetc(FILE *f) { uint8_t ch = 0; HAL_UART_Receive(&huart1, &ch, 1, 0xffff); return ch; } 在main.c中添加 /* USER CODE BEGIN 2 */ for(i=0; i<256; i++) WriteBuffer[i]=i; /* WriteBuffer init */ printf("rn***************I2C Example Z小旋测试*******************************rn"); for (int j=0; j<32; j++) { if(HAL_I2C_Mem_Write(&hi2c1, ADDR_24LCxx_Write, 8*j, I2C_MEMADD_SIZE_8BIT,WriteBuffer+8*j,8, 1000) == HAL_OK) { printf("rn EEPROM 24C02 Write Test OK rn"); HAL_Delay(20); } else { HAL_Delay(20); printf("rn EEPROM 24C02 Write Test False rn"); } } /* // wrinte date to EEPROM 如果要一次写一个字节,写256次,用这里的代码 for(i=0;i HAL_I2C_Mem_Write(&hi2c1, ADDR_24LCxx_Write, i, I2C_MEMADD_SIZE_8BIT,&WriteBuffer[i],1,0xff);//使用I2C块读,出错。因此采用此种方式,逐个单字节写入 HAL_Delay(5);//此处延时必加,与AT24C02写时序有关 } printf("rn EEPROM 24C02 Write Test OK rn"); */ HAL_I2C_Mem_Read(&hi2c1, ADDR_24LCxx_Read, 0, I2C_MEMADD_SIZE_8BIT,ReadBuffer,BufferSize, 0xff); for(i=0; i<256; i++) printf("0x%02X ",ReadBuffer[i]); /* USER CODE END 2 */ 注意事项: AT24C02的IIC每次写之后要延时一段时间才能继续写 每次写之后要delay 5ms左右 不管硬件IIC采用何种形式(DMA,IT),都要确保两次写入的间隔大于5ms; 读写函数最后一个超时调整为1000以上 因为我们一次写8个字节,延时要久一点 AT24C02页写入只支持8个byte,所以需要分32次写入。这不是HAL库的bug,而是AT24C02的限制,其他的EEPROM可以支持更多byte的写入。 当然,你也可以每次写一个字节,分成256次写入,也是可以的 那就用注释了的代码即可 /* // wrinte date to EEPROM 如果要一次写一个字节,写256次,用这里的代码 for(i=0;i HAL_I2C_Mem_Write(&hi2c1, ADDR_24LCxx_Write, i, I2C_MEMADD_SIZE_8BIT,&WriteBuffer[i],1,0xff);//使用I2C块读,出错。因此采用此种方式,逐个单字节写入 HAL_Delay(5);//此处延时必加,与AT24C02写时序有关 } printf("rn EEPROM 24C02 Write Test OK rn");
上一篇:【STM32】HAL库 STM32CubeMX教程十三---RTC时钟
下一篇:【STM32】 DMA原理,步骤超细详解,一文看懂DMA
推荐阅读最新更新时间:2024-11-06 15:27
设计资源 培训 开发板 精华推荐
- LF33ABDT-TR 3.3V 低压降稳压器的典型应用
- 基于沁恒CH571的电脑状态显示表
- EB7610,用于超声成像的 SPT7610SIQ 6 位、1GSPS ADC 的评估板
- ESP32C3墨水屏摆件
- LT3825EFE 演示板,具有 36V < 电压的同步反激式转换器VIN< 72V,VOUT = 12V @ 5A
- LT4320HMSE-1 理想二极管桥控制器的典型应用电路
- CN5711-1S
- LT8304IS8E 4V 至 80Vin、5Vout 隔离反激式转换器的典型应用电路
- AD5664R 四路 16 位 nanoDAC 的典型应用
- LT6656BIS6-3.3、3.3V ADC 电压基准和桥式激励电源的典型应用