IIC
IIC
1. 理论知识
1.1 IIC基础概念
I2C:Inter-Intergrated Circuit ,Phiilps公司80年代初期开发的,引脚少,硬件实现简单,具有可拓展性,广泛使用在系统内多个集成电路间的低速通信
双向两线制总线协议标准,支持同步串行半双工通讯。
标准模式传输速率:100 kbit/s 快速模式传输速率: 400 kbit/s 高速模式传输速率:3.4Mbit/s,大多数设备不支持高速。
术语定义:

1.2 硬件构成
通信的实现通过SCL串行时钟总线和SDA串行数据总线实现。
SCL:用于数据的收发同步;
SDA:用于传输数据,高低电平表示:
可同时连接多个IIC通信设备,支持一主多从也支持多主多从。每个设备都有唯一的地址,主机通过地址与从机通信。总线通过上拉电阻连接到电源。设备空闲时,都输出高阻态,由上拉电阻把总线拉成高电平。

1.3 工作原理
-
位传输机制:IIC为串行通信,按位进行数据传输,高位先行。
-
起始信号:当SCL为高电平,SDA由高电平变化为低电平。
-
停止信号:当SCL为高电平,SDA由低电平变化为高电平。

-
传输地址:主机通过SDA信号线发送设备地址(SLAVE_ADDRESS)来查找从机。
IIC协议规定设备地址可以是7位或10位,实际中7位的地址应用比较广泛。紧跟设备地址的一个数据位用来表示数据传输方向,它是数据方向位(R/W),第8位或第11位。数据方向位为“1”时表示主机由从机读数据,该位为“0”时表示主机向从机写数据。
-
数据的有效性:
SDA高或低电平状态只有在SCL时钟信号是低电平是才能才能改变。
SDA线上的数据必须在时钟的高电平周期保持稳定。

-
响应:接收方接收到数据后要给发送方响应,有2种响应
应答响应,给发送方一个低电平。
非应答响应,给发送方一个高电平。

1.4 时序图
- 写入一个字节时序

- 读出一个字节时序

- 单次写入多个字节时序

一次性写入多个字节,也叫页写入(Page Write)。AT24C02每页只有16个字节,每次只能写入单独的一个页中,所以一次性最多只能写入16个字节。当一次性写入超过16个字节的时候,则超过的部分会重新从这页的首地址重新写入。 - 单次读出多个字节时序

读出多个字节的时候没有限制,可以读出任意多个。
1.5 补充:24C02简介



2. 实践验证
2.1 寄存器方式
验证一:软件模拟IIC通信
实现逻辑:
配置IO端口为通用开漏输出(总线接上拉电阻,IO开漏输出为低电平或高阻态),软件定义端口的电平输出状态和输入状态。根据IIC通信的时序图完成通信。
效果:

