stm32(十三)IIC

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协议基本时序信号

  1. 起始信号:当SCL为高电平时,SDA由高电平向低电平跳变。情景:两个人碰面:吃了没?
  2. 设备地址:IIC的设备地址是7位+1位最低位(方向位:0表示写方向,1表示读方向),举例:假如有一个IIC设备,其地址为1110 100B,则该设备“读地址”是1110 1001B;“写地址”是1110 1000B。
  3. 应答信号:在第9个时钟时,如果SDA为低电平则表示“应答”(ACK);在第9个时钟时,如果SDA为高电平则表示“非应答”(NACK);如果主机给从机写数据时,从机回应是“非应答”,则IIC数据传输无法进行(IIC从机故障,IIC从机正在做其他事情,数据传输结束)。IIC的通讯过程中,每接收到8位数据的一方需要给对方发送一个应答信号。
  4. 结束信号:当SCL为高电平时,SDA由低电平向高电平跳变。
  5. 当SDA和SCL都位高电平时,则总线空闲;
  6. 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();
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值