EEPROM存储实验

描述

16.1 EEPROM概述

EEPROM(Electrically Erasable Programmable read only memory),称为带电可擦除可编程只读存储器,是一种可以断电保存数据的存储芯片,EEPROM可以在电脑上或专用设备上擦除已有信息,重新编程,一般用在即插即用设备中,这种存储芯片可以通过高于普通电压的作用来擦除或重写,EEPROM芯片一般用在需要频繁存储数据,但是数据量不大的场合,本实验以Atmel公司设计的AT24C02为例,来详细描述EEPROM的基本操作。

AT24C02是一片存储容量在2Kbit的的存储芯片,即存储容量512Byte,通过IIC总线协议进行数据通信,STM32F1内置的IIC模块,但是由于当时设计的时候为了规避飞利浦关于IIC通信协议的专利技术,将IIC设计的比较复杂,并且当操作不当的时候容易锁住总线,但是ST公司关于硬件IIC方面也提出了对应的软件解决方案,我们在这个存储实验中采用IO口模拟IIC协议与硬件IIC模块两种方式来实现EEPROM存储。

IIC是一种只利用两根线来进行数据交换的串行通信协议,IIC的电气线路包括两根线,即时钟线SCL和数据线SDA,高速IIC总线一般可达400kbps以上,在传送过程中一共有三种类型的信号,分别是开始信号,结束信号和应答信号,我们在51单片机开发中曾将IIC协议通过端口模拟成功的控制了EEPROM的读写,现在只需要将之前的代码移植过来修改一下底层寄存器即可使用。STM32F1系列的硬件IIC结构框图如下图所示。

EEPROM

从结构可以发现,STM32的硬件IIC模块我们只需要配置好寄存器,然后既可以不考虑具体的IIC协议,直接读数据寄存器就可以获取到总线上的数据,这也是硬件IIC的优势所在。

16.2 AT24C02通信时序

16.2.1 写时序

(1)写1个字节

EEPROM

第1步:发送开始信号

第2步:发送器件7位地址+1位读写控制后等待芯片应答

第3步:发送写入的地址后等待芯片应答

第4步:写入需要存储的数据后等待芯片应答

第5步:发送结束信号

第6步:等待20ms左右

(2)写n个字节

EEPROM

写n个字节适用于在连续的n个地址上写入n个数据,当需要写入n个数据的时候,这种连续写的方式比单个写的速度有显著性优势,具体步骤如下。

第1步:发送开始信号

第2步:发送器件7位地址+1位读写控制后等待芯片应答

第3步:发送写入的地址后等待芯片应答

第4步:写入需要存储的数据1后等待芯片应答

……

第n+4步:写入需要存储的数据n后等待芯片应答

第n+5步:发送结束信号

第n+6步:等待20ms左右

注:AT24C系列芯片进行1次完整的写时序,必须等待5ms以上,手册给出的典型值是5ms,一般默认20ms。

16.2.2 读时序

(1)读1个字节

EEPROM

第1步:发送开始信号

第2步:发送器件7位地址+1位读写控制后等待芯片应答

第3步:发送写入的地址后等待芯片应答

第4步:重新发送开始信号

第5步:发送器件7位地址+1位读写控制(读)后等待芯片应答

第6步:开始接收返回的的数据

第7步:发送结束信号

(2)读n个字节

EEPROM

写n个字节适用于读取存储在连续的n个地址上写入n个数据,当需要写入n个数据的时候,这种连续写的方式比单个写的速度有显著性优势,具体步骤如下。

第1步:发送开始信号

第2步:发送器件7位地址+1位读写控制后等待芯片应答

第3步:发送写入的地址后等待芯片应答

第4步:重新发送开始信号

第5步:发送器件7位地址+1位读写控制(读)后等待芯片应答

第6步:接收返回的的数据1后发送应答信号

第7步:接收返回的的数据2后发送应答信号

……

第n+6步:接收返回的的数据n

第n+7步:发送结束信号

16.3 STM32内部IIC协议相关寄存器

