通讯协议之路主要分为两部分,第一部分从理论上面讲解各类协议的通讯原理以及通讯格式,第二部分从具体运用上讲解各类通讯协议的具体应用方法。
后续文章会同时发表在个人博客(jason1016.club)、CSDN;视频会发布在bilibili(UID:399951374)
本文前缀:
通讯协议专栏:通讯协议_JASON丶LI的博客-CSDN博客
UART理论部分:通讯协议学习之路:IIC协议理论-CSDN博客
硬件IIC
硬件IIC(Inter-Integrated Circuit)是一种串行通信协议,由飞利浦公司(现在的恩智浦半导体)开发。它使用两根线(SDA和SCL)来进行数据传输,其中SDA线用于数据传输,SCL线用于时钟同步。硬件IIC通常由硬件电路实现,例如在微控制器或集成电路中集成的硬件模块。
硬件I2C的效率要远高于软件的,而软件I2C由于不受管脚限制,接口比较灵活。
软件IIC
软件IIC是一种通过软件实现的IIC通信协议。它不依赖于硬件电路,而是通过软件控制GPIO(通用输入输出)引脚来模拟IIC通信。软件IIC通常用于一些资源受限的系统,例如单片机或嵌入式系统,这些系统可能没有硬件IIC模块。
软件IIC和硬件IIC之间的关系是,软件IIC是对硬件IIC的一种软件实现。软件IIC可以在没有硬件IIC模块的情况下实现IIC通信,但由于软件实现的限制,软件IIC的速度和可靠性可能不如硬件IIC。在一些资源受限的系统中,软件IIC是一种常用的替代方案。
- 硬件IIC用法比较复杂,模拟IIC的流程更清楚一些。
- 硬件IIC速度比模拟快,并且可以用DMA
- 模拟IIC可以在任何管脚上,而硬件只能在固定管脚上。
软件i2c是程序员使用程序控制SCL,SDA线输出高低电平,模拟i2c协议的时序。一般较硬件i2c稳定,但是程序较为繁琐,但不难。
硬件i2c程序员只要调用i2c的控制函数即可,不用直接的去控制SCL,SDA高低电平的输出。但是有些单片机的硬件i2c不太稳定,调试问题较多。
参考文章:硬件IIC和软件IIC区别-CSDN博客
开发实践
标准库
软件IIC:
软件IIC的实现核心在于把握IIC的通讯格式,所谓协议通讯,我的理解就是将10的高低电平状态整合成一段段有规律的预警,就像我们规定逗号是停顿,句号是句子结束一样,其实协议通讯同样如此,具体的IIC通讯格式可以移步到另外一篇文章:通讯协议学习之路:IIC协议理论-CSDN博客
IIC_Control.c
#include "IIC_Control.h"
#include "IIC_Software.h"
#define IIC_ADDRESS 0xA0
void IIC_Write(uint8_t RegAddress,uint8_t Data)
{
IIC_Software_Start();
IIC_Software_SendByte(IIC_ADDRESS);
IIC_Software_ReadBck();
IIC_Software_SendByte(RegAddress);
IIC_Software_ReadBck();
IIC_Software_SendByte(Data);
IIC_Software_ReadBck();
IIC_Software_Stop();
}
uint8_t IIC_Read(uint8_t RegAddress)
{
uint8_t Data;
IIC_Software_Start();
IIC_Software_SendByte(IIC_ADDRESS);
IIC_Software_ReadBck();
IIC_Software_SendByte(RegAddress);
IIC_Software_ReadBck();
IIC_Software_Start();
IIC_Software_SendByte(IIC_ADDRESS | 0x01);
IIC_Software_ReadBck();
Data = IIC_Software_ReadByte();
IIC_Software_SendBck();
IIC_Software_Stop();
return Data;
}
IIC_Control.h
#ifndef __IICCONTROL_H
#define __IICCONTROL_H
#include "stm32f10x.h" // Device header
void IIC_Write(uint8_t RegAddress,uint8_t Data);
uint8_t IIC_Read(uint8_t RegAddress);
#endif
IIC_Software.c
#include "stm32f10x.h" // Device header
#include "IIC_Software.h"
#include "Delay.h"
//PB10-SCL PB11-SDA
//- 常态:SCL==1;SDA==1
//- 工作态:SCL电频周期翻转;SDA电频由数据而定
//- 起始位:SCL==1时:SDA==1-->0
//- 停止位:SCL==1时:SDA==0-->1
//- 逻辑1:SCL==1时:SDA==1
//- 逻辑0:SCL==1时:SDA==0
void IIC_Delay(void)
{
Delay_us(4);
}
void IIC_Software_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_Initstructure;
GPIO_Initstructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_Initstructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
GPIO_Initstructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_Initstructure);
SDA_H;SCL_H; //设置空闲态
}
void IIC_Software_Start(void) //起始位
{
SDA_H;
SCL_H;
IIC_Delay();
SDA_L;
IIC_Delay();
SCL_L;
}
void IIC_Software_Stop(void) //停止位
{
SDA_L;
SCL_H;
IIC_Delay();
SDA_H;
// IIC_Delay();
// SCL_H;
}
void IIC_Software_SendByte(unsigned char byte) //发送
{
int i;
for(i=0; i<8; i++)
{
SCL_L;
if(byte&1<<(7-i))
{
SDA_H;
}
else{
SDA_L;
}
SCL_H;
IIC_Delay();
}
SCL_L;
}
unsigned char IIC_Software_ReadBck(void) //接收应答
{
u8 ack;
SDA_L; //拉低SDA 这里是用来处理半高电平,直接拉低sda总线再接收应答,这样一来应答信号为0时可以直接读取,当应答信号为1时也可以直接报错
// SDA_H; //拉高SDA
SCL_H; //拉高SCL
IIC_Delay(); //等待
ack = Read_SDA; //读取
SCL_L; //这里拉低回来是为了对齐时序
SDA_L;
return ack;
}
void IIC_Software_SendBck(void) //发送应答
{
SCL_L;
SDA_L;
SCL_H;
IIC_Delay();
SCL_L;
}
unsigned char IIC_Software_ReadByte(void) //从总线上读一个字节
{
int i;
unsigned char byte=0;
SDA_H; //释放SDA
for(i=0; i<8; i++)
{
SCL_H; //拉高,然后读
IIC_Delay();
if(Read_SDA)
{
byte |= (1<<(7-i));
}
SCL_L;
IIC_Delay();
}
return byte;
}
IIC_Software.h
#ifndef __IICSOFTWARE_H
#define __IICSOFTWARE_H
//PB10-SCL PB11-SDA
#define SCL_H GPIO_SetBits(GPIOB, GPIO_Pin_10)
#define SCL_L GPIO_ResetBits(GPIOB, GPIO_Pin_10)
#define SDA_H GPIO_SetBits(GPIOB, GPIO_Pin_11)
#define SDA_L GPIO_ResetBits(GPIOB, GPIO_Pin_11)
#define Read_SDA GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11)
void IIC_Delay(void);
void IIC_Software_Init(void);
void IIC_Software_Start(void); //起始位
void IIC_Software_Stop(void); //停止位
void IIC_Software_SendByte(unsigned char byte); //发送
unsigned char IIC_Software_ReadBck(void); //接收应答
void IIC_Software_SendBck(void); //发送应答
unsigned char IIC_Software_ReadByte(void); //从总线上读一个字节
#endif
硬件IIC:
硬件IIC就不需要像软件配置一样一步步来,只需要使能配置I2Cx外设即可,并且标准库对硬件IIC的各项指令都有对应的函数封装,配置完成后只需要调用封装好的函数即可实现通讯。
标准库IIC函数:
//配置自身设备地址2
void I2C_OwnAddress2Config(I2C_TypeDef* I2Cx, uint8_t Address);
//发送设备地址
void I2C_Send7bitAddress(I2C_TypeDef* I2Cx, uint8_t Address, uint8_t I2C_Direction);
//接收数据
uint8_t I2C_ReceiveData(I2C_TypeDef* I2Cx);
//停止接收
void I2C_AcknowledgeConfig(I2C_TypeDef* I2Cx, FunctionalState NewState);
//IIC外设开始正常工作
void I2C_Cmd(I2C_TypeDef* I2Cx, FunctionalState NewState);
MYIIC.c
#include "MYIIC.h"
#include "delay.h"
#define EERADDRESS 0xA0
void MYIIC_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);
GPIO_InitTypeDef GPIO_Initstructure;
GPIO_Initstructure.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_Initstructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
GPIO_Initstructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_Initstructure);
I2C_DeInit(I2C2);
I2C_InitTypeDef I2C_Initstructure;
I2C_Initstructure.I2C_Mode = I2C_Mode_I2C;
I2C_Initstructure.I2C_DutyCycle = I2C_DutyCycle_2; //该参数只有在 I2C 工作在快速模式(时钟工作频率高于 100KHz)下才有意义
I2C_Initstructure.I2C_OwnAddress1 = 0xFF;
I2C_Initstructure.I2C_Ack = I2C_Ack_Enable;
I2C_Initstructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_Initstructure.I2C_ClockSpeed = 100000;
I2C_Init(I2C2, &I2C_Initstructure);
I2C_Cmd(I2C2, ENABLE);
}
//指定超时退出机制,防止下述死循环卡死
void I2C_WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
{
uint32_t Timeout;
Timeout = 10000;
while(I2C_CheckEvent(I2Cx, I2C_EVENT) != SUCCESS)
{
Timeout --;
if(Timeout == 0)
{
break;
}
}
}
void I2C_ByteWrite(uint8_t *pBuffer, uint8_t WriteAddr)
{
//读一个字节
while(I2C_GetFlagStatus(DEBUG_I2Cx_Port, I2C_FLAG_BUSY));
//发送Start信号
I2C_GenerateSTART(DEBUG_I2Cx_Port,ENABLE);
//等待EV5事件:IIC开始信号已经发出 (I2C_SR1内SB位置1)
while(I2C_CheckEvent(DEBUG_I2Cx_Port,I2C_EVENT_MASTER_MODE_SELECT)==ERROR);
//发送7位“EEPROM地址”
I2C_Send7bitAddress(DEBUG_I2Cx_Port,EERADDRESS,I2C_Direction_Transmitter);
//等待EV6事件:表示地址已经发送
while(I2C_CheckEvent(DEBUG_I2Cx_Port,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)==ERROR);
//写入EEPROM内将要写入的地址数据
I2C_SendData(DEBUG_I2Cx_Port,WriteAddr);
//等待EV8事件:返回SET则数据寄存器DR为空
while(I2C_CheckEvent(DEBUG_I2Cx_Port,I2C_EVENT_MASTER_BYTE_TRANSMITTED)==ERROR);
//写入数据
I2C_SendData(DEBUG_I2Cx_Port,*pBuffer);
//等待EV8事件:返回SET则数据寄存器DR为空
while(I2C_CheckEvent(DEBUG_I2Cx_Port,I2C_EVENT_MASTER_BYTE_TRANSMITTED)==ERROR);
//一个字节发送完成,发送Stop信号
I2C_GenerateSTOP(DEBUG_I2Cx_Port, ENABLE);
}
/**
* @brief 从EEPROM里面读取一块数据
* @param
* @arg pBuffer:存放从EEPROM读取的数据的缓冲区指针
* @arg WriteAddr:接收数据的EEPROM的地址
* @retval 无
*/
void I2C_ByteRead(uint8_t *pBuffer, uint8_t ReadAddr)
{
//发送Start信号
I2C_GenerateSTART(DEBUG_I2Cx_Port,ENABLE);
//等待EV5事件:IIC开始信号已经发出 (I2C_SR1内SB位置1)
while(I2C_CheckEvent(DEBUG_I2Cx_Port,I2C_EVENT_MASTER_MODE_SELECT)==ERROR);
//发送7位“EEPROM地址”
I2C_Send7bitAddress(DEBUG_I2Cx_Port,EERADDRESS,I2C_Direction_Transmitter);
//等待EV6事件:表示地址已经发送
while(I2C_CheckEvent(DEBUG_I2Cx_Port,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)==ERROR);
//写入EEPROM内存“单元地址”
I2C_SendData(DEBUG_I2Cx_Port,ReadAddr);
//等待EV8事件:数据寄存器DR为空 ,地址数据已经发送
while(I2C_CheckEvent(DEBUG_I2Cx_Port,I2C_EVENT_MASTER_BYTE_TRANSMITTED)==ERROR);
//重新发送Start信号
I2C_GenerateSTART(DEBUG_I2Cx_Port,ENABLE);
//等待EV5事件
while(I2C_CheckEvent(DEBUG_I2Cx_Port,I2C_EVENT_MASTER_MODE_SELECT)==ERROR);
//发送7位“EEPROM地址”
I2C_Send7bitAddress(DEBUG_I2Cx_Port,EERADDRESS,I2C_Direction_Receiver);//注意方向
//等待EV6事件(接收):表示地址已经发送
while(I2C_CheckEvent(DEBUG_I2Cx_Port,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)==ERROR);//注意方向
//产生非应答
I2C_AcknowledgeConfig(DEBUG_I2Cx_Port, DISABLE);
//发送Stop信号
I2C_GenerateSTOP(DEBUG_I2Cx_Port, ENABLE);
//等待EV7事件, BUSY, MSL and RXNE flags
while(I2C_CheckEvent(DEBUG_I2Cx_Port, I2C_EVENT_MASTER_BYTE_RECEIVED)==ERROR);
*pBuffer = I2C_ReceiveData(DEBUG_I2Cx_Port);
//重新初始化 为下次做准备
I2C_AcknowledgeConfig(DEBUG_I2Cx_Port, ENABLE);
}
MYIIC.h
#ifndef __MYIIC_H
#define __MYIIC_H
#include "stm32f10x.h" // Device header
#define DEBUG_I2Cx_Port I2C2
void MYIIC_Init(void);
void I2C_ByteWrite(uint8_t *pBuffer, uint8_t WriteAddr);
void I2C_ByteRead(uint8_t *pBuffer, uint8_t ReadAddr);
#endif
DMA IIC:
HAL库
/* IO operation functions ****************************************************/
/******* Blocking mode: Polling */
HAL_StatusTypeDef HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_I2C_Slave_Transmit(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_I2C_Slave_Receive(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_I2C_Mem_Write(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_I2C_Mem_Read(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_I2C_IsDeviceReady(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint32_t Trials, uint32_t Timeout);
/******* Non-Blocking mode: Interrupt */
HAL_StatusTypeDef HAL_I2C_Master_Transmit_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Master_Receive_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Slave_Transmit_IT(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Slave_Receive_IT(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Mem_Write_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Mem_Read_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Master_Seq_Transmit_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t XferOptions);
HAL_StatusTypeDef HAL_I2C_Master_Seq_Receive_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t XferOptions);
HAL_StatusTypeDef HAL_I2C_Slave_Seq_Transmit_IT(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size, uint32_t XferOptions);
HAL_StatusTypeDef HAL_I2C_Slave_Seq_Receive_IT(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size, uint32_t XferOptions);
HAL_StatusTypeDef HAL_I2C_EnableListen_IT(I2C_HandleTypeDef *hi2c);
HAL_StatusTypeDef HAL_I2C_DisableListen_IT(I2C_HandleTypeDef *hi2c);
HAL_StatusTypeDef HAL_I2C_Master_Abort_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress);
/******* Non-Blocking mode: DMA */
HAL_StatusTypeDef HAL_I2C_Master_Transmit_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Master_Receive_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Slave_Transmit_DMA(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Slave_Receive_DMA(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Mem_Write_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Mem_Read_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Master_Seq_Transmit_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t XferOptions);
HAL_StatusTypeDef HAL_I2C_Master_Seq_Receive_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t XferOptions);
HAL_StatusTypeDef HAL_I2C_Slave_Seq_Transmit_DMA(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size, uint32_t XferOptions);
HAL_StatusTypeDef HAL_I2C_Slave_Seq_Receive_DMA(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size, uint32_t XferOptions);
HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);
- *hi2c 设置使用的是那个IIC 例:&hi2c2
- DevAddress 写入的地址 设置写入数据的地址 例 0xA0
- *pData 需要写入的数据
- Size 要发送的字节数
- Timeout 最大传输时间,超过传输时间将自动退出传输函数
IIC读函数
HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);
- *hi2c: 设置使用的是那个IIC 例:&hi2c2
- DevAddress: 写入的地址 设置写入数据的地址 例 0xA0
- *pDat:a 存储读取到的数据
- Size: 发送的字节数
- Timeout: 最大读取时间,超过时间将自动退出读取函数
HAL_I2C_Master_Transmit(&hi2c1,0xA1,(uint8_t*)TxData,2,1000) ;//发送两个字节数据
IIC写数据函数
HAL_I2C_Mem_Write(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t pData, uint16_t Size, uint32_t Timeout);
/* 第1个参数为I2C操作句柄
第2个参数为从机设备地址
第3个参数为从机寄存器地址
第4个参数为从机寄存器地址长度
第5个参数为发送的数据的起始地址
第6个参数为传输数据的大小
第7个参数为操作超时时间 */
功能: IIC写多个数据 该函数适用于IIC外设里面还有子地址寄存器的设备,比方说E2PROM,除了设备地址,每个存储字节都有其对应的地址
- *hi2c: I2C设备号指针,设置使用的是那个IIC 例:&hi2c2
- DevAddress: 从设备地址 从设备的IIC地址 例E2PROM的设备地址 0xA0
- MemAddress: 从机寄存器地址 ,每写入一个字节数据,地址就会自动+1
- MemAddSize: 从机寄存器地址字节长度 8位或16位
- I2C_MEMADD_SIZE_8BIT
- I2C_MEMADD_SIZE_16BIT
- *pData: 需要写入的的数据的起始地址
- Size: 传输数据的大小 多少个字节
- Timeout: 最大读取时间,超过时间将自动退出函数
使用HAL_I2C_Mem_Write等于先使用HAL_I2C_Master_Transmit传输第一个寄存器地址,再用HAL_I2C_Master_Transmit传输写入第一个寄存器的数据。可以传输多个数据
void Single_WriteI2C(uint8_t REG_Address,uint8_t REG_data)
{
uint8_t TxData[2] = {REG_Address,REG_data};
while(HAL_I2C_Master_Transmit(&hi2c1,I2C1_WRITE_ADDRESS,(uint8_t*)TxData,2,1000) != HAL_OK)
{
if (HAL_I2C_GetError(&hi2c1) != HAL_I2C_ERROR_AF)
{
Error_Handler();
}
}
}
至于读函数也是如此,因此用HAL_I2C_Mem_Write和HAL_I2C_Mem_Read,来写读指定设备的指定寄存器数据是十分方便的,让设计过程省了好多步骤。
HAL_I2C_Mem_Write(&hi2c2, ADDR, i, I2C_MEMADD_SIZE_8BIT,&(I2C_Buffer_Write[i]),8, 1000);
HAL_I2C_Mem_Read(&hi2c2, ADDR, i, I2C_MEMADD_SIZE_8BIT,&(I2C_Buffer_Write[i]),8, 1000);
HAL_I2C_Mem_Write(&hi2c2, ADDR, i, I2C_MEMADD_SIZE_16BIT,&(I2C_Buffer_Write[i]),8, 1000);
HAL_I2C_Mem_Read(&hi2c2, ADDR, i, I2C_MEMADD_SIZE_16BIT,&(I2C_Buffer_Write[i]),8, 1000);
如果只往某个外设中写数据,则用Master_Transmit。 如果是外设里面还有子地址,例如我们的E2PROM,有设备地址,还有每个数据的寄存器存储地址。则用Mem_Write。Mem_Write是2个地址,Master_Transmit只有从机地址
IIC接收函数(proteus未实现)
在mian.c文件前面声明,AT24C02 写地址和读地址 ,定义写数据数组,和读数据数组
/* USER CODE BEGIN PV */
#include <string.h>
#define ADDR_24LCxx_Write 0xA0
#define ADDR_24LCxx_Read 0xA1
#define BufferSize 256
uint8_t WriteBuffer[BufferSize],ReadBuffer[BufferSize];
uint16_t i;
/* USER CODE END PV */
/* USER CODE BEGIN 2 */
for(i=0; i<256; i++)
WriteBuffer[i]=i; /* WriteBuffer init */
printf("\r\n***************I2C Example Z小旋测试*******************************\r\n");
for (int j=0; j<32; j++)
{
if(HAL_I2C_Mem_Write(&hi2c1, ADDR_24LCxx_Write, 8*j, I2C_MEMADD_SIZE_8BIT,WriteBuffer+8*j,8, 1000) == HAL_OK)
{
printf("\r\n EEPROM 24C02 Write Test OK \r\n");
HAL_Delay(20);
}
else
{
HAL_Delay(20);
printf("\r\n EEPROM 24C02 Write Test False \r\n");
}
}
/*
// wrinte date to EEPROM 如果要一次写一个字节,写256次,用这里的代码
for(i=0;i<BufferSize;i++)
{
HAL_I2C_Mem_Write(&hi2c1, ADDR_24LCxx_Write, i, I2C_MEMADD_SIZE_8BIT,&WriteBuffer[i],1,0xff);//使用I2C块读,出错。因此采用此种方式,逐个单字节写入
HAL_Delay(5);//此处延时必加,与AT24C02写时序有关
}
printf("\r\n EEPROM 24C02 Write Test OK \r\n");
*/
HAL_I2C_Mem_Read(&hi2c1, ADDR_24LCxx_Read, 0, I2C_MEMADD_SIZE_8BIT,ReadBuffer,BufferSize, 0xff);
for(i=0; i<256; i++)
printf("0x%02X ",ReadBuffer[i]);
/* USER CODE END 2 */
AT24C02的IIC每次写之后要延时一段时间才能继续写 每次写之后要delay 5ms左右 不管硬件IIC采用何种形式(DMA,IT),都要确保两次写入的间隔大于5ms;
读写函数最后一个超时调整为1000以上 因为我们一次写8个字节,延时要久一点
AT24C02页写入只支持8个byte,所以需要分32次写入。这不是HAL库的bug,而是AT24C02的限制,其他的EEPROM可以支持更多byte的写入。
当然,你也可以每次写一个字节,分成256次写入,也是可以的 那就用注释了的代码即可
/*
// wrinte date to EEPROM 如果要一次写一个字节,写256次,用这里的代码
for(i=0;i<BufferSize;i++)
{
HAL_I2C_Mem_Write(&hi2c1, ADDR_24LCxx_Write, i, I2C_MEMADD_SIZE_8BIT,&WriteBuffer[i],1,0xff);//使用I2C块读,出错。因此采用此种方式,逐个单字节写入
HAL_Delay(5);//此处延时必加,与AT24C02写时序有关
}
printf("\r\n EEPROM 24C02 Write Test OK \r\n");
*/
参考文章:【精选】【STM32】HAL库 STM32CubeMX教程十二---IIC(读取AT24C02 )_hal iic-CSDN博客