如图,页写入超出16字节后会从头写入
代码如下:
mian.c
#include "USART.h"
#include "led.h"
#include "m24c02.h"
#include "string.h"
int main()
{
// 1. 初始化
Driver_USART1_Init();
M24C02_Init();
printf("******实践软件模拟IIC通信*******\r\n");
printf("写入单个字符\r\n");
// 2. 向EEPROM写入字符
M24C02_WriteByte(0x00, 'a');
M24C02_WriteByte(0x01, 'b');
M24C02_WriteByte(0x02, 'c');
printf("读取单个字符\r\n");
// 3. 读取字符
uint8_t byte1 = M24C02_ReadByte(0x00);
uint8_t byte2 = M24C02_ReadByte(0x01);
uint8_t byte3 = M24C02_ReadByte(0x02);
// 4. 串口输出打印
printf("byte1 = %c\t byte2 = %c\t byte3 = %c\r\n", byte1, byte2, byte3);
printf("写入多个字符\r\n");
// 5. 写入多个字符
M24C02_WriteBytes(0x00, "yangabc123", 10);
printf("读取多个字符\r\n");
// 6. 读取多个字符
uint8_t buffer[100] = {0};
M24C02_ReadBytes(0x00, buffer, 10);
// 7. 串口输出打印
printf("buffer = %s\r\n", buffer);
// 8. 测试超出16个字节的写入
printf("测试写入超过16个字符:1234567890abcdefghijk\r\n");
// 清零缓冲区
memset(buffer, 0, sizeof(buffer));
M24C02_WriteBytes(0x00, "1234567890abcdefghijk", 21);
M24C02_ReadBytes(0x00, buffer, 21);
printf("buffer = %s\r\n", buffer);
while (1)
{
}
}
iic.c
#include "iic.h"
// 初始化
void IIC_Init(void)
{
// 1. 配置时钟
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
// 2. GPIO工作模式配置,通用开漏输出CNF-01,MODE-11
GPIOB->CRH |= (GPIO_CRH_MODE10 | GPIO_CRH_MODE11);
GPIOB->CRH &= ~(GPIO_CRH_CNF10_1 | GPIO_CRH_CNF11_1);
GPIOB->CRH |= (GPIO_CRH_CNF10_0 | GPIO_CRH_CNF11_0);
}
// 发出起始信号
void IIC_Start(void)
{
// 1. SCL拉高,SDA拉高
SCL_HIGH;
SDA_HIGH;
IIC_DELAY;
// 2. SCL保持不变,SDA拉低
SDA_LOW;
IIC_DELAY;
}
// 发出停止信号
void IIC_Stop(void)
{
// 1. SCL拉高,SDA拉低
SCL_HIGH;
SDA_LOW;
IIC_DELAY;
// 2. SCL保持不变,SDA拉高
SDA_HIGH;
IIC_DELAY;
}
// 发送一个字节数据
void IIC_SendByte(uint8_t byte)
{
for (uint8_t i = 0; i < 8; i++)
{
// 1. SCL拉低,SDA拉低,等待数据上传
SCL_LOW;
SDA_LOW;
IIC_DELAY;
// 2. 取字节的最高位,向SDA写入数据
if (byte & 0x80)
{
SDA_HIGH;
}
else
{
SDA_LOW;
}
IIC_DELAY;
// 3. SCL拉高,进行数据采样
SCL_HIGH;
IIC_DELAY;
// 4. SCL拉低,采样结束
SCL_LOW;
IIC_DELAY;
// 5. 左移一位
byte <<= 1;
}
}
// 接收一个字节数据
uint8_t IIC_ReadByte(void)
{
// 1. 定义一个变量,用来保存接收的数据
uint8_t data = 0;
// 2. 循环处理每一位
for (uint8_t i = 0; i < 8; i++)
{
// 1. SCL拉低,SDA拉高,释放数据总线,等待数据翻转
SCL_LOW;
SDA_HIGH;
IIC_DELAY;
// 2. SCL拉高,进行数据采样
SCL_HIGH;
IIC_DELAY;
// 3. SCL保持不变,读取SDA上的电平
data <<= 1; // 先做左移,新存入的位永远在最低位
if (READ_SDA)
{
data |= 0x01; // 先存入最低位,然后每次都左移1位
}
// 4. SCL拉低,结束数据线上信号采样
SCL_LOW;
IIC_DELAY;
}
return data;
}
// 发送ACK信号
void IIC_SendACK(void)
{
// 1. SCL拉低,SDA拉高,准备发出信号
SCL_LOW;
SDA_HIGH;
IIC_DELAY;
// 2. SCL保持不变,SDA拉低,发送ACK信号
SDA_LOW;
IIC_DELAY;
// 3. SDA保持不变,SCL拉高,开始数据线上信号采样
SCL_HIGH;
IIC_DELAY;
// 4. SDA保持不变,SCL拉低,结束数据线上信号采样
SCL_LOW;
IIC_DELAY;
// 5. SCL保持不变,SDA拉高,释放数据总线
SDA_HIGH;
IIC_DELAY;
}
// 发送NACK信号
void IIC_SendNACK(void)
{
// 1. SCL拉低,SDA拉高,准备发出信号
SCL_LOW;
SDA_HIGH;
IIC_DELAY;
// 2. SCL保持不变,SDA拉高(即不变),发送ACK信号
//SDA_LOW;
//IIC_DELAY;
// 3. SDA保持不变,SCL拉高,开始数据线上信号采样
SCL_HIGH;
IIC_DELAY;
// 4. SDA保持不变,SCL拉低,结束数据线上信号采样
SCL_LOW;
IIC_DELAY;
// 5. SCL保持不变,释放数据总线
//SDA_HIGH;
//IIC_DELAY;
}
// 读取ACK信号
uint8_t IIC_WaitACK(void)
{
// 1. SCL拉低,SDA拉高,释放数据总线
SCL_LOW;
SDA_HIGH;
IIC_DELAY;
// 2. SCL保持不变,SDA拉低,开始数据线上信号采样
SCL_HIGH;
IIC_DELAY;
// 3. 读取SDA上的数据
uint16_t ack = READ_SDA;
// 4. SCL拉低,结束数据线上信号采样
SCL_LOW;
IIC_DELAY;
return ack ? NACK : ACK;
}
iic.h
#ifndef __IIC_H__
#define __IIC_H__
#include "stm32f10x.h"
#include "delay.h"
// 宏定义
#define ACK 0
#define NACK 1
// 控制SCL、SDA输出高低电平
#define SCL_HIGH (GPIOB->ODR |= GPIO_ODR_ODR10)
#define SCL_LOW (GPIOB->ODR &= ~GPIO_ODR_ODR10)
#define SDA_HIGH (GPIOB->ODR |= GPIO_ODR_ODR11)
#define SDA_LOW (GPIOB->ODR &= ~GPIO_ODR_ODR11)
// 读取SDA
#define READ_SDA (GPIOB->IDR &= GPIO_IDR_IDR11)
// 定义操作的基本延迟
#define IIC_DELAY Delay_us(10)
// 初始化
void IIC_Init(void);
// 发出起始信号
void IIC_Start(void);
// 发出停止信号
void IIC_Stop(void);
// 发送一个字节数据
void IIC_SendByte(uint8_t byte);
// 接收一个字节数据
uint8_t IIC_ReadByte(void);
// 发送ACK信号
void IIC_SendACK(void);
// 发送NACK信号
void IIC_SendNACK(void);
// 读取ACK信号
uint8_t IIC_WaitACK(void);
#endif
m24c02.c
#include "m24c02.h"
// 初始化
void M24C02_Init(void)
{
IIC_Init();
}
// 向EEPROM写入一个字节
void M24C02_WriteByte(uint8_t innerAddr, uint8_t byte)
{
// 1. 发出开始信号
IIC_Start();
// 2. 发送设备地址及写入命令
IIC_SendByte(M24C02_WRITE_ADDR);
// 3. 等待m24c02应答ACK
uint8_t ack = IIC_WaitACK();
if (ack == ACK)
{
// 4. 发送内部地址
IIC_SendByte(innerAddr);
// 5. 等待m24c02应答ACK
IIC_WaitACK();
// 6. 发送数据
IIC_SendByte(byte);
// 7. 等待应答
IIC_WaitACK();
// 8. 发送停止信号
IIC_Stop();
}
// 延迟等待写入周期结束
Delay_ms(5);
}
// 从EEPROM读取一个字节
uint8_t M24C02_ReadByte(uint8_t innerAddr)
{
// 1. 发出开始信号
IIC_Start();
// 2. 发送设备地址及写入命令
IIC_SendByte(M24C02_WRITE_ADDR);
// 3. 等待m24c02应答ACK
IIC_WaitACK();
// 4. 发送内部地址
IIC_SendByte(innerAddr);
// 5. 等待m24c02应答ACK
IIC_WaitACK();
// 6. 发出开始信号
IIC_Start();
// 7. 发送设备地址及读取命令
IIC_SendByte(M24C02_READ_ADDR);
// 8. 等待m24c02应答ACK
IIC_WaitACK();
// 9. 读取一个字节数据
uint8_t byte = IIC_ReadByte();
// 10. 发送非应答NACK
IIC_SendNACK();
// 11. 发送停止信号
IIC_Stop();
return byte;
}
// 连续写入多个字节(页写)
void M24C02_WriteBytes(uint8_t innerAddr, uint8_t *bytes, uint8_t size)
{
// 1. 发出开始信号
IIC_Start();
// 2. 发送设备地址及写入命令
IIC_SendByte(M24C02_WRITE_ADDR);
// 3. 等待m24c02应答ACK
uint8_t ack = IIC_WaitACK();
if (ack == ACK)
{
// 4. 发送内部地址
IIC_SendByte(innerAddr);
// 5. 等待m24c02应答ACK
IIC_WaitACK();
// 利用循环不停发送数据
for (uint8_t i = 0; i < size; i++)
{
// 6. 发送数据
IIC_SendByte(bytes[i]);
// 7. 等待应答
IIC_WaitACK();
}
// 8. 发送停止信号
IIC_Stop();
}
// 延迟等待写入周期结束
Delay_ms(5);
}
// 连续读取多个字节
void M24C02_ReadBytes(uint8_t innerAddr, uint8_t *buffer, uint8_t size)
{
// 1. 发出开始信号
IIC_Start();
// 2. 发送设备地址及写入命令
IIC_SendByte(M24C02_WRITE_ADDR);
// 3. 等待m24c02应答ACK
IIC_WaitACK();
// 4. 发送内部地址
IIC_SendByte(innerAddr);
// 5. 等待m24c02应答ACK
IIC_WaitACK();
// 6. 发出开始信号
IIC_Start();
// 7. 发送设备地址及读取命令
IIC_SendByte(M24C02_READ_ADDR);
// 8. 等待m24c02应答ACK
IIC_WaitACK();
// 利用循环读取数据
for (uint8_t i = 0; i < size; i++)
{
// 9. 读取一个字节数据
buffer[i] = IIC_ReadByte();
// 发送应ACK应答
IIC_SendACK();
}
// 10. 发送非应答NACK
IIC_SendNACK();
// 11. 发送停止信号
IIC_Stop();
return;
}
m24c02.h
#ifndef __M24C02_H__
#define __M24C02_H__
#include "iic.h"
// 宏定义
#define M24C02_WRITE_ADDR 0xA0
#define M24C02_READ_ADDR 0xA1
// 初始化
void M24C02_Init(void);
// 向EEPROM写入一个字节
void M24C02_WriteByte(uint8_t innerAddr, uint8_t byte);
// 从EEPROM读取一个字节
uint8_t M24C02_ReadByte(uint8_t innerAddr);
// 连续写入多个字节(页写)
void M24C02_WriteBytes(uint8_t innerAddr, uint8_t *bytes, uint8_t size);
// 连续读取多个字节
void M24C02_ReadBytes(uint8_t innerAddr, uint8_t *buffer, uint8_t size);
#endif
验证二:硬件IIC通信
实现逻辑:
配置IO端口为复用开漏输出,根据IIC通信的时序图完成通信驱动文件的编程。
效果:

