STM32F103 EEPROM_24C02实验

实验目的

本实验向大家介绍如何使用STM32的IIC硬件接口,实现和24C02之间的双向通信,通过本实验的学习,我们将对IIC通信的过程会有一个详细的了解。

实验简介

IIC简介
IIC(Inter-Integrated Circuit)总线是一种由PHILIPS公司开发的两线式串行总线,用于连接微控制器及外围设备。它是由数据线SDA和时钟线SCL构成的串行总线,可用于发送和接收数据。在CPU与被控IC之间,IC与IC之间进行双向传送,高速IIC总线一般可达400kbps以上,但是目前大多数IIC设备不支持高速IIC。

使用IIC总线通信时,只需要2根通信线,一条双向串行数据线(SDA),一条串行时钟线(SCL)。每个IIC设备都有一个独立的地址,主机可以利用这个地址进行不同设备间的访问。当多主机使用总线时,IIC有一个仲裁机制,会决定由那个主机使用总线。

IIC总线协定如下:

  1. 只有在总线空闲时才允许启动数据发送。
  2. 在数据传送过程中,当时钟线为高电平时,数据线必须保持稳定状态,不允许有跳变。时钟线为高电平时,数据线的任何电平变化将被看作总线的起始或停止信号。

IIC在数据通信过程中共有3种信号,分别是起始信号,停止信号和应答信号。

**起始信号:**时钟线(SCL)保持高电平期间,数据线(SDA)电平从高到低跳变作为I2C总线的起始信号。

**停止信号:**时钟线(SCL)保持高电平期间,数据线(SDA)电平从低到高的跳变作为I2C总线的停止信号

**应答信号:**接收数据的IC在接收到8bit数据后,向发送数据的IC发出特定的低电平脉冲,表示已收到数据。CPU向受控单元发出一个信号后,等待受控单元发出一个应答信号,CPU接收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,则判断为受控单元出现故障。
在这里插入图片描述
器件简介
本实验所用IIC器件为非易失性存储器EEPROM——AT24C02,容量为256字节,可擦写100万次,掉电数据不会丢失,通常被用来保存一些用户数据。这个系列芯片容量可以达到1Mbit,即128K字节,如AT24C1024.
AT24C02的管脚配置如下,关于这个芯片详细介绍请参看数据手册:
在这里插入图片描述

电路设计

星光STM32F103开发板采用的EEPROM芯片为AT24C02,容量为256字节,这里我们把A0-A2均接地,也就是将AT24C02的地址设为0,写程序时要注意这点,连接到STM32的I2C2,对应管脚PB10,PB11,电路如图所示
在这里插入图片描述

代码

main.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include "MyIncludes.h"
u16 sys_cnt = 0;
void systick_isr(void)
{
    if(sys_cnt < 1000)
    {
        sys_cnt++;
    }
    else
    {
        sys_cnt = 0;
        HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_4|GPIO_PIN_5);
    }
}

u8 Wr_Buf[100];
//写缓冲器
u8 Rd_Buf[100];
//读缓冲器
int main()
{
     u16 i;
     System_Init();
     LED_Init();
     SysTick_Init(systick_isr);
     USART1_Init(115200,NULL,NULL);
     printf("EEPROM ReadWrite Test\n");
     while(EEPROM_Init()!=EEPROM_OK)
     {
         printf("EEPROM Init ERROM\n");
     }
     //当EEPROM初始化未完成
     //输出EEPROM Init ERROM
     for(i=0; i<100; i++)
     {
         Wr_Buf[i] = i+1;
     }
     
     EEPROM_Write(Wr_Buf,0,100);
     EEPROM_Read(Rd_Buf,0,100);
     
     if(strncmp((char *)Wr_Buf,(char *)Rd_Buf,100) == 0)
     {
         printf("EEPROM TEST OK\n");
     }
     else
     {
         printf("EEPROM TEST ERROR");
     }
     
     while(1)
     {
         
     }
}

24cxx.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#ifndef __24CXX_H_
#define __24CXX_H_

