AT24C02使用详解

简介: ---恢复内容开始---这篇文章是写给一个学弟看的,关于IIC,关于24C02的单字节写入\读取..页写入和读取,,学弟总是害怕协议,,,我总是对人家说,本来就这样的,协议就是人家这样规定的,,,如果你早生几十年你也可能规定个IIC协议.

---恢复内容开始---

这篇文章是写给一个学弟看的,关于IIC,关于24C02的单字节写入\读取..页写入和读取,,学弟总是害怕协议,,,我总是对人家说,本来就这样的,协议就是人家这样规定的,,,如果你早生几十年你也可能规定个IIC协议......

我的单片机和24C02通信,,,我的单片机就叫主机,,,24C02叫从机

先看IIC

 

IIC协议规定开始传输数据的时候要先发一个起始信号,,,目的应该是告诉从机要开始通信了,准备准备

终止信号就是拜拜啦,再见!

起始信号就是 在SCL在高电平期间SDA来一个下降沿,,终止信号就是在SCL在高电平期间SDA来一个上升沿(所以协议上才会说,在正常传输数据的时候,只有在SCL为低电平的时候,数据线SDA的高低电平状态才允许改变,要不然岂不是和起始信号或者终止信号冲突了)

 

/*******************************************************************
                     起动总线函数               
函数原型: void  Start_I2c();  
功能:     启动I2C总线,即发送I2C起始条件.  
********************************************************************/
void Start_I2c()
{
  SDA=1;         /*发送起始条件的数据信号*/
  _Nop();
  SCL=1;
  _Nop();        /*起始条件建立时间大于4.7us,延时*/
  _Nop();
  _Nop();
  _Nop();
  _Nop();    
  SDA=0;         /*发送起始信号*/
  _Nop();        /* 起始条件锁定时间大于4μs*/
  _Nop();
  _Nop();
  _Nop();
  _Nop();       
  SCL=0;       /*钳住I2C总线,准备发送或接收数据 */
  _Nop();
  _Nop();
}

 

void Stop_I2c()
{
  SDA=0;      /*发送结束条件的数据信号*/
  _Nop();       /*发送结束条件的时钟信号*/
  SCL=1;      /*结束条件建立时间大于4μs*/
  _Nop();
  _Nop();
  _Nop();
  _Nop();
  _Nop();
  SDA=1;      /*发送I2C总线结束信号*/
  _Nop();
  _Nop();
  _Nop();
  _Nop();
}

 


 

发送完起始信号就能传输数据了

下面是程序

void  SendByte(unsigned char  c)
{
    unsigned char  BitCnt;
    //SCL=0; 起始信号最后是SCL=0;所以不用写了
    for(BitCnt=0;BitCnt<8;BitCnt++)  /*要传送的数据长度为8位*/
    {
      if((c<<BitCnt)&0x80)/*判断发送位*/
      {
        SDA=1;   
      }
      else
      {
        SDA=0;  
      }                      
      _Nop();
      SCL=1;               /*置时钟线为高,通知被控器开始接收数据位*/
      _Nop(); 
      _Nop();             /*保证时钟高电平周期大于4μs*/
      _Nop();
      _Nop();
      _Nop();         
      SCL=0; 
    }

    _Nop();
    _Nop();
    SDA=1;                /*8位发送完后释放数据线,准备接收应答位*/
    _Nop();
    _Nop();   
    SCL=1;
    _Nop();
    _Nop();
    _Nop();
    if(SDA==1)/*判断是否接收到应答信号*/
        ack=0;//没有接收到应答信号     
    else 
        ack=1;//接收到应答信号        
    SCL=0;
    _Nop();
    _Nop();
}


现在说一下接收,,,假设上面发送完0xaa以后,从机就返回给我们数据(11001100, 0xcc),当然SCL为低电平的时候模块准备数据,,SCL为高电平的时候,从机就把数据放在了SDA上,这样循环8次,一个8位数据就过来了

 整体上应该是

Start_I2c();起始信号程序

SendByte(0xaa);

判断下ack是不是等于1,应答了(是继续执行还是停止看自己了)

Data = RcvByte();//接收数据

Ack_I2c(1);//发送非应答,就是SDA=1;,这个程序在下面

Stop_I2c();发送停止信号

 接收程序如下