main.c
#include "USART.h"
#include "led.h"
#include "m24c02.h"
#include "string.h"
int main()
{
// 1. 初始化
Driver_USART1_Init();
M24C02_Init();
printf("******实践寄存器硬件IIC通信*******\r\n");
printf("写入单个字符\r\n");
// 2. 向EEPROM写入字符
M24C02_WriteByte(0x00, 'a');
M24C02_WriteByte(0x01, 'b');
M24C02_WriteByte(0x02, 'c');
printf("读取单个字符\r\n");
// 3. 读取字符
uint8_t byte1 = M24C02_ReadByte(0x00);
uint8_t byte2 = M24C02_ReadByte(0x01);
uint8_t byte3 = M24C02_ReadByte(0x02);
// 4. 串口输出打印
printf("byte1 = %c\t byte2 = %c\t byte3 = %c\r\n", byte1, byte2, byte3);
printf("写入多个字符\r\n");
// 5. 写入多个字符
M24C02_WriteBytes(0x00, "yangabc123", 10);
printf("读取多个字符\r\n");
// 6. 读取多个字符
uint8_t buffer[100] = {0};
M24C02_ReadBytes(0x00, buffer, 10);
// 7. 串口输出打印
printf("buffer = %s\r\n", buffer);
// 8. 测试超出16个字节的写入
printf("测试写入超过16个字符:1234567890abcdefghijk\r\n");
// 清零缓冲区
memset(buffer, 0, sizeof(buffer));
M24C02_WriteBytes(0x00, "1234567890abcdefghijk", 21);
M24C02_ReadBytes(0x00, buffer, 21);
printf("buffer = %s\r\n", buffer);
while (1)
{
}
}
iic.c
#include "iic.h"
// 初始化
void I2C_Init(void)
{
// 1. 配置时钟
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
RCC->APB1ENR |= RCC_APB1ENR_I2C2EN;
// 2. GPIO工作模式配置:复用开漏输出 CNF-11,MODE-11
GPIOB->CRH |= (GPIO_CRH_MODE10 | GPIO_CRH_MODE11 |
GPIO_CRH_CNF10 | GPIO_CRH_CNF11);
// 3. I2C2配置
// 3.1 硬件工作模式
I2C2->CR1 &= ~I2C_CR1_SMBUS;
I2C2->CCR &= ~I2C_CCR_FS;
// 3.2 选择输入的时钟频率
I2C2->CR2 |= 36;
// 3.3 配置CCR,对应数据传输速率100kb/s,SCL高电平时间为 5us
I2C2->CCR |= 180;
// 3.4 配置TRISE,SCL上升沿最大时钟周期数 + 1
I2C2->TRISE |= 37;
// 3.5 使能I2C2模块
I2C2->CR1 |= I2C_CR1_PE;
}
// 发出起始信号
uint8_t I2C_Start(void)
{
// 产生一个起始信号
I2C2->CR1 |= I2C_CR1_START;
// 引入一个超时时间
uint16_t timeout = 0xffff;
// 等待起始信号发出
while ((I2C2->SR1 & I2C_SR1_SB) == 0 && timeout)
{
timeout--;
}
return timeout ? OK : FAIL;
}
// 设置接收完成之后发出停止信号
void I2C_Stop(void)
{
I2C2->CR1 |= I2C_CR1_STOP;
}
// 主机设置使能应答信号
void I2C_Ack(void)
{
I2C2->CR1 |= I2C_CR1_ACK;
}
// 主机设置使能非应答信号
void I2C_Nack(void)
{
I2C2->CR1 &= ~I2C_CR1_ACK;
}
// 主机发送设备地址,并等待应答
uint8_t I2C_SendAddr(uint8_t addr)
{
// 直接将要发送的地址给到DR
I2C2->DR = addr;
// 等待应答
uint16_t timeout = 0xffff;
while ((I2C2->SR1 & I2C_SR1_ADDR) == 0 && timeout)
{
timeout--;
}
// 访问SR2,清除ADDR标志位
if (timeout > 0)
{
I2C2->SR2;
}
return timeout ? OK : FAIL;
}
// 主机发送一个字节的数据(写入),并等待应答
uint8_t I2C_SendByte(uint8_t byte)
{
// 1. 先等待DR为空,上一个字节数据已经发送完毕
uint16_t timeout = 0xffff;
while ((I2C2->SR1 & I2C_SR1_TXE) == 0 && timeout)
{
timeout--;
}
// 2. 将要发送的字节放入DR中
I2C2->DR = byte;
// 3. 等待应答
timeout = 0xffff;
while ((I2C2->SR1 & I2C_SR1_BTF) == 0 && timeout)
{
timeout--;
}
return timeout ? OK : FAIL;
}
// 主机从EEPROM接收一个字节的数据(读取)
uint8_t I2C_ReadByte(void)
{
// 1. 先等待DR为满
uint16_t timeout = 0xffff;
while ((I2C2->SR1 & I2C_SR1_RXNE) == 0 && timeout)
{
timeout--;
}
// 2. 将收到的字节数据返回
return timeout ? I2C2->DR : FAIL;
}
iic.h
#ifndef __IIC_H
#define __IIC_H
#include "stm32f10x.h"
#include "delay.h"
// 宏定义
#define OK 0
#define FAIL 1
// 初始化
void I2C_Init(void);
// 发出起始信号
uint8_t I2C_Start(void);
// 设置发出停止信号
void I2C_Stop(void);
// 主机设置使能应答信号
void I2C_Ack(void);
// 主机设置使能非应答信号
void I2C_Nack(void);
// 主机发送设备地址,并等待应答
uint8_t I2C_SendAddr(uint8_t addr);
// 主机发送一个字节的数据(写入),并等待应答
uint8_t I2C_SendByte(uint8_t byte);
// 主机从EEPROM接收一个字节的数据(读取)
uint8_t I2C_ReadByte(void);
#endif
m24c02.c
#include "m24c02.h"
// 初始化
void M24C02_Init(void)
{
I2C_Init();
}
// 向EEPROM写入一个字节
void M24C02_WriteByte(uint8_t innerAddr, uint8_t byte)
{
// 1. 发出开始信号
I2C_Start();
// 2. 发送写地址
I2C_SendAddr(W_ADDR);
// 3. 发送内部地址
I2C_SendByte(innerAddr);
// 4. 发送具体数据
I2C_SendByte(byte);
// 5. 发出一个停止信号
I2C_Stop();
// 延迟等待写入周期结束
Delay_ms(5);
}
// 读取EEPROM的一个字节
uint8_t M24C02_ReadByte(uint8_t innerAddr)
{
// 1. 发出开始信号
I2C_Start();
// 2. 发送写地址(假写)
I2C_SendAddr(W_ADDR);
// 3. 发送内部地址
I2C_SendByte(innerAddr);
// 4. 发出开始信号
I2C_Start();
// 5. 发送读地址(真读)
I2C_SendAddr(R_ADDR);
// 6. 设置非应答
I2C_Nack();
// 7. 设置在接收下一个字节后发出停止信号
I2C_Stop();
// 8. 读取一个字节
uint8_t byte = I2C_ReadByte();
return byte;
}
// 连续写入多个字节(页写)
void M24C02_WriteBytes(uint8_t innerAddr, uint8_t *bytes, uint8_t size)
{
// 1. 发出开始信号
I2C_Start();
// 2. 发送写地址
I2C_SendAddr(W_ADDR);
// 3. 发送内部地址
I2C_SendByte(innerAddr);
// 利用循环不停发送数据
for (uint8_t i = 0; i < size; i++)
{
// 4. 发送具体数据
I2C_SendByte(bytes[i]);
}
// 5. 发出一个停止信号
I2C_Stop();
// 延迟等待写入周期结束
Delay_ms(5);
}
// 连续读取多个字节
void M24C02_ReadBytes(uint8_t innerAddr, uint8_t *buffer, uint8_t size)
{
// 1. 发出开始信号
I2C_Start();
// 2. 发送写地址(假写)
I2C_SendAddr(W_ADDR);
// 3. 发送内部地址
I2C_SendByte(innerAddr);
// 4. 发出开始信号
I2C_Start();
// 5. 发送读地址(真读)
I2C_SendAddr(R_ADDR);
// 利用循环连续读取多个字节
for (uint8_t i = 0; i < size; i++)
{
// 6. 设置应答或非应答
if (i < size - 1)
{
I2C_Ack();
}
else
{
I2C_Nack();
// 7. 设置发出停止信号
I2C_Stop();
}
// 8. 读取一个字节
buffer[i] = I2C_ReadByte();
}
}
m24c02.h
#ifndef __M24C02_H
#define __M24C02_H
#include "iic.h"
// 宏定义
#define W_ADDR 0xA0
#define R_ADDR 0xA1
// 初始化
void M24C02_Init(void);
// 向EEPROM写入一个字节
void M24C02_WriteByte(uint8_t innerAddr, uint8_t byte);
// 读取EEPROM的一个字节
uint8_t M24C02_ReadByte(uint8_t innerAddr);
// 连续写入多个字节(页写)
void M24C02_WriteBytes(uint8_t innerAddr, uint8_t * bytes, uint8_t size);
// 连续读取多个字节
void M24C02_ReadBytes(uint8_t innerAddr, uint8_t * buffer, uint8_t size);
#endif
2.2 库函数方式
验证二:硬件IIC通信
实现逻辑:
在cubemx配置IIC通信,然后再根据M24C02通信时序图,添加对应驱动文件。