#include "stm32f1xx.h"
#include "stm32_types.h"
#include "stm32f1xx_hal.h"

#define EEPROM_MAX_TRIALS  ((uint32_t)3000)
//函数的最大试用次数(貌似未用到)
#define EEPROM_OK ((uint8_t)0)
//状态:成功
#define EEPROM_FAIL   ((uint8_t)1)
#define EEPROM_TIMEOUT  ((uint8_t)2)

#define EEPROM_I2C_ADDRESS ((uint16_t)0xA0)
//EEPROM地址 0xA0
#define AT24C01  127
#define AT24C02  255
#define AT24C04  511
#define AT24C08  1023
#define AT24C16  2047
#define AT24C32  4095
#define AT24C64  8191
#define AT24C128 16383
#define AT24C256 32767

uint8_t EEPROM_Init(void);
//EEPROM初始化

uint32_t EEPROM_Read(uint8_t* pBuffer,uint16_t ReadAddr,uint16_t NumToRead);
//指定位置读出指定个数的数据(数据数组首地址,开
//始读出的地址,要读出数据的个数);

uint32_t EEPROM_Write(uint8_t* pBuffer,uint16_t WriteAddr,uint16_t NumToWrite);
//在指定位置写入指定个数的数据,(数据数组首地址
//开始写入地址,要写入数据个数)

void EEPROM_WriteLenByte(u16 WriteAddr,u32 DataToWrite,u8 Len);
//在指定地址写入长度为Len的数据(开始写入的地址
//,数据数组首地址,要写入数据的长度)

uint32_t EEPROM_ReadLenByte(u16 ReadAddr,u8 Len);
//在指定地址读出长度为Len的数据(开始读出的地址
//要读的长度)

#endif

24cxx.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
#include "24cxx.h"
#include "iic.h"

I2C_HandleTypeDef hI2cEEpromHandler;

void EEPROM_IO_Init(void)
//EEPROM的I2c接口初始化
{
  I2C2_Init(&hI2cEEpromHandler);
}



HAL_StatusTypeDef EEPROM_IO_WriteData(uint16_t DevAddress,uint16_t MemAddress,uint8_t* pBuffer,uint32_t BufferSize)
//向EEPROM写入数据(器件地址,器件寄存器地址,代写入数据,写入长度)
{
      return (I2C2_WriteMultiple(&hI2cEEpromHandler,DevAddress,MemAddress,I2C_MEMADD_SIZE_8BIT,pBuffer,BufferSize));
      //I2C写操作(I2chandler,器件地址,器件寄存器地址
      //一次发送数据大小,待写入的数据,写入长度)
}



HAL_StatusTypeDef EEPROM_IO_ReadData(uint16_t DevAddress,uint16_t MemAddress,uint8_t* pBuffer,uint32_t BufferSize)
//从EEPROM读取数据(器件地址,器件寄存器地址,待
//读取数据,待读取长度)
{
    return (I2C2_ReadMultiple(&hI2cEEpromHandler,DevAddress,MemAddress,I2C_MEMADD_SIZE_8BIT,pBuffer,BufferSize));
    //I2C读操作(i2c handler,器件地址,器件寄存
    //器地址,一次发送数据大小8位,待写入数据,长度)
}


HAL_StatusTypeDef EEPROM_IO_IsDeviceReady(uint16_t DevAddress,uint32_t Trials)
//检查EEPROM是否准备就绪通信(器件地址,检测最大次数)
{
      return(I2C2_IsDeviceReady(&hI2cEEpromHandler,DevAddress,Trials));
      //判断器件是否就绪(I2C Hanler,器件地址
      //检测最大次数)
}

__IO uint16_t EEPROMAddress = 0;
//EEPROMAddress 地址声明
uint8_t EEPROM_Init(void)
{
    EEPROM_IO_Init();
    //初始化I2C总线
    EEPROMAddress = EEPROM_I2C_ADDRESS;
    //EEPROMAddress = 0xA0
    if(EEPROM_IO_IsDeviceReady(EEPROMAddress,EEPROM_MAX_TRIALS) != HAL_OK)
    //检查EEPROM是否准备就绪通信
    {
        return EEPROM_FAIL;
        //返回失败
    }
   
    return EEPROM_OK;
    //返回成功
}




