STM32之 AT24C16(EEPROM)驱动代码(程序稳定,清晰明了)

article/2025/10/9 10:49:33
AT24C16电路连接
AT24C16电路图

 

第一部分:IIC协议代码头文件(iic.h)

#ifndef IIC_H
#define IIC_H
#include "stm32f10x.h"
#include "sys.h"
#include "delay.h"#define write 0
#define read  1//IIC总线地址接口定义
#define IIC_SCL PBout(7)
#define IIC_SDA_OUT PBout(6)
#define IIC_SDA_IN PBin(6)#define IIC_INPUT_MODE_SET()  {GPIOB->CRL&=0xF0FFFFFF;GPIOB->CRL|=0x08000000;}
#define IIC_OUTPUT_MODE_SET() {GPIOB->CRL&=0xF0FFFFFF;GPIOB->CRL|=0x03000000;}//函数声明
void IIC_Init(void);
void IIC_START(void);
void IIC_STOP(void);
u8 IIC_GetACK(void);
void IIC_SendAck(u8 ack);
void IIC_WriteOneByte(u8 data);
u8 IIC_ReadOneByte(void);
#endif

第二部分:IIC协议代码(iic.c)

#include "iic.h"
#include "stdio.h"
/*
函数功能: IIC总线初始化
硬件连接:SCL-PB7SDA-PB6
*/
void IIC_Init(void)
{/*1. 开时钟*/RCC->APB2ENR|=1<<3; //PBRCC->APB2ENR|=1<<0; //开启AFIO时钟/*2. 配置模式*/
//	AFIO->MAPR |= 0x2<<24;//关闭JTAG-DP,启用SW-DPGPIOB->CRL&=0x00FFFFFF;GPIOB->CRL|=0x33000000;/*3. 上拉*/GPIOB->ODR|=0x1<<6;printf("IIC_Init OK!\n");
}/*
函数功能: 起始信号
*/
void IIC_START(void)
{IIC_OUTPUT_MODE_SET(); //配置输出模式IIC_SDA_OUT = 1;       //拉高数据线IIC_SCL = 1;           //拉高时钟线DelayUs(2);            //延时IIC_SDA_OUT = 0;       //产生下降沿DelayUs(2);            //延时IIC_SCL = 0;           //拉低时钟线//告诉从机,通信开始(主机将要给从机发送数据)。
}/*
函数功能: 停止信号
*/
void IIC_STOP(void)
{IIC_OUTPUT_MODE_SET(); 	//配置输出模式IIC_SDA_OUT = 0;        //拉低数据线IIC_SCL = 1;            //拉高时钟线DelayUs(2);             //延时IIC_SDA_OUT = 1;        //产生上升沿DelayUs(2);             //延时
}/*
函数功能:  获取从机发给主机的应答
返回值  :  0表示获取成功,1表示获取失败
目的: 读取总线上一位数据的值。这一位数据的正确值0
*/
u8 IIC_GetACK(void)
{u8 cnt=0;IIC_INPUT_MODE_SET(); //输入模式IIC_SDA_OUT=1; //上拉IIC_SCL=0; //告诉从机主机需要数据DelayUs(2);IIC_SCL=1; //告诉从机主机正在读数据while(IIC_SDA_IN) //等待应答{cnt++;DelayUs(1);if(cnt>=250)//等待时间过长,产生停止信号,返回1,表示接收应答失败{IIC_STOP();//printf("N0 ACK\n");return 1;}}IIC_SCL=0; //告诉从机,主机准备发送数据return 0;
}/*
函数功能: 主机给从机发送应答
函数参数: 1(非应答) 0(应答)
目的: 发送一位数据
*/
void IIC_SendAck(u8 ack)
{IIC_OUTPUT_MODE_SET(); 	//配置输出模式IIC_SCL = 0;              //拉低时钟线,告诉从机,主机将要发送数据if(ack) IIC_SDA_OUT = 1; else IIC_SDA_OUT = 0;     //写应答信号DelayUs(2);               //延时IIC_SCL = 1;              //拉高时钟线DelayUs(2);               //延时IIC_SCL = 0;              //拉低时钟线
}/*
函数功能:  发送一个字节数据
函数参数:  data将要发送数据
*/
void IIC_WriteOneByte(u8 data)
{u8 i;IIC_OUTPUT_MODE_SET();IIC_SCL = 0;	//拉低时钟线, 数据准备发送DelayUs(2);		//延时for(i=0;i<8;i++)//从高位开始一位一位地传送{if(data&0x80)IIC_SDA_OUT=1;//送数据口else IIC_SDA_OUT=0;data<<=1;//移出数据的最高位IIC_SCL=1;//拉高时钟线,发送数据DelayUs(2);IIC_SCL=0;//拉低时钟线,数据发送完毕DelayUs(2);}
}/*
函数功能:  读取一个字节数据
返回值  :  读取成功的数据
*/
u8 IIC_ReadOneByte(void)
{u8 data=0,i=0;IIC_INPUT_MODE_SET();//使能内部上拉,准备读取数据for(i=0;i<8;i++){IIC_SCL=0;//置时钟线为低,准备接收数据位DelayUs(2);//时钟低电平周期大于4.7μsIIC_SCL=1;//置时钟线为高使数据线上数据有效,主机开始读数据,从机不能再改变数据了,即改变SDA的电平data<<=1;if(IIC_SDA_IN)data|=0x01;//读数据 DelayUs(2);}IIC_SCL=0;return data;
}

