微雪官网产品资料
立即注册 找回密码

QQ登录

只需一步,快速开始

微雪课堂

搜索
微雪课堂 首页 STM32 STM32CubeMX系列教程 查看内容

STM32CubeMX系列教程9:内部集成电路(I2C)

2016-5-3 14:45| 发布者: MyMX1213| 查看: 34862| 评论: 22|原作者: MyMX1213

摘要: 本章介绍I2C总线。通过I2C控制AT24Cxx芯片。
1.I2C总线简介

        I2C(Inter-Integrated Circuit ,内部集成电路)总线是一种由飞利浦Philip公司开发的串行总线。是两条串行的总线,它由一根数据线(SDA)和一根 时钟线(SDL)组成。I2C总线上可以接多个I2C设备,每个器件都有一个唯一的地址识别。同一时间只能有一个主设备,其他为从设备。通常MCU作为主设备控制,外设作为从设备。

2.I2C硬件电路

        I2C总线为漏极开路结构(OD),因此它们必须接有上拉电阻,阻值常为  4k7 或 10k ;当总线空闲时,两根线均为高电平。OD门与其它任意数量的OD与OC门成"线与"关系,即当总线上的任一器件输出的低电平,都将使总线的信号变低。


3.I2C协议

 I2C有三种状态信号:开始信号、结束信号和应答信号

开始信号:SCL为高电平时,SDA由高电平转变为低电平跳变,表示开始通信。
结束信号:SCL为高电平时,SDA由低电平转变为高电平跳变,结束结束通信。
应答信号:接收数据的IC在接收到一个字节数据后,向发送数据的IC发出特定的低电平脉冲,表示已收到数据。


        在数据传输过程中,SCL时钟为主设备控制,SCL为高的时候读取数据SDA的数据,SCL为低的时候,主设备改变SDA的数据准备传输下一位。数据从高位开始传输,当传输8位后,主设备会释放SDA总线。如果从设备正确接收到数据,则从设备会拉低SDA总线,则产生一个应答信号。如果从设备出错,不拉低SDA总线,由于上拉电阻的作用,SDA的电平会变为高电平,即为非应答信号。数据传输总是以开始信号开始传输,以结束信号终止传输,中间可以传输多个字节的数据。

4.AT24Cxx

        AT24C系列为美国ATMEL公司推出的串行COMSE2PROM。芯片型号后两位表示芯片容量,例如ATC24C02为2K。引脚图中A0、A1、A2为器件地址引脚,GND为地,VCC为正电源,WP为写保护,SCL为串行时钟线,SDA为串行数据线。


AT24C设备地址为如下,前四位固定为1010,A2~A0为由管脚电平。AT24CXX EEPROM Board模块中默认为接地。A2~A0为000,最后一位表示读写操作。所以AT24Cxx的读地址为0xA1,写地址为0xA0。


写字节操作MCU先发送一个开始信号(START),然后发送器件写操作地址(DEVICE ADDRESS),E2PROM接收到地址后,如果地址和自身的地址一样,E2PROM就会回一个应答信号(ACK)。MCU接收到应答信号后,再发送要操作的内存地址(WORD ADDRESS),得到应答后再传输写入的数据(DATA)。再次等待E2PROM应答后发送结束信号(STOP)结束传输。



E2PROM支持连续写操作,操作和单个字节类似,先发送设备写操作地址(DEVICE ADDRESS),然后发送内存起始地址(WORD ADDRESS),然后不断传输数据。E2PROM的地址指针会自动递增,数据会依次保存在内存中。


E2PROM读字节操作如下,先发送设备写操作地址(DEVICE ADDRESS),然后发送要读取内存的地址(WORD ADDRESS)。然后重新发送开始信号(START),发送设备读操作地址(DEVICE ADDRESS)对E2PROM进行读操作。此时MCU释放SDA数据线,E2PROM控制SDA数据总线,将内存地址中对应的数据发送给MCU。


同理,E2PROM可以连续读操作,在读一个字节后,MCU会回应一个应答信号(ACK)后,E2PROM会继续传输下一个地址的数据,MCU不断回应应答信号可以不断读取内存的数据,不应答发送结束信号后终止传输。


