接着上一篇博客文章讲解了IIC协议的原理及编程思路,本篇博客文章将以IIC为基础,从芯片手册入手,梳理讲解如何对AT24C02进行驱动编程,实现数据的读写操作。
IIC通信驱动硬件编程 (1)-CSDN博客https://blog.csdn.net/weixin_49337111/article/details/136216296?spm=1001.2014.3001.5502
1、存储器基础
在对AT24C02进行编程驱动时,有必要先了解一下存储器的基本知识,这有助于对AT24C02的进一步了解,并且在一些公司的笔试或者面试时会进行基本的存储器知识考察。
存储器是计嵌入式系统中用于存储数据和程序的关键组件。根据存储器的特点和用途,可以将其分为以下几类:
①、RAM(Random Access Memory,随机访问存储器):RAM是最常用的存储器之一,它允许设备快速读取和写入数据。RAM的特点是速度快,但数据在断电后会丢失。RAM通常用于存储正在运行的程序和临时数据。
②、ROM(Read-Only Memory,只读存储器):ROM是一种只能读取数据而不能写入数据的存储器。它的数据是以螺旋状的方式从中心向外散开状的顺序排列的。ROM的数据是以螺旋状的方式从中心向外散开状的顺序排列的,因此它非常适合存储固定的程序代码或系统数据。常见的ROM类型包括Mask ROM和PROM(Programmable Read-Only Memory,可编程只读存储器)。
③、EEPROM(Electrically Erasable Programmable Read-Only Memory,电可擦除可编程只读存储器):EEPROM是一种可以通过电子方式擦除和重新编程的ROM。它的特点是数据可以反复擦写,且断电后数据不会丢失。EEPROM通常用于存储需要频繁更新的数据,如BIOS(基本输入输出系统)程序。
本次编程驱动的AT24C02就属于EEPROM这一类存储设备。
④、Flash Memory(闪存):Flash Memory是一种非易失性存储器,它的特点是可以快速地擦写和重新编程。Flash Memory的数据是以块为单位进行擦除和编程的,因此它非常适合用于存储大量的数据。Flash Memory广泛应用于U盘、SD卡、固态硬盘等存储设备中。
⑤、高速缓存(Cache):Cache是一种高速存取指令和数据的存储器,通常位于CPU和主存储器之间。Cache的存取速度非常快,但存储容量相对较小。其主要作用是减少CPU访问主存储器的次数,从而提高系统的运行效率。
2、AT24C02说明
由下图所示的官方手册可知,AT24C02的的存储容量为2K位,而一个字节有8位,那么AT24C02的存储空间也就是有2048 / 8 = 256个字节的串行CMOS E2PROM(电可擦除可编程只读存储器)。它采用了CATALYST公司的先进CMOS技术,从而实质性地减少了器件的功耗。这个设备具有一个8字节的页写缓冲器,并通过IIC总线接口进行操作。此外,AT24C02还提供了一个专门的写保护功能,以确保数据的完整性和安全性。
AT24C02的引脚包括SERIAL DATA(SDA),这是一个双向引脚,用于串行数据传输。此外,还有一个WRITE PROTECT(WP)引脚,用于提供硬件数据保护。
在操作方面,AT24C02有两种寻址方式:片寻址和存储单元寻址。AT24C02存储器可以分成32页,每页有8个字节,所以总共可以存储256个字节的数据。
A0、A1、A2 器件地址输入端。
Device Address 从机设备地址
结合上图所示的AT24C02在原理图中接线可知,A0、A1、A2引脚已进行接地操作,则AT24C02的设备从机地址为 1010 000x,(x表示读写位(R/W)的值,当x为0时,表示写数据,当x为0时,表示读数据)
由IIC协议可知,从机的设备地址为7位,通信时需要将设备地址左移1位,然后最低位用于标识读写模式。为了便于记忆,也可说成IIC通信的从机设备地址为一个字节,即8位,而最低位用于标识读写(W/R)模式。
读模式时,AT24C02的设备地址为 1010 0001 = 0xA1
写模式时,AT24C02的设备地址为 1010 0000 = 0xA0
WORD ADDRESS的值由用户决定,最大值由芯片的存储空间决定 AT24C02——2K (256 x 8)——0~256
3、AT24C02时序图
在编写AT24C02的设备驱动程序时,需要严格遵循官方参考手册进行编写,如下所示为AT24C02系列设备的逻辑时序图。
①、Byte Write 字写
AT24C02单字节写程序编写思路
①、发送IIC启动信号
②、发送写AT24C02的地址
③、接收AT24C02的有效应答信号
④、发送芯片内部的字地址
⑤、接收AT24C02的有效应答信号
⑥、发送写入的1个字节数据
⑦、接收AT24C02的有效应答信号
⑧、发送IIC停止信号
②、Page Write 页写
AT24C02多字节页写程序编写思路
①、发送IIC启动信号
②、发送写AT24C02的地址
③、接收AT24C02的有效应答信号
④、发送芯片内部的字地址
⑤、接收AT24C02的有效应答信号
⑥、发送写入的1个字节数据
⑦、接收AT24C02的有效应答信号
......
这一部分持续循环⑥、⑦步骤的操作,直到写数据结束
......
⑧、发送IIC停止信号
③、Current Address Read 当前地址读取
AT24C02当前地址读取程序编写思路
①、发送IIC启动信号
②、发送读AT24C02的地址
③、接收AT24C02的有效应答信号
④、接收AT24C02的数据
⑤、发送无效应答信号
⑥、发送IIC停止信号
④、Random Read 随机读取
AT24C02单字节读取程序编写思路
①、发送IIC启动信号
②、发送写AT24C02的地址
③、接收AT24C02的有效应答信号
④、发送芯片内部的字地址
⑤、接收AT24C02的有效应答信号
⑥、发送IIC启动信号
⑦、发送读AT24C02的地址
⑧、接收AT24C02的有效应答信号
⑨、接收的AT24C02存储的数据
⑩、发送无应答信号
11、发送IIC停止信号
⑤、Sequential Read 顺序读取
AT24C02顺序连续读取程序编写思路
①、发送IIC启动信号
②、发送写AT24C02的地址
③、接收AT24C02的有效应答信号
④、发送芯片内部的字地址
⑤、接收AT24C02的有效应答信号
⑥、发送IIC启动信号
⑦、发送读AT24C02的地址
⑧、接收AT24C02的有效应答信号
⑨、接收的AT24C02存储的数据
⑩、发送有效应答信号
11、接收的AT24C02存储的数据
12、发送有效应答信号
......
这一部分持续循环⑥、⑦步骤的操作,直到读取数据结束
......
13、发送无效应答信号
14、发送IIC停止信号
4、AT24C02驱动源码
在这一部分中的AT24C02代码也同样适用于AT24C01A/02/04/08A/16A等型号的存储器。
①、IIC通信讲解及源码
在上一篇博客中已经对IIC通信协议及通信代码做了较为详细的说明,故而在这里就不过多描述了,如果有不理解的地方可以参考这篇博客文章:
IIC通信驱动硬件编程 (1)-CSDN博客https://blog.csdn.net/weixin_49337111/article/details/136216296?spm=1001.2014.3001.5502
②、AT24C02.h
#ifndef __AT24C02_H
#define __AT24C02_H
#include "stm32g4xx_hal.h"
#include "stdio.h"
#include "iic.h"
#define AT24C02_WRITE_ADDR 0xA0
#define AT24C02_READ_ADDR 0xA1
void AT24C02_Init(void);
int AT24C02_Write_Byte(unsigned char word_addr, unsigned char data);
int AT24C02_Read_Byte(int word_addr, unsigned char *data);
int AT24C02_Write_Page(unsigned char word_addr, unsigned char *data, int data_len);
int AT24C02_Read_Sequential(unsigned char word_addr, unsigned char buf[], int buf_size);
int AT24C02_Read_Current_addr(unsigned char *cur_addr);
#endif
③、AT24C02.c
#include "AT24C02.h"
/**
* @brief AT24C02初始化
* @param None
* @retval None
*/
void AT24C02_Init(void)
{
IIC_Init();
}
/**
* @brief AT24C02写1个字节
* @param word_addr: 字地址
* @param data:需要写入的数据
* @retval 成功返回0,失败返回-1
*/
int AT24C02_Write_Byte(unsigned char word_addr, unsigned char data)
{
int ret = 0;
IIC_Start();
IIC_Write_Byte(AT24C02_WRITE_ADDR);
ret = IIC_Wait_Ack();
if(ret == -1)
{
IIC_Stop();
return -1;
}
IIC_Write_Byte(word_addr);
ret = IIC_Wait_Ack();
if(ret == -1)
{
IIC_Stop();
return -1;
}
IIC_Write_Byte(data);
ret = IIC_Wait_Ack();
if(ret == -1)
{
IIC_Stop();
return -1;
}
IIC_Stop();
return 0;
}
/**
* @brief AT24C02读取1个字节
* @param word_addr:字地址
* @param data:数据
* @retval 成功返回0,失败返回-1
*/
int AT24C02_Read_Byte(int word_addr, unsigned char *data)
{
int ret = 0;
IIC_Start();
IIC_Write_Byte(AT24C02_WRITE_ADDR);
ret = IIC_Wait_Ack();
if(ret == -1)
{
IIC_Stop();
printf("%s\t%d\r\n", __FUNCTION__, __LINE__);
return -1;
}
IIC_Write_Byte(word_addr);
ret = IIC_Wait_Ack();
if(ret == -1)
{
IIC_Stop();
printf("%s\t%d\r\n", __FUNCTION__, __LINE__);
return -1;
}
IIC_Start();
IIC_Write_Byte(AT24C02_READ_ADDR);
ret = IIC_Wait_Ack();
if(ret == -1)
{
IIC_Stop();
printf("%s\t%d\r\n", __FUNCTION__, __LINE__);
return -1;
}
*data = I2CReceiveByte();
IIC_Write_Ack(1);
IIC_Stop();
return 0;
}
/**
* @brief AT24C02页写
* @param word_addr:字地址
* @param data:需要被写入的数据数组
* @param data_len:数据长度
* @retval 成功返回0,失败返回-1
*/
int AT24C02_Write_Page(unsigned char word_addr, unsigned char *data, int data_len)
{
int ret = 0;
IIC_Start();
IIC_Write_Byte(AT24C02_WRITE_ADDR);
ret = IIC_Wait_Ack();
if(ret == -1)
{
IIC_Stop();
return -1;
}
IIC_Write_Byte(word_addr);
ret = IIC_Wait_Ack();
if(ret == -1)
{
IIC_Stop();
return -1;
}
//连续写数据
for(int i=0; i<data_len; i++)
{
IIC_Write_Byte(data[i]);
ret = IIC_Wait_Ack();
if(ret == -1)
{
IIC_Stop();
return -1;
}
}
IIC_Stop();
return 0;
}
/**
* @brief AT24C02连续读取
* @param word_addr:字地址
* @param buf:存储数组
* @param buf_size:数组大小
* @retval 成功返回0,失败返回-1
*/
int AT24C02_Read_Sequential(unsigned char word_addr, unsigned char buf[], int buf_size)
{
int ret = 0;
IIC_Start();
IIC_Write_Byte(AT24C02_WRITE_ADDR);
ret = IIC_Wait_Ack();
if(ret == -1)
{
IIC_Stop();
printf("%s\t%d\r\n", __FUNCTION__, __LINE__);
return -1;
}
IIC_Write_Byte(word_addr);
ret = IIC_Wait_Ack();
if(ret == -1)
{
IIC_Stop();
printf("%s\t%d\r\n", __FUNCTION__, __LINE__);
return -1;
}
IIC_Start();
IIC_Write_Byte(AT24C02_READ_ADDR);
ret = IIC_Wait_Ack();
if(ret == -1)
{
IIC_Stop();
return -1;
}
for(int i=0; i<buf_size; i++)
{
buf[i] = IIC_Read_Byte();
IIC_Write_Ack(0);//Ack
}
IIC_Write_Ack(1); //No Ack
return 0;
}
/**
* @brief AT24C02连续读取
* @param word_addr:字地址
* @param buf:存储数组
* @param buf_size:数组大小
* @retval 成功返回0,失败返回-1
*/
int AT24C02_Read_Current_addr(unsigned char *cur_addr)
{
int ret = 0;
IIC_Start();
IIC_Write_Byte(AT24C02_READ_ADDR);
ret = IIC_Wait_Ack();
if(ret == -1)
{
IIC_Stop();
return -1;
}
*cur_addr = IIC_Read_Byte();
IIC_Write_Ack(1);
IIC_Stop();
return 0;
}