通讯协议学习之路(实践部分):IIC开发实践

news/2024/10/22 3:20:14/

通讯协议之路主要分为两部分,第一部分从理论上面讲解各类协议的通讯原理以及通讯格式,第二部分从具体运用上讲解各类通讯协议的具体应用方法。

后续文章会同时发表在个人博客(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是一种常用的替代方案。

  1. 硬件IIC用法比较复杂,模拟IIC的流程更清楚一些。
  2. 硬件IIC速度比模拟快,并且可以用DMA
  3. 模拟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        0xA0void 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 headervoid 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==0void 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;        //拉高SCLIIC_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;            //释放SDAfor(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        0xA0void 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 flagswhile(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库

HAL库IIC接收和发送的轮询、中断、DMA三种模式的函数

/* 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);

IIC写函数

HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);

功能:IIC写数据

参数:

  1. *hi2c 设置使用的是那个IIC 例:&hi2c2
  2. DevAddress 写入的地址 设置写入数据的地址 例 0xA0
  3. *pData 需要写入的数据
  4. Size 要发送的字节数
  5. Timeout 最大传输时间,超过传输时间将自动退出传输函数

IIC读函数

HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);

功能:IIC读一个字节

参数:

  1. *hi2c: 设置使用的是那个IIC 例:&hi2c2
  2. DevAddress: 写入的地址 设置写入数据的地址 例 0xA0
  3. *pDat:a 存储读取到的数据
  4. Size: 发送的字节数
  5. 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,除了设备地址,每个存储字节都有其对应的地址

参数:

  1. *hi2c: I2C设备号指针,设置使用的是那个IIC 例:&hi2c2
  2. DevAddress: 从设备地址 从设备的IIC地址 例E2PROM的设备地址 0xA0
  3. MemAddress: 从机寄存器地址 ,每写入一个字节数据,地址就会自动+1
  4. MemAddSize: 从机寄存器地址字节长度 8位或16位
    1. 写入数据的字节类型 8位还是16位
    2. I2C_MEMADD_SIZE_8BIT
    3. I2C_MEMADD_SIZE_16BIT
  5. 在stm32f1xx_hal_i2c.h中有定义

  1. *pData: 需要写入的的数据的起始地址
  2. Size: 传输数据的大小 多少个字节
  3. 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,来写读指定设备的指定寄存器数据是十分方便的,让设计过程省了好多步骤。

举例:

8位:

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);

16位:

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 */

在main.c中添加

  /* 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博客


http://www.ppmy.cn/news/1217757.html

相关文章

记录一种引起 CL.exe/ C++ 编译器无任何提示直接崩溃的问题

只需定义在源文件或公共引入的头文件之中&#xff0c;编译必定CL.exe 退出&#xff0c;错误代码2&#xff0c;它不会产生任何语法意义上的错误提示&#xff0c;感兴趣的可以记录下。 引发崩溃的代码&#xff1a; template <typename T> class IS_CONFIG_LOADER_T_CLASS…

【ATTCK】ATTCK开源项目Caldera学习笔记

CALDERA是一个由python语言编写的红蓝对抗工具&#xff08;攻击模拟工具&#xff09;。它是MITRE公司发起的一个研究项目&#xff0c;该工具的攻击流程是建立在ATT&CK攻击行为模型和知识库之上的&#xff0c;能够较真实地APT攻击行为模式。 通过CALDERA工具&#xff0c;安全…

63基于matlab的生物地理的优化器(BBO)被用作多层感知器(MLP)的训练器。

基于matlab的生物地理的优化器&#xff08;BBO&#xff09;被用作多层感知器&#xff08;MLP&#xff09;的训练器。粒子群优化&#xff08;PSO&#xff09;、蚁群优化&#xff08;ACO&#xff09;、遗传算法&#xff08;GA&#xff09;、进化策略&#xff08;ES&#xff09;和…

【C/C++底层】内存分配:栈区(Stack)与堆区(Heap)

/*** poject * author jUicE_g2R(qq:3406291309)* file 底层内存分配&#xff1a;栈区(Stack)与堆区(Heap)* * language C/C* EDA Base on MVS2022* editor Obsidian&#xff08;黑曜石笔记软件&#xff09;* * copyright 2023* COPYRIGHT …

Arduino驱动SHT1x系列数字温湿度传感器(温湿度传感器)

目录 1、传感器特性 2、控制器和传感器连线图 3、驱动程序 瑞士Sensirion公司推出了SHT1x单片数字温湿度集成传感器。采用CMOS微加工专利技术(CMOSenstechnology),确保产品具有极高的可靠性和出色的长期稳定性。该传感器由1个电容式聚合体测湿元件和1个能隙式测温元件组成…

iText v1.8.1(OCR截图文字识别工具)

iText for mac是一款OCR&#xff08;光学字符识别&#xff09;工具&#xff0c;可以从图片中识别文字&#xff0c;适用于从扫描版的PDF等任意图片中提取文字。 使用iText&#xff0c;您可以方便快捷地从图片中摘抄和批注文字&#xff0c;满足您的各种需求。其自带截图功能&…

目标检测问题总结

目标检测问题总结 目标检测二阶段和一阶段的核心区别目标检测二阶段比一阶段的算法精度高的原因1. 正负样本不平衡2.样本的不一致性 如何解决目标检测中遮挡问题如何解决动态目标检测FPN的作用如何解决训练数据样本过少的问题IOU代码实现NMS代码实现NMS的改进思路 目标检测二阶…

买房和租房哪个划算?

目录 1、考虑因素 1. 1费用比较 1.2 资产增值 1.3 税收影响 1.4 灵活性 1.5 贷款利率 1.6 长期计划 1.7 当地市场条件 2、买房计算 2.1等额本息 2.2等额本金 3、租房计算 1、考虑因素 在比较买房和租房哪个更划算时&#xff0c;需要考虑多个因素。以下是一些可以考…