16.3.1 控制寄存器1:I2Cx_CR1

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
SWRST - ALERT PEC POS ACK STOP START NOSTRETCH ENGC ENPEC ENAPR SMBTYPE - SMBUS PE

Bit 15:软件复位

0:I2C模块不处于复位

1:I2C模块处于复位

Bit 13:SMBus提醒:软件可以设置或清除该位,当PE=0时,由硬件清除

0:释放SMBAlert引脚使其变高,提醒响应地址头紧跟在NACK信号后面

1:驱动SMBAlert引脚使其变低,提醒响应地址头紧跟在ACK信号后面

Bit 12:数据包出错检测

0:无PEC传输

1:PEC传输

Bit 11:应答/PEC位置

0:ACK位控制当前移位寄存器内正在接收的字节的ACK。PEC位表明当前移位寄存器内的字节是PEC

1:ACK位控制在移位寄存器里接收的下一个字节的ACK。PEC位表明在移位寄存器里接收的下一个字节是PEC

注1:POS位只能用在2字节的接收配置中,必须在接收数据之前配置

注2:为了NACK第2个字节,必须在清除ADDR为之后清除ACK位

注3:为了检测第2个字节的PEC,必须在配置了POS位之后,拉伸ADDR事件时设置PEC位

Bit 10:应答使能

0:无应答返回

1:在接收到一个字节后返回一个应答

Bit 9:停止条件产生

在主模式下:

0:无停止条件产生

1:在当前字节传输或在当前起始条件发出后产生停止条件

在从模式下:

0:无停止条件产生

1:在当前字节传输或释放SCL和SDA线

Bit 8:起始条件产生

在主模式下:

0:无起始条件产生

1:重复产生起始条件

在从模式下:

0:无起始条件产生

1:当总线空闲时,产生起始条件

Bit 7:禁止时钟延长

0:允许时钟延长

1:禁止时钟延长

Bit 6:广播呼叫使能

0:禁止广播呼叫,以非应答响应地址00h

1:允许广播呼叫,以应答响应地址00h

Bit 5:PEC使能

0:禁止PEC计算

1:开启PEC计算

Bit 4:ARP使能

0:禁止ARP

1:使能ARP

注1:如果SMBTYPE=0,使用SMBus设备的默认地址

注2:如果SMBTYPE=1,使用SMBus的主地址

Bit 3:SMBus类型

0:SMBus设备

1:SMBus主机

Bit 1:SMBus模式

0:I2C模式

1:SMBus模式

Bit 0:I2C模块使能

0:禁用I2C模块

1:启用I2C模块,根据SMBus位的设置,相应的I/O口需配置为复用功能

注:在主模式下,通讯结束之前,绝不能清除该位

16.3.2 控制寄存器2:I2Cx_CR2

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
- LAST DMAEN ITBUFEN ITEVTEN ITERREN - FREQ[5:0]

Bit 12:DMA最后一次传输

0:下一次DMA的EOT不是最后的传输

1:下一次DMA的EOT是最后的传输

Bit 11:DMA请求使能

0:禁止DMA请求

1:当TxE=1或RxNE=1时,允许DMA请求

Bit 10:缓冲器中断使能

0:当TxE=1或RxNE=1时,不产生任何中断

1:当TxE=1或RxNE=1时,产生事件中断

Bit 9:事件中断使能

0:禁止事件中断

1:允许事件中断

在下列条件下,将产生该中断:

SB=1(主模式)

ADDR=1(主/从模式)

ADD10=1(主模式)

STOPF=1(从模式)

BTF=1,但是没有TxE或RxNE事件

如果ITBUFEN=1,TxE事件为1

如果ITBUFEN=1,RxNE事件为1

Bit 8:出错中断使能

0:禁止出错中断

1:允许出错中断

在下列条件下,将产生该中断:

BERR=1

ARLO=1

AF=1

OVR=1

PECERR=1

TIMEOUT=1

SMBAlert=1

Bit 5~Bit 0:I2C模块时钟频率,允许的范围在2~36MHz之间

000000:禁用

000001:禁用

000010:2MHz

...

100100:36MHz

大于100100:禁用

16.3.3 上升时间寄存器:I2Cx_TRISE

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
- TRISE[5:0]

