使用STM32 HAL库实现AT24C02的IIC通信

一、IIC通信协议

IIC是一种通用串行同步通信协议,将IIC通信分为物理层和协议层两方面来学习。

1.物理层

物理层上有如下特点:

(1) 它是一个支持设备的总线。总线指多个设备共用的信号线。在一个 I2C 通讯总线中,可连
接多个 I2C 通讯设备,支持多个通讯主机及多个通讯从机。
 (2) 一个 I2C 总线只使用两条总线线路,一条双向串行数据线 (SDA) ,一条串行时钟线 (SCL)
据线即用来表示数据,时钟线用于数据收发同步。
(3) 每个连接到总线的设备都有一个独立的地址,主机可以利用这个地址进行不同设备之间的访 问。
(4) 总线通过上拉电阻接到电源。 I2C 设备空闲时,会输出高阻态,而当所有设备都空闲,都输出高阻态时,由上拉电阻把总线拉成高电平。
(5) 多个主机同时使用总线时,为了防止数据冲突,会利用仲裁方式决定由哪个设备占用总线。 (6) 具有三种传输模式:标准模式传输速率为 100kbit/s ,快速模式为 400kbit/s ,高速模式下可达
3.4Mbit/s,但目前大多 I2C 设备尚不支持高速模式。
 (7) 连接到相同总线的 IC 数量受到总线的最大电容 400pF 限制。

 

上拉电阻和高阻态:根据IIC物理层,在使用IIC通信协议的SDASCL两个线时,需要设置内部上拉电阻或者外加电阻至高电平。当stm32作为硬件主机时,连接到总线上所有设备有唯一的IIC地址,主机可以拉低SDA电平以开始通信。(使用CubeMX配置HAL库一般配置开漏输出,且硬件上拉)

SDA线和SCL线:IIC通信主要通过这两根线实现通信,SDAIIC数据线,用于传输数据,SCL为时钟线,用于同步主机与从机的读写数据时钟,所以IIC通信为同步通信协议

2.协议层

起始信号S:由主机拉低SDA电平,来表示开始通信,S起始信号发出后,主机拉低SCL电平,准备开始发送数据

从机地址SLAVE ADDRESS:在总线上的从机的唯一地址,有7位或者10位,一般用7位地址,紧跟在S信号后,当SCL由低电平变为高电平时,地址数据由低位到高位按位发送

读写位:R/W为读写位,0表示写W1表示读R。一般与从机地址7位地址组成8位写地址或者读地址

应答位A:由从机拉低SDA线表示应答,若从机没有拉低SDA线,则为非应答。主机会在这里拉高SCL线,并等待一段时间的应答。主机接收到应答后会继续发送数据,若未接收到即接收到非应答位,停止发送数据

终止信号P:当数据发送完毕,SCL由低电平变为高电平时,SDA变为高电平表示终止信号

IIC协议的写数据与读数据(来自数据手册)

S 表示由主机的IIC接又产生的传输起始信号,这时连接到IIC总线上的所有从都会接收到这个信号。

起始信号产生后,所有从机就开始等待主机紧接下来广播的从机地址信号(SLAVE_ ADDRESS)。在PC总线上,每个设备的地址都是唯一的,当主机广播的地址与某个设备地 址相同时,这个设备就被选中了,没被选中的设备将会忽略之后的数据信号。根据IIC协议, 这个从机地址可以是7位或10 位。

在地址位之后,是传输方向的选择位,该位为0时,表示后面的数据传输方向是由主机 传输至从机,即主机向从机写数据。该位为1时,则相反,即主机由从机读取数据。

从机接收到匹配的地址后,主机或从机会返回 一个应答或非应答信号,只有接收到应答 信号后, 主机 才能继续发送或接 收数据。
若配置的方向传输位为“写数据” 方向,即图22-2的情况,广播完地址,接收到应答 信号后,主机开始正式向从机传输数据(DATA),数据包的大小为8 位。主机每发送完 一个 字节数据,都要等待从机的应答信号,重复这个过程,可以向从机传输N 个数据,这个N 没有大小限制。当数据传输结束时,主机向从机发送一个停止传输信号(P),表示不再传输 数据。

