IIC通信与EEPROM的应用

文章目录

  • 前言
  • 一、IIC通信
    • 1.名词解释
    • 2.基本通信时序
    • 3.具体通信时序
      • 1.主设备向从设备发送数据
      • 2.从设备向主设备发送数据
    • 4.编程实现
      • 1.iic.h
      • 2.iic.c
  • 二、AT24C02的应用
    • 1.主要特性
    • 2.引脚定义和原理图
    • 3.写时序
    • 4.读时序
    • 5.编程实现
      • 1.AT24C02.h
      • 2.AT24C02.c
  • 三、实验测试
    • 1.smg.h
    • 2.smg.c
    • 3.main.c
  • 四、测试现象
  • 总结


前言

  本次编程实验以IAP15F2K61S2为单片机主控芯片,头文件为STC15F2K60S2.H。若用于51系列单片机,以reg52.h为头文件,则读者需将程序中可能涉及的定时器初始化程序和LED亮灭程序和数码管显示程序,根据自身所用单片机原理图和手册进行修改。


一、IIC通信

  IIC通信为串行通信,总线结构为一主多从,即总线上的每个设备都有一个特定的设备地址,用来区分同一IIC总线上的其他设备。IIC总线有两根双向线,串行时钟线(SCL)和串行数据线(SDA)组成,可用于发送和接收数据,但是通信都是由主设备发起,从设备只能被动响应,实现数据的传输。

1.名词解释

  1.时钟及数据传输:SDA 引脚的数据应在 SCL 为低时变化,否则当数据在 SCL 为高时变化时,将被视为开始或停止信号(开始或停止信号见下文所述)。
  2.开始信号:当 SCL 为高,SDA 由高到低的变化被视为开始信号,用于启动IIC通信,必须以开始信号作为任何一次读/写操作命令
的开始(读/写操作命令见下文)。
  3.停止信号:当 SCL 为高,SDA 由低到高的变化被视为停止信号,在数据传输完成后,停止信号用于结束IIC通信。
  4.应答位:所有的从设备地址和数据字节都是以 8 位为一组串行输入和输出的。当从设备每收到一组 8 位的数据后,会向主设备发送一个应答位,用于告知主设备是否接收成功(0成功,1非成功);当主设备每收到一组 8 位的数据后,会向从设备发送一个应答位,用于告知从设备是否需要继续发送数据(0需要,1不需要)。

2.基本通信时序

  IIC通信基本时序图如下:
IIC通信与EEPROM的应用_第1张图片
  具体步骤如下:
  1.首先,主设备需要发送一个开始(START)信号,用于“唤醒”挂在IIC总线上的从设备;
  2.之后发送8位数据,位0-位7用于声明从设备的地址(ADDRESS),建立与特定从设备的通信,位8用于声明此次是主—从还是从—主(数据流向,0主—从,1从—主);
  3.在从设备接收到主设备发来的通信请求时,会返回一个应答位,用于主设备确认从设备是否准备好接收数据;
  4.在主设备确认从设备准备好后,如果数据流向是主—从,那么主设备将向从设备发送8位数据,从设备接收完成后发回一个应答位,告诉主设备是否接收成功;如果数据流向是从—主,那么从设备将向主设备发送8位数据,主设备接收完成后发回一个应答位,告诉从设备是否需要继续发送数据;
  5.在数据完成收发后,主设备需要发送一个停止(STOP)信号,结束通信。
  注意:有时我们需要让主从设备之间收发多个数据,则每完成一个数据字节的收发时,需要进行一次应答位的收发和判断,直至数据收发完成后,再让主设备发送停止信号。SDA所有数据的发均在SCL的上升沿完成,SCL每迎来一次上升沿,SDA发送一位数据,高位在前,低位在后。

3.具体通信时序

