I2C
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);
}
}
本文来自博客园,作者:Laplace蒜子,转载请注明原文链接:https://www.cnblogs.com/RedNoseBo/p/17831006.html