效果:

main.c
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_I2C2_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
printf("******实践cubemx硬件IIC通信*******\r\n");
printf("写入单个字符\r\n");
// 2. 向EEPROM写入字符
M24C02_WriteByte(0x00, 'a');
M24C02_WriteByte(0x01, 'b');
M24C02_WriteByte(0x02, 'c');
printf("读取单个字符\r\n");
// 3. 读取字符
uint8_t byte1 = M24C02_ReadByte(0x00);
uint8_t byte2 = M24C02_ReadByte(0x01);
uint8_t byte3 = M24C02_ReadByte(0x02);
// 4. 串口输出打印
printf("byte1 = %c\t byte2 = %c\t byte3 = %c\r\n", byte1, byte2, byte3);
printf("写入多个字符\r\n");
// 5. 写入多个字符
M24C02_WriteBytes(0x00, "yangabc123", 10);
printf("读取多个字符\r\n");
// 6. 读取多个字符
uint8_t buffer[100] = {0};
M24C02_ReadBytes(0x00, buffer, 10);
// 7. 串口输出打印
printf("buffer = %s\r\n", buffer);
// 8. 测试超出16个字节的写入
printf("测试写入超过16个字:1234567890abcdefghijk\r\n");
// 清零缓冲区
memset(buffer, 0, sizeof(buffer));
M24C02_WriteBytes(0x00, "1234567890abcdefghijk", 21);
M24C02_ReadBytes(0x00, buffer, 21);
printf("buffer = %s\r\n", buffer);
/* USER CODE END 2 */
m24c02.c
#include "m24c02.h"
// 初始化
void M24C02_Init(void)
{
MX_I2C2_Init();
}
// 向EEPROM写入一个字节
void M24C02_WriteByte(uint8_t innerAddr, uint8_t byte)
{
HAL_I2C_Mem_Write(&hi2c2, W_ADDR, innerAddr, I2C_MEMADD_SIZE_8BIT, &byte, 1, 1000);
// 延迟等待写入周期结束
HAL_Delay(5);
}
// 读取EEPROM的一个字节
uint8_t M24C02_ReadByte(uint8_t innerAddr)
{
uint8_t byte;
HAL_I2C_Mem_Read(&hi2c2, R_ADDR, innerAddr, I2C_MEMADD_SIZE_8BIT, &byte, 1, 1000);
return byte;
}
// 连续写入多个字节(页写)
void M24C02_WriteBytes(uint8_t innerAddr, uint8_t *bytes, uint8_t size)
{
HAL_I2C_Mem_Write(&hi2c2, W_ADDR, innerAddr, I2C_MEMADD_SIZE_8BIT, bytes, size, 1000);
// 延迟等待写入周期结束
HAL_Delay(5);
}
// 连续读取多个字节
void M24C02_ReadBytes(uint8_t innerAddr, uint8_t *buffer, uint8_t size)
{
HAL_I2C_Mem_Read(&hi2c2, R_ADDR, innerAddr, I2C_MEMADD_SIZE_8BIT, buffer, size, 1000);
}
m24c02.h
#ifndef __M24C02_H
#define __M24C02_H
#include "i2c.h"
// 宏定义
#define W_ADDR 0xA0
#define R_ADDR 0xA1
// 初始化
void M24C02_Init(void);
// 向EEPROM写入一个字节
void M24C02_WriteByte(uint8_t innerAddr, uint8_t byte);
// 读取EEPROM的一个字节
uint8_t M24C02_ReadByte(uint8_t innerAddr);
// 连续写入多个字节(页写)
void M24C02_WriteBytes(uint8_t innerAddr, uint8_t * bytes, uint8_t size);
// 连续读取多个字节
void M24C02_ReadBytes(uint8_t innerAddr, uint8_t * buffer, uint8_t size);
#endif

浙公网安备 33010602011771号