1.主设备向从设备发送数据

  1. 主设备发送开始信号,“唤醒”挂在IIC总线上的从设备;
  2. 主设备发送从设备地址,选择与哪个从设备通信;
  3. 等待从设备响应;
  4. 主设备发送数据到从设备,每发送一个数据字节,接收一次从设备的应答;
  5. 数据发送完毕,主设备发送停止信号,结束通信。
IIC通信与EEPROM的应用_第2张图片

2.从设备向主设备发送数据

  1. 主设备发送开始信号;
  2. 主设备发送从设备地址,选择与哪个从设备通信;
  3. 等待从设备响应;
  4. 主设备接收来自从设备的数据,每接收一个数据字节,向从设备发送一个应答;
  5. 主设备接收到最后一个数据后,发送一个无效的应答,然后主设备发送停止信号,结束通信。

IIC通信与EEPROM的应用_第3张图片

4.编程实现

1.iic.h

#ifndef _IIC_H
#define _IIC_H

#include 
#include "intrins.h"

void IIC_Start(void);   //开始信号
void IIC_Stop(void);    //停止信号
bit IIC_WaitAck(void);  //主设备等待从设备的应答
void IIC_SendAck(bit ackbit);   //主设备向从设备发送应答
void IIC_SendByte(unsigned char byt);   //主设备发送一个字节数据
unsigned char IIC_RecByte(void);    //主设备接收一个字节数据

#endif


2.iic.c

#include "iic.h"
#define DELAY_TIME 5

//总线引脚定义
sbit SDA = P2^1;  /* 数据线 */
sbit SCL = P2^0;  /* 时钟线 */
//IIC专用延时函数
void IIC_Delay(unsigned char i)
{
    do{_nop_();}
    while(i--);        
}
//总线启动条件
void IIC_Start(void)
{
    SDA = 1;   //将SDA和SCL拉高
    SCL = 1;
    IIC_Delay(DELAY_TIME);  //防止单片机运行速度过快,总线和从设备反应不及时
    SDA = 0;		//SCL为高时,SDA迎来下降沿,视为开始信号
    IIC_Delay(DELAY_TIME);
    SCL = 0;	//SCL拉低,准备发送字节
}

//总线停止条件
void IIC_Stop(void)
{
    SDA = 0;  //将SDA拉低
    SCL = 1;  //将SCL拉高
    IIC_Delay(DELAY_TIME);
    SDA = 1;  //SCL为高时,SDA迎来下降沿,视为停止信号
    IIC_Delay(DELAY_TIME);
}

//主设备发送应答
void IIC_SendAck(bit ackbit)
{
    SCL = 0;    //SCL拉低,使SDA上的数据可以改变
    SDA = ackbit;  					// 0:应答,1:非应答
    IIC_Delay(DELAY_TIME);
    SCL = 1;   //SCL拉高,迎来上升沿,使SDA上的数据发送
    IIC_Delay(DELAY_TIME);
    SCL = 0;   //拉低SCL
    SDA = 1;   //拉高SDA,这两句都是为了发数据做准备
    IIC_Delay(DELAY_TIME);
}

//主设备等待从设备应答
bit IIC_WaitAck(void)
{
    bit ackbit;
	
    SCL  = 1;   //由于开始信号和主设备发送完一个字节数据后,SCL最后均置0,所以此时SCL迎来上升沿
    IIC_Delay(DELAY_TIME);
    ackbit = SDA;   //取出从设备的应答
    SCL = 0;   //拉低SCL
    IIC_Delay(DELAY_TIME);
    return ackbit;  //返回从设备的应答,可用于进行扩展判断
}

//通过I2C总线发送数据
void IIC_SendByte(unsigned char byt)  //byt为要发送的字节数据
{
    unsigned char i;

    for(i=0; i<8; i++)
    {
        SCL  = 0;			//在SCL低电平时,改变SDA上的数据
        IIC_Delay(DELAY_TIME);
        if(byt & 0x80) SDA  = 1;  //取出byt最高位的数据
        else SDA  = 0;
        IIC_Delay(DELAY_TIME);
        SCL = 1;   //SCL上升沿,SDA发送一位数据
        byt <<= 1;  //byt左移一位,使位6变成位7,位5变成位6...
        IIC_Delay(DELAY_TIME);
    }
    SCL  = 0;  //拉低SCL
}