第三部分:AT24C16代码头文件(AT24C16.h)

#ifndef AT24C16_H
#define AT24C16_H
#include <stdio.h>
#include "stm32f10x.h"
#include "iic.h"#define AT24C16_WRITE_ADDR 0xA0
#define AT24C16_READ_ADDR  0xA1void AT24C16_WriteOneByte(u8 data,u8 addr);
void AT24C16_ReadData(u16 addr,u16 len,char *p);
void AT24C16_WriteData(u16 addr,u16 len,u8 *p);
void AT24C16_PageWrite(u16 addr,u16 len,u8 *p);#endif

第四部分:AT24C16代码(AT24C16.c)

#include "at24c16.h"/*
函数功能:  AT24C16写一个字节
函数参数:data:写入的字节数据addr:存放位置(0~2048)
*/void AT24C16_WriteOneByte(u8 data,u8 addr)
{IIC_START();IIC_WriteOneByte(AT24C16_WRITE_ADDR); //发送设备地址IIC_GetACK(); //获取应答IIC_WriteOneByte(addr);  //发送存放数据的地址IIC_GetACK(); //获取应答IIC_WriteOneByte(data);  //发送实际要存放的数据IIC_GetACK(); //获取应答IIC_STOP();   //发送停止信号DelayMs(10);  //等待写完成
}/*
函数功能: 指定位置读取指定数量的数据
函数参数:addr: 从哪里开始读取数据len : 读取多长的数据*p  : 存放数据的缓冲区编写程序对AT24C16第100页的第3个字节进行读数据的时候,步骤如下:
1)发送起始信号;
2)发送器件地址0XA6(1010 0110,1010是固定地址,011是页地址的高三位,0表示写操作);
3)发送操作地址0X43(0100 0011,0100是页地址的低四位,0011是页地址偏移量,即第100页内的第三个字节,
4)发送要写的数据,
5)发送终止信号。
(对于AT24C02,直接写设备地址和数据地址)
*/
void AT24C16_ReadData(u16 addr,u16 len,char *p)
{u16 i;u8 page_addr,page_addr_H,Devicce_Write_Addr,Devicce_Read_Addr;page_addr=addr>>4;page_addr_H=page_addr>>5;Devicce_Write_Addr=AT24C16_WRITE_ADDR + (page_addr_H<<1);Devicce_Read_Addr=AT24C16_READ_ADDR+(page_addr_H<<1);
//	printf("Devicce_Write_Addr = 0x%X \n Devicce_Read_Addr = 0x%X \n addr =0x%X \n",Devicce_Write_Addr,Devicce_Read_Addr,addr&0xFF);IIC_START();IIC_WriteOneByte(Devicce_Write_Addr); //发送设备地址(写)IIC_GetACK(); //获取应答IIC_WriteOneByte(addr&0xFF); //发送存放数据的地址(即将读取数据的地址)IIC_GetACK(); //获取应答IIC_START();IIC_WriteOneByte(Devicce_Read_Addr); //发送设备地址(读)IIC_GetACK(); //获取应答for(i=0;i<len;i++){p[i]=IIC_ReadOneByte(); //接收数据IIC_SendAck(0);  //给从机发送应答}IIC_SendAck(1);  //给从机发送非应答IIC_STOP();      //停止信号
} /*
函数功能: 指定位置写入指定数量的数据
函数参数:addr: 从哪里开始写数据len : 写入多长的数据*p  : 存放数据的缓冲区
*/
void AT24C16_WriteData(u16 addr,u16 len,u8 *p)
{u8 page_remain=16-addr%16; //得到当前页剩余的字节数量if(page_remain>=len){page_remain=len;}while(1){AT24C16_PageWrite(addr,page_remain,p);if(page_remain==len)break;addr+=page_remain;p+=page_remain;len-=page_remain;if(len>=16)page_remain=16;else page_remain=len;}
}/*
函数功能:  AT24C16页写函数
函数参数:addr: 从哪里开始写数据len : 写入多长的数据*p  : 存放数据的缓冲区说明: AT24C16内部有一个页写缓冲器,页地址超出之后会复位到当前页的起始地址。
AT24C16页缓冲器大小:  16个字节
编写程序对AT24C16第100页的第3个字节进行写数据的时候,步骤如下:
1)发送起始信号;
2)发送器件地址0XA6(1010 0110,1010是固定地址,011是页地址的高三位,0表示写操作);
3)发送操作地址0X43(0100 0011,0100是页地址的低四位,0011是页地址偏移量,即第100页内的第三个字节,
4)发送要写的数据,
5)发送终止信号。
(对于AT24C02,直接写设备地址和数据地址)
*/
void AT24C16_PageWrite(u16 addr,u16 len,u8 *p)
{u8 i;u8 page_addr,page_addr_H,Devicce_Write_Addr,Devicce_Read_Addr;page_addr=addr>>4;page_addr_H=page_addr>>5;Devicce_Write_Addr=AT24C16_WRITE_ADDR + (page_addr_H<<1);Devicce_Read_Addr=AT24C16_READ_ADDR+(page_addr_H<<1);printf("Devicce_Write_Addr = 0x%X \n Devicce_Read_Addr = 0x%X \n addr =0x%X \n",Devicce_Write_Addr,Devicce_Read_Addr,addr&0xFF);IIC_START();IIC_WriteOneByte(Devicce_Write_Addr); //发送设备地址IIC_GetACK(); //获取应答IIC_WriteOneByte(addr&0xFF);  //发送存放数据的地址IIC_GetACK(); //获取应答for(i=0;i<len;i++){IIC_WriteOneByte(p[i]);  //发送实际要存放的数据IIC_GetACK(); //获取应答}IIC_STOP();   //发送停止信号DelayMs(10);  //等待写完成
}

 