若配置的方向传输位为 “读数据” 方向,即图22-3的情况,广播完地址,接收到应答信 号后,从机开始向主机返回数据,数据包大小也 8 位。从机每发送完 一个数据,都会等待 主机的应答信号,重复这个过程,可以返回N 个数据,这个N 也没有大小限制。当主机希 望停止接收数据时,就向从机返回一个非应答信号,则从机自动停止数据传输。

除了基本的读写,FC通信更常用的是复合格式,即图22- 4的情况,该传输过程有两次 起始信号。一般在第1次传输中,主机通过SLAVE_ADDRESS 寻找到从设备后,发送一段 “ 数据”,这段数据通常用于表示从设备内部的寄存器或存储器地址(注意区分它与SLAVE_ ADDRESS的区别);在第2 次的传输中,对该地址的内容进行读或写。也就是说,第1次通信是告诉从机读写地址,第2 次则是读写的实际内容。

二、IIC通信的HAL

1.HAL库函数和关键词分析

HAL_I2C_Master_Transmit(I2C_HandleTypeDef* hi2c, uint16_t DevAddress,uint8_t *pData, uint16_t Size, uint32_t Timeout);HAL库的IIC写数据函数,适用于不需要写寄存器的时候调用该函数,比如用于与IIC从机握手的时候。*hi2cIIC端口,DevAddress为从机设备地址,*pData为要写入的数据的地址,Size为要发送的字节数(EEPROM为按字节写的存储器),Timeout为最大传输时间

HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);HAL库的IIC读数据函数,*hi2cIIC端口,DevAddress为从机设备地址,*pData为要读区的数据的地址,Size为要接收的字节数(EEPROM为按字节写的存储器),Timeout为最大读取时间

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库的写数据函数,用于数据需要写到从机寄存器时调用该函数,比如向EEPROM写存储数据。*hi2cIIC端口,DevAddress为从机设备地址,MemAddress为从机寄存器地址,MenAddSize为从机寄存器地址长度,*pData为发送数据地址,Size为传输数据字节数,Timeout为最大传输时间

注:使用HAL_I2C_Mem_Write相当于先用HAL_I2C_Master_Transmit先传输寄存器地址,再用HAL_I2C_Master_Transmit传输寄存器的数据。

2.CubeMX配置IIC

IIC中一般用默认配置即可,在IIC使用的引脚被占用时,可以通过端口映射到其他引脚

如果使用CubeMX配置IIC,不需要再去设置IIC的引脚,默认开漏即可,一般会有外部硬件上拉电阻(有的需要自己手动添加外部硬件上拉!)

三、IIC通信实现(AT24C02读写)

IIC读写EEPROM为例的IIC通信实验。EEPROM是一种掉电后数据不会丢失的存储器,以AT24C02型号的EEPROM存储器为例,AT24C02可以存储2kb的数据,STM32F407可以通过IIC读写EEPROM

