1、介绍
IIC 即Inter-Integrated Circuit(集成电路总线),这种总线类型是由飞利浦半导体公司在八十年代初设计出来的。
IIC是一种芯片与芯片之间进行数据传输的一种协议,芯片和芯片之间的一种“交流语言”。编程者主要熟悉IIC协议的“语法”即可使用IIC进行芯片与芯片之间的交流。
2、IIC器件接线
IIC的总线两条:①SDA(Serial Data):串行数据管脚用于传输数据(二进制数据);②SCL(SCK) :(serial clock)串行时钟管脚。
IIC总线上需要挂载两个上拉电阻:常见的上拉电阻的阻值是4.7K和10K。
IIC通讯协议有两个对象:
①主机:主机负责产生时钟,主机可以发送数据给从机,也可以接收从机的数据;
②从机:从机可以接收来自于主机的数据,也可以发送数据给主机。
IIC的数据传输是在时钟节拍控制下进行位传输:一个时钟脉冲(一个方波)可以传输1位数据。传送8个字节的数据需要8个时钟。
IIC也有一种“名字”机制来区分多个从机,这个机制叫“设备地址”,主机根据IIC的设备地址来区分和那个从机进行数据交互。
3、IIC通讯协议
IIC的通讯协议,也就是IIC数据传输协议
(1)起始信号:标志着IIC设备之间的通讯开始
(2)在8个时钟的作用下传输8位数据,传输数据要求先传输高位(MSB),再传输低位
(3)ACK : 应答,IIC的设备进行数据传输时,需要用一个应答机制。①主机给从机写一个字节的数据后,从机需要给主机一个应答;②从机传输一个字节的数据给主机之后,主机需要给一个应答。情景:老师--学生对话,一问一答。在第九个时钟控制下进行应答传输。
(4)停止信号:标志IIC数据传输过程结束。
3.1、IIC协议基本时序信号
- 起始信号:当SCL为高电平时,SDA由高电平向低电平跳变。情景:两个人碰面:吃了没?
- 设备地址:IIC的设备地址是7位+1位最低位(方向位:0表示写方向,1表示读方向),举例:假如有一个IIC设备,其地址为1110 100B,则该设备“读地址”是1110 1001B;“写地址”是1110 1000B。
- 应答信号:在第9个时钟时,如果SDA为低电平则表示“应答”(ACK);在第9个时钟时,如果SDA为高电平则表示“非应答”(NACK);如果主机给从机写数据时,从机回应是“非应答”,则IIC数据传输无法进行(IIC从机故障,IIC从机正在做其他事情,数据传输结束)。IIC的通讯过程中,每接收到8位数据的一方需要给对方发送一个应答信号。
- 结束信号:当SCL为高电平时,SDA由低电平向高电平跳变。
- 当SDA和SCL都位高电平时,则总线空闲;
- IIC的数据传输是在时钟方波的低电平准备数据,高电平可以读取数据。
①主机读取从机的数据:从机在时钟方波的低电平是准备1位数据,主机在时钟的高电平读取数据。
②主机发送数据给从机:主机在时钟方波的低电平是准备1位数据,从机在时钟的高电平读取数据。
7. IIC主从机的数据交互过程如下:
①主机给从机发送数据:主机发送起始信号--》主机发送一个字节的“写地址”--》从机在第九个时钟时给主机应答--》主机写数据给从机--》可以循环写--》写完之后,主机发送结束信号表示整个通讯过程结束。
②主机读取从机的数据:主机发送起始信号--》主机发送一个字节的“读地址”--》从机在第九个时钟给主机应答信号--》紧接着,从机会返回一个字节的数据给主机--》主机接收到8位的数据后,给从机一个应答--》从机可以循环的将数据发给主机--》主机发送结束信号,表示通讯过程结束。
4、IIC通讯速度
IIC的通讯速度是指IIC的时钟频率,常用的有100kbit/s (100KHZ),400kbit/s (400KHZ),时钟没有不会很严格,可以偏差。
举例:如果IIC的时钟频率为100KHZ,那么一个时钟的周期是 T= 1/F , T = 1/100000 = 10us。
可以通过控制SCL时钟方波的高电平时长为5us,低电平时长为5us,则产生的时钟频率就是100KHZ。
即:
SCL_H;
Delay_us(5);
SCL_L;
Delay_us(5);
5、AT24C02
5.1、简介
AT24C02是一个存储器,掉电后,数据不丢失,存储空间为255个字节。(地址范围:0x00~0xFF)。
AT24C02的“页”:8个字节的空间为1页。
通讯接口是IIC接口
可按字节写,也可以按“页”写
如果“页”写,地址可以自动递增。举例:向AT24C02的内部的地址为0x20的空间写1页(8个字节),这8个字节会存储在0x20~0x27。
5.2、硬件图&设备地址
SCL&SDA: IIC通讯管脚
WP:写保护管脚,WP接GND时,AT24C02可读可写;当WP接VCC时,只可读,不可写。
A0,A1,A2是用于控制IIC的设备地址,具体如下:
AT24C02的地址:1010 000 B ~1010 111 B,IIC总线上最多可以挂载8个AR24C02。
在我用的开发板上上,A0~A2都接GND,所以地址为1010 000 B,“写地址”为1010 0000 B,读地址:1010 0001 B。
5.3、读写时序
- 单字节写
①S : START 主机发送IIC起始信号
②SLAVE ADDRESS : 主机发送从机地址 ,即“写地址”
③ACK : 从机应答
④BYTE ADDRESS : 主机发送字节地址(AT24C02存储空间为255个字节。地址范围:0x00~0xFF)。
⑤ACK : 从机应答
⑥DATA: 主机发送8位的数据
⑦ACK : 从机应答
⑧STOP: 主机IIC结束信号
- 页写
页写操作的启动和字节写一样 不同在于传送了一字节数据后并不产生停止信号,而是继续写7个字节。写到最后一个字节则,发送结束信号
- 单字节读
①S : START 主机发送IIC起始信号
②SLAVE ADDRESS : 主机发送从机地址 ,即“写地址”
③ACK : 从机应答
④BYTE ADDRESS : 主机发送字节地址(AT24C02存储空间为255个字节。地址范围:0x00~0xFF)。
⑤ACK : 从机应答
⑥S : START 主机发送IIC起始信号
⑦SLAVE ADDRESS : 主机发送从机地址 ,即“读地址”
⑧ACK : 从机应答
⑨DATA n : 从机返回一个字节的数据
⑩NO ACK : 主机不给应答
11、STOP :主机发送结束信号
- 连续读
连续读操作可通过单字节读操作来启动,在 AT24C02 发送完一个 8 位字节数据给主机后,主机产生一个应答信号来响应,告知 AT24C02, 主器件要求更多的数据 ,对应每个主机产生的应答信号 , AT24C02 将发送一个 8 位数据字节,当主器件不发送应答信号,而发送停止位时,结束多字节读操作。
AT24C02支持整个芯片的数据全部读取出来,一次性读取255个字节的存储数据。
6、软件设计
寄存器版
#include "stm32f4xx.h"
#include "stdio.h"
#define SCL_H (GPIOB->BSRRL = 1<<8)
#define SCL_L (GPIOB->BSRRH = 1<<8)
#define SDA_H (GPIOB->BSRRL = 1<<9)
#define SDA_L (GPIOB->BSRRH = 1<<9)
#define SDA_IN !!(GPIOB->IDR&(1<<9))
void Delay_us(u16 us)
{
SysTick->CTRL &=~(1<<2); //选择时钟源为21MHZ
SysTick->CTRL &=~(1<<1); //禁止滴答中断
SysTick->LOAD = 21*us; //设置重装载寄存器的值
SysTick->CTRL |= (1<<0); //使能滴答定时器
while(!(SysTick->CTRL&(1<<16)));//阻塞判断定时时间是否到达,判断SysTick->CTRL的位
}
/******************** 串口打印函数 ***************************************/
/* fputc是printf最底层的调用函数 */
int fputc(int data,FILE *file)
{
while( !(USART1->SR & (1 << 6)) );//等待发送完成
USART1->DR = data; //发送数据
return data;
}
void usart1_Init()
{
u16 integer ;
u16 fraction;
float USARTDIV;
u32 baudRate = 115200;
RCC->AHB1ENR |= 1<<0;//使能GPIOA的时钟 RCC_AHB1ENR
GPIOA->MODER &= ~(3<<18);//清零
GPIOA->MODER |= 2<<18;//设置PA9为复用功能模式MODER
GPIOA->MODER &= ~(3<<20);//清零
GPIOA->MODER |= 2<<20;//设置PA10为复用功能模式MODER
GPIOA->AFR[1]&= ~(0XF<<4);//清零
GPIOA->AFR[1]|= (7<<4);//设置PA9的复用功能为第7复用功能 RXD, AFRH
GPIOA->AFR[1]&= ~(0XF<<8);//清零
GPIOA->AFR[1]|= (7<<8);//设置PA10的复用功能为第7复用功能 TXD, AFRH
RCC->APB2ENR |= 1<<4;//使能串口1模块时钟 RCC_APB2ENR
USART1->CR1 |= 1<<15;//设置串口OVER8为1
USART1->CR1 &= ~(1<<12);//设置串口数据位长度 :1 起始位, 8 数据位
USART1->CR2 &=~(3<<12);//设置串口停止位长度 :1 个停止位
USART1->CR1 &= ~(1<<10);//无校验
USART1->CR1 |= 1<<3;//发送使能
USART1->CR1 |= 1<<2;//接收使能
USARTDIV = 84000000/8/baudRate;//串口波特率设置:USARTDIV = fCK/(8*(2-OVER8) /波特率
integer = (u16)USARTDIV;
fraction = ((u16)(USARTDIV-integer))<<4;
USART1->BRR |= integer<<4 | fraction;
USART1->CR1 |= 1<<13;//使能串口
}
void IIC_Init(void)
{
//将GPIOB8配置为推挽输出,GPIOB9配置为开漏输出
RCC->AHB1ENR |= 1<<1;
GPIOB->MODER &=~(3<<16);
GPIOB->MODER |= 1<<16;
GPIOB->OTYPER&=~(1<<8);
GPIOB->MODER &=~(3<<18);
GPIOB->MODER |= 1<<18;
GPIOB->OTYPER|=(1<<9);
SCL_H; //总线空闲
SDA_H; //总线空闲
}
//起始信号
void IIC_Start(void)
{
Delay_us(5);
SCL_H;
SDA_H;
Delay_us(5);
SDA_L;
Delay_us(5);
SCL_L; //SDA在SCL为低电平是准备数据
}
//停止信号
void IIC_Stop(void)
{
SDA_L;
Delay_us(5);
SCL_H;
Delay_us(5);
SDA_H;
Delay_us(5);
}
//发送一个字节
u8 IIC_Write(u8 data)
{
u8 i;
u8 ack;
for(i=0;i<8;i++) //产生8个时钟,将data一位一位的传输,先传输高位
{
if(data&(1<<(7-i)))//判断发送数据的某位的电平
{
SDA_H;//1发高电平
}
else
{
SDA_L;//0发低电平
}
Delay_us(5);
//时钟线拉高再拉低
//主机发送数据给从机:主机在时钟方波的低电平是准备1位数据,从机在时钟的高电平读取数据
SCL_H;//时钟线拉高
Delay_us(5);
SCL_L; //时钟线拉低,准备接受下一位
}
SDA_H;//SDA输出高电平,准备读取数据
Delay_us(5); //产生第九个时钟
SCL_H;
Delay_us(5);
if(SDA_IN) //读取SDA的电平:低电平表示应答,高电平表示非应答
{
ack = 1;
}
else
{
ack = 0;
}
SCL_L;
return ack;//返回应答
}
//读取一个字节
u8 IIC_ReadByte(u8 ack)
{
u8 i;
u8 data = 0;
SDA_H;//SDA输出高电平,切换为输入
for(i=0;i<8;i++)
{
Delay_us(5);//循环产生8个时钟
//主机读取从机的数据:从机在时钟方波的低电平是准备1位数据,主机在时钟的高电平读取数据。
SCL_H;
Delay_us(5);
if(SDA_IN) //循环读取8位数据,最先读到的位存到最高位
{
data = (data<<1 )| 1;
}
else
{
data = data<<1;
}
SCL_L;
}
if(ack == 1) //低电平准备数据
{
SDA_H; //非应答NACK
}
if(ack == 0)
{
SDA_L; //应答ACK
}
Delay_us(5);//产生第九个时钟
SCL_H;
Delay_us(5);
SCL_L;
return data;//返回读到的数据
}
//AT24C02单字节写
void AT24C02_WriteByte(u8 data)
{
IIC_Start(); //主机发送IIC起始信号
IIC_Write(0xA0); //器件地址:主机发送一个字节的器件地址,器件地址的最低位是0(“写地址”)
IIC_Write(0x88); //字地址:主机发送一个字节的“字地址”给AT24C02。取值范围是:0x00~0xFF
IIC_Write(data); //数据:主机发送一个字节的“数据”给AT24C02,这个“数据”会被保存到“字地址”所在的空间
IIC_Stop();//停止条件:主机发送一个“结束信号”。
}
//AT24C02单字节读 byteAddr : 需要读取的字节地址
u8 AT24C02_ReadByte(u8 byteAddr)
{
u8 data;
IIC_Start(); //主机发送IIC起始信号
IIC_Write(0xA0); //主机发送从机地址 ,即“写地址”
IIC_Write(byteAddr); //写字节地址
IIC_Start(); //主机发送IIC起始信号
IIC_Write(0xA1); //主机发送从机地址 ,即“读地址”
data = IIC_ReadByte(1); //读一个字节
IIC_Stop();
return data;
}
int main(void)
{
u8 readData = 0xFF;
u8 writeData = 5;
IIC_Init();
usart1_Init();
AT24C02_WriteByte(writeData);
printf("写入的数据是:%d\r\n",writeData);
Delay_us(5000);
readData = AT24C02_ReadByte(0x88);
printf("读取到的数据是:%d\r\n",readData);
while(1)
{
}
}
其中页写和多字节写在文中未使用
此时将AT24C02_WriteByte(writeData);屏蔽后,重新烧入,发现读取到的还是5,断电不丢失
库函数版
#include "stm32f4xx.h"
#include "stdio.h"
#define SCL_H GPIO_SetBits(GPIOB,GPIO_Pin_8)
#define SCL_L GPIO_ResetBits(GPIOB,GPIO_Pin_8)
#define SDA_H GPIO_SetBits(GPIOB,GPIO_Pin_9)
#define SDA_L GPIO_ResetBits(GPIOB,GPIO_Pin_9)
#define SDA_IN GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_9)
void Delay_us(u16 us)
{
SysTick->CTRL &=~(1<<2); //选择时钟源为21MHZ
SysTick->CTRL &=~(1<<1); //禁止滴答中断
SysTick->LOAD = 21*us; //设置重装载寄存器的值
SysTick->CTRL |= (1<<0); //使能滴答定时器
while(!(SysTick->CTRL&(1<<16)));//阻塞判断定时时间是否到达,判断SysTick->CTRL的位
}
/******************** 串口打印函数 ***************************************/
/* fputc是printf最底层的调用函数 */
int fputc(int data,FILE *file)
{
while( !(USART1->SR & (1 << 6)) );//等待发送完成
USART1->DR = data; //发送数据
return data;
}
void usart1_Init()
{
u32 baudRate = 115200;
GPIO_InitTypeDef GPIO_InitStructe;
USART_InitTypeDef USART_InitStructe;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能 A 端口
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); //使能串口端口
GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); //GPIO 端口映射到 USART
GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); //GPIO 端口映射到 USART
// USART_DeInit(USART1); //复位
GPIO_InitStructe.GPIO_Mode =GPIO_Mode_AF; //配置为输出模式
GPIO_InitStructe.GPIO_Pin =GPIO_Pin_9|GPIO_Pin_10; //初始化 GPIOA9/10;
GPIO_Init(GPIOA,&GPIO_InitStructe);
USART_InitStructe.USART_BaudRate =baudRate; //波特率
USART_InitStructe.USART_HardwareFlowControl =USART_HardwareFlowControl_None;//无硬件流控制
USART_InitStructe.USART_Mode =USART_Mode_Tx|USART_Mode_Rx; //接收模式,发送模式使能
USART_InitStructe.USART_Parity =USART_Parity_No; //无奇偶校验
USART_InitStructe.USART_StopBits =USART_StopBits_1; //一位停止位
USART_InitStructe.USART_WordLength =USART_WordLength_8b;//8 位数据位
USART_Init(USART1,&USART_InitStructe);
USART_Cmd(USART1,ENABLE); //使能串口
}
void IIC_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_OUT;
GPIO_InitStruct.GPIO_OType=GPIO_OType_PP;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_8;
GPIO_InitStruct.GPIO_PuPd=GPIO_PuPd_NOPULL;
GPIO_InitStruct.GPIO_Speed=GPIO_Fast_Speed;
GPIO_Init(GPIOB,&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_OUT;
GPIO_InitStruct.GPIO_OType=GPIO_OType_OD;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_9;
GPIO_InitStruct.GPIO_PuPd=GPIO_PuPd_NOPULL;
GPIO_InitStruct.GPIO_Speed=GPIO_Fast_Speed;
GPIO_Init(GPIOB,&GPIO_InitStruct);
SCL_H; //总线空闲
SDA_H; //总线空闲
}
//起始信号
void IIC_Start(void)
{
Delay_us(5);
SCL_H;
SDA_H;
Delay_us(5);
SDA_L;
Delay_us(5);
SCL_L; //SDA在SCL为低电平是准备数据
}
//停止信号
void IIC_Stop(void)
{
SDA_L;
Delay_us(5);
SCL_H;
Delay_us(5);
SDA_H;
Delay_us(5);
}
//发送一个字节
u8 IIC_Write(u8 data)
{
u8 i;
u8 ack;
for(i=0;i<8;i++) //产生8个时钟,将data一位一位的传输,先传输高位
{
if(data&(1<<(7-i)))//判断发送数据的某位的电平
{
SDA_H;//1发高电平
}
else
{
SDA_L;//0发低电平
}
Delay_us(5);
//时钟线拉高再拉低
//主机发送数据给从机:主机在时钟方波的低电平是准备1位数据,从机在时钟的高电平读取数据
SCL_H;//时钟线拉高
Delay_us(5);
SCL_L; //时钟线拉低,准备接受下一位
}
SDA_H;//SDA输出高电平,准备读取数据
Delay_us(5); //产生第九个时钟
SCL_H;
Delay_us(5);
if(SDA_IN) //读取SDA的电平:低电平表示应答,高电平表示非应答
{
ack = 1;
}
else
{
ack = 0;
}
SCL_L;
return ack;//返回应答
}
//读取一个字节
u8 IIC_ReadByte(u8 ack)
{
u8 i;
u8 data = 0;
SDA_H;//SDA输出高电平,切换为输入
for(i=0;i<8;i++)
{
Delay_us(5);//循环产生8个时钟
//主机读取从机的数据:从机在时钟方波的低电平是准备1位数据,主机在时钟的高电平读取数据。
SCL_H;
Delay_us(5);
if(SDA_IN) //循环读取8位数据,最先读到的位存到最高位
{
data = (data<<1 )| 1;
}
else
{
data = data<<1;
}
SCL_L;
}
if(ack == 1) //低电平准备数据
{
SDA_H; //非应答NACK
}
if(ack == 0)
{
SDA_L; //应答ACK
}
Delay_us(5);//产生第九个时钟
SCL_H;
Delay_us(5);
SCL_L;
return data;//返回读到的数据
}
//AT24C02单字节写
void AT24C02_WriteByte(u8 data)
{
IIC_Start(); //主机发送IIC起始信号
IIC_Write(0xA0); //器件地址:主机发送一个字节的器件地址,器件地址的最低位是0(“写地址”)
IIC_Write(0x88); //字地址:主机发送一个字节的“字地址”给AT24C02。取值范围是:0x00~0xFF
IIC_Write(data); //数据:主机发送一个字节的“数据”给AT24C02,这个“数据”会被保存到“字地址”所在的空间
IIC_Stop();//停止条件:主机发送一个“结束信号”。
}
//AT24C02单字节读 byteAddr : 需要读取的字节地址
u8 AT24C02_ReadByte(u8 byteAddr)
{
u8 data;
IIC_Start(); //主机发送IIC起始信号
IIC_Write(0xA0); //主机发送从机地址 ,即“写地址”
IIC_Write(byteAddr); //写字节地址
IIC_Start(); //主机发送IIC起始信号
IIC_Write(0xA1); //主机发送从机地址 ,即“读地址”
data = IIC_ReadByte(1); //读一个字节
IIC_Stop();
return data;
}
int main(void)
{
u8 readData = 0xFF;
u8 writeData = 5;
IIC_Init();
usart1_Init();
AT24C02_WriteByte(writeData);
printf("写入的数据是:%d\r\n",writeData);
Delay_us(5000);
readData = AT24C02_ReadByte(0x88);
printf("读取到的数据是:%d\r\n",readData);
while(1)
{
}
}
记录一个没用到的多字节读写,页写函数,写的风格不一样,因为这个会严谨点,以前写的,现在回顾就从简
u8 IIC_Get_ACK(void)
{
u8 ErrorTimer = 0;
SCL_L();
delay_us(5);
SCL_H();
while( SDA_IN)
{
ErrorTimer++;
if( ErrorTimer >= 250)
{
IIC_Stop();
return 1;
}
}
delay_us(5);
SCL_L();
return 0;
}
void AT24C02_Write_Byte(u8 Dev_adder, u8 Wor_adder, u8 Data)
{
IIC_Start();
IIC_Write_Byte(Dev_adder);
if(IIC_Get_ACK())
{
goto WR_End;
}
IIC_Write_Byte(Wor_adder);
if(IIC_Get_ACK())
{
goto WR_End;
}
IIC_Write_Byte(Data);
if(IIC_Get_ACK())
{
goto WR_End;
}
WR_End:
IIC_Stop();
delay_ms(5);
}
u8 AT24C02_Read_Byte(u8 Dev_adder, u8 Wor_adder)
{
u8 rec=0;
IIC_Start();
IIC_Write_Byte(Dev_adder);
if(IIC_Get_ACK())
{
goto WR_End;
}
IIC_Write_Byte(Wor_adder);
if(IIC_Get_ACK())
{
goto WR_End;
}
IIC_Start();
IIC_Write_Byte(Dev_adder | 0x01);
if(IIC_Get_ACK())
{
goto WR_End;
}
rec=IIC_Read_Byte(1);
WR_End:
IIC_Stop();
return rec;
}
void AT24C02_Buffer_Write(uint8_t Dev_adder,uint16_t Wor_adder, uint8_t* Data, uint8_t Length)
{
IIC_Start();
IIC_Write_Byte(Dev_adder); //芯片器件地址
if( IIC_Get_ACK() ) //检测应答信号 0:有应答,1:没有应答
{
goto WR_End;
}
IIC_Write_Byte(Wor_adder % 256); //发送内部地址
if( IIC_Get_ACK() ) //检测应答信号 0:有应答,1:没有应答
{
goto WR_End;
}
while(Length--)
{
IIC_Write_Byte(*Data); //发送字节数据
if( IIC_Get_ACK() )
{
goto WR_End;
}
Data++; //指针变化
}
WR_End:
IIC_Stop();
delay_ms(5);
}
void AT24C02_Page_Write(uint8_t Dev_adder,uint16_t Wor_adder, uint8_t* Data, uint8_t Length)
{
u8 num_of_page = 0, num_of_single = 0, addr = 0, count = 0;
addr = Wor_adder % 8; //写入地址是开始页的第几位
count = 8 - addr; //在开始页要写入的个数
num_of_page = Length / 8; //要写入的页数
num_of_single = Length % 8; //不足一页的个数
if(addr == 0) //写入地址是页的开始
{
if(num_of_page == 0) //数据小于一页 100byte/8 12.x
{
AT24C02_Buffer_Write(Dev_adder, Wor_adder, Data, num_of_single); //写少于一页的数据
}
else //数据大于等于一页
{
while(num_of_page)//要写入的页数
{
AT24C02_Buffer_Write(Dev_adder, Wor_adder, Data, 8); //写一页的数据
Wor_adder += 8; //地址跨过8个字节
Data += 8; //写入的数据也跨过8个字节
num_of_page--; //页数减
}
if(num_of_single != 0) //剩余数据小于一页
{
AT24C02_Buffer_Write(Dev_adder, Wor_adder,Data, num_of_single); //写少于一页的数据
}
}
}
else //写入地址不是页的开始
{
if(num_of_page == 0) //数据小于一页
{
AT24C02_Buffer_Write(Dev_adder, Wor_adder, Data, num_of_single); //写少于一页的数据
}
else //数据大于等于一页
{
Length -= count;
num_of_page = Length / 8; //重新计算要写入的页数
num_of_single = Length % 8; //重新计算不足一页的个数
if(count != 0)
{
AT24C02_Buffer_Write(Dev_adder, Wor_adder, Data, count); //将开始的空间写满一页
Wor_adder += count;
Data += count;
}
while(num_of_page--) //要写入的页数
{
AT24C02_Buffer_Write(Dev_adder, Wor_adder, Data, 8); //写一页的数据
Wor_adder += 8; //地址跨过8个字节
Data += 8; //写入的数据也跨过8个字节
}
if(num_of_single != 0)//剩余数据小于一页
{
AT24C02_Buffer_Write(Dev_adder, Wor_adder, Data, num_of_single); //写少于一页的数据
}
}
}
}
void AT24C02_Continuous_Read(uint8_t Dev_adder,uint16_t Wor_adder,uint8_t *rec, uint8_t Length)
{
IIC_Start();
IIC_Write_Byte(Dev_adder);
if( IIC_Get_ACK() )
{
goto WR_End;
}
IIC_Write_Byte(Wor_adder);
if( IIC_Get_ACK() )
{
goto WR_End;
}
/*********** 以上是伪写 ********************/
IIC_Start();
IIC_Write_Byte(Dev_adder | 0x01);
if( IIC_Get_ACK() )
{
goto WR_End;
}
while(Length)
{
/* 最后一个字节发送非应答信号 */
if(Length == 1)
{
*rec = IIC_Read_Byte(1);
}
/* 每读一个字节发送一个应答信号 */
else
{
*rec = IIC_Read_Byte(0);
}
rec++; //指针变化
Length--; //数据长度递减
}
WR_End:
IIC_Stop();
}