IIC通信-----GPIO模拟IIC协议

一、IIC通信--------多机通信

1、物理层

    1,IIC通信是飞利浦开发的通信方式
    2,IIC通信是一种两线串行的通信方式:SDA(数据线),SCL(时钟线)
    3,在IIC总线上必须接两个上拉电阻,用于总线空闲时将总线拉高
    4,在IIC总线上的每个设备都有一个唯一的ID,用于确定通信设备
    5,IIC的通信速率   标准100k/bit   快速 400k/bit   高速 3.4M/bit
    6,IIC是一种多主机多从机的通信方式
    7,IIC的时钟线的作用:
             掌握数据有效性:
                当SCL为高电平时,SDA线上的数据有效
                当SCL为低电平时,SDA线上的数据无效
    8,IIC通信,时钟线只能由主机控制
    9,IIC通信必须由起始信号开始,由停止信号结束
    10,IIC通信是一种稳定的通信方式,在通信过程中的每一步都有应答
            接收方应答
            发送方等待应答
    11,IIC是一种高位先行的通信方式


2、协议层

起始信号:SCL为高电平期间,SDA由高电平跳变到低电平,产生一个有效的下降沿
停止信号:SCL为高电平期间,SDA由低电平跳变到高电平,产生一个有效的上升沿
应答信号:SCL为高电平期间,SDA产生一个有效的低电平
非应答信号:SCL为高电平期间,SDA产生一个有效的高电平
等待应答信号:拉高时钟,读取SDA线上的高电平或者低电平
发送数据信号:高位先行,一个SCL高电平发送一个位的数据,SCL为低电平时,准备数                                         据,SCL为高电平时,数据稳定发送
接收数据信号:拉高时钟线,读取SDA线上的数据  

3、实现IIC通信的方式

    1,片上外设
    2,GPIO模拟IIC协议

4、AT24Cxx   EEPROM

电可擦除可改写存储器(存储数据,对它进行读写)  具有掉电保护功能

5、EEPROM页写思路

写入的首地址ADDR  写入的数据指针Str  写入数据的长度 size

判断写入的首地址ADDR+size>你的总空间大小

        1,判断地址是否为页头
        ADDR%8=0?
        如果是则只需要写分两次写:
            1》写入整数页
                有多少个整数页:size/8
                    
            2》写入最后一页的数据
                最后一页有多少个数据:size%8

        2,如果ADDR%8=0?不为0,则需要分三次写
            0》补全第一页
                     第一页可以写多少个数据:ADDR%8  8-(ADDR%8)
            1》写入整数页
                  (size-(8-(ADDR%8)))/8
            2》写入最后一页的数据
                  (size-(8-(ADDR%8)))%

二、实际操作

1、查找原理图手册,根据原理图初始化引脚

SCL(时钟线)

        只能由主机控制

        先设置SCL,PB6引脚、10MHZ(最大3.4MHZ)、GPIO开漏输出

void IIC_Config(void)
{
	GPIO_InitTypeDef  GPIO_InitStruct;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	GPIO_InitStruct.GPIO_Pin    = GPIO_Pin_6;         //SCL
	GPIO_InitStruct.GPIO_Mode   = GPIO_Mode_Out_OD;   // 开漏输出
	GPIO_InitStruct.GPIO_Speed  = GPIO_Speed_10MHz;
	GPIO_Init(GPIOB,&GPIO_InitStruct);
}

SDA(数据线)

        设置SDA,PB7引脚、10MHZ(最大3.4MHZ)、因为数据线需要接收和发送数据,所以我们用函数来切换接收和发送,封装成两个函数


void IIC_SDA_Out(void)
{
	GPIO_InitTypeDef  GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Pin    = GPIO_Pin_7;       //SDA
	GPIO_InitStruct.GPIO_Mode   = GPIO_Mode_Out_OD; // 开漏输出
	GPIO_InitStruct.GPIO_Speed  = GPIO_Speed_10MHz;
	GPIO_Init(GPIOB,&GPIO_InitStruct);
}

void IIC_SDA_In(void)
{
	GPIO_InitTypeDef  GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_7;            //SDA
	GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_IN_FLOATING; // 浮空输入
	GPIO_Init(GPIOB,&GPIO_InitStruct);
}

2、根据协议层开始模拟

(1)起始信号:

        SCL为高电平期间,SDA由高电平跳变到低电平,产生一个有效的下降沿

