【STC单片机学习】第十五课:I2C通信-EEPROM

【朱老师课程总结 侵删】

本节课我们来学习如何使用 51 单片机的 IO 口模拟 I2C 时序,并实现与 AT24C02(EEPROM)之间的双向通信。

第二部分、章节介绍

1.15.1.EEPROM及其背景知识
本节围绕EEPROM讲述其相关概念,重点是单片机系统中的存储器和I2C接口。
1.15.2.原理图和数据手册1
本节先简单分析原理图和接线,然后重点围绕AT24C02的数据手册进行带读。
1.15.3.原理图和数据手册2
本节主要讲述I2C协议的关键概念,以及数据手册中和I2C协议有关的部分。
1.15.4.I2C低层时序图和程序1
本节主要分析官方例程中的底层时序部分代码,并且结合数据手册中的时序图来对照分析
1.15.5.EEPROM读写测试1
本节讲述EEPROM的上层读写时序,重点是I2C从地址的确定原则和寄存器设置。
1.15.6.EEPROM读写测试2
本节接着分析EEPROM的上层读写时序,重点是数据手册中的上层时序图。

第三部分、随堂记录

1.15.1.EEPROM及其背景知识

1.15.1.1、EEPROM
(1)一些概念:

  • ROM(只读存储器)、RAM(随机存取存储器)、PROM(可编程ROM)、EPROM(可擦除ROM)、EEPROM(电可擦除ROM)
    踩链接简单了解下吧!

(2)为什么需要EEPROM?

  • 单片机内部的ROM只能在程序下载时进行擦除和改写,但是程序运行本身是不能改写的。单片机内部的RAM中的数据程序运行时可以改,但是掉电就丢失了。有时候我们有一些数据要存在系统中,要求掉电不丢失,而且程序还要能改。所以内部ROM和RAM都不行。这时候系统中就需要一块EEPROM

(3)EEPROM和flash的区别与联系

  • flash属于广义的EEPROM,因为它也是电擦除的rom。但是为了区别于一般的按字节为单位的擦写的EEPROM,我们都叫它flash。
    flash做的改进就是擦除时不再以字节为单位,而是以块为单位,一次简化了电路,数据密度更高,降低了成本。上M的ROM一般都是flash。

(4)EEPROM存在系统中的2种形式:内置在单片机内部,外部扩展
EEPROMEEPROM原理图

1.15.1.2、EEPROM如何编程

  • I2C接口底层时序
  • 器件定义的寄存器读写时序

1.15.2.原理图和数据手册1

1.15.2.1、原理图和接线确定
(1)关键性引脚定义及其接法

SCL 时钟线,传输 CLK 信号。
SDA 数据线,传输通信数据
WE 写保护使能信号

(2)接线

P2.0接SCL
P2.1接SDA
WE接GND

1.15.2.2、AT24C02数据手册浏览
(1)芯片信息

  • 描述
  • 特点
  • 框图
  • 引脚说明

(2)I2C基本信息在这里插入图片描述

  • 详细操作说明
  • I2C手册-突出特征
  • 主器件、从器件、发送器、接收器
  • 器件寻址(区分从设备)

(3)I2C低层时序
在这里插入图片描述

  • 起始信号:SCL高电平,SDA下降沿
  • 停止信号:SCL高电平,SDA上升沿
  • 发送字节:开始之后,主设备广播式,先发送从地址,然后发有效数据,最后是停止
  • 读取字节:开始之后,从设备先发送自己的地址,然后把数据放总线就行,最后停止
  • 重复起始条件:和起始条件相似,重复起始条件发生在停止条件之前。主机想继续给从机发送消息时,一个字节传输完成后可以发送重复起始条件,而不是产生停止条件。

(4)I2C总结

  • 主CPU和其附属芯片之间最常用的接口,尤其是各种传感器,物联网时代非常重要;
  • 三根线:GND、SCL、SDA,串行、电平式;
  • 总线式结构,可以一对多,总线上可以挂上百个器件,用从地址来区分;
  • 主从式,由主设备来发起通信及总线仲裁,从设备被动响应;
  • 同一时刻,只能有一个从设备和主设备通信,其他从设备处于“冬眠”状态;
  • 通信速率一般(kbps级别),不适合语音、视频等信息类型;
  • 器件地址信息的LSB为读/写操作选择位,高为读操作,低为写操作;
  • 数据传输是大端传输,也就是先传输高位
  • 数据传输是以字节为单位,而且可以发送连续多个字节;
  • 开始和停止条件由主设备控制,接收和应答分别由接收器和发送器执行。

1.15.4.I2C低层时序图和程序