unsigned char   RcvByte()
{
  unsigned char  retc;
  unsigned char  BitCnt;
  
  retc=0; 
  SDA=1;                     /*置数据线为输入方式*/
  for(BitCnt=0;BitCnt<8;BitCnt++)
    {
        _Nop();           
        SCL=0;                  /*置时钟线为低,准备接收数据位*/
        _Nop();
        _Nop();                 /*时钟低电平周期大于4.7μs*/
        _Nop();
        _Nop();
        _Nop();
        SCL=1;                  /*置时钟线为高使数据线上数据有效*/
        _Nop();
        _Nop();
        retc=retc<<1;
        if(SDA==1)retc=retc+1;  /*读数据位,接收的数据位放入retc中 */
        _Nop();
        _Nop(); 
    }
  SCL=0;    
  _Nop();
  _Nop();
  return(retc);
}

 

 应答或者非应答程序如下

 

/********************************************************************
                     应答子函数
函数原型:  void Ack_I2c(bit a);
功能:      主控器进行应答信号(可以是应答0或非应答1信号,由位参数a决定)
********************************************************************/
void Ack_I2c(bit a)
{
  if(a==0)SDA=0;              /*在此发出应答或非应答信号 */
  else SDA=1;
  _Nop();
  _Nop();
  _Nop();      
  SCL=1;
  _Nop();
  _Nop();                    /*时钟低电平周期大于4μs*/
  _Nop();
  _Nop();
  _Nop();  
  SCL=0;                     /*清时钟线,钳住I2C总线以便继续接收*/
  _Nop();
  _Nop();    
}

IIC其实就这样了,主要看支持IIC通信的芯片的资料了,写好这些就是IIC通用的了

资料链接

https://wenku.baidu.com/view/3fc8558002d276a200292ef9.html

现在看芯片资料如何写进去一个字节

关于器件的地址

写就是0xa0;;;;读就是0xa1

所以写函数就是

 

/**
* @brief  向24C02写数据
* @param  Data--数据
* @param  Address--地址
* @param  None
* @retval None
* @example 
**/
unsigned char WriteData(unsigned char Data,unsigned char Address)
{
    Start_I2c();
    SendByte(0xa0);//最后一位为0写入
    if(ack==0)return(0);
    
    SendByte(Address);              //发送地址
    if(ack==0)return(0);
    
    SendByte(Data);              //发送数据
    if(ack==0)return(0);
    
    Stop_I2c();               //结束总线
    return(1);
}

关于应答哈我的SendByte(unsigned char  c)函数里面发送完8位数据后就写了应答,然后把应答标志给ack,,后面直接判断的ack

现在想想为什么叫应答...直接说判断从机正没正确接收到数据就完了呗,就是把SDA拉高,然后把SCL拉高,等一会然后判断SDA引脚有没有被从机拉低,拉低了就说明好了......没拉低从机可能接收的数据不正确

 _Nop();
 _Nop();
 SDA=1;                /*8位发送完后释放数据线,准备接收应答位*/
 _Nop();
 _Nop();  
 SCL=1;
 _Nop();
 _Nop();
 _Nop();
 if(SDA==1)/*判断是否接收到应答信号*/
  ack=0;//没有接收到应答信号    
 else
  ack=1;//接收到应答信号       
 SCL=0;
 _Nop();
 _Nop();
}

 

再看从任意地址读一个数据


注意哈第一个发送的器件地址是0xa0,后面的是0xa1

所以程序如下

 

/**
* @brief  从24C02读出数据
* @param  None
* @param  Address--地址
* @param  None
* @retval 读到的数据
* @example 
**/
unsigned char ReadData(unsigned char Address)
{
    unsigned char Data =0;
    
    Start_I2c();
    SendByte(0xa0);//最后一位为0
    if(ack==0)return(0);
    
    SendByte(Address);              //发送地址
    if(ack==0)return(0);
    
    Start_I2c();
    
    SendByte(0xa1);//最后一位为1
    if(ack==0)return(0);
    
    Data  = RcvByte();
    Ack_I2c(1);           //发送非就答位
    Stop_I2c();          //结束总线
    return(Data);
}

现在看页写


把程序先放上,对了写的时候的开始地址最好是0,8,16,24,32,40,68,,,,8的倍数,要不然数据可能有错误,当然我用的芯片页写最多一次能写入8个字节.....感觉有点少哈......可以在现在的基础上再做一个函数实现哈,,或者用写单字节的for循环下....

 