// 起始信号:SCL为高电平期间,SDA由高电平跳变到低电平,产生一个有效的下降沿
void IIC_Start(void)
{
	SCL_Low;             //将SCL拉低,使SDA线上的数据无效,就可以去改变SDA线上的信号
	IIC_SDA_Out();       //将SDA引脚设置成输出
	SDA_High;            //拉高SDA,准备好高电平
	SCL_High;            //让高电平有效
	Systicks_DelayNus(5);//有效时长为5us	
	SDA_Low;             //由高电平跳变到低电平
	Systicks_DelayNus(5);//有效时长为5us
	SCL_Low;             //不让其他设备发起IIC,因为我已经在通信
}

(2)停止信号:

        SCL为高电平期间,SDA由低电平跳变到高电平,产生一个有效的上升沿

// 停止信号:SCL为高电平期间,SDA由低电平跳变到高电平,产生一个有效的上升沿
void IIC_Stop(void)
{
	SCL_Low;              //将SCL拉低,使SDA线上的数据无效,就可以去改变SDA线上的信号
	IIC_SDA_Out();        //将SDA引脚设置成输出
	SDA_Low;              //准备低电平
	SCL_High;             //让低电平有效
	Systicks_DelayNus(5); //有效时长为5us
	SDA_High;             //有低电平跳变到高电平
	Systicks_DelayNus(5); //有效时长为5us
}

(3)应答信号:

        SCL为高电平期间,SDA产生一个有效的低电平

void IIC_SendACK(void)
{
	SCL_Low;              //将SCL拉低,使SDA线上的数据无效,就可以去改变SDA线上的信号
	IIC_SDA_Out();        //将SDA引脚设置成输出
	SDA_Low;              //准备低电平
	SCL_High;             //让低电平有效
	Systicks_DelayNus(5); //有效时长为5us
	SCL_Low;              //不让其他设备发起IIC,因为我已经在通信
}

(4)非应答信号:

        SCL为高电平期间,SDA产生一个有效的高电平

void IIC_SendNoACK(void)
{
	SCL_Low;              //将SCL拉低,使SDA线上的数据无效,就可以去改变SDA线上的信号
	IIC_SDA_Out();        //将SDA引脚设置成输出
	SDA_High;             //准备高电平
	SCL_High;             //让低电平有效
	Systicks_DelayNus(5); //有效时长为5us
	SCL_Low;              //不让其他设备发起IIC,因为我已经在通信
}

(5)等待应答信号:

        拉高时钟,读取SDA线上的高电平或者低电平

uint8_t IIC_WaitACK(void)
{
	uint8_t temp=0; //用于超时等待
	SCL_Low;        //将SCL拉低,使SDA线上的数据无效,就可以去改变SDA线上的信号
	IIC_SDA_In();   //将SDA引脚设置成输入
	SCL_High;
	Systicks_DelayNus(5); //等待从机反应,再去读取
	while(SDA_Read){      //判断是否有应答信号
		temp++;           //非应答信号,计数等待
		if(temp>100){     //等待足够时间,返回非应答,停止IIC
			IIC_Stop();   //停止IIC
			return NoACK; //返回非应答
		}
	}
	SCL_Low;    //不让其他设备发起IIC,因为我已经在通信
	return ACK; //等待到应答信号
}

(6)发送数据信号:

        高位先行,一个SCL高电平发送一个位的数据,SCL为低电平时,准备数               据,SCL为高电平时,数据稳定发送

void IIC_SendByteData(uint8_t Txdata)
{
	uint8_t i;
	SCL_Low;                  //将SCL拉低,使SDA线上的数据无效,就可以改变SDA线上的信号
	IIC_SDA_Out();            //将SDA引脚设置成输出
	for(i=0;i<8;i++){         // 有八个位的数据需要发送,循环八次
		if(Txdata&0x80){      //高位先行,先发送最高位的数据,判断高位是为0还是为1
			SDA_High;         //为真,发送高电平
		}else{
			SDA_Low;          //为假,发送低电平
		}
		SCL_High;             //让准备高低电平有效
		Systicks_DelayNus(5); //有效时长为5us
		SCL_Low;              //拉低时钟线,准备下一个数据位的发送
		Systicks_DelayNus(5); //有效时长为5us
		Txdata=Txdata<<1;     //左移一位,准备下一个数据位的发送
	}
	SCL_Low;                  //不让其他设备发起IIC,因为我已经在通信
}

(7)接收数据信号:

        拉高时钟线,读取SDA线上的数据  