注意:每次操作都是由MCU先发送开始信号,然后发送器件地址,写操作发送写设备地址,读操作发送读设备地址,然后再传输数据。

5.示例程序

        本章程序在串口printf工程的基础上修改,复制串口printf的工程,修改文件夹名。击STM32F746I.ioc打开STM32cubeMX的工程文件重新配置。I2C1选择I2C模式,PB8,PB9管脚分别配置为I2C1_SDA,I2C2_SCL。

        


I2C为默认设置不作修改。只需注意一下,I2C为标准模式,I2C传输速率(I2C Speed Frequency)为100KHz。


生成报告以及代码,编译程序。在i2c.c文件中可以看到ADC初始化函数。在stm32f7xx_hal_i2c.h头文件中可以看到I2C的操作函数。分别对应轮询,中断和DMA三种控制方式。


在mian.c文件前面声明两个输出存储读写数据,宏定义E2PROM读写地址以及缓存数据长度。

/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
#define ADDR_24LCxx_Write 0xA0
#define ADDR_24LCxx_Read 0xA1
#define BufferSize 0x100
uint8_t WriteBuffer[BufferSize],ReadBuffer[BufferSize];
uint16_t i;
/* USER CODE END PV */

在mian()函数里面添加应用程序读写E2PROM。

/* USER CODE BEGIN 2 */
printf("\r\n***************I2C Example*******************************\r\n");
for(i=0; i<256; i++)
    WriteBuffer[i]=i;    /* WriteBuffer init */
/* wrinte date to EEPROM */
if(HAL_I2C_Mem_Write(&hi2c1, ADDR_24LCxx_Write, 0, I2C_MEMADD_SIZE_8BIT,WriteBuffer,BufferSize, 0x10) == HAL_OK)
    printf("\r\n EEPROM 24C02 Write Test OK \r\n");
else
    printf("\r\n EEPROM 24C02 Write Test False \r\n");
 
/* read date from EEPROM */
HAL_I2C_Mem_Read(&hi2c1, ADDR_24LCxx_Read, 0, I2C_MEMADD_SIZE_8BIT,ReadBuffer,BufferSize, 0x10);
for(i=0; i<256; i++)
    printf("0x%02X  ",ReadBuffer[i]);

if(memcmp(WriteBuffer,ReadBuffer,BufferSize) == 0 ) /* check date */
    printf("\r\n EEPROM 24C02 Read Test OK\r\n");
else
    printf("\r\n EEPROM 24C02 Read Test False\r\n");
/* USER CODE END 2 */

程序中先初始化写数据缓存。然后调用HAL_I2C_Mem_Write()函数将数据写入E2PROM中。根据函数返回值判断写操作是否正确。在I2C中可以找到内存写函数说明。第一个参数为I2C操作句柄。第二个参数为E2PROM的写操作设备地址。第三个参数为内存地址,第二个参数为内存地址长度,E2PROM内存长度为8bit,第四个参数为数据缓存的起始地址,第五个参数为传输数据的大小,第六个参数为操作超时时间。

/**
  * @brief  Write an amount of data in blocking mode to a specific memory address
  * @param  hi2c : Pointer to a I2C_HandleTypeDef structure that contains
  *                the configuration information for the specified I2C.
  * @param  DevAddress: Target device address
  * @param  MemAddress: Internal memory address
  * @param  MemAddSize: Size of internal memory address
  * @param  pData: Pointer to data buffer
  * @param  Size: Amount of data to be sent
  * @param  Timeout: Timeout duration
  * @retval HAL status
  */  
HAL_StatusTypeDef 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)


调用HAL_I2C_Mem_Read()函数读取E2PROM中刚才写入的数据。
HAL_I2C_Mem_Read()函数描述如下。第一个参数为I2C操作句柄。第二个参数为E2PROM的读操作设备地址。第三个参数为内存地址,第二个参数为内存地址长度,第四个参数为读取数据存储的起始地址,第五个参数为传输数据的大小,第六个参数为操作超时时间。

