I2C

发布时间 2023-11-14 15:41:35作者: Laplace蒜子
u8 time_temp=0;
IIC_SCL=1;//时钟线为高电平
delay_10us(1);
while(IIC_SDA){//等待数据线为0
  time_temp++;//计数超时
  if(time_temp>100){
    iic_stop();
    return 1;
  }
}
IIC_SCL=0;//时钟线为0,允许从机写
return 0;

I2C

I2C 总线只有两根双向信号线。一根是数据线 SDA,另一根是时钟线 SCL。

I2C特点

支持多设备。使用仲裁方式防止数据冲突。

连接到总线的设备都有独立地址。

数据线表示数据,时钟线用于数据收发同步。

总线通过上拉电阻接到电源。

速度最快3.4Mbit/s。

 

I2C协议

基本约束:传输数据时,时钟线高电平的时候,数据线不能跳变,时钟线为低电平时,数据线才可以跳变。

信号 作用   跳变 代码   图
起始信号 占用总线 时钟线:高电平;数据线:高->低(下降沿)

IIC_SCL=1;

IIC_SDA=1;

delay_10us(1);

IIC_SDA=0

delay_10us(1);

IIC_SCL=0;//钳住总线

停止信号 释放总线 时钟线:高电平;数据线:低->高(上升沿)

IIC_SCL=1;

IIC_SDA=0;

delay_10us(1);

IIC_SDA=1

delay_10us(1);

应答ACK 希望对方继续发送数据

主机发送完第8个数据帧时,释放数据线,由从机掌控数据线。但时钟线是由主机控制的

时钟线为低电平时,数据线跳到低电平(设定数据线信号为0),然后时钟线跳到高电平稳定一段时间再变低电平。(保持数据线信号)。

若从机在时钟线低电平的时候没来得及跳变,之后时钟线变为高电平就无法跳变,此时就默认是非应答了。(要在主机控制时钟线为低电平时及时决定是否跳变)

IIC_SCL=0;//允许设置

IIC_SDA=0; //设置为0

delay_10us(1);

IIC_SCL=1;//保持

delay_10us(1);

IIC_SCL=0;//释放

非应答ACK 希望对方停止发送数据

主机发送完第8个数据帧时,释放数据线,由从机掌控数据线。但时钟线是由主机控制的。

时钟线为低电平时,数据线跳到高电平(设定数据线信号位1),然后时钟线跳到高电平稳定一段时间再变成低电平。(保持数据线信号)。

IIC_SCL=0;//允许设置

IIC_SDA=1; //设置为1

delay_10us(1);

IIC_SCL=1;//保持

delay_10us(1);

IIC_SCL=0;//释放

 

总线寻址

10为寻址和7位寻址兼容,可以结合使用,10位可以向下兼容7位,因此以7位为例子介绍。

从机地址用7位表示,从机接到一个信号后与自己地址比较,判断自己是否为传输目标。

通常7位寻址位有固定位和可编程位,可编程位的个数决定接入总线期间的最大数目。如7位寻址,有4位固定,3位可编程,则可以接8位。

R/W表示数据传输方向,0表示主机写数据到从机,1表示主机读从机数据。

主机向一个从机发送完后又向另一个从机发送,可以不产生终止信号放弃主线,直接发另一个从机地址即可。

主机向一个从机发送数据后,想改变传输方向,此时需要重复发送一次从机地址和新的方向。

 

数据传输

当寻址完毕后,就可以发送数据,一个数据为8帧。第9帧由从机控制数据线,决定应答还是非应答。

 

 

AT24C系列芯片

芯片作用

用于存储值,即使在断电情况都不会清空。该芯片是接入在I2C上的,同I2C操作

 

芯片  串行CMOS 8位字节个数 字节页缓冲区
AT23C01 1K 128 8字节页
AT23C02 2K 256 16字节页
AT23C03 3K 512 16字节页
AT23C04 8K 1024 16字节页
AT23C05 16K 2048 16字节页

 

AT24C02器件寻址位为7位,高四位固定位1010,低三位由A0,A1,A2表示。

在51开发板中,芯片的A0/A1/A2已经接地,索引该芯片在总线上的器件地址为1010 000;

SDA:数据线引脚

SCL:时钟线引脚

WP:写保护。

VCC:电源。

VSS:接地。

若要从芯片读数据,则发送数据为1010 000 1,十六进制为0xA1;若要向芯片写数据,则发送数据为1010 000 0,十六进制为0xA1。

AT24芯片操作方式

不管是读写操作,都需要先在总线上写入寻址地址,确定寻址之后,才可以继续其他操作。