//从I2C总线上接收数据,各语句作用请读者自行思考
unsigned char IIC_RecByte(void)
{
  unsigned char i, byt;
	for(i=0; i<8; i++)
	{   
		SCL = 1;
		IIC_Delay(DELAY_TIME);
		byt <<= 1;
		if(SDA) byt |= 1;
		SCL = 0;
		IIC_Delay(DELAY_TIME);
	}
	return byt;    
}

二、AT24C02的应用

  AT24C02是一个2KBit的串行EEPROM存储器(掉电不丢失),内部含有256个字节,每个8字节为一页,通过IIC总线与单片机进行通信。

1.主要特性

IIC通信与EEPROM的应用_第4张图片

2.引脚定义和原理图

IIC通信与EEPROM的应用_第5张图片

IIC通信与EEPROM的应用_第6张图片
  其中,A2-A0为器件寻址:24C02 在一个IIC总线上最多可寻址八个,A2-A0的用于选择哪一个24C02;WP:写保护,置高电平时无法向24C02写入数据。
IIC通信与EEPROM的应用_第7张图片
  由原理图可知,A2-A0均接地,即此单片机只有一块24C02;WP接地,即写保护一直关闭。

3.写时序

IIC通信与EEPROM的应用_第8张图片
  1.MCU发送一个开始信号;
  2.发送24C02写操作地址0xA0,等待应答信号;
  3.发送数据的存储地址。24C02一共有256个字节的存储空间,地址从0x00~0xFF,想要存在哪就写入哪,等待应答信号;
  4.发送要写入的数据。数据发送可以有多个,数据存储地址会自动加一,但不能跨页写入,且写入多个字节时,每写入一个字节后需要等待应答信号,再写入下一个字节;
  5.MCU发送结束信号,停止通信。

4.读时序

IIC通信与EEPROM的应用_第9张图片

  1.MCU发送一个开始信号;
  2.发送器件写操作地址0xA0,等待应答信号;
注意:这里写操作是为了把要读出数据的存储地址先写进去,告诉24C02要读取哪个地址的数据。
  3.发送要读取内存的地址,等待应答信号;
  4.MCU重新发送开始信号;
  5.发送设备读操作地址0xA1,等待应答信号;
  6.24C02会自动向主机发送数据,主机读取数据。在读一个字节后,如果MCU回应一个应答信号0,24C02会继续传输下一个地址的数据,直到MCU发送一个非应答信号1。
  7.MCU发送结束信号,结束通信。

5.编程实现

1.AT24C02.h

#ifndef __AT24C02_H
#define __AT24C02_H

#include 
#include "iic.h"

void at24c02_write(unsigned char addr,byte);  //写数据
void at24c02_delay5ms(void);	//@11.0592MHz
unsigned char at24c02_read(unsigned char addr);  //读数据

#endif

2.AT24C02.c

#include "at24c02.h"

/*
  *  @brief     写入一个字节数据
  *  @param     addr:数据存储地址;byt:要存储的数据
  *  @reval      
  *  @note:    
*/

void at24c02_write(unsigned char addr,byte)
{
	IIC_Start();   //开始信号
	IIC_SendByte(0xa0);  //建立与24C02的通信,写操作
	IIC_WaitAck();   //等待应答
	IIC_SendByte(addr);  //发送数据的存储地址
	IIC_WaitAck();  //等待应答
	IIC_SendByte(byte);  //发送存储数据
	IIC_WaitAck();  //等待应答
	IIC_Stop();    //停止信号
}

