STM32F030 硬件I2C驱动 AT24C16

article/2025/10/9 10:46:48

网络上很多F1系列的ATC24的读写程序,但F0几乎没有。由于F0完全重写了I2C,所以以往的代码并不能直接使用,修改事件、接口上会浪费很多时间,特别是对于使用F0系列进行入门的新手。
在此十分感谢 畅学电子网 的对于AT24C16的资料,特别是AT24C16地址的解释。调试过程中这篇文章给了很大的帮助。建议不想只当伸手党的同志们认真阅读,否则只会Ctrl C Ctrl V,你又怎么能说自己是嵌入式开发者?

http://www.eeskill.com/group/topic_scan/id/282

废话不多说,进入正题。(哈哈,每篇文章都用这个开头)

I2C 的配置

static void InitI2C()
{I2C_InitTypeDef I2C_InitStructure;GPIO_InitTypeDef GPIO_InitA;RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);//使能I2C1,I2C2的时钟RCC_I2CCLKConfig(RCC_I2C1CLK_SYSCLK);//时钟源设定GPIO_PinAFConfig(GPIOB, GPIO_PinSource8, GPIO_AF_1);  //配置PB8 成第二功能引脚 I2C1_SCLGPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_1);  //配置PB9 成第二功能引脚 I2C1_SDAGPIO_InitA.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;GPIO_InitA.GPIO_Mode = GPIO_Mode_AF;GPIO_InitA.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitA.GPIO_OType = GPIO_OType_PP;GPIO_InitA.GPIO_PuPd = GPIO_PuPd_UP;GPIO_Init(GPIOB, &GPIO_InitA);I2C_InitStructure.I2C_Mode = I2C_Mode_SMBusHost;I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;I2C_InitStructure.I2C_AnalogFilter = I2C_AnalogFilter_Enable;I2C_InitStructure.I2C_DigitalFilter = 0x01;I2C_InitStructure.I2C_OwnAddress1 = 0x00;I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;I2C_InitStructure.I2C_Timing = 0x0090174F;I2C_Init(I2C1, &I2C_InitStructure);I2C_Cmd(I2C1, ENABLE);
}

一样的配置方案,I2C_Timing的意思请移步本博客GY30那篇文章。
I2C引脚为PB8 与PB9(使用的C8T6,f4p6可以用PA的)

#define AT24C16_Base_Address 0xA0
void AT24C16_WriteByte(uint8_t Page,uint8_t WordAddress,uint8_t Data);
uint8_t AT24C16_ReadByte(uint8_t Page,uint8_t WordAddress);
void AT24C16_PageWrite(uint8_t Page,uint8_t WordAddress,uint8_t Length,uint8_t* Data);
void AT24C16_SequentialRead(uint8_t Page,uint8_t WordAddress, uint8_t length , uint8_t* p);

下面是相关函数:

void AT24C16_WriteByte(uint8_t Page,uint8_t WordAddress,uint8_t Data)
{if(WordAddress > 0x10){return;}WordAddress |= ( Page & 0x0F ) << 4;while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) != RESET);//IF BUSYI2C_TransferHandling(I2C1,AT24C16_Base_Address | ( ( Page & 0xF0 ) >> 3 ),2,I2C_AutoEnd_Mode,I2C_Generate_Start_Write);while(I2C_GetFlagStatus(I2C1, I2C_FLAG_TXIS) == RESET);//If Write OKI2C_SendData(I2C1,WordAddress);while(I2C_GetFlagStatus(I2C1, I2C_FLAG_TXIS) == RESET);//If Write OKI2C_SendData(I2C1,Data);while(I2C_GetFlagStatus(I2C1, I2C_FLAG_STOPF) == RESET);
}
uint8_t AT24C16_ReadByte(uint8_t Page,uint8_t WordAddress)
{uint8_t Recev = 0x00;if(WordAddress > 0x10){return 0;}WordAddress |= ( Page & 0x0F ) << 4;while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) != RESET);//IF BUSYI2C_TransferHandling(I2C1,AT24C16_Base_Address | ( ( Page & 0xF0 ) >> 3 ),1,I2C_SoftEnd_Mode,I2C_Generate_Start_Write);while(I2C_GetFlagStatus(I2C1, I2C_FLAG_TXIS) == RESET);//If Write OKI2C_SendData(I2C1,WordAddress);while(I2C_GetFlagStatus(I2C1, I2C_FLAG_TC) == RESET);I2C_TransferHandling(I2C1,AT24C16_Base_Address | ( ( Page & 0xF0 ) >> 3 ),1,I2C_AutoEnd_Mode,I2C_Generate_Start_Read);while(I2C_GetFlagStatus(I2C1, I2C_FLAG_RXNE) == RESET);Recev = I2C_ReceiveData(I2C1);while(I2C_GetFlagStatus(I2C1, I2C_FLAG_STOPF) == RESET);return Recev;
}

下面是页读取,页写入:

