历史上的今天
返回首页

历史上的今天

今天是: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通讯中会有需要检测寄存器标志位状态的时候。

我把检测标志位的函数封装了一下,加入了期望值匹配状态以及超时机制,也是为了方便使用。


/**************************************************************************************************

推荐阅读

史海拾趣

格莱尔(GLE)公司的发展小趣事

面对日益激烈的市场竞争,格莱尔积极推进数字化转型和智能化升级。公司引入了ERP、EHR、OA等信息化系统,实现了生产、管理、销售等各个环节的信息化和智能化。同时,格莱尔还加大了对自动化生产设备的投入和研发力度,提高了生产效率和产品质量。这些举措不仅提升了企业的竞争力,也为格莱尔在未来的发展中注入了新的动力。

Data Translation Inc公司的发展小趣事

在电子行业中,市场竞争激烈、技术更新换代迅速。Data Translation在发展过程中也遇到了不少挑战和困难。但公司始终坚持以客户为中心、以技术创新为驱动的发展理念,积极应对市场变化和挑战。公司通过加大研发投入、优化产品结构、拓展市场渠道等方式不断提升自身的竞争力和适应能力。这些努力使公司在面对市场变化和挑战时能够保持稳健的发展态势。

Faraday Technology公司的发展小趣事

Data Translation Inc公司成立于1973年,初期专注于数据采集和处理技术的研发。在成立初期,公司就凭借对技术的深刻理解和敏锐的市场洞察力,成功开发了一系列具有创新性的数据采集和处理产品。这些产品不仅满足了当时工业界对高精度、高速度数据采集的需求,还引领了整个行业的发展方向。随着技术的不断进步和市场需求的不断变化,Data Translation不断推出新产品,逐渐确立了其在数据采集和处理领域的领先地位。

Exar公司的发展小趣事

Exar公司从一个小型创业公司起步,凭借其独特的技术和创新的思维,在电子行业中崭露头角。公司初期专注于数据传输和存储技术的研发,通过不断的技术创新和产品优化,逐渐在市场上建立起良好的口碑。随着公司规模的扩大,Exar开始涉足更多的领域,包括网络通信、消费电子等,逐步发展成为一家跨行业的综合性企业。

Dell公司的发展小趣事

Exar公司通过收购和兼并多家公司,实现了技术融合和产品线拓展。其中,Neterion和Hifn的收购是Exar发展历程中的重要里程碑。Neterion在万兆以太网数据传输技术和虚拟化IO技术方面处于领先地位,而Hifn则在数据精简、加密、压缩技术方面拥有领先技术。Exar将这两家公司的技术融合在一起,推出了一系列高性能的数据传输和存储产品,进一步巩固了其在行业中的领先地位。

ETA Electric Industry Co Ltd公司的发展小趣事

随着技术实力的增强,ETA Electric Industry Co Ltd开始积极拓展国内外市场。他们参加了多个国际电子展会,与世界各地的潜在客户建立了联系。同时,公司还加强了与国内外大型电子企业的合作,为其提供定制化的解决方案。这些举措极大地提升了公司的知名度和市场份额,为公司的快速发展奠定了基础。

问答坊 | AI 解惑

三相有源滤波器控制策略的研究

着电力电子技术的不断发展,越来越多的电力电子装置被广泛应用于各种领域,然而电力电子器件所固有的非线性使得它对市电的影响如谐波污染及输入端功率因数问题等显得日益突出。以前,我们使用无源滤波网来解决谐波问题。但是,无源滤波网的滤波特性 ...…

查看全部问答>

感谢万能的互联网: 网上找来的ZigBee2007中文资料

    看大家都喜欢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的地址是什么? 见下图…

查看全部问答>

40个单片机(51)基础实验 C 和汇编都有

40个单片机(51)基础实验 C 和汇编都有,非常好的资料…

查看全部问答>

Helper2416-06——Helper2416启动详情

本帖最后由 yuanlai2010 于 2014-7-10 19:20 编辑 Helper2416启动详情 参与Helper2416开发板助学计划心得 前几天发过一篇有关于S3C2416启动流程的帖子,写的不够详细。在参阅老版本的S3C2416用户手册和Helper2416底板、核心板原理图之后,重新 ...…

查看全部问答>

【allegro】求问allegro中怎么一键删除所有Island

如题,在allegro软件中要怎么一键删除所有的Island??? …

查看全部问答>

利用包络追踪功能提高声频放大器的效率

声频放大器的一个关键设计难题在于产生电源电压。使用单芯锂电池作为电源时,升压转换器会将该电压升高,从而使声频放大器产生偏压。升高的电压水平要在声频质量和功耗之间达成折衷。您希望将电源电压升高到足以不扭曲或修剪某些声频信号(峰值功 ...…

查看全部问答>