uint32_t EEPROM_WaitEepromStandbyState(void)
//等待EEPROM就绪
{
   if(EEPROM_IO_IsDeviceReady(EEPROMAddress,EEPROM_MAX_TRIALS) != HAL_OK)
   //检查EEPROM是否准备就绪(器件地址,检测最大次数)
     {
      return EEPROM_TIMEOUT;
     }
   return EEPROM_OK;
}



//实验未用到
uint8_t EEPROM_ReadOneByte(uint16_t ReadAddr)
//在AT24C02中读出指定个个数数据
{
   uint8_t ReadVal = 0;
   
   if(EEPROM_IO_ReadData(EEPROMAddress,ReadAddr,&ReadVal,1) != HAL_OK)
   //从EEPROM中读取数据(器件地址,开始读取地址,待写入的数据,写入长度)
   {
        ReadVal = 0xFF;
   }

   return ReadVal;

}




uint32_t EEPROM_Read(uint8_t* pBuffer,uint16_t ReadAddr,uint16_t NumToRead)
//EEPROM读数据  缓冲区首地址,要读的地址,要读的个数
{
    uint32_t buffersize = NumToRead;
   
    if(EEPROM_IO_ReadData(EEPROMAddress,ReadAddr,pBuffer,buffersize) != HAL_OK)
    //从EEPROM读取数据(器件地址,器件寄存器地
    //址,待读取数据,待读入长度)
        {
          return EEPROM_FAIL;
       
        }

   return EEPROM_OK;
}




uint32_t EEPROM_WriteOneByte(uint16_t WriteAddr,uint8_t DataToWrite)
//向EEPROM写入一字节数据
//(写入的地址,待写入的数据)
{
   uint32_t status = EEPROM_OK;
   
   if(EEPROM_IO_WriteData(EEPROMAddress,WriteAddr,&DataToWrite,1) != HAL_OK)
   //向EEPROM写入数据(器件地址,器件寄存器地址,
   //代写入数据,写入长度)
     {
      status = EEPROM_FAIL;
     }
   
   if(EEPROM_WaitEepromStandbyState() != EEPROM_OK)
   //等待EEPROM就绪
   {
      return EEPROM_FAIL;
     }

    return status;
 }




uint32_t EEPROM_Write(uint8_t *pBuffer,uint16_t WriteAddr,uint16_t NumToWrite)
//在EEPROM里面的指定地址开始写入指定个数的数据
//(数据数组首地址,开始写入的地址,要写入数据的个数)
{
    uint32_t status = EEPROM_OK;
   
    while(NumToWrite--)
    {
        status = EEPROM_WriteOneByte(WriteAddr,*pBuffer);
        //向EEPROM写入一字节数据
      if(status != EEPROM_OK)
      {
         return status;
      }
      WriteAddr++;
      //要写入地址自加
      pBuffer++;
      //缓冲地址自加
    }
    return EEPROM_OK;
}


//实验未用到
void EEPROM_WriteLenByte(u16 WriteAddr,u32 DataToWrite,u8 Len)
//向EEPROM里面的指定地址开始写入长度为Len的数据
{
     u8 t;
    for(t=0;t<Len;t++)
    {
        EEPROM_WriteOneByte(WriteAddr+t,(DataToWrite>>(8*t))&0xff);
        //每次写入一个(开始地址,待写入的数据)
    }
}


uint32_t EEPROM_ReadLenByte(u16 ReadAddr, u8 Len)
//读取指定长度为Len的数据。
{
    u8 t;
    u32 temp=0;
    for(t=0;t<Len;t++)
    {
        temp <<= 8;
        temp += EEPROM_ReadOneByte(ReadAddr+Len-t-1);
        //读取指定个数数据(地址)
    }
    return temp;
}

实验现象