通讯协议学习之路(实践部分):IIC开发实践
通讯协议之路主要分为两部分,第一部分从理论上面讲解各类协议的通讯原理以及通讯格式,第二部分从具体运用上讲解各类通讯协议的具体应用方法。
后续文章会同时发表在个人博客(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 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库
/* 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博客
相关文章:
通讯协议学习之路(实践部分):IIC开发实践
通讯协议之路主要分为两部分,第一部分从理论上面讲解各类协议的通讯原理以及通讯格式,第二部分从具体运用上讲解各类通讯协议的具体应用方法。 后续文章会同时发表在个人博客(jason1016.club)、CSDN;视频会发布在bilibili(UID:399951374) 本文…...
『亚马逊云科技产品测评』活动征文|搭建带有“弱”图像处理功能的流媒体服务器
授权声明:本篇文章授权活动官方亚马逊云科技文章转发、改写权,包括不限于在 Developer Centre, 知乎,自媒体平台,第三方开发者媒体等亚马逊云科技官方渠道。 本文基于以下软硬件工具: aws ec2 frp-0.52.3 mediamtx-1.3…...
正交矩阵的定义
对于n阶矩阵A,如果,其中为单位矩阵,为A的转置矩阵,那么就称A为正交矩阵。 对于正交矩阵, 对于正交矩阵,其列向量都是单位向量,行向量都是单位向量...
K8S集群etcd 某个节点数据不一致如何修复 —— 筑梦之路
背景说明 二进制方式安装的k8s集群,etcd集群有3个节点,某天有一台机器hang住了,无法远程ssh登陆,于是被管理员直接重启了,重启后发现k8s集群删除一个deployment应用,多次刷新一会有,一会没有&am…...
selenium/webdriver运行原理与机制
最近在看一些底层的东西。driver翻译过来是驱动,司机的意思。如果将webdriver比做成司机,竟然非常恰当。 我们可以把WebDriver驱动浏览器类比成出租车司机开出租车。在开出租车时有三个角色: 乘客:他/她告诉出租车司机去哪里&a…...
论文阅读[121]使用CAE+XGBoost从荧光光谱中检测和识别饮用水中的有机污染物
【论文基本信息】 标题:Detection and Identification of Organic Pollutants in Drinking Water from Fluorescence Spectra Based on Deep Learning Using Convolutional Autoencoder 标题译名:基于使用卷积自动编码器的深度学习,从荧光光谱…...
Juniper SRX PPPoE配置
直接上配置脚本 6号口接运营商进行拨号 ---------- set interfaces ge-0/0/6 unit 0 encapsulation ppp-over-ether set interfaces ge-0/0/6 description "Connect_to_Modem" set interfaces pp0 unit 0 pppoe-options underlying-interface ge-0/0/6.0 set inte…...
虚拟仪器软件结构VISA
1、什么是VISA VISA是虚拟仪器软件结构(Virtual Instrument Software Architectuere)的简称,是由VXI plug & play系统联盟所统一制定的I/O接口软件标准及其相关规范的总称。一般称这个I/O函数库为VISA库(用于仪器编程的标准I/O函数库)。…...
/etc/init.d/functions: Syntax error: “(“ unexpected (expecting “done“)
一.问题描述: ubuntu系统安装服务时报错: 二.问题解析: Ubuntu安装时默认使用dash,shell脚本命令失败,需要安装bash来运行,长期解决该问题就是重新配置dash 三:问题解决: sudo dpkg-reconfi…...
Google/微端/Amazon/IBM四个厂家在分布式里面提供的服务总结
1.背景 最近在复习分布式的课程,发现总有四家公司——Google/微端/Amazon/IBM绕不过去,而他们又开发了许许多多的服务和架构,需要去记忆,于是乎就整理了一下他们提供的服务 2.Google提供的服务 (1)GFS(Go…...
计网:第一章 概述
目录 1.1计算机网络在信息时代作用 1.2因特网概述 1.3三种交换方式 1.4计算机网络的定义和分类 1.5计算机网络的性能指标 1.6计算机网络的体系结构 基于湖科大教书匠b站计算机网络教学视频以及本校课程老师ppt 整合出的计算机网络学习笔记 根据文章目录,具体内…...
RT-DETR算法优化改进:新颖的多尺度卷积注意力(MSCA),即插即用,助力小目标检测 | NeurIPS2022
💡💡💡本文独家改进: 多尺度卷积注意力(MSCA),有效地提取上下文信息,新颖度高,创新十足。 1)代替RepC3进行使用; 2)MSCAAttention直接作为注意力进行使用; 推荐指数:五星 RT-DETR魔术师专栏介绍: https://blog.csdn.net/m0_63774211/category_12497375.ht…...
基于遗传算法改进的GRNN多输入多输出回归预测,基于多目标遗传算法+GRNN的帕累托前沿求解,基于遗传工具箱调用GRNN模型的多目标求解
目录 背影 遗传算法的原理及步骤 基本定义 编码方式 适应度函数 运算过程 代码 结果分析 展望 完整代码下载链接:grnn多输入多输出训练测试,遗传算法改进grnn神经网络,NSGA-2多目标遗传算法,多目标遗传算法和grnn结合优化资源-CSDN文库 https://download.csdn.net/downloa…...
vue2按需导入Element(vite打包)
1.安装element 说明:-S是生产依赖。 npm install element-ui2 -S 2.安装babel-plugin-component 说明:-D是开发模式使用。 npm install babel-plugin-component -D 3. vite.config.js 说明:借助 babel-plugin-component ,我们可…...
力扣117双周赛
第 117 场双周赛 给小朋友们分糖果 I 同T2 给小朋友们分糖果 II 数学 class Solution { public:long long distributeCandies(int n, int limit) {long long ans 0;for (int i 0; i < min(n, limit); i) {if (n - i < limit) {ans n - i 1;} else if (n - i <…...
SPI简介及FPGA通用MOSI模块实现
简介 SPI(Serial Peripheral Interface,串行外围设备接口)通讯协议,是Motorola公司提出的一种同步串行接口技术。是一种高速、全双工、同步通信总线。在芯片中只占用四根管脚用来控制及数据传输。 优缺点: SPI通讯协…...
K8S篇之K8S详解
一、K8S简介 k8s全称kubernetes,是为容器服务而生的一个可移植容器的编排管理工具。k8s目前已经主导了云业务流程,推动了微服务架构等热门技术的普及和落地。 k8s是自动化容器操作的开源平台。这些容器操作包括:部署、调度和节点集群间扩展。…...
进博会再现上亿大单 EZZ携手HIC海橙嗨选签署2024年度合作备忘录
正在举行的第六届中国国际进口博览会上,再现上亿大单。11月6日,在澳大利亚新南威尔士州政府代表的见证下,澳交所基因组龙头上市公司EZZ生命科学和中国跨境社交电商龙头HIC海橙嗨选签署2024合作备忘录,在未来的一年,EZZ…...
深度学习基于python+TensorFlow+Django的花朵识别系统
欢迎大家点赞、收藏、关注、评论啦 ,由于篇幅有限,只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 花朵识别系统,基于Python实现,深度学习卷积神经网络,通过TensorFlow搭建卷积神经…...
Azure 机器学习 - 机器学习中的企业安全和治理
目录 限制对资源和操作的访问网络安全性和隔离数据加密数据渗透防护漏洞扫描审核和管理合规性 在本文中,你将了解可用于 Azure 机器学习的安全和治理功能。 如果管理员、DevOps 和 MLOps 想要创建符合公司策略的安全配置,那么这些功能对其十分有用。 通过…...
(十)学生端搭建
本次旨在将之前的已完成的部分功能进行拼装到学生端,同时完善学生端的构建。本次工作主要包括: 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...
黑马Mybatis
Mybatis 表现层:页面展示 业务层:逻辑处理 持久层:持久数据化保存 在这里插入图片描述 Mybatis快速入门 是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...
【第二十一章 SDIO接口(SDIO)】
第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...
Leetcode 3577. Count the Number of Computer Unlocking Permutations
Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接:3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯,要想要能够将所有的电脑解锁&#x…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序
一、开发准备 环境搭建: 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 项目创建: File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...
HBuilderX安装(uni-app和小程序开发)
下载HBuilderX 访问官方网站:https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本: Windows版(推荐下载标准版) Windows系统安装步骤 运行安装程序: 双击下载的.exe安装文件 如果出现安全提示&…...
uniapp微信小程序视频实时流+pc端预览方案
方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度WebSocket图片帧定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐RTMP推流TRTC/即构SDK推流❌ 付费方案 (部分有免费额度&#x…...