//调用这一个函数,主机一定作为接收方,作为接收方要应答
uint8_t IIC_RxByteData(uint8_t ackflag)
{
	uint8_t i;
	uint8_t Rxdata=0;
	SCL_Low;                      //将SCL拉低,使SDA线上的数据无效,就可以改变SDA线上的信号
	IIC_SDA_In();
	for(i=0;i<8;i++){             // 有八个位的数据需要接收,循环八次
		Rxdata=Rxdata<<1;         //这里只需要有效左移次数为7次,将最低上的数据移至最高位
		SCL_High;                 //拉高时钟线读取数据
		Systicks_DelayNus(5);     //等待从机反应,再去读取
		if(SDA_Read){             //读数据线
			Rxdata =Rxdata|0x01;  //读到高电平,将读到数据先放在低位,经过左移移到高位
		}
		SCL_Low;                  //拉低时钟线,让从机准备下一个为的数据
		Systicks_DelayNus(5);     //等待从机反应,再去读取
	}
	if(ACK==ackflag){
		IIC_SendACK();
	}else{
		IIC_SendNoACK();
	}
	SCL_Low;                      //拉低时钟线,让从机准备下一个为的数据
	return Rxdata;
}

关于延时函数需设置为多长时间,根据高低电平的有效时间设置(5us)

 三、完整程序

主函数main
#include "stm32f10x.h"
#include "drv_systick.h"
#include "drv_usart.h"
#include "stdio.h"
#include "drv_iic.h"
#include "drv_at24cxx.h"

int main(void)
{	
	Systicks_Config(72);
	Usart1_Config();
	IIC_Config();
	//选择一种管理中断的方式----整个程序只需要配置一次----写在main函数的while(1)之前
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
    //传入一个地址,一个数字
	AT24Cxx_WriteByteData(0x55,0x88);
	Systicks_DelayNms(1000);
    //在0x55的地址上会输出“88”这个数字
	printf("AT24Cxx_ReadBtyeData=%x\n",AT24Cxx_ReadBtyeData(0x55));
	
	while(1){;}
}

Systick程序
#ifndef __DRV_SYSTICK_H__
#define __DRV_SYSTICK_H__

#include "stm32f10x.h"

void Systicks_Config(uint32_t sysclk);
void Systicks_DelayNms(uint32_t Nms);
void Systicks_DelayNus(uint32_t Nus);

#endif  //__DRV_SYSTICK_H__
#include "drv_systick.h"
uint32_t fu_us;    //定时1us需要计数的次数
uint32_t fu_ms;    //定时1ms需要计数的次数

void Systicks_Config(uint32_t sysclk)//传入的是系统时钟,比如72M就传入72
{
	//  1,配置时钟源为8分频,并且关闭定时器  -----CTRL
	SysTick->CTRL  &=~(0x5);

	 //定时1us需要计数的次数
	fu_us =sysclk/8;

	//定时1ms需要计数的次数
	fu_ms=fu_us*1000;
}

void Systicks_DelayNms(uint32_t Nms)
{
	uint32_t temp;
	uint8_t sys_flag=0;
	while(0==sys_flag){
		if(Nms>1864){
			// 2,设置导入值-----1864*9000   ------LOAD
			SysTick->LOAD    =1864*fu_ms;
			Nms=Nms-1864;
		}else{
			// 2,设置导入值-----NMS*9000   ------LOAD
			SysTick->LOAD    =Nms*fu_ms;
			sys_flag=1;
		}
		// 3,清除当前值寄存器          ------VAL
		SysTick->VAL     =0;

		// 4,打开定时器                -----CTRL
		SysTick->CTRL   |=0x01;

		// 5,等待定时器结束            -----CTRL
		do{
			temp=SysTick->CTRL;
		}while(!(temp&(1<<16)));

		// 6,关闭定时器                -----CTRL
		SysTick->CTRL  &=~0x01;
	}
}

void Systicks_DelayNus(uint32_t Nus)
{
	uint32_t temp;
	// 2,设置导入值-----NMS*9------LOAD
	SysTick->LOAD    =Nus*fu_us;

	// 3,清除当前值寄存器          ------VAL
	SysTick->VAL     =0;

	// 4,打开定时器                -----CTRL
	SysTick->CTRL   |=0x01;

	// 5,等待定时器结束            -----CTRL
	do{
		temp=SysTick->CTRL;
	}while(!(temp&(1<<16)));

	// 6,关闭定时器                -----CTRL
	SysTick->CTRL  &=~0x01;
}
Usart程序 
#ifndef __DRV_USART_H__
#define __DRV_USART_H__