http://chatgpt.dhexx.cn/article/PdwAMorw.shtml

相关文章

GD32F4xx MCU控制I2C EEPROM(AT24C16)记录

1、AT24C16简介 1.1 主要参数 工作电压:1.8v ~ 5.5v存储空间:2048 Bytes ,分128页,16Bytes/页, 地址范围 0~2047。接口: I2C 总线I2C时钟频率: 1MHz( 5v ) , 400KHz( 1.8v, 2.5v, 2.7v)。1.2 电路连接 1.3 其他说明 AT24C16未使用器件地址引脚,总线上最多只可以连接一…

AT24C04、AT24C08、AT24C16系列EEPROM芯片单片机读写驱动程序

一、概述 在之前的一篇博文中&#xff0c;记录了AT24C01、AT24C02芯片的读写驱动&#xff0c;先将之前的相关文章include一下&#xff1a; 1.IIC驱动&#xff1a;4位数码管显示模块TM1637芯片C语言驱动程序 2.AT24C01/AT24C02读写&#xff1a;AT24C01/AT24C02系列EEPROM芯片单…

IIC方式读驱动AT24C16芯片

闲来无事&#xff0c;找了块msp430的板子编写了个IIC驱动AT24C16的程序。 IIC作是一种简单&#xff0c;双向&#xff0c;同步的二进制总线&#xff0c;由SDA数据线和SCL时钟线组成&#xff0c;所有接到IIC总线上的各设备的SDA数据线都连接到总线的SDA数据线上&#xff0c;用来…