Bit 5~Bit 0:在快速/标准模式下的SCL最大上升时间(主模式)

例如:标准模式中最大允许SCL上升时间为1000ns。如果在I2C_CR2寄存器中FREQ中的值等于0x08且TPCLK1=125ns,故TRISE中必须写入09h(1000ns/125ns=8+1)

注:只有当PE=0时,才能设置TRISE

16.3.4 时钟控制寄存器:I2Cx_CCR

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
F/S DUTY - CCR[11:0]

Bit 15:I2C主模式选项

0:标准模式的I2C

1:快速模式的I2C

Bit 14:快速模式时的占空比

0:快速模式下T low /T high =2

1:快速模式下T low /T high =16/9

Bit 11~Bit 0:快速/标准模式下的时钟控制分频系数(主模式)

在I2C标准模式或SMBus模式下:

T high =CCR×TPCLK1

T low =CCR×TPCLK1

在I2C快速模式下:

如果DUTY=0:

T high =CCR×TPCLK1

T low =2×CCR×TPCLK1

如果DUTY=1:

T high =9×CCR×TPCLK1

T low =16×CCR×TPCLK1

例如:在标准模式下,产生100kHz的SCL的频率,如果FREQR=08,TPCLK1=125ns,则CCR必须写入0x28(40×125ns=5000ns)

注1:允许设定的最小值为0x04,在快速DUTY模式下允许的最小值为0x01

注2:fCK应当是10MHz的整数倍,这样可以正确产生400kHz的快速时钟

16.3.5 自身地址寄存器1:I2Cx_OAR1

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
ADDMODE - ADD[9:8] ADD[7:1] ADD0

Bit 15:寻址模式(从模式)

0:7位从地址(不响应10位地址)

1:10位从地址(不响应7位地址)

Bit 9~Bit 8:接口地址

7位地址模式时不用关心

10位地址模式时为地址的9~8位

Bit 7Bit 1:接口地址,地址的71位

Bit 0:接口地址

7位地址模式时不用关心

10位地址模式时为地址第0位

16.3.6 自身地址寄存器2:I2Cx_OAR2

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
- ADD2[7:1] ENDUAL

Bit 7Bit 1:接口地址,在双地址模式下地址的71位

Bit 0:双地址模式使能位

0:在7位地址模式下,只有OAR1被识别

1:在7位地址模式下,OAR1和OAR2都被识别

16.3.7 状态寄存器1:I2Cx_SR1

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
SMBALERT TIMEOUT - PECERR OVR AF ARL0 BERR TXE RXNE - STOPF ADD10 BTF ADDR SB

Bit 15: SMBus提醒

在SMBus主机模式下:

0:无SMBus提醒

1:在引脚上产生SMBAlert提醒事件

在SMBus从机模式下:

0:没有SMBAlert响应地址头序列

1:收到SMBAlert响应地址头序列至SMBAlert变低

Bit 14:超时或Tlow错误

0:无超时错误

1:SCL低电平达到25ms;或主机低电平累积时间超过10ms;或从设备低电平累积时间超过25ms

Bit 12:在接收时发生PEC错误

0:无PEC错误:接收到PEC后接收器返回ACK(如果ACK=1)

1:有PEC错误:接收到PEC后接收器返回NACK(不管ACK是什么值)

Bit 11:过载/欠载

0:无过载/欠载

1:出现过载/欠载

Bit 10:应答失败

0:没有应答失败

1:应答失败

Bit 9:仲裁丢失(主模式)

0:没有检测到仲裁丢失

1:检测到仲裁丢失

Bit 8:总线出错

0:无起始或停止条件出错

1:起始或停止条件出错

Bit 7:数据寄存器为空(发送时)

0:数据寄存器非空

1:数据寄存器空

Bit 6:数据寄存器非空(接收时)

0:数据寄存器为空

1:数据寄存器非空

Bit 4:停止条件检测位(从模式)

0:没有检测到停止条件

1:检测到停止条件

Bit 3:10位头序列已发送(主模式)

0:没有ADD10事件发生

