历史上的今天
今天是:2024年10月14日(星期一)
2021年10月14日 | STM32的I2C通讯配置(硬件实现)——学习笔记(6-2)
2021-10-14 来源:eefocus
文章内容根据野火学习教程进行整理,仅仅是学习记录。
开发板: 野火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通讯中会有需要检测寄存器标志位状态的时候。
我把检测标志位的函数封装了一下,加入了期望值匹配状态以及超时机制,也是为了方便使用。
/**************************************************************************************************
史海拾趣
|
着电力电子技术的不断发展,越来越多的电力电子装置被广泛应用于各种领域,然而电力电子器件所固有的非线性使得它对市电的影响如谐波污染及输入端功率因数问题等显得日益突出。以前,我们使用无源滤波网来解决谐波问题。但是,无源滤波网的滤波特性 ...… 查看全部问答> |
|
看大家都喜欢zigbee, 上传网上找来的ZigBee中文资料, 是中文的,大家慢慢看 发现大家只是喜欢zigbee, 交流喜欢zigbee体会的人很少.… 查看全部问答> |
|
寄存器跟存储器有什么区别? 寄存器上:“一个操作码+一个操作数”等于一条微指令吗?一条微指令是完成一条机器指令的一个步骤对吗? cpu是直接跟寄存器打交道的对吗?也就是说寄存器是运算器、控制器的组成部分对不? 设计一条指令就是说把几条 ...… 查看全部问答> |
|
Symbian正式进军中国市场,联手其正式合作伙伴Symbio中国研发中心招聘开发测试工程师! Symbian正式进军中国市场啦!!! Symbian中国研发中心硬件设施已经建成,由于负责人均为英国总公司人员,现委托其正式合作伙伴Symbio中国研发中心协助招募开发测试工程师。 工作地点:北京中关村 Junior& Senior Tester 主要职责是编写code进 ...… 查看全部问答> |
|
版主,STM8S207的的registermap里怎么找不到ADC_DB0RH的地址 下载 (72.38 KB) 2010-9-21 14:23 如题: STM8S207的的register map里怎么找不到ADC_DB0RH的地址。 当ADC设置在buffer scan模式的时候,ADC_DB0RH-ADC_DB0RH还有ADC_DB0RL-ADC_DB0RL的地址是什么? 见下图… 查看全部问答> |
|
本帖最后由 yuanlai2010 于 2014-7-10 19:20 编辑 Helper2416启动详情 参与Helper2416开发板助学计划心得 前几天发过一篇有关于S3C2416启动流程的帖子,写的不够详细。在参阅老版本的S3C2416用户手册和Helper2416底板、核心板原理图之后,重新 ...… 查看全部问答> |
|
声频放大器的一个关键设计难题在于产生电源电压。使用单芯锂电池作为电源时,升压转换器会将该电压升高,从而使声频放大器产生偏压。升高的电压水平要在声频质量和功耗之间达成折衷。您希望将电源电压升高到足以不扭曲或修剪某些声频信号(峰值功 ...… 查看全部问答> |