#include "stm32f10x.h"
#include "stdio.h"

void Usart1_Config(void);

#endif //__DRV_USART_H__
#include "drv_usart.h"
void Usart1_Config(void)
{
	GPIO_InitTypeDef  GPIO_InitStruct;
	USART_InitTypeDef  USART_InitStruct;
	NVIC_InitTypeDef  NVIC_InitStruct;
	// 1,打开时钟----串口1,GPIOA,AFIO(引脚受片上外设控制时打开)
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1,ENABLE);

	/*
	2,初始化引脚
			-----GPIO_Pin_9   TX
			-----复用推挽输出
			-----速度:2MHZ
			-----GPIO_Pin_10  Rx
			-----浮空输入
	*/
	GPIO_InitStruct.GPIO_Pin     =GPIO_Pin_9;//GPIO_Pin_9   TX
	GPIO_InitStruct.GPIO_Mode    =GPIO_Mode_AF_PP;//复用推挽输出
	GPIO_InitStruct.GPIO_Speed   =GPIO_Speed_2MHz;//速度:2MHZ
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	GPIO_InitStruct.GPIO_Pin     =GPIO_Pin_10;//GPIO_Pin_10  Rx
	GPIO_InitStruct.GPIO_Mode    =GPIO_Mode_IN_FLOATING;//浮空输入
	GPIO_Init(GPIOA,&GPIO_InitStruct);

	/*
    3,初始化串口
        -----波特率:115200
        -----数据位数:8bit 
        -----奇偶校验:失能
        -----停止位:1bit 
        -----硬件流控:失能 
        -----发送和接收使能	
	*/
	USART_InitStruct.USART_BaudRate      		 = 115200;//波特率:115200
	USART_InitStruct.USART_HardwareFlowControl   = USART_HardwareFlowControl_None;//硬件流控:失能 
	USART_InitStruct.USART_Mode                  = USART_Mode_Rx|USART_Mode_Tx;//发送和接收使能	
	USART_InitStruct.USART_Parity                = USART_Parity_No;//奇偶校验:失能
	USART_InitStruct.USART_StopBits              = USART_StopBits_1;//停止位:1bit 
	USART_InitStruct.USART_WordLength            = USART_WordLength_8b;//数据位数:8bit 
	USART_Init(USART1,&USART_InitStruct);    
	//  2,配置一个中断源----告诉NVIC哪一个事件可以打断CPU------给谁配置中断源,就写在它的配置程序中
	USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
	USART_ITConfig(USART1,USART_IT_IDLE,ENABLE);//配置总线空闲的中断源

	// 3,给这个中断源配置它的主优先级和次优先级(注意中断通道号不要到固件库手册中去找,去“stm32f10x.h”这个头文件的枚举中去找)------给谁配置中断优先级,就写在它的配置程序中
	NVIC_InitStruct.NVIC_IRQChannel  =USART1_IRQn;//中断通道号
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority    = 0;     //主优先级
	NVIC_InitStruct.NVIC_IRQChannelSubPriority           = 0;     //次优先级
	NVIC_InitStruct.NVIC_IRQChannelCmd                   = ENABLE;//使能此通道
	NVIC_Init(&NVIC_InitStruct);

	USART_ClearFlag(USART1,USART_FLAG_TC|USART_FLAG_TXE);

	// 4,打开串口1
	USART_Cmd(USART1,ENABLE);
}

int fputc(int ch,FILE*f)
{
	USART1->DR=(uint8_t)ch;
	while(0==(USART1->SR&(1<<7)));
	USART1->SR &=~(1<<7);
	return ch;
}
 IIC程序
#ifndef __DRV_IIC_H__
#define __DRV_IIC_H__

#include "stm32f10x.h"
#include "drv_systick.h"

#define SCL_High  GPIO_SetBits(GPIOB,GPIO_Pin_6)
#define SCL_Low   GPIO_ResetBits(GPIOB,GPIO_Pin_6)

#define SDA_High  GPIO_SetBits(GPIOB,GPIO_Pin_7)
#define SDA_Low   GPIO_ResetBits(GPIOB,GPIO_Pin_7)
#define SDA_Read  GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_7)

#define ACK   0
#define NoACK   1

void IIC_Config(void);
void IIC_SDA_Out(void);
void IIC_SDA_In(void);

void IIC_Start(void);
void IIC_Stop(void);
void IIC_SendACK(void);
void IIC_SendNoACK(void);
uint8_t IIC_WaitACK(void);
void IIC_SendByteData(uint8_t Txdata);
uint8_t IIC_RxByteData(uint8_t ackflag);