/**
  * @brief  Read an amount of data in blocking mode from a specific memory address
  * @param  hi2c : Pointer to a I2C_HandleTypeDef structure that contains
  *                the configuration information for the specified I2C.
  * @param  DevAddress: Target device address
  * @param  MemAddress: Internal memory address
  * @param  MemAddSize: Size of internal memory address
  * @param  pData: Pointer to data buffer
  * @param  Size: Amount of data to be sent
  * @param  Timeout: Timeout duration
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_I2C_Mem_Read(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t 
                MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout)

程序最后调用memcmp()函数判断读写的两个缓存的数据是否一致。memcmp()是比较内存区域是否相等,标准库里面的函数,在main.c前面添加string.h头文件。

/* USER CODE BEGIN Includes */
#include 
/* USER CODE END Includes */

AT24CXX EEPROM Board模块插入到Open746I开发板I2C1中,编译程序并下载到开发板。打开串口调试助手。设置波特率为115200。串口助手上会显示如下信息。


381

顶一下

刚表态过的朋友 (381 人)

相关阅读

发表评论

最新评论

引用 游客 2019-5-16 10:21
爲什麽我的結果都是0x00?寫都不成功
引用 游客 2018-8-28 09:50
: for (j=0; j<32; j++)         {                 if(HAL_I2C_Mem_Write(&hi2c2, ADDR_24LCxx_Write, 8*j, I2C_MEMADD_SIZE_8BIT,WriteBuffer+8*j,8, 1000) == H ...
这位兄弟的代码是对的
引用 游客 2018-8-6 16:14
for (j=0; j<32; j++)
        {
                if(HAL_I2C_Mem_Write(&hi2c2, ADDR_24LCxx_Write, 8*j, I2C_MEMADD_SIZE_8BIT,WriteBuffer+8*j,8, 1000) == HAL_OK)
                {
                                printf("\r\n EEPROM 24C02 Write Test OK \r\n");
                        HAL_Delay(20);
                }
                else
                {
                         HAL_Delay(20);
                                printf("\r\n EEPROM 24C02 Write Test False \r\n");
                }
         ...
引用 游客 2018-7-26 13:57
麻烦发一个程序呗 我按照教程调不通 返回数据不对 我的邮箱462421430@qq.com 求助各位大神~
引用 游客 2018-7-12 15:40
: AT24C256读出来也是FF,怎么改啊
找到了,把I2C_MEMADD_SIZE_8BIT改成I2C_MEMADD_SIZE_16BIT
引用 游客 2018-7-10 18:44
AT24C256读出来也是FF,怎么改啊
引用 游客 2018-6-30 23:12
总结一下之前的评论
1. 需要修改24c02的写操作,按照前面有个人说的,分成32次写入,延时,写入成功,代码修改如下。
for (i=0; i<32; i++)
        {
                if(HAL_I2C_Mem_Write(&hi2c4, ADDR_24LCxx_Write, 0, I2C_MEMADD_SIZE_8BIT,WriteBuffer,8, 0xff) == HAL_OK)
                {
                                printf("\r\n EEPROM 24C02 Write Test OK \r\n");
                        HAL_Delay(5);
                }
                else
                {
                         HAL_Delay(5);
                                printf("\r\n EEPROM 24C02 Write Test False \r\n");
                }
        }
2. IIC的回调函数里面不需要修改也可以的 ...
引用 游客 2018-6-22 09:46
: 为什么我读出来都是0XFF呢AT24C256
终于搞好了跟着教程会死人的
引用 游客 2018-6-15 14:01
为什么我读出来都是0XFF呢AT24C256
引用 游客 2018-4-22 11:07
我纠正一下上一个评论,硬件I2C测试成功,注意@开始现在 在评论中的调整
1. IIC的回调函数里面需要将时钟初始化放在引脚初始化之前
  /* USER CODE BEGIN I2C1_MspInit 0 */
    __HAL_RCC_I2C1_CLK_ENABLE(); //默认生成的放在了引脚初始化后面!
  /* USER CODE END I2C1_MspInit 0 */

2. 读写函数最后一个超时调整为1000以上
3. 每次写之后要delay 5ms
4. AT24C02页写入只支持8个byte,所以需要分32次写入。这不是HAL库的bug,而是AT24C02的限制,其他的EEPROM可以支持更多byte的写入。 ...
引用 游客 2018-4-11 12:54
STM的I2C一直不好用,最好出一个软件I2C的教程
引用 游客 2018-3-22 10:14
读写EEPROM 写的时候前八个数据一直有问题?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ...
引用 游客 2017-12-21 21:14
讲三点:
1.一楼解决了M3的bug;
2.如果但是请楼上没有调通的同志们,把读写函数最后一个超时调整为1000以上。否则,不能成功!
3.另外,楼上有个说一次只能写8个的也要注意一下。
引用 开始现在 2017-12-11 11:14
首先说明下开发条件:

1、开发板:秉火霸道,STM32F103ZET

2、软件:Cubemx V4.23(F1 V1.60库)

3、硬件:AT24C02 256KByte

问题描述:Cubemx生成IIC代码会出现死机问题,或者压根运行不了!

问题原因:

1、ST为了规避飞利浦IIC专利问题,将STM32的硬件IIC设计的比较复杂,而且稳定性不怎么好,所以一般教程都不推荐使用。

2、Cubemx生成的代码有Bug!

解决办法:

1、设置

捕获.JPG

2、IIC的回调函数里面需要将时钟初始化放在引脚初始化之前!

void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c)

{



  GPIO_InitTypeDef GPIO_InitStruct;

  if(hi2c->Instance==I2C1)

  {

  /* USER CODE BEGIN I2C1_MspInit 0 */

    __HAL_RCC_I2C1_CLK_ENABLE(); //默认生成的放在了引脚初始化后面!

  /* USER CODE END I2C1_MspInit 0 */

  

    /**I2C1 GPIO Configuration   

    PB6     ------> I2C1_SCL

    PB7     ------> I2C1_SDA

    */

    GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;

    GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;

    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;

    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);



    ...
引用 游客 2017-10-31 21:05
24C02意味着2千比特= 2048/8 = 256字节==>可寻址8位索引(我们说子地址)

==>使用1字节寻址作为子地址就足够了。
引用 游客 2017-10-24 16:11
MyMX1213您好!首先对你发布的教程表示感谢,但此篇文章您发布的有点不负责任,首先ATC24C02只支持每页8KB的读写,可能这也是HAL驱动忽略的地方,如按你的代码最多只能写入前面8位的数据,不知道你串口打印输出怎么会是对的,我整整折腾了两天,查了好多资料最确定是写入部分出问题,按你生成的256数据必须分成32次去写,且每次写必须中断5ms以上,读没有问题。EPROM同是ATC24C02,在此跟贴希望警示后来的读者,HAL库版本是V1.7.1,其它版本没试过,有可能作者的版本与我的不一样。 ...
引用 游客 2017-10-23 14:47
这个跟本写不进去,是STM32CubeMX版本不对呢?还是其它有设置问题,用原生库读写没问题
引用 游客 2017-8-19 23:25
LZ  我按照您这个来配置,读写EEPROM 写的时候前八个数据一直有问题,HAL库的硬件IIC是不是还是存在BUG?
引用 游客 2017-8-2 18:01
我想请问下,我按照你的这个程序来写的,可是读出的数据是0xF0,F1....0XFF,请问这个是什么原因呢?
引用 游客 2017-2-25 10:30
: 楼主 我想咨询一个关于I2c的问题,HAL_I2C_Mem_Read()函数中,参数pdata是8位的,但是我有一个模块的数据存储是16位的,请问这个时候我该怎么办呢 ...
就读2个8bint的数据再转为16位数据

查看全部评论(22)

CubeMX教程

微雪官网|产品资料|手机版|小黑屋|微雪课堂. ( 粤ICP备05067009号  

GMT+8, 2019-5-23 05:01 , Processed in 0.075891 second(s), 26 queries .

Powered by Discuz! X3.2 © 2001-2013 Comsenz Inc & Style Design

返回顶部