一、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、推挽和开漏的选择