写操作,需要先用写模式,写入想保存的芯片内存位置地址,然后再写入数据。

读操作,需要先用写模式,写入向读取的芯片内存位置地址,然后再发送寻址改变成读模式,开始接收数据。

AT24C2芯片写操作 

void at24c02_write_one_byte(u8 addr,u8 dat)
{
    iic_start();
    iic_write_byte(0XA0); //在总线上寻址芯片地址
    iic_wait_ack();
    iic_write_byte(addr);//发送写地址,data数据存储在AT24芯片的内存位置
    iic_wait_ack();
    iic_write_byte(dat); //发送字节,发送数据
    iic_wait_ack();
    iic_stop(); //产生一个停止条件
    delay_ms(10);
}

AT24C2芯片读操作

u8 at24c02_read_one_byte(u8 addr)
{
    u8 temp=0;
    iic_start();
    iic_write_byte(0XA0); //向AT24芯片发送写命令
    iic_wait_ack();
    iic_write_byte(addr); //向AT24写入要读取的数据在芯片内存的位置
    iic_wait_ack();
    iic_start();
    iic_write_byte(0XA1); //进向AT24芯片发送接收模式
    iic_wait_ack();
    temp=iic_read_byte(0); //开始读取字节
    iic_stop(); //产生一个停止条件
    return temp; //返回读取的数据
}

 

驱动代码

等待ACK

u8 time_temp=0;
IIC_SCL=1;//时钟线为高电平
delay_10us(1);
while(IIC_SDA){//等待数据线为0
  time_temp++;//计数超时
  if(time_temp>100){
    iic_stop();
    return 1;
  }
}

IIC_SCL=0;//时钟线为0,钳住

return 0;    

写一个字节

u8 i=0;
IIC_SCL=0;//设置为0,允许数据线写
for(i=0;i<8;i++) //循环 8 次将一个字节传出,先传高再传低位
{
    if((dat&0x80)>0)//数据线写0和1
        IIC_SDA=1;
    else
        IIC_SDA=0;
    dat<<=1; 
    delay_10us(1);
    IIC_SCL=1;   //时钟线保持
    delay_10us(1); 
    IIC_SCL=0;   //时钟线允许写
    delay_10us(1);
}

读一个字节

u8 i=0,receive=0;
for(i=0;i<8;i++ ) //循环 8 次将一个字节读出,先读高再传低位
{
    IIC_SCL=0;
    delay_10us(1);
    IIC_SCL=1;
    receive<<=1; //从高位开始读
    if(IIC_SDA)receive++; //数据线为1,rec+1,否则为0
    delay_10us(1);
 }
if (!ack) //应答
    iic_nack();
else
    iic_ack();
return receive;
}
/*
假设有14,存储的二进制位1110
进制转换
初始rec=0,左移后,rec=0,读到1,rec=1
rec左移,rec=2,读到1,rec=3
rec左移,rec=6,读到1,rec=7
rec左移,rec=14,读到0,rec=14
左移为乘以2。

二进制转换十进制就是
从高到低遍历,每遍历一次都乘2,如果当前位为1则加1,否则不加。最终的到十进制。
/*

 

数码管按键小程序

/********************************************************************
****************** 实验名称:I2C-EEPROM 实验
接线说明:
实验现象:下载程序后,数码管右 4 位显示 0,按 K1 键将数据写入到 EEPROM 内保存,
按 K2 键读取 EEPROM 内保存的数据,按 K3 键显示数据加 1,按 K4 键显示数据清
零,
最大能写入的数据是 255。
注意事项:
*********************************************************************
******************/
#include "public.h"
#include "24c02.h"
#include "key.h"
#include "smg.h"
#define EEPROM_ADDRESS 0 //定义数据存入 EEPROM 的起始地址
/********************************************************************
***********
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*********************************************************************
**********/
void main()
{
    u8 key_temp=0;
    u8 save_value=0;
    u8 save_buf[3];
    while(1){    
        key_temp=key_scan(0);
        if(key_temp==KEY1_PRESS){
            at24c02_write_one_byte(EEPROM_ADDRESS,save_value);
        }
        else if(key_temp==KEY2_PRESS){
            save_value=at24c02_read_one_byte(EEPROM_ADDRESS);
        }
        else if(key_temp==KEY3_PRESS){
            save_value++;
            if(save_value==255)save_value=255;
        }
        else if(key_temp==KEY4_PRESS){
            save_value=0;
        }
        save_buf[0]=save_value/100;
        save_buf[1]=save_value%100/10;
        save_buf[2]=save_value%100%10;//save buf用三个位表示,在数码管上三位显示。
        smg_display(save_buf,6);
    }
}