官方示例程序:\步骤3 51例程\16、EEPROM(24C02)\数码管显示EEPROM\程序
程序下载链接
1.15.4.1起始信号和结束信号
(1)起始信号: SCL保持高时,SDA有一个从高到低(下降沿
(2)结束信号: SCL保持高时,SDA有一个从低到高(上升沿
(3)总线空闲: 如果一个设备的SCL和SDA保持高电平,代表该设备在空闲中
- 看代码
在这里插入图片描述
1.15.4.2、位传输
每个时钟脉冲传送一位数据。SCL为高时SDA必须保持稳定,因为此时SDA的改变被认为是起始/结束信号如下:
在这里插入图片描述
参考资料
在这里插入图片描述

1.15.4.3、I2C发送一个字节
(1) 应答位处理(接收方—>发送方)

  • 每个字节后必须跟一个响应位(ACK)

(2) I2C发送一个字节时,都是从高位开始的(单片机—>接收方)

  • 以传输 Byte:1010 1010 (0xAAh)为例,SDA SCL 传输时序如下所示:
    在这里插入图片描述
    如上图可知,从设备拉低SDA表示应答,并在应答脉冲期间保持稳定的低电平!
    - 看代码

1.15.4.4、I2C接收一个字节(接收方—>单片机)
(1)概念:释放总线。

  • 在51单片机中,SDA=1就是释放总线;在其他更高级的单片机(譬如STM32等)这里的处理还会有点不一样。
  • 为什么SDA=1就是释放总线,是因为当51单片机把引脚拉高时,从设备可以选择再把这个引脚拉高或者拉低;但是当51单片机把这个引脚拉低(接地)后,从设备再也没办法把这个引脚拉高了。

1.15.5.EEPROM读写测试1

1.15.5.1、24C02读写高层时序(STC51–>AT24C02)

高层时序就是利用低层时序去完成和芯片之间的通信,精确到24C02手册

(1)写操作
在这里插入图片描述

  • 字地址就是写入内存地址
    在这里插入图片描述

- 看代码

(2)读操作

  • 用的随机读
    随机读

  • 看手册、看代码

(3)大致的读写操作明白了,但是地址怎么来的呢?

  • 器件地址
    是由从器件自身定义的,不同的从器件的地址定义方式是不同的,硬件定义,要查具体的芯片数据手册来确定。
    在这里插入图片描述

在这里插入图片描述
通过分析原理图和24C02的地址定义,可以得出:

读地址:1010 0001 (0xA1)
写地址:1010 0000 (0xA0)

最多可以连接多少片AT24C02呢?

  • 字节地址(EEPROM内存地址)
    在这里插入图片描述
    画的比较丑,为了便于理解,大家还是看黑板吧!

1.15.6.EEPROM读写测试2

给EEPROM发送一个字节的数据,把它们写在EEPROM特定内存下;
然后读取EEPROM的数据之后,通过串口打印出来。

1.15.6.1、工程建立与文件导入

  • 直接使用官方示例程序的i2c.h和i2c.c

1.15.6.2、加入串口输出代码

  • 直接使用之前写好的串口文件:serial.c和serial.h

1.15.6.3、测试EEPROM

  • 发送数据–接收数据–向串口打印

1.15.6.4、程序问题解决
读出内容不对,是EEPROM经不起快速的连续读写,所以在读和写之间加入20ms的delay,测试后发现读写正确了。

附上最终的程序代码:

//main.c
#include "serial.h"
#include "i2c.h"
/*******************************************************************************
一、接线
P2.0--SCL、P2.1--SDA  
设置在i2c.h

二、程序功能
给EEPROM发送一个字节的数据,把它们写在EEPROM特定内存下;
然后读取EEPROM的数据之后,通过串口打印出来。
*******************************************************************************/

void At24c02Write(unsigned char ,unsigned char );
unsigned char At24c02Read(unsigned char );
void delay20ms(void)   //误差 -0.000000000005us
{
    unsigned char a,b,c;
    for(c=1;c>0;c--)
        for(b=222;b>0;b--)
            for(a=40;a>0;a--);
}

void main(void)
{
	//变量声明
	u8 src_data[] = "12345678"; //要发送的数据
	u8 buf[8];//接收数据的buf
	u8 addr = 0;	//特定的地址
	u8 i = 0;
	//串口初始化
	uart_init();
	
	//发送数据
	for(i = 0;i<8;i++)
	{
		At24c02Write(addr,src_data[i]);
		delay20ms();
		addr++;
	}
	
	//接收数据
	addr = 0;
	for(i = 0;i<8;i++)
	{
		buf[i] = At24c02Read(addr);
		delay20ms();
		addr++;
	}
	
	//向串口打印
	for(i = 0;i<8;i++)
	{
		uart_send_byte(buf[i]);
	}
	while(1);
}

void At24c02Write(unsigned char addr,unsigned char dat)
{
	I2C_Start();
	I2C_SendByte(0xa0, 1);//发送写器件地址
	I2C_SendByte(addr, 1);//发送要写入内存地址
	I2C_SendByte(dat, 0);	//发送数据
	I2C_Stop();
}
/*******************************************************************************
* 函 数 名         : unsigned char At24c02Read(unsigned char addr)
* 函数功能		   : 读取24c02的一个地址的一个数据
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/

unsigned char At24c02Read(unsigned char addr)
{
	unsigned char num;
	I2C_Start();
	I2C_SendByte(0xa0, 1); //发送写器件地址
	I2C_SendByte(addr, 1); //发送要读取的字地址
	I2C_Start();
	I2C_SendByte(0xa1, 1); //发送读器件地址
	num = I2C_ReadByte(); //读取数据
	I2C_Stop();
	return num;	
}

本节课程序下载链接:I2C
本节课结束!

猜你喜欢

转载自blog.csdn.net/qq_27148893/article/details/110168332