MCU 51-7 I2C Communication EEPROM
Posted darren-pty
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MCU 51-7 I2C Communication EEPROM相关的知识,希望对你有一定的参考价值。
目前常用的微机与外设之间进行数据传输的串行总线主要有I2C总线、SPI总线和SCI总线。
其中I2C总线以同步串行2线方式进行通信(一条时钟线,一条数据线)。
SPI总线则以同步串行3线方式进行通信(一条时钟线,一条数据输入线,一条数据输出线)。 SCI总线是以异步方式进行通信(一条数据输入线,一条数据输出线)。
1-wire,即单线总线,又叫单总线。
IIC Communication
I2C总线是PHLIPS公司推出的一种串行总线,它只有两根双向信号线。一根是数据线SDA(serial data I/O),另一根是时钟线SCL(serial clock)。
如下图所示,IIC总线上可以挂多个器件,而每个器件都有唯一的地址,这样可以标识通信目标。数据的通信的方式采用主从方式,主机负责主动联系从机,而从机则被动回应数据。
I2C总线通过上拉电阻接正电源。当总线空闲时,两根线均为高电平。连到总线上的任一器件输出的低电平,都将使总线的信号变低,即各器件的SDA及SCL都是线“与”关系。
开发板上的 I2C总线以及总线上的设备AT24C02
I2C总线传输协议
数据位的有效性规定
SCL为高电平期间,数据线上的数据必须保持稳定,只有SCL信号为低电平期间,SDA状态才允许变化。
I2C的起始和终止信号
SCL线为高电平期间,SDA线由高电平向低电平的变化表示起始信号;SCL线为高电平期间,SDA线由低电平向高电平的变化表示终止信号。
I2C字节的传送与应答
每一个字节必须保证是8位长度。数据传送时,先传送最高位(MSB),每一个被传送的字节后面都必须跟随一位应答位(即一帧共有9位)。
I2C字节的传送与应答
每一个字节必须保证是8位长度。数据传送时,先传送最高位(MSB),每一个被传送的字节后面都必须跟随一位应答位(即一帧共有9位)。
应答位的作用
主机在发送数据时,每次发送一字节数据,都需要读取从机应答位,当从机空闲可以接收该字节数据时,从机会发出应答(一帧数据的第9位为“0”),当从机正忙于其他工作的处理来不及接收主机发送的数据时,从机会发出非应答(一帧数据的第9位为“1”)主机则应发出终止信号以结束数据的继续传送,主机通过从机发出的应答位来判断从机是否成功接收数据。 当主机接收数据时,它收到最后一个数据字节后,必须向从机发出一个结束传送的信号。这个信号是由对从机的“非应答”来实现的。然后,从机释放SDA线,以允许主机产生终止信号。
I2C写数据流程
在起始信号后必须传送一个从机的地址(7位)我们开发板上的AT24C02地址为0xa0,第8位是数据的传送方向位(R/T),用“0”表示主机发送数据(T),“1”表示主机接收数据(R)。
I2C读数据流程
在读数据时也要先发送器件地址,读写方向为写,因为我们下一帧需要发送从AT24C02内那个单元开始读,之后需在发一次器件地址这个时候读写方向就为读了,接着我们就可以从总线上读取数据。
软件模拟I2C通信时序
I2C总线的数据传送有严格的时序要求。I2C总线的起始信号、终止信号、发送“0”及发送“1”的模拟时序 :
//I2C起始信号程序 void I2C_Start() { SCL = 1; _nop_(); //1.08506us SDA = 1; delay_5us(); SDA = 0; delay_5us(); }
//I2C终止信号程序 void I2C_Stop() { SDA = 0; _nop_(); SCL = 1; delay_5us(); SDA = 1; delay_5us(); }
//I2C主机检测从机应答 bit Test_ACK() { SCL = 1; //在时钟总线为高电平期间可以读取应答信号 delay_5us(); if (SDA) { SCL = 0; I2C_Stop(); return(0); } else { SCL = 0; return(1); } }
//I2C主机发送应答 void Master_ACK(bit i) { SCL = 0; _nop_(); if (i) { SDA = 0; } else { SDA = 1; } _nop_(); SCL = 1;//数据保持稳定 _nop_(); SCL = 0; _nop_(); SDA = 1; _nop_(); }
# include <reg52.h> # include <intrins.h> #define uchar unsigned char #define uint unsigned int #define AT24C02_ADDR 0xa0 //AT24C02地址 /*I2C IO口定义*/ sbit SDA = P2^0; sbit SCL = P2^1; /*5us延时*/ void delay_5us() { _nop_(); } /*1Ms延时*/ void delay(uint z) { uint x,y; for(x = z; x > 0; x--) for(y = 114; y > 0 ; y--); } /*I2C初始化*/ void I2C_init() { SDA = 1; _nop_(); SCL = 1; _nop_(); } /*I2C起始信号*/ void I2C_Start() { SCL = 1; _nop_(); SDA = 1; delay_5us(); SDA = 0; delay_5us(); } /*I2C终止信号*/ void I2C_Stop() { SDA = 0; _nop_(); SCL = 1; delay_5us(); SDA = 1; delay_5us(); } /*主机发送应答*/ void Master_ACK(bit i) { SCL = 0; // 拉低时钟总线允许SDA数据总线上的数据变化 _nop_(); // 让总线稳定 if (i) //如果i = 1 那么拉低数据总线 表示主机应答 { SDA = 0; } else { SDA = 1; //发送非应答 } _nop_();//让总线稳定 SCL = 1;//拉高时钟总线 让从机从SDA线上读走 主机的应答信号 delay_5us(); SCL = 0;//拉低时钟总线, 占用总线继续通信 _nop_(); SDA = 1;//释放SDA数据总线。 _nop_(); } /*检测从机应答*/ bit Test_ACK() { SCL = 1; delay_5us(); if (SDA) { SCL = 0; _nop_(); I2C_Stop(); return(0); } else { SCL = 0; _nop_(); return(1); } } /*发送一个字节*/ void I2C_send_byte(uchar byte) { uchar i; for(i = 0 ; i < 8 ; i++) { SCL = 0; _nop_(); if (byte & 0x80) { SDA = 1; _nop_(); } else { SDA = 0; _nop_(); } SCL = 1; _nop_(); byte <<= 1; // 0101 0100B } SCL = 0; _nop_(); SDA = 1; _nop_(); } /*I2C 读一字节*/ uchar I2C_read_byte() { uchar dat,i; SCL = 0; _nop_(); SDA = 1; _nop_(); for(i = 0 ; i < 8 ; i++) { SCL = 1; _nop_(); if (SDA) { dat |= 0x01; // } else { dat &= 0xfe; //1111 1110 } _nop_(); SCL = 0 ; _nop_(); if(i < 7) { dat = dat << 1; } } return(dat); } /*I2C发送数据*/ bit I2C_TransmitData(uchar ADDR, DAT) { I2C_Start(); I2C_send_byte(AT24C02_ADDR+0); if (!Test_ACK()) { return(0); } I2C_send_byte(ADDR); if (!Test_ACK()) { return(0); } I2C_send_byte(DAT); if (!Test_ACK()) { return(0); } I2C_Stop(); return(1); } /*I2C接收数据*/ uchar I2C_ReceiveData(uchar ADDR) { uchar DAT; I2C_Start(); I2C_send_byte(AT24C02_ADDR+0); if (!Test_ACK()) { return(0); } I2C_send_byte(ADDR); Master_ACK(0); I2C_Start(); I2C_send_byte(AT24C02_ADDR+1); if (!Test_ACK()) { return(0); } DAT = I2C_read_byte(); Master_ACK(0); I2C_Stop(); return(DAT); } void main() { I2C_init();//I2C初始化 if(!I2C_TransmitData(255,0xf0)); //往AT24C02第255个单元中写入数据0XF0 { P1 = 0; } delay(5); /**/ P1 = I2C_ReceiveData(255);//从AT24C02第255个单元中读取数据 while(1); }
往QX-MCS51开发板的AT24C02内任意一个单元写数据,开发板上电后首先读取此单元的原有的数据,然后赋给程序中的计数变量,计数变量再以5秒的速度+1并且写入AT24C02,当计数大于99时清零再写,用数码管显示。
以上是关于MCU 51-7 I2C Communication EEPROM的主要内容,如果未能解决你的问题,请参考以下文章
ARM Cortex-M3 MCU的I2C&DMA操作被频繁的中断打挂了