AT24C16页写和多页写

AT24C16 2K字节(存储内存) 128&#xff08;页面数&#xff09;* 16 &#xff08;每页的字节数&#xff09; 2^11 (寻址地址位数 11位)。 AT24C16有128(2^7128)页只需要7位地址&#xff0c;分为高3位和低4位&#xff0c;高3位在设备地址中&#xff0c;低4位在字地址中。 设备…

EEPROM(AT24C16)页写算法

1. 写在前面 学习单片机或者从事嵌入式开发的&#xff0c;对于EEPROM绝不会陌生&#xff0c;尤其的24系列的EEPROM很是经典&#xff0c;或者与此兼容的FRAM系列&#xff0c;如AT24C02、AT24C16、FM24C16等。 驱动起这个系列的EEPROM&#xff0c;可以说是没有任何难点&#xff0…

AT24C16 读写注意点

开篇一张时序图镇楼&#xff1a; 这篇文章介绍了AT24C16的页写、连续读、写保护功能&#xff1a;AT24C16 读写_D.luffy的博客-CSDN博客_at24c16 页写算法我是参考这篇文章的&#xff1a;https://acuity.blog.csdn.net/article/details/78550427?utm_ char ee_24clxx_writeby…

AT24C16 读写

at24c16 有8块 256字节组成&#xff0c;共2K字节16K bit I2C开始信号后&#xff0c;第一个字节为器件地址&#xff0c;由10103位块地址1位读写标志组成&#xff0c; 3位块地址刚好可以表示 8个块&#xff0c; 所以一次写完256字节&#xff0c;换到下一下块的时候&#xff0c;要…

进程间通信的方式(附代码分析)

进程间通信的方式 1. 进程间通信的几种方式 管道 比如 ls | grep 1;也就是将 进程 ls 拿到的结果作为 grep 1 这个进程的输入。实现了进程间的通信。 消息队列 消息队列就是我们的内核给我们创建的一种消息队列。我们可以往其中发送消息&#xff0c;也可以从其中接收消息。 …

linux进程--进程间通信方式(一)

一、多进程 首先&#xff0c;先来讲一下fork之后&#xff0c;发生了什么事情。 由fork创建的新进程被称为子进程&#xff08;child process&#xff09;。该函数被调用一次&#xff0c;但返回两次。两次返回的区别是子进程的返回值是0&#xff0c;而父进程的返回值则是新进程…

进程间通信的方式——信号、管道、消息队列、共享内存

进程间通信的方式——信号、管道、消息队列、共享内存 多进程&#xff1a; 首先&#xff0c;先来讲一下fork之后&#xff0c;发生了什么事情。 由fork创建的新进程被称为子进程&#xff08;child process&#xff09;。该函数被调用一次&#xff0c;但返回两次。两次返回的区别…