/*
  *  @brief     24C02专用5ms延时
  *  @param    
  *  @reval      
  *  @note:     当连续调用at24c02_write()写入数据时,每调用一次需要延时5ms
*/

void at24c02_delay5ms(void)	//@11.0592MHz
{
	unsigned char data i, j;

	i = 54;
	j = 199;
	do
	{
		while (--j);
	} while (--i);
}

/*
  *  @brief     读出一个字节数据
  *  @param     addr:要读出数据的存储地址
	*  @reval     temp:读出的数据
  *  @note:    
*/

unsigned char at24c02_read(unsigned char addr)
{
	unsigned char temp=0;
	
	IIC_Start();   //开始信号
	IIC_SendByte(0xa0);  //建立与24C02的通信,写操作
	IIC_WaitAck(); //等待应答
	IIC_SendByte(addr);  //发送要读出数据的存储地址
	IIC_WaitAck(); //等待应答
	
	IIC_Start();	 //重新发送开始信号
	IIC_SendByte(0xa1);  //建立与24C02的通信,读操作
	IIC_WaitAck(); //等待应答
	temp=IIC_RecByte();  //读出数据
	IIC_SendAck(1); //发送非应答位
	IIC_Stop();    //停止信号
	
	return temp;  //返回读出的数据
}

注意:上述程序只适应一个字节数据的收发,如何一次收发多个数据,请读者结合上述内容自行思考。

三、实验测试

  在进行测试前,我们首先需要一个可视化工具,为此加入数码管显示模块。

1.smg.h

#ifndef __SMG_H
#define __SMG_H

#include 

#define outputp0(y,x)  P0=x,P2&=0x1f,P2|=y,P2&=0x1f;

void showbit(unsigned char pos,dat,dot);
#endif

2.smg.c

#include "smg.h"

unsigned char code Seg_Table[]={
0xc0,   //共阳数码管
0xf9,
0xa4,
0xb0,
0x99,
0x92,
0x82,
0xf8,
0x80,
0x90,
0xff
};

/*
  *  @brief     1位数码管显示函数
  *  @param     pos:位选;dat:显示数字;dot:小数点选择位,1有0无
  *  @reval      
  *  @note:    
*/

void showbit(unsigned char pos,dat,dot)
{
	outputp0(0xc0,0x01<<pos);
	outputp0(0xe0,Seg_Table[10]);  //这两行用于消影
	
	outputp0(0xc0,0x01<<pos);   //位选
	outputp0(0xe0,Seg_Table[dat]+0x80*dot);	  //段选
}

3.main.c

  创建一个新工程,将上述所有代码分模块添加入工程中,主程序如下:

#include 
#include "at24c02.h"
#include "smg.h"

unsigned char showbyte=0;
void Delay2ms(void)	//@11.0592MHz
{
	unsigned char data i, j;

	_nop_();
	_nop_();
	i = 22;
	j = 128;
	do
	{
		while (--j);
	} while (--i);
}

void test()
{
	showbit(0,showbyte,0);
	Delay2ms();	
}

void main()
{
	at24c02_write(0x00,0x03);  //第二次上电删除
	at24c02_delay5ms();   //第二次上电删除
	showbyte=at24c02_read(0x00);
	while(1)
	{
		test();	
	}
}

四、测试现象

  下载程序后,数码管显示3;之后将标记的代码删除,编译再次下载,数码管显示3,说明测试成功。


总结

  IIC作为极其重要的通信方式,掌握它的原理和编程是十分有必要。在此次编程实验中,讲述了24C02一次收发多字节的原理和时序,未给出编程实现,读者可自行思考。
  与IIC较为接近的通信方式还有SPI通信,具体见DS1302原理和代码或51单片机DS1302可调时钟。
  有任何问题和补充,欢迎私信或评论区交流。

你可能感兴趣的:(单片机,单片机,蓝桥杯,c语言,mcu,51单片机)