文章内容根据野火学习教程进行整理,仅仅是学习记录。
开发板: 野火STM32F429-挑战者V2
官方固件库版本: STM32F4xx_DSP_StdPeriph_Lib_V1.8.0
这里以与EEPROM进行通讯的代码作为例子。
一、看电路图
1、要得知那些信息
从电路图上主要是要知道3点关键信息
I2C设备挂载在I2C1、I2C2、I2C3的哪一个总线上
SDA、SCL接的是哪两个GPIO
I2C设备地址(有的是可以选择的,比如这个EEPROM)
2、挂在哪个I2C总线以及哪个GPIO
从电路图上可以看出:
挂载在I2C1上的
SDA接的是PB7引脚
SCL接的是PB6引脚
这个一般硬件工程师都会在电路图上标出来吧。
另外如果只标出了使用的GPIO引脚,也可以通过 《STM32F4xx中文数据手册》 查找相关GPIO的复用得知所挂的I2C总线。
3、I2C设备地址
查看EPPROM的参考手册可以得知设备地址是由1010 A2A1A0一共位组合而成,而从电路图可以得知A2=0、A1=0、A0=0,所以最终的I2C地址位101 0000(0x50)。
其实在I2C总线上只要设备地址是唯一的就可以了。
二、开始编码
由于是使用I2C对EEPROM进行读写,所以编码主要是分为I2C配置和对EEPROM读写两个部分。
1、I2C配置
(1)宏定义声明
根据电路连接情况就可以知道GPIO引脚以及哪个I2C总线啦。
#define I2C1_OwnAddress 0X0A /* STM32的I2C1设备自身地址,自定义,与其他I2C设备地址不同即可 */
#define I2C1_Speed 400000 /* I2C1设备速率400KHz */
#define I2C1_GPIO_PORT GPIOB /* I2C1所接GPIO的端口 */
#define I2C1_SCL_PIN GPIO_Pin_6 /* I2C1的SCL所接的GPIO引脚 */
#define I2C1_SDA_PIN GPIO_Pin_7 /* I2C1的SDA所接的GPIO引脚 */
#define I2C1_SCL_SOURCE GPIO_PinSource6 /* I2C1的SCL所接的GPIO引脚序号 */
#define I2C1_SDA_SOURCE GPIO_PinSource7 /* I2C1的SDA所接的GPIO引脚序号 */
#define I2C1_GPIO_AF GPIO_AF_I2C1 /* I2C1的SDA、SCL的GPIO引脚复用功能 */
I2C速率:有三种,标准模式(100kbit/s)、快速模式(400kbit/s)、Hs模式(3.4Mbit/s)。我自己试了一下速率过低是会有问题的,过高似乎没有什么问题,一般就填400K好了。
(2)使能GPIO和I2C总线时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); /* 初始化GPIO端口时钟 */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); /* 初始化I2C1的外围时钟 */
不管是使用GPIO还是I2C,相应的总线时钟都是要使能的。
(3)初始化GPIO
/**************************************************************************************************
** 函数名称: gpio_cfg
** 功能描述: I2C的GPIO配置
** 输入参数: 无
** 输出参数: 无
** 返回参数: 无
**************************************************************************************************/
static void gpio_cfg(void)
{
GPIO_InitTypeDef GPIO_def;
GPIO_def.GPIO_Pin = I2C1_SCL_PIN | I2C1_SDA_PIN; /* 要配置的GPIO的PIN脚 */
GPIO_def.GPIO_Mode = GPIO_Mode_AF; /* 要配置的GPIO模式(复用) */
GPIO_def.GPIO_Speed = GPIO_Speed_50MHz; /* 要配置的GPIO速率(50MHz) */
GPIO_def.GPIO_OType = GPIO_OType_OD; /* 输出类型(开漏) */
GPIO_def.GPIO_PuPd = GPIO_PuPd_UP; /* 引脚默认状态(上拉) */
GPIO_Init(I2C1_GPIO_PORT, &GPIO_def);
GPIO_PinAFConfig(I2C1_GPIO_PORT, I2C1_SCL_SOURCE, I2C1_GPIO_AF); /* 配置复用类型 */
GPIO_PinAFConfig(I2C1_GPIO_PORT, I2C1_SDA_SOURCE, I2C1_GPIO_AF); /* 配置复用类型 */
}
GPIO类型必须配置成 开漏输出 ,因为这样I2C设备才能输出 低电平 和 高阻态。因为 SCL 和 SDA 连接上拉电阻到电源,那么GPIO输出低电平则拉低,输出高阻态则拉高。高阻态可类似看为开路。
(4)初始化I2C
/**************************************************************************************************
** 函数名称: i2c_cfg
** 功能描述: I2C配置
** 输入参数: 无
** 输出参数: 无
** 返回参数: 无
**************************************************************************************************/
static void i2c_cfg(void)
{
I2C_InitTypeDef I2C_def;
I2C_def.I2C_Mode = I2C_Mode_I2C; /* I2C模式选择 */
I2C_def.I2C_DutyCycle = I2C_DutyCycle_2; /* 低电平时间:高电平时间 = 2:1 */
I2C_def.I2C_OwnAddress1 = I2C1_OwnAddress; /* I2C设备地址 */
I2C_def.I2C_Ack = I2C_Ack_Enable; /* 使能应答 */
I2C_def.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; /* I2C的寻址模式 */
I2C_def.I2C_ClockSpeed = I2C1_Speed; /* 通信速率 */
I2C_Init(I2C1, &I2C_def); /* I2C初始化 */
I2C_Cmd(I2C1, ENABLE); /* 使能I2C */
I2C_AcknowledgeConfig(I2C1, ENABLE); /* 使能指定I2C总线的应答功能 */
}
SCL占空比:当I2C设置为快速模式时,可以配置SCL的占空比,有两个选项。一:I2C_DutyCycle_2(低电平:高电平=2:1),二:I2C_DutyCycle_16_9(低电平:高电平=16:9)。其实配哪个好像没啥区别。
I2C设备自身地址位数:有两种选择,可以选7位的也可以选10位的。其实都可以用,不过一般情况下是配置7个位的。
I2C设备自身地址:只要与I2C总线上其他设备的地址不同即可。前7位或10位有效,根据配置。
2、对EEPROM进行读写
(1)宏定义声明
#define EE_I2C I2C1 /* 所挂载的I2C总线 */
#define EE_I2C_ADDR 0XA0 /* EEPROM的I2C设备地址 1010000(0x50), (0x50 << 1)*/
#define EE_SHORT_TIMEOUT 0x1000 /* I2C检测短等待超时时间 */
#define EE_LONG_TIMEOUT 0xA000 /* I2C检测长等待超时时间 */
#define EE_WAIT_TIMES 300 /* I2C检测设备状态最大次数 */
#define TEST_ADDR 0x30 /* 要写入数据的EEPROM地址 */
#define TEST_DATA 0x55 /* 要写入的数据 */
设备地址:由上面分析的结果可以的得知设备地址为101 0000(0x50),那么由于最后一位为读写位,所以需要左移1位,最终的到1010 0000(0XA0)。
(2)检测I2C事件的封装函数
在I2C通讯中根据协议会需要检测EV5、EV6、EV7、EV8、EV9等事件。
我自己把检测I2C事件函数封装了一下,加入了超时跳出的机制,方便调用。
/**************************************************************************************************
** 函数名称: i2c_check_event
** 功能描述: 检测I2C设备的事件
** 输入参数: 无
** 输出参数: 无
** 返回参数: 检测成功返回0,失败返回-1
**************************************************************************************************/
static s32 i2c_check_event(u32 i2c_event)
{
__IO u32 timeout;
timeout = EE_SHORT_TIMEOUT;
while(SUCCESS != I2C_CheckEvent(EE_I2C, i2c_event)) {
timeout--;
if (timeout == 0) {
return -1;
}
}
return 0;
}
(3)检测I2C寄存器标志位封装函数
在I2C通讯中会有需要检测寄存器标志位状态的时候。
我把检测标志位的函数封装了一下,加入了期望值匹配状态以及超时机制,也是为了方便使用。
/**************************************************************************************************
上一篇:STM32-IIC通信(基于AT24C02的软件IIC通信)
下一篇:STM32之IIC通讯升级版--SHT30温湿度读取
推荐阅读最新更新时间:2024-11-17 11:33
设计资源 培训 开发板 精华推荐
- TS78L 三端100mA正电压稳压器典型应用
- N76E003AT20最小系统板
- 使用 Analog Devices 的 LT3470HDDB 的参考设计
- LF50ABPT-TR 5V 顺序极低压降稳压器多输出电源的典型应用
- 基于S2-LP的Sub-1GHz(452-527 MHz)收发器开发套件
- 使用 Semtech 的 EZ5Z3 的参考设计
- AM2G-4815SZ 15V 2 瓦 DC/DC 转换器的典型应用
- CY8C4128LQI-BL553 4100_BLE PSoC 可编程片上系统的典型应用
- LTC2970-1 支持电源跟踪
- 使用 Richtek Technology Corporation 的 RT5701 的参考设计