void AT24C16_PageWrite(uint8_t Page,uint8_t WordAddress,uint8_t Length,uint8_t* Data)
{uint8_t i = 0;if(WordAddress > 0x10){return;}WordAddress |= ( Page & 0x0F ) << 4;while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) != RESET);//IF BUSYI2C_TransferHandling(I2C1,AT24C16_Base_Address | ( ( Page & 0xF0 ) >> 3 ),Length + 1,I2C_AutoEnd_Mode,I2C_Generate_Start_Write);while(I2C_GetFlagStatus(I2C1, I2C_FLAG_TXIS) == RESET);//If Write OKI2C_SendData(I2C1,WordAddress);for(i = 0;i < Length; i++){while(I2C_GetFlagStatus(I2C1, I2C_FLAG_TXIS) == RESET);//If Write OKI2C_SendData(I2C1,Data[i]);}while(I2C_GetFlagStatus(I2C1, I2C_FLAG_STOPF) == RESET);
}
void AT24C16_SequentialRead(uint8_t Page,uint8_t WordAddress, uint8_t length , uint8_t* p)
{uint8_t i;if(WordAddress > 0x10){return;}WordAddress |= ( Page & 0x0F ) << 4;while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) != RESET);//IF BUSYI2C_TransferHandling(I2C1,AT24C16_Base_Address | ( ( Page & 0xF0 ) >> 3 ),1,I2C_SoftEnd_Mode,I2C_Generate_Start_Write);while(I2C_GetFlagStatus(I2C1, I2C_FLAG_TXIS) == RESET);//If Write OKI2C_SendData(I2C1,WordAddress);while(I2C_GetFlagStatus(I2C1, I2C_FLAG_TC) == RESET);I2C_TransferHandling(I2C1,AT24C16_Base_Address | ( ( Page & 0xF0 ) >> 3 ),length,I2C_AutoEnd_Mode,I2C_Generate_Start_Read);for(i = 0;i < length;i++){while(I2C_GetFlagStatus(I2C1, I2C_FLAG_RXNE) == RESET);p[i] = I2C_ReceiveData(I2C1);}while(I2C_GetFlagStatus(I2C1, I2C_FLAG_STOPF) == RESET);
}

感觉没什么说的,GY30那篇文章基本都说完了,添点小知识点吧。

I2C_AutoEnd_Mode,顾名思义,操作length字节后自动添加STOP。
I2C_SoftEnd_Mode ,同样顾名思义,操作length字节后需要手动添加STOP。( I2C_GenerateSTOP() )
这个模式比自动多了一步,需要 I2C_GetFlagStatus(I2C1, I2C_FLAG_TC) ,Translate Completed,是否传输完成,自动模式下访问这个会得到Reset值,然而手动模式下需要访问他,然后生成Stop。

然后……差不多了吧?举个 上面网址的例子吧,我觉得很多人不会看……

所以在编写程序对AT24C16第100页的第3个字节进行写数据的时候,步骤如下:
1)发送起始信号;
2)发送器件地址0XA6(1010 0110,1010是固定地址,011是页地址的高三位,0表示写操作);
3)发送操作地址0X43(0100 0011,0100是页地址的低四位,0011是页地址偏移量,即第100页内的第三个字节,
4)发送要写的数据,
5)发送终止信号。

我相信各位最起码都看了AT24C16的地址了,0xA0。(再次引用畅学电子网的图片)
这里写图片描述

P0P1P2为页地址高三位,发送的字地址(WordAddress)高四位为页地址的第四位,低四位为字地址。
AT24C16有128页,每页16bytes。所以正好匹配上。
写的间隔至少为5ms,否则用循环等待的话I2C会卡死。

以上。
另:代码我测试是通过的,若有Bug欢迎指出。


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

相关文章

EEPROM 之 AT24C16 - 备忘录

因为论坛里看到STM的I2C有点小bug&#xff0c;所以这里采用的是模拟I2C时序 【注】m0.6us表示的是这一段时间最小不能小于为0.6us&#xff0c;M0.6us表示的是这一段时间最大为0.6us 对AT24C16的操作有读和写&#xff0c;读又分为CURRENT ADDRESS READ、RANDOM READ、SEQUENTIAL…

S32K144:12.LPI2C驱动AT24C16

1.打开官方例程 2.修改引脚配置 3.时钟可按照实际情况修改&#xff0c;也可不用更改&#xff0c;本例时钟不做更改 4.配置LPI2C模块 设置从机地址&#xff1a;从机地址如下图所示&#xff0c;低三位表示为AT24C16的块地址&#xff0c;AT24C16将2KB的内存空间分为8个块&…

stm32cubemx I2C读取AT24C16

本文对如何使用stm32cube生成I2C工程不作说明&#xff0c;仅对在对AT24Cxx系列的使用时作出易忽略的说明&#xff1b; 1、at24cxx页面结构&#xff1a; 从该图可以看出16K&#xff08;bit&#xff09;共有128个页&#xff0c;每页由16byte构成。16k 128 * 16 * 8; 特别注意&…

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

AT24C16电路图 第一部分&#xff1a;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) #d…

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…