Android 进程间通信的几种实现方式

一、概述 由于应用程序之间不能共享内存。在不同应用程序之间交互数据&#xff08;跨进程通讯&#xff09;&#xff0c;在android SDK中提供了4种用于跨进程通讯的方式。这4种方式正好对应于android系统中4种应用程序组件&#xff1a;Activity、Content Provider、Broadcast和S…

进程间通信的方式及原理

# 进程间通信的方式 文章目录 # 进程间通信的方式消息队列使用步骤 管道 消息队列 信号 信号量 socket 消息队列 首先消息队列就是内核维护的一块链表区域&#xff0c;只要是有足够权限的进程都可以向队列中添加消息&#xff0c;只要是有读权限的进程都可以在里面拿出消息 克…

C | 进程间通信的方式

C | 进程间通信的方式 1.无名管道 无名管道是实现亲缘间进程通信的一种方式&#xff0c;属于半双工通信。 无名管道的实现是队列&#xff0c;不能使用lseek对读写指针偏移。 无名管道有两个端&#xff1a;数据流入端和数据流出端&#xff0c;也叫写端和读端。它们是两个固定…

面试题:进程间通信的方式

liunx六大进程间通信方式 管道&#xff0c;消息队列&#xff0c;共享内存&#xff0c;信号量&#xff0c;socket&#xff0c;信号&#xff0c;文件锁 1&#xff0c;管道 1&#xff0c;匿名管道&#xff1a; 概念&#xff1a;在内核中申请一块固定大小的缓冲区&…

进程间通信的几种方式浅谈(上)

程序员必须让拥有依赖关系的进程集协调&#xff0c;这样才能达到进程的共同目标。可以使用两种技术来达到协调。第一种技术在具有通信依赖关系的两个进程间传递信息。这种技术称做进程间通信&#xff08;interprocess communication&#xff09;。第二种技术是同步&#xff0c;…

进程间通信的几种方式

一、管道 在Linux 中&#xff0c;管道是一种使用非常频繁的通信机制。从本质上说&#xff0c;管道也是一种文件&#xff0c;但它又和一般的文件有所不同&#xff0c;管道可以克服使用文件进行通信的两个问题&#xff0c;具体表现如下所述。 • 限制管道的大小。实际上&#x…

【进程间通信】进程间通信方式汇总

个人主页&#xff1a;董哥聊技术 我是董哥&#xff0c;嵌入式领域新星创作者 创作理念&#xff1a;专注分享高质量嵌入式文章&#xff0c;让大家读有所得&#xff01; 文章目录 1、管道模型1.1 匿名管道1.2 命名管道 2、消息队列2.1 创建消息队列2.2 发送消息2.3 接收消息 3、共…

android中进程间通信的几种方式

进程间通信&#xff08;IPC&#xff09;方式 使用Bundle使用文件共享使用Messenger使用AIDL使用COntentProvider使用Socket 一、使用Bundle 我们都知道Android中三大组件Activity&#xff0c;Service&#xff0c;Receiver都支持在Intent中传递Bundle数据&#xff0c;而Bundle…

操作系统——进程间通信

文章目录 其他文章管道消息队列共享内存信号量信号Socket总结 个人博客网站&#xff1a; https://xingkongdiyiren.github.io/myblog/,完整的Java知识体系&#xff0c;包括408&#xff0c;架构&#xff0c;业务&#xff0c;产品&#xff0c;软技能等 其他文章 操作系统——概…

进程间的通信方式(六种)

进程之间的通信 参考文章&#xff1a;https://blog.csdn.net/qq_34827674/article/details/107678226 前提知识&#xff1a;每个进程都有自己的用户空间&#xff0c;而内核空间是每个进程共享的。因此进程之间想要进行通信&#xff0c;就需要通过内核来实现。 管道&#xff1…