/**
* @brief  向24C02写数据----页写,,,最多一次写入8个字节,多了会覆盖前面的
* @param  Data--数据
* @param  StartAddress--开始的地址--最大255
* @param  None
* @retval None
* @example 
**/
unsigned char PageWrite(unsigned char *Data,unsigned char Address,unsigned char cnt)
{
    Start_I2c();
    SendByte(0xa0);//最后一位为0写入
    if(ack==0)return(0);
    
    SendByte(Address);            //发送地址
    if(ack==0)return(0);
    
    while(cnt--)
    {
        SendByte(*Data++);          //发送数据
        if(ack==0)return(0);
         DelayMs(10);
    }
    Stop_I2c();               //结束总线
    return(1);
}

现在看页读


看程序

/**
* @brief  从24C02读出数据----页读
* @param  Data--数据指针
* @param  StartAddress--开始的地址--最大255
* @param  None
* @retval None
* @example 
**/
unsigned char PageRead(unsigned char *Data,unsigned char Address,unsigned char cnt)
{
    Start_I2c();
    SendByte(0xa0);//最后一位为0
    if(ack==0)return(0);
    
    SendByte(Address);              //发送要读的地址
    if(ack==0)return(0);
    
    Start_I2c();
    
    SendByte(0xa1);//最后一位为1
    if(ack==0)return(0);
    
    while(cnt--)
    {
        *Data  = RcvByte();
        Data ++;
        Ack_I2c(0);         //发送应答位
        DelayMs(10);
  }
    
    Ack_I2c(1);           //发送非应答位
    Stop_I2c();           //结束总线
    return(1);
}

 

说一下读的时候最好开始读取的地址是8的倍数,读取的数据个数也是8的倍数,,,我测试的如果不是这样有时候,第二次页读的时候就会读错........

这芯片和8干上了............

还有一个立即读,,,看明白就行,就是立即返回当前读地址加1后的那个数据

源码链接

链接:http://pan.baidu.com/s/1i4M7BId%20密码:r9ov

 

相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
目录
相关文章
|
8月前
|
存储 缓存 Rust
Polars (最强Pandas平替)
Polars (最强Pandas平替)
256 1
|
2月前
|
监控 关系型数据库 MySQL
MySQL自增ID耗尽应对策略:技术解决方案全解析
在数据库管理中,MySQL的自增ID(AUTO_INCREMENT)属性为表中的每一行提供了一个唯一的标识符。然而,当自增ID达到其最大值时,如何处理这一情况成为了数据库管理员和开发者必须面对的问题。本文将探讨MySQL自增ID耗尽的原因、影响以及有效的应对策略。
167 3
|
8月前
在 Win 11 中添加小鹤双拼
在 Win 11 中添加小鹤双拼
316 0
|
运维 Kubernetes 监控
阿里云 ACK 云上大规模 Kubernetes 集群高可靠性保障实战
本文基于 ACK 稳定性保障实践经验,帮助用户全面理解 ACK 稳定性理论和优化策略,并了解如何使用相应的工具和服务进行稳定性保障。
184684 23
|
XML SQL Java
Spring 中声明式事务和编程式事务的使用
Spring 中声明式事务和编程式事务的使用
177 0
|
8月前
|
Web App开发 小程序 Android开发
Uniapp 视图容器 随机拖拽滑动 放大缩小 movable-view movable-area
Uniapp 视图容器 随机拖拽滑动 放大缩小 movable-view movable-area
232 0
|
网络协议 Linux 网络安全
使用frp时遇到的问题connect: connection refuseddial tcp xxxx:7000: connect: connection refused
最近在做的项目需要用到frp来做代理连接本地内网机,卡在最后启动客户端的时候,提示报错:login to server failed: dial tcp xxxx:7000: connect: connection refuseddial tcp xxxx:7000: connect: connection refused!!找了很多尝试的办法,现在给大家列一下希望对大家有帮助。
3567 0
|
传感器 数据采集 SDN
STM32(HAL库)驱动AD8232心率传感器
STM32(HAL库)驱动AD8232心率传感器
|
JSON JavaScript 前端开发
学习Vue3 第三章(Vite目录 & Vue单文件组件 & npm run dev 详解)
index.html 非常重要的入口文件 (webpack,rollup 他们的入口文件都是enrty input 是一个js文件 而Vite 的入口文件是一个html文件,他刚开始不会编译这些js文件 只有当你用到的时候 如script src="xxxxx.js" 会发起一个请求被vite拦截这时候才会解析js文件)
402 0
学习Vue3 第三章(Vite目录 & Vue单文件组件 & npm run dev 详解)
|
机器学习/深度学习 传感器 自然语言处理
【LSTM回归预测】基于长短期记忆网络的数据回归预测附matlab完整代码
【LSTM回归预测】基于长短期记忆网络的数据回归预测附matlab完整代码

热门文章

最新文章

下一篇
开通oss服务