#endif //__DRV_IIC_H__
#include "drv_iic.h"

void IIC_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStruct;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);

	GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_Out_OD;
	GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_6;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
	GPIO_Init(GPIOB, &GPIO_InitStruct);
}	

void IIC_SDA_Out(void)
{
	GPIO_InitTypeDef  GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Pin    = GPIO_Pin_7;       //SDA
	GPIO_InitStruct.GPIO_Mode   = GPIO_Mode_Out_OD; // 开漏输出
	GPIO_InitStruct.GPIO_Speed  = GPIO_Speed_10MHz;
	GPIO_Init(GPIOB,&GPIO_InitStruct);
}

void IIC_SDA_In(void)
{
	GPIO_InitTypeDef  GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Pin    =GPIO_Pin_7;            //SDA
	GPIO_InitStruct.GPIO_Mode   =GPIO_Mode_IN_FLOATING; // 浮空输入
	GPIO_Init(GPIOB,&GPIO_InitStruct);
}

// 起始信号:SCL为高电平期间,SDA由高电平跳变到低电平,产生一个有效的下降沿
void IIC_Start(void)
{
	SCL_Low;              //将SCL拉低,使SDA线上的数据无效,就可以去改变SDA线上的信号
	IIC_SDA_Out();        //将SDA引脚设置成输出
	SDA_High;             //拉高SDA,准备好高电平
	SCL_High;             //让高电平有效
	Systicks_DelayNus(5); //有效时长为5us
	
	SDA_Low;              //由高电平跳变到低电平
	Systicks_DelayNus(5); //有效时长为5us

	SCL_Low;              //不让其他设备发起IIC,因为我已经在通信
}


// 停止信号:SCL为高电平期间,SDA由低电平跳变到高电平,产生一个有效的上升沿
void IIC_Stop(void)
{
	SCL_Low;              //将SCL拉低,使SDA线上的数据无效,就可以去改变SDA线上的信号
	IIC_SDA_Out();        //将SDA引脚设置成输出

	SDA_Low;              //准备低电平
	SCL_High;             //让低电平有效
	Systicks_DelayNus(5); //有效时长为5us

	SDA_High;             //有低电平跳变到高电平
	Systicks_DelayNus(5); //有效时长为5us

}

// 应答信号:SCL为高电平期间,SDA产生一个有效的低电平
void IIC_SendACK(void)
{
	SCL_Low;              //将SCL拉低,使SDA线上的数据无效,就可以去改变SDA线上的信号
	IIC_SDA_Out();        //将SDA引脚设置成输出

	SDA_Low;              //准备低电平
	SCL_High;             //让低电平有效
	Systicks_DelayNus(5); //有效时长为5us

	SCL_Low;              //不让其他设备发起IIC,因为我已经在通信
}

// 非应答信号:SCL为高电平期间,SDA产生一个有效的高电平
void IIC_SendNoACK(void)
{
	SCL_Low;              //将SCL拉低,使SDA线上的数据无效,就可以去改变SDA线上的信号
	IIC_SDA_Out();        //将SDA引脚设置成输出

	SDA_High;             //准备高电平
	SCL_High;             //让低电平有效
	Systicks_DelayNus(5); //有效时长为5us

	SCL_Low;              //不让其他设备发起IIC,因为我已经在通信
}

// 等待应答信号:拉高时钟,读取SDA线上的高电平或者低电平
uint8_t IIC_WaitACK(void)
{
	uint8_t temp=0;       //用于超时等待
	SCL_Low;              //将SCL拉低,使SDA线上的数据无效,就可以去改变SDA线上的信号
	IIC_SDA_In();         //将SDA引脚设置成输入

	SCL_High;
	Systicks_DelayNus(5); //等待从机反应,再去读取
	while(SDA_Read){      //判断是否有应答信号
		temp++;           //非应答信号,计数等待
		if(temp>100){     //等待足够时间,返回非应答,停止IIC
			IIC_Stop();   //停止IIC
			return NoACK; //返回非应答
		}
	}
	SCL_Low;    //不让其他设备发起IIC,因为我已经在通信
	return ACK; //等待到应答信号
}