1:主设备已经将第一个地址字节发送出去

Bit 2:字节发送结束

0:字节发送未完成

1:字节发送结束

Bit 1:地址已被发送(主模式)/地址匹配(从模式)

地址匹配(从模式)

0:地址不匹配或没有收到地址

1:收到的地址匹配Bit 1:

地址发送标志(主模式)

0:地址发送没有结束

1:地址发送结束

10位地址模式时,当收到地址的第二个字节的ACK后该位被置1

7位地址模式时,当收到地址的ACK后该位被置1

Bit 0:起始位(主模式)

0:未发送起始条件

1:起始条件已发送

16.3.8 状态寄存器2:I2Cx_SR2

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
PEC[7:0] DUALF SMBHOST SMBDEFAULT GENCALL - TRA BUSY MSL

Bit 15~Bit 8:数据包出错检测,当ENPEC=1时,PEC[7:0]存放内部的PEC的值

Bit 7:双标志(从模式)

0:接收到的地址与OAR1内的内容相匹配

1:接收到的地址与OAR2内的内容相匹配

Bit 6:SMBus主机头系列(从模式)

0:未收到SMBus主机的地址

1:当SMBTYPE=1且ENARP=1时,收到SMBus主机地址

Bit 5:SMBus设备默认地址(从模式)

0:未收到SMBus设备的默认地址

1:当ENARP=1时,收到SMBus设备的默认地址

Bit 4:广播呼叫地址(从模式)

0:未收到广播呼叫地址

1:当ENGC=1时,收到广播呼叫的地址

Bit 2:发送/接收

0:接收到数据

1:数据已发送

Bit 1:总线忙,在检测到SDA或SCl为低电平时,硬件将该位1

0:在总线上无数据通讯

1:在总线上正在进行数据通讯

Bit 0:主从模式

0:从模式

1:主模式

16.3.9 数据寄存器:I2Cx_DR

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
- DR[7:0]

Bit 7~Bit 0:8位数据寄存器,用于存放接收到的数据或放置用于发送到总线的数据

发送器模式:当写一个字节至DR寄存器时,自动启动数据传输。一旦传输开始,如果能及时把下一个需传输的数据写入DR寄存器,I2C模块将保持连续的数据流

接收器模式:接收到的字节被拷贝到DR寄存器。在接收到下一个字节之前读出数据寄存器,即可实现连续的数据传送

注1:在从模式下,地址不会被拷贝进数据寄存器DR

注2:硬件不管理写冲突(如果TxE=0,仍能写入数据寄存器)

注3:如果在处理ACK脉冲时发生ARLO事件,接收到的字节不会被拷贝到数据寄存器里,因此不能读到它

16.4 实验例程

16.4.1 软件模拟IIC控制

(1)创建at24cxx.h文件,并输入以下代码。

/*********************************************************************************************************
                EEPROM    驱    动    文    件
*********************************************************************************************************/
#ifndef _AT24Cxx_H_
#define _AT24Cxx_H_


#include "sys.h"
/*********************************************************************************************************
                硬    件    端    口    定    义
*********************************************************************************************************/
#define IIC_SCL      PBout( 6 )
#define IIC_SDA      PBout( 7 )
#define IIC_SDA_READ  PBin( 7 )
/*********************************************************************************************************
                    函    数    列    表
*********************************************************************************************************/
void AT24Cxx_Init( void ) ;                                        //AT24C初始化
void AT24Cxx_Write_Data( u16 Address, u8 Data ) ;                            //写入1个数据
void AT24Cxx_Write_nData( u16 Address, u8 *Buffer, u16 Len ) ;                      //写入n个数据
void AT24Cxx_Read_Data( u16 Address, u8 *Data ) ;                            //读取1个数据
void AT24Cxx_Read_nData( u16 Address, u8 *Buffer, u16 Len ) ;                      //读取n个数据


#endif

(2)创建at24cxx.c文件,并输入以下代码。