AT24C02的地址为七位地址,地址取决于芯片上的引脚A0A1A2的电平。以带有该芯片的STM32F407ZGT6开发版为例,其地址为0xA0SDA连接在PB6SCL连接在PB7IICI2C1。(注意!!!实验室的f103c8t6开发版所带的AT24C02芯片与f103之间IIC没有外部上拉,必须外接10k电阻上拉!!!TMD就因为这个折腾我好久!!!

这里我将AT24C02IIC操作封装成库文件,便于代码的快速移植,以提高main.c文件的可读性(注意:AT24C02支持连续写入,每次写入需要等待5ms左右,而且连续写入最大为8字节,即一次行写整个2kb内存需要32次!!!)

at24c02.h文件

#ifndef __AT24C02_H
#define __AT24C02_H

#include "stm32f1xx_hal.h"
#include "stm32f1xx_hal_i2c.h"

#define AT24_Write 0xA0
#define AT24_Read 0xA1


uint8_t AT24_Init(I2C_HandleTypeDef);
uint8_t AT24_Write_Byte(uint8_t reg , uint8_t data);
uint8_t AT24_Read_Byte(uint8_t reg);
uint8_t AT24_Write_Len(uint8_t reg,uint8_t len,uint8_t *buf); //iic连续读写
uint8_t AT24_Read_Len(uint8_t reg,uint8_t len,uint8_t *buf);
#endif

at24c02.c文件

#include "at24c02.h"

I2C_HandleTypeDef AT24_IIC;

uint8_t AT24_Init(I2C_HandleTypeDef hi2c){
  AT24_IIC = hi2c;
  uint8_t R_Data;
  return (HAL_I2C_Mem_Read(&AT24_IIC, AT24_Read, 0x00, I2C_MEMADD_SIZE_8BIT, &R_Data, 1, 0xff) != HAL_OK);
}


uint8_t AT24_Read_Byte(uint8_t reg){
  unsigned char R_Data=0;
 
    HAL_I2C_Mem_Read(&AT24_IIC, AT24_Read, reg, I2C_MEMADD_SIZE_8BIT, &R_Data, 1, 0xfff);
    HAL_Delay(20);
 
    return R_Data; 
}

uint8_t AT24_Write_Byte(uint8_t reg,uint8_t data)         
{
  unsigned char W_Data=0;

  W_Data = data;
  HAL_I2C_Mem_Write(&AT24_IIC, AT24_Write, reg, I2C_MEMADD_SIZE_8BIT, &W_Data, 1, 0xfff);
  HAL_Delay(20);
 
  return 0;
}
//连续写入函数需要根据AT24C02芯片的特性更改
uint8_t AT24_Write_Len(uint8_t reg,uint8_t len,uint8_t *buf)
{
  if(len >8){
    uint8_t count = len/8;
    uint8_t noun = len%8;
    for(int p=0; p<count ; p++){
      HAL_I2C_Mem_Write(&AT24_IIC, AT24_Write, reg+8*p,I2C_MEMADD_SIZE_8BIT,buf+8*p,8,0xff);
			HAL_Delay(5);
    }
    if(noun){
      HAL_I2C_Mem_Write(&AT24_IIC, AT24_Write, reg+8*count, I2C_MEMADD_SIZE_8BIT, buf+8*count, noun, 0xff);
			HAL_Delay(5);
    }
  }
  else{
      HAL_I2C_Mem_Write(&AT24_IIC, AT24_Write, reg ,I2C_MEMADD_SIZE_8BIT, buf,len,0xff);
      HAL_Delay(5);
  }
  return 0;
}

uint8_t AT24_Read_Len(uint8_t reg,uint8_t len,uint8_t *buf)
{
  HAL_I2C_Mem_Read(&AT24_IIC, AT24_Read, reg, I2C_MEMADD_SIZE_8BIT, buf, len, 0xfff);
  HAL_Delay(20);
 
  return 0;
}

main.c主文件(在main()中,while(1)前)

  /* USER CODE BEGIN 2 */
  //调用printf格式化输出需要重写fputc和fgetc函数,并在target中设置“Use MircoLIB”
        HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,1);
        if(AT24_Init(hi2c1)){
                printf("AT24C02 connected ERROR!\r\n");
                while(1);
        }
        HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,0);
        printf("AT24C02 connected SUCCESS!\r\n");
       
        uint8_t recv_buff[256];
       
        for (int i = 0; i<256; i++){
                AT24_Write_Byte(i,i);
        }
        printf("AT24C02 transmission SUCCESS!\r\n");
        for (int i = 0; i<256; i++){
                AT24_Read_Len(0x00, 255 , recv_buff);
        }
       
        for (int i = 0; i<256; i++){
                printf("0x%02X  ",recv_buff[i]);
        }
        printf("\r\n");
        printf("AT24C02 receive SUCCESS!\r\n");
  /* USER CODE END 2 */

演示如下

MD,都看到底了,不点个赞?!!

作者:hrilug

物联沃分享整理
物联沃-IOTWORD物联网 » 使用STM32 HAL库实现AT24C02的IIC通信

发表评论