// 发送数据信号:高位先行,一个SCL高电平发送一个位的数据,SCL为低电平时,准备数据,SCL为高电平时,数据稳定发送
void IIC_SendByteData(uint8_t Txdata)
{
	uint8_t i;
	SCL_Low;                  //将SCL拉低,使SDA线上的数据无效,就可以去改变SDA线上的信号
	IIC_SDA_Out();            //将SDA引脚设置成输出

	for(i=0;i<8;i++){         // 有八个位的数据需要发送,循环八次
		if(Txdata&0x80){      //高位先行,先发送最高位的数据,判断高位是为0还是为1
			SDA_High;         //为真,发送高电平
		}else{
			SDA_Low;          //为假,发送低电平
		}
		SCL_High;             //让准备高低电平有效
		Systicks_DelayNus(5); //有效时长为5us
		SCL_Low;              //拉低时钟线,准备下一个数据位的发送
		Systicks_DelayNus(5); //有效时长为5us
		Txdata=Txdata<<1;     //左移一位,准备下一个数据位的发送
	}
	SCL_Low;                  //不让其他设备发起IIC,因为我已经在通信
}

// 接收数据信号:拉高时钟线,读取SDA线上的数据
uint8_t IIC_RxByteData(uint8_t ackflag)//调用这一个函数,主机一定作为接收方,作为接收方要应答
{
	uint8_t i;
	uint8_t Rxdata=0;
	SCL_Low;                     //将SCL拉低,使SDA线上的数据无效,就可以去改变SDA线上的信号
	IIC_SDA_In();
	for(i=0;i<8;i++){            // 有八个位的数据需要接收,循环八次
		Rxdata=Rxdata<<1;        //这里只需要有效左移次数为7次,将最低上的数据移至最高位
		SCL_High;                //拉高时钟线读取数据
		Systicks_DelayNus(5);    //等待从机反应,再去读取
		if(SDA_Read){            //读数据线
			Rxdata =Rxdata|0x01; //读到高电平,将读到数据先放在低位,经过左移移到高位
		}
		SCL_Low;                 //拉低时钟线,让从机准备下一个为的数据
		Systicks_DelayNus(5);    //等待从机反应,再去读取
	}
	if(ACK==ackflag){
		IIC_SendACK();
	}else{
		IIC_SendNoACK();
	}
	SCL_Low;//拉低时钟线,让从机准备下一个为的数据
	return Rxdata;
}
AT24CXX程序
#ifndef __DRV_AT24CXX_H__
#define __DRV_AT24CXX_H__

#include "drv_iic.h"

void AT24Cxx_WriteByteData(uint32_t Addr,uint8_t W_data);
uint8_t AT24Cxx_ReadBtyeData(uint32_t Addr);

#endif //__DRV_AT24CXX_H__
#include "drv_at24cxx.h"
void AT24Cxx_WriteByteData(uint32_t Addr,uint8_t W_data)
{
	// 发送起始信号
	IIC_Start();
	// 发送与之通信的设备ID
	IIC_SendByteData(0xa0);
	// 等待设备应答
	if(ACK!=IIC_WaitACK()){
		IIC_Stop();
		return;
	}
	// 发送写入设备的哪一个地址
	IIC_SendByteData(Addr);
	// 等待设备应答
	if(ACK!=IIC_WaitACK()){
		IIC_Stop();
		return;
	}
	// 发送写入的数据
	IIC_SendByteData(W_data);
	// 等待设备应答
	if(ACK!=IIC_WaitACK()){
		IIC_Stop();
		return;
	}
	// 写完了,停止IIC
	IIC_Stop();	
}

uint8_t AT24Cxx_ReadBtyeData(uint32_t Addr)
{
	uint8_t R_data=0;
	// 发送起始信号
	IIC_Start(); 
	// 发送与之通信的设备ID
	IIC_SendByteData(0xa0);
	// 等待设备应答
	if(ACK!=IIC_WaitACK()){
		IIC_Stop();
		return 0;
	}
	// 发送写入设备的哪一个地址
	IIC_SendByteData(Addr);
	// 等待设备应答
	if(ACK!=IIC_WaitACK()){
		IIC_Stop();
		return 0;
	} 
	// 发送起始信号
	IIC_Start(); 
	// 发送与之通信的设备ID
	IIC_SendByteData(0xa1);
	// 等待设备应答
	if(ACK!=IIC_WaitACK()){
		IIC_Stop();
		return 0;
	}
	// 接收数据,并且接收完数据后发送Noack
	R_data=IIC_RxByteData(NoACK);
	// 停止IIC
	IIC_Stop();
	
	return R_data;	
}

四、运行结果

五、改进方案

1、写页思路

2、进位原理

3、推挽和开漏的选择

  • 1
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值