/*********************************************************************************************************
                EEPROM    驱    动    程    序
*********************************************************************************************************/
#include "at24cxx.h"
#include "delay.h"
/***************************************************
Name    :IIC_Start
Function  :IIC起始信号
Paramater  :None
Return    :None
***************************************************/
void IIC_Start()
{
  GPIOB->CRL &= 0x0FFFFFFF ;                                      //PB7推挽输出
  GPIOB->CRL |= 0x30000000 ;
  IIC_SDA = 1 ;
  IIC_SCL = 1 ;
  delay_us( 4 ) ;
  IIC_SDA = 0 ;
  delay_us( 4 ) ;
  IIC_SCL = 0 ;
}
/***************************************************
Name    :IIC_Stop
Function  :IIC停止信号
Paramater  :None
Return    :None
***************************************************/
void IIC_Stop()
{
  GPIOB->CRL &= 0x0FFFFFFF ;                                      //PB7推挽输出
  GPIOB->CRL |= 0x30000000 ;
  IIC_SCL = 0 ;
  IIC_SDA = 0 ;
   delay_us( 4 ) ;
  IIC_SCL = 1 ;
  IIC_SDA = 1 ;
  delay_us( 4 ) ;
}
/***************************************************
Name    :IIC_Wait_Ack
Function  :IIC等待应答
Paramater  :None
Return    :
      0:成功
      1:失败
***************************************************/
void IIC_Wait_Ack()
{
  u8 Time = 0 ;
  GPIOB->CRL &= 0x0FFFFFFF ;
  GPIOB->CRL |= 0x80000000 ;
  IIC_SDA = 1 ;
  delay_us( 1 ) ;
  IIC_SCL = 1 ;
  delay_us( 1 ) ;
  while( IIC_SDA_READ )
  {
    Time ++ ;
    if( Time>250 )
    {
      IIC_Stop() ;
      break ;
    }
  }
  IIC_SCL = 0 ;
}
/***************************************************
Name    :IIC_Send_Byte
Function  :IIC发送一个字节
Paramater  :
      ack:应答使能
        0:不应答
        1:应答
Return    :None
***************************************************/
void IIC_Send_Byte( u8 Byte )
{
  u8 i;
  GPIOB->CRL &= 0x0FFFFFFF ;                                      //PB7推挽输出
  GPIOB->CRL |= 0x30000000 ;
  IIC_SCL = 0 ;
  for( i=0; i<8; i++ )
  {
    if( ( Byte&0x80 )==0x80 )
      IIC_SDA = 1 ;
    else
      IIC_SDA = 0 ;
    Byte <<= 1 ;
    delay_us( 2 ) ;
    IIC_SCL = 1 ;
    delay_us( 2 ) ;
    IIC_SCL = 0 ;
    delay_us( 2 ) ;
  }
}
/***************************************************
Name    :IIC_Read_Byte
Function  :IIC读取一个字节
Paramater  :
      ack:应答使能
        0:不应答
        1:应答
Return    :None
***************************************************/
u8 IIC_Read_Byte( u8 Ack )
{
  u8 i,Byte=0;
  GPIOB->CRL &= 0x0FFFFFFF ;
  GPIOB->CRL |= 0x80000000 ;
  for( i=0; i<8; i++ )
  {
    IIC_SCL = 0 ;
    delay_us( 2 ) ;
    IIC_SCL = 1 ;
    Byte <<= 1 ;
    if( IIC_SDA_READ )
      Byte |= 0x01 ;
    delay_us( 1 ) ;
  }
  IIC_SCL = 0 ;
  GPIOB->CRL &= 0x0FFFFFFF ;                                      //PB7推挽输出
  GPIOB->CRL |= 0x30000000 ;
  IIC_SDA = 1 - Ack ;
  delay_us( 2 ) ;
  IIC_SCL = 1 ;
  delay_us( 2 ) ;
  IIC_SCL = 0 ;
  return Byte ;
}
/***************************************************
Name    :AT24Cxx_Write_Data
Function  :写入1个数据
Paramater  :
      Address:地址
      Data:数据
Return    :读到的数据
***************************************************/
void AT24Cxx_Write_Data( u16 Address, u8 Data )
{                                                      
  IIC_Start() ;
  IIC_Send_Byte( 0xA0|( Address/256 )<<1 ) ;                              //发送器件地址,写数据
  IIC_Wait_Ack() ;
  IIC_Send_Byte( Address%256 ) ;                                    //发送低地址
  IIC_Wait_Ack() ;
  IIC_Send_Byte( Data ) ;                                        //发送字节
  IIC_Wait_Ack() ;
  IIC_Stop() ;                                            //产生一个停止条件
  delay_ms( 10 ) ;                                          //EEPROM的写入速度比较慢
}
/***************************************************
Name    :AT24Cxx_Write_nData
Function  :写入n个数据
Paramater  :
      Address:地址
      *Buffer:数据缓存
      Len:数据长度
Return    :None
***************************************************/
void AT24Cxx_Write_nData( u16 Address, u8 *Buffer, u16 Len ) 
{
  u16 i ;
  for( i=0; iAPB2ENR |= 1<<3 ;                                        //先使能外设GPIOB时钟
  GPIOB->CRL &= 0x00FFFFFF ;                                      //PB6和PB7推挽输出
  GPIOB->CRL |= 0x33000000 ;
  GPIOB->ODR |= 3<<6 ;                                        //PB6和PB7输出高
  while( AT24Cxx_Check()==0 ) ;
}

(3)创建1.c文件并输入以下代码。

#include "sys.h"
#include "delay.h"
#include "usart1.h"
#include "lcd.h"
#include "at24cxx.h"


u8 TEXT_Buffer[] = "STM32F103 IIC Test" ;
int main()
{
  u8 datatemp[ 17 ] ;
  STM32_Clock_Init( 9 ) ;                                        //STM32时钟初始化
  SysTick_Init( 72 ) ;                                        //SysTick初始化
  USART1_Init( 72, 115200 ) ;                                      //初始化串口1波特率115200
  LCD_Init() ;                                            //LCD初始化
  AT24Cxx_Init() ;                                          //AT24C初始化
   POINT_COLOR = RED ;                                          //设置字体为红色
  AT24Cxx_Write_nData( 0, TEXT_Buffer, 18 ) ;                              //从第0个地址处开始写入
  AT24Cxx_Read_nData( 0, datatemp, 18 ) ;                                //从第0个地址处开始读出
  LCD_ShowString( 0, 0, datatemp ) ;                                  //显示读到的字符串
  while( 1 )
  {

  }
}

16.4.2 硬件IIC控制

注:由于STM32的硬件IIC总是容易卡死(这也是为什么网络上几乎没有硬件IIC通讯的例子的原因),所以这里采用了ST内部提供的通讯机制来保证IIC的正常使用。

(1)创建at24cxx.h文件并输入以下代码。

/*********************************************************************************************************
                EEPROM    驱    动    文    件
*********************************************************************************************************/
#ifndef _AT24Cxx_H_
#define _AT24Cxx_H_


#include "sys.h"
/*********************************************************************************************************
                    函    数    列    表
*********************************************************************************************************/
void AT24Cxx_Init( void ) ;                                        //AT24C初始化
void IIC_Write_Data( u8 Address, u8 Data ) ;                              //写入1个数据
void AT24Cxx_Write_nData( u16 Address, u8 *Buffer, u16 Len ) ;                      //写入n个数据
void AT24Cxx_Read_Data( u16 Address, u8 *Data ) ;                            //读取1个数据
void AT24Cxx_Read_nData( u16 Address, u8 *Buffer, u16 Len ) ;                      //读取n个数据


#endif

(2)创建at24cxx.c文件并输入以下代码。

/*********************************************************************************************************
                EEPROM    驱    动    程    序
*********************************************************************************************************/
#include "at24cxx.h"
#include "delay.h"
/***************************************************
Name    :IIC_Write_Data
Function  :写入1个数据
Paramater  :
      Address:地址
      Byte:读取的字节
Return    :None
***************************************************/
void IIC_Write_Data( u8 Address, u8 Byte )
{
  u16 Time, tmpreg ;
  tmpreg = tmpreg ;
  //等待BUSY标志置0
  Time = 0 ;
  while( ( ( I2C1->SR2&0x02 )==0x02 )&&( Time<65535 ) )
    Time ++ ;
  I2C1->CR1 &= ~( 1<<11 ) ;                                      //禁用Pos
  I2C1->CR1 |= 1<<8 ;                                          //开始信号
  //等待SB标志置1
  Time = 0 ;
  while( ( ( I2C1->SR1&0x01 )==0 )&&( Time<65535 ) )
    Time ++ ;
  I2C1->DR = 0xA0 ;                                          //发送从机地址
  //等待地址发送结束
  Time = 0 ;
  while( ( ( I2C1->SR1&0x02 )==0 )&&( Time<65535 ) )
  {
    Time ++ ;
    if( ( I2C1->SR1&0x400 )==0x400 )
    {
      I2C1->SR1 &= ~( 1<<10 ) ;                                  //清除AF标志
      I2C1->CR1 |= 1<<9 ;                                      //结束信号
      break ;
    }
  }
  tmpreg = I2C1->SR1 ;                                        //清除地址标志
  tmpreg = I2C1->SR2 ;
  //等待TXE标志置0
  Time = 0 ;
  while( ( ( I2C1->SR2&0x04)==0 )&&( Time<65535 ) )
  {
    Time ++ ;
    //检查是否检测到NACK
    if( ( I2C1->SR1&0x400 )==0x400 )
    {
      I2C1->SR1 &= ~( 1<<10 ) ;
      I2C1->CR1 |= 1<<9 ;                                      //结束信号
      break ;
    }
  }
  I2C1->DR = Address ;                                        //发送寄存器地址
  //等待TXE标志置1
  Time = 0 ;
  while( ( ( I2C1->SR1&0x80 )==0 )&&( Time<65535 ) )
  {
    Time ++ ;
    //检查是否检测到NACK
    if( ( I2C1->SR1&0x400 )==0x400 )
    {
      I2C1->SR1 &= ~( 1<<18 ) ;                                  //清除NACKF标志
      I2C1->CR1 |= 1<<9 ;                                      //结束信号
      break ;
    }
  }
  I2C1->DR = Byte ;                                          //发送数据
  //等待BTF标志被置1
  Time = 0 ;
  while( ( ( I2C1->SR1&0x04 )==0 )&&( Time<65535 ) )
  {
    Time ++ ;
    //检查是否检测到NACK
    if( ( I2C1->SR1&0x400 )==0x400 )
    {
      I2C1->SR1 &= ~( 1<<10 ) ;                                  //清除NACKF标志
      I2C1->CR1 |= 1<<9 ;                                      //结束信号
      break ;
    }
  }
  I2C1->CR1 |= 1<<9 ;                                          //结束信号
  delay_ms( 10 ) ;
}
/***************************************************
Name    :IIC_Read_Data
Function  :读取1个数据
Paramater  :
      Address:地址
Return    :读取的数据
***************************************************/
void IIC_Read_Data( u8 Address, u8 *Data )
{
  u16 tmpreg, Time;
  tmpreg = tmpreg ;
    //等待BUSY标志置0
  Time = 0 ;
  while( ( ( I2C1->SR1&0x02 )==0x02 )&&( Time<65535 ) )
    Time ++ ;
  I2C1->CR1 &= ~( 1<<11 ) ;                                      //禁用Pos
    //发送从机地址
  I2C1->CR1 |= 1<<8 ;                                          //开始信号
  Time = 0 ;
  //等待SB标志置1
  while( ( ( I2C1->SR1&0x01 )==0 )&&( Time<65535 ) )
    Time ++ ;
  I2C1->DR = 0xA0 ;                                          //发送从机地址
  //等待地址发送结束
  while( ( ( I2C1->SR1&0x02 )==0 )&&( Time<65535 ) )
  {
    if( ( I2C1->SR1&0x400 )==0x400 )
    {
      I2C1->SR1 &= ~( 1<<10 ) ;                                  //清除AF标志
      I2C1->CR1 |= 1<<9 ;                                      //停止信号
      break ;
    }
  }
  tmpreg = I2C1->SR1;                                          //清除ADDR标志
  tmpreg = I2C1->SR2;
  //等待TXE标志置1
  while( ( ( I2C1->SR1&0x80 )==0 )&&( Time<65535 ) )
  {
    //检查是否检测到NACK
    if( ( I2C1->SR1&0x400)==0x400 )
    {
      I2C1->SR1 &= ~( 1<<10 ) ;                                  //清除NACKF标志
      I2C1->CR1 |= 1<<9 ;                                      //通用结束
      break ;
    }
  }
  I2C1->DR = Address ;                                        //写入数据
  //等待BTF标志置1
  while( ( ( I2C1->SR1&0x04 )==0 )&&( Time<65535 ) )
  {
    //检查是否检测到NACK
    if( ( I2C1->SR1&0x400)==0x400 )
    {
      I2C1->SR1 &= ~( 1<<10 ) ;                                  //清除NACKF标志
      I2C1->CR1 |= 1<<9 ;                                      //通用结束
      break ;
    }
  }
  I2C1->CR1 |= 1<<9 ;                                          //通用结束
  //等待忙标志退出
  Time = 0 ;
  while( ( ( I2C1->SR1&0x02 )==0x02 )&&( Time<65535 ) )
    Time ++ ;
  I2C1->CR1 &= ~( 1<<11 ) ;                                      //禁用Pos
    //发送从机地址
  I2C1->CR1 |= 1<<10 ;                                        //开启应答信号
  I2C1->CR1 |= 1<<8 ;                                          //开始信号
  //等待SB标志置1
  Time = 0 ;
  while( ( ( I2C1->SR1&0x01 )==0 )&&( Time<65535 ) )
    Time ++ ;
  I2C1->DR = 0xA1 ;                                          //发送从机地址
  //等待地址标志置1
  while( ( ( I2C1->SR1&0x02 )==0 )&&( Time<65535 ) )
  {
    Time ++ ;
    //检查是否检测到STOPF
    if( ( I2C1->SR1&0x10 )==0x10 )
    {
      I2C1->SR1 &= ~( 1<<4 ) ;                                  //清除停止标志
      break ;
    }
  }
  I2C1->CR1 &= ~( 1<<10 ) ;                                      //禁止应答
  tmpreg = I2C1->SR1;                                          //清除ADDR标志
  tmpreg = I2C1->SR2;
  I2C1->CR1 |= 1<<9 ;                                          //通用应答
  //等待直到RXNE标志置1
  Time = 0 ;
  while( ( ( I2C1->SR1&0x40 )==0 )&&( Time<65535 ) )
  {
    Time ++ ;
    //检查是否检测到STOPF
    if( ( I2C1->SR1&0x10 )==0x10 )
    {
      I2C1->SR1 &= ~( 1<<4 ) ;                                  //清除停止标志
      break ;
    }
  }
  *Data = I2C1->DR ;                                          //从DR读取数据
}
/***************************************************
Name    :AT24Cxx_Write_nData
Function  :写入n个数据
Paramater  :
      Address:地址
      *Buffer:数据缓存
      Len:数据长度
Return    :None
***************************************************/
void AT24Cxx_Write_nData( u16 Address, u8 *Buffer, u16 Len ) 
{
  u16 i ;
  for( i=0; i

(3)创建1.c文件并输入以下代码。

#include "sys.h"
#include "delay.h"
#include "usart1.h"
#include "at24cxx.h"


u8 TEXT_Buffer[] = "STM32F103 IIC Test" ;
int main()
{
  u8 datatemp[ 17 ] ;
  STM32_Clock_Init( 9 ) ;                                        //STM32时钟初始化
  SysTick_Init( 72 ) ;                                        //SysTick初始化
  USART1_Init( 72, 115200 ) ;                                      //初始化串口1波特率115200
  AT24Cxx_Init() ;                                          //AT24C初始化
  AT24Cxx_Write_nData( 0, TEXT_Buffer, 18 ) ;                              //从第0个地址处开始写入
  AT24Cxx_Read_nData( 0, datatemp, 18 ) ;                                //从第0个地址处开始读出
  while( 1 )
  {

  }
}
打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分