STM32CubeMX HAL库:I2C详解——读取和写入EEPROM

在之前的标准库中,STM32的硬件IIC非常复杂,更重要的是它并不稳定,所以都不推荐使用。但是在我们的HAL库中,对硬件IIC做了全新的优化,使得之前软件IIC几百行代码,在HAL库中,只需要寥寥几行就可以完成 那么这篇文章将带你去感受下它的优异之处。

本文将详细地讲解I2C协议,并基于I2C
来读写EEPROM模块以达到练习的目的

通过本篇博客您将学到:

  • I2C的基本原理
  • STM32CubeMX创建I2C例程
  • I2C函数库(HAL)
  • AT24C256芯片原理及读写方法
  • I2C简介 

    IIC(Inter-Integrated Circuit)总线是一种由NXP(原PHILIPS)公司开发的两线式串行总线,用于连接微控制器及其外围设备。多用于主控制器和从器件间的主从通信。

    I2C特性

  • 半双工
  • 没有严格的波特率要求
  • 小数据量场合使用
  • 传输距离短、传输速率不如SPI
  • 任何时刻只能有一个主机,但任何设备都可成为主机
  •  只需要两条总线——SDA与SCL 
  •  IIC一共有只有两个总线: 一条是双向的数据线SDA,一条是串行时钟线SCL

    所有接到I2C总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上。I2C总线上的每一个设备都对应一个唯一的地址。

     I2C起始信号与终止信号(代码段为软件I2C)

    1、起始信号:SCL为高电平时,SDA由高->低

    void I2C_Start()
    {
    SDA=1;               //确保SDA为高电平
    HAL_Delay(1);
    SCL=1;               //确保SCL为高电平
    HAL_Delay(1);
    SDA=0;              //SCL为高时拉低SDA线
    HAL_Delay(1);
    SCL=0;              //钳住I2C总线,准备数据的发送与接收
    }

    2、终止信号:SCL为高电平时,SDA由低->高

    void I2C_Stop()
    {
    SCL=0;            
    SDA=0;
    HAL_Delay(1);
    SCL=1;             //确保SCL为高电平
    HAL_Delay(1);
    SDA=1;            //SCL为高时拉高SDA
    } 

    I2C应答信号与非应答信号 (代码段为软件I2C)

    每当主机向从机发送完一个字节的数据,主机总是需要等待从机给出一个应答信号,以确认从机是否成功接收到了数据。

  • 应答信号为低电平时,规定为有效应答位(ACK,简称应答位),表示接收器已经成功地接收了该字节;
  • 应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。
     
  • 1、应答信号‘0’:SCL高时SDA低

    void I2C_Ack()        //产生应答信号
    {
    SCL=0;
    SDA=0;
    HAL_Delay(1);
    SCL=1;             //确保SCL为高时SDA为低
    HAL_Delay(1);
    SCL=0;
    }

    2、非应答信号‘1’:SCL高时SDA高

    void I2C_NAck()     //不产生应答信号
    {
    SCL=0;
    SDA=1;
    HAL_Delay(1);
    SCL=1;             //SCL高时SDA高
    HAL_Delay(1);
    SCL=0;
    }

    I2C数据有效性

    I2C信号在进行数据传输时, 当SCL=1高电平时,数据线SDA必须保持稳定状态,不允许有电平跳变,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。

    SCL=1时 数据线SDA的任何电平变换会看做是总线的起始信号或者停止信号。也就是在IIC传输数据的过程中,SCL时钟线会频繁的转换电平,以保证数据的传输

     接着我们介绍一下基于AT24C256的I2C通信

    AT24C256芯片手册如下:

     描述:

    AT24C256是提供131072/262144位串行电可擦除可编程只读存储器(EEPROM),该设备最多允许4个设备共享一条公共的双线总线。

    特征:

  • 低电压和标准电压操作-2.7V(Vcc=2.7V至5.5V)(一般选择3.3V)
  • 双线串行接口
  • 施密特触发器,用于噪声抑制的滤波输入
  • 双向数据传输协议
  • 400kHz兼容性
  • 硬件和软件的写保护引脚
  • 64字节页写入模式(允许部分页写入)
  • 自定时写入周期
  • 高可靠性(数据保留:40年)
  •  A0,A1,A2:硬件地址引脚
  • WP:写保护引脚,接高电平只读,接地允许读和写
  •  芯片的寻址:

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

    也就是说如果是\写24C02的时候,从器件地址为10100000(0xA0);读24C02的时候,从器件地址为10100001(0xA1)。

    注意:

    1. 在写数据的过程中,每成功写入一个字节,E2PROM存储空间的地址就会自动加1,当加到0xFF后,再写一个字节,地址就会溢出又变成0x00。
    2. 当我们在写多个字节时,写入一个字节之后,再写入下一个字节之前,必须延时5ms才可以

     STM32CubeMX创建I2C例程(作者使用的是STM32f4ccu6)

    1、设置RCC时钟

    2、配置I2C

  •  Master  features  主模式特性
  • I2C Speed Mode: IIC模式设置 快速模式和标准模式。实际上也就是速率的选择。I2C Clock Speed:I2C传输速率,默认为100KHz
  • Slave  features  从模式特性
  • Clock No Stretch Mode: 时钟没有扩展模式
  • IIC时钟拉伸(Clock stretching) – -clock stretching通过将SCL线拉低来暂停一个传输.直到释放SCL线为高电平,传输才继续进行.clock stretching是可选的,实际上大多数从设备不包括SCL驱动,所以它们不能stretch时钟.
  • Primary Address Length selection: 从设备地址长度 设置从设备的地址是7bit还是10bit 大部分为7bit
  • Dual Address Acknowledged: 双地址确认
  • Primary slave address:  从设备初始地址
  • 3、配置串口

    这里配置串口是为了读EEPROM并打印出来。 

    4、时钟树配置

    5、项目文件配置

  • 设置项目名称
  • 设置项目路径(不能包含中文)
  • 选择IDE(作者用的是keil5)  
  • 复制所用的.c和.h文件 
  • 不同功能生成独立的.c和.h文件
  • 6、 生成代码

    7、配置下载工具

    8、重写printf函数

    重写printf函数能更方便与上位机通信 ,方法参考printf重写

    I2C函数库(HAL)

    在I2C.c文件中可以看到I2C初始化函数。在stm32f1xx_hal_i2c.h头文件中可以看到I2C的操作函数。分别对应轮询中断DMA三种控制方式


     

     这里我们简单介绍一下等下用到的函数

    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)

    功能: IIC写多个数据 该函数适用于IIC外设里面还有子地址寄存器的设备,比方说E2PROM,除了设备地址,每个存储字节都有其对应的地址 

  • I2C_HandleTypeDef *hi2c  i2c句柄,例如上文配置的&hi2c1,实际上是一个结构体
  • uint16_t DevAddress    从机设备地址,对AT24Cxx来说,0xA0代表写数据,0xA1代表读数据
  • uint16_t MemAddress    从机寄存器地址,每写入一个字节的数据,地址自动+1
  • uint16_t MemAddSize    从机寄存器地址长度,8bit和16bit可选
  • uint8_t *pData    发送数据的起始地址
  •  uint16_t Size     传输数据的大小
  • uint32_t Timeout     传输超时时间 
  •  读写AT24C256

    /* Private define ------------------------------------------------------------*/
    /* USER CODE BEGIN PD */
    #define AT24C256_Write 0xA0
    #define AT24C256_Read 0xA1
    #define BufferSize 64
    /* USER CODE END PD */

    宏定义,增加了程序的可读性

    /* Private variables ---------------------------------------------------------*/
    
    /* USER CODE BEGIN PV */
    uint8_t w[BufferSize],r[BufferSize];
    uint16_t i,j;
    /* USER CODE END PV */

    定义接收与发送数组

     /* USER CODE BEGIN 2 */
    for(i=0;i<64;i++)
    {
    	w[i]=i;
    }
    printf("I2C Example Test\n");
    //for( j=0;j<16;j++)
    //{
    	if(HAL_I2C_Mem_Write(&hi2c1,AT24C256_Write,0,I2C_MEMADD_SIZE_16BIT,w,64,10000)==HAL_OK)
    	{
    		printf("EEPROM AT24C256 Write Test OK\n");
    		HAL_Delay(100);
    	}
    	else
    	{
    		printf("EEPROM AT24C256 Write Test False");
    		HAL_Delay(50);
    	}
    //}
    HAL_Delay(100);
    HAL_I2C_Mem_Read(&hi2c1,AT24C256_Read,0,I2C_MEMADD_SIZE_16BIT,r,BufferSize,10000);
    for(i=0;i<64;i++)
    {
    	HAL_Delay(10);
    	printf("0x%02X  ",r[i]);
    }
      /* USER CODE END 2 */

    64字节一次性写入

     /* USER CODE BEGIN 2 */
    for(i=0;i<64;i++)
    {
    	w[i]=i;
    }
    printf("I2C Example Test\n");
    for( j=0;j<16;j++)
    {
    	if(HAL_I2C_Mem_Write(&hi2c1,AT24C256_Write,0+16*j,I2C_MEMADD_SIZE_16BIT,w+16*j,16,10000)==HAL_OK)
    	{
    		printf("EEPROM AT24C256 Write Test OK\n");
    		HAL_Delay(100);
    	}
    	else
    	{
    		printf("EEPROM AT24C256 Write Test False");
    		HAL_Delay(50);
    	}
    }
    HAL_Delay(100);
    HAL_I2C_Mem_Read(&hi2c1,AT24C256_Read,0,I2C_MEMADD_SIZE_16BIT,r,BufferSize,10000);
    for(i=0;i<64;i++)
    {
    	HAL_Delay(10);
    	printf("0x%02X  ",r[i]);
    }
      /* USER CODE END 2 */

    分批次写入

    单片机烧录,运行

     注意:确保写入时地址不会溢出,否则可能像当时的菜鸡博主一样搞半天(如下)

     

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32CubeMX HAL库:I2C详解——读取和写入EEPROM

    发表评论