stm32使用HAL库 使用非阻塞模式 中断完成I2C数据传输
调用HAL_I2C_Mem_Write_IT函数通过中断实现非阻塞I2C通讯
在调试过程中,程序中设定从起始地址0x00开始写入0x11 0x22,在程序debug时,HAL_I2C_Mem_Write_IT函数的形参也是0x11 0x22,结果从逻辑分析仪上看,实际主机向从机发送的是0x8E、0x04,调用读函数HAL_I2C_Mem_Read_IT后,发现写入的数据确实就是0x8E、0x04,出现了设定写入的数据和实际发送的数据不一致,发送错误数据的情况。
目录
主机为STM32G0B1RET6,从机为24C08
24C08
24C08数据手册参考:
https://www.findic.com/jiage/m24c08-fmn6tp-VXQonm9zm.html#doc_tab
存储大小
8 Kbit (1 Kbyte) of EEPROM,Page size: 16 bytes,共64Page
设备地址

高四位固定为Ah
低四位分别是:
b2:E2,这一位与芯片引脚E2接VCC还是VSS(GND)有关,接VCC时为1,接VSS时为0

A9、A8:将24C08的内存分为4部分:(这里的地址是以byte为单位)
第一部分的地址为0x000-0x0FF,A9 = 0 A8 = 0
第二部分的地址为0x100-0x1FF,A9 = 0 A8 = 1
第三部分的地址为0x200-0x2FF,A9 = 1 A8 = 0
第四部分的地址为0x300-0x3FF,A9 = 1 A8 = 1
实际上A9和A8就是地址的第9和第8位(最低位是第0位)
RW:指读写命令,读命令时,该位为1,写命令时,该位为0
因此,对于24C08硬件引脚E2已接VSS,且硬件引脚WC已接VSS,如果想要向0x000地址写入数据,那么设备地址为0xA0,如果想要从0x000地址读取数据,那么设备地址为0xA1
数据地址

写命令和读命令
①写命令
If the Write Control input is driven High, the Write instruction is not executed and the
accompanying data bytes are not acknowledged.
24C08有写使能引脚,需在硬件上,将写使能引脚接地,才可以写数据

注意:页写入模式允许在单个写入周期内写入最多16个字节,前提是这些字节都位于内存中的同一页面:即最高有效内存地址位A9/A4相同。如果发送的字节数超过页末尾可容纳的字节数,则会发生"滚动",即超出页末尾的字节将从位置0开始写入同一页面。
从时序图上看,例如对于24C08硬件引脚E2已接VSS,且硬件引脚WC已接VSS,如果想要向0x000和0x001地址分别写入数据0x11和0x22,那么先发送设备地址0xA0,再发送数据地址0x00,然后再发送0x11、0x22

②读命令:
从时序图上看,例如对于24C08硬件引脚E2已接VSS,如果想要读取0x000和0x001地址,那么先发送设备地址0xA0,再发送数据地址0x00,然后再发送0xA1,再开始读取两次数据

STM32G0B1RET6
本文使用TI官方NUCLEO-G0B1RE板,相关资料参考以下博文:
NUCLEO-G0B1RE STM32G0B1RET6的学习(1)------STM32CubeIDE的安装、新建工程和配置硬件SPI-CSDN博客
I2C复用PB7和PB6
PB6 ------> I2C1_SCL
PB7 ------> I2C1_SDA

程序编写
cpp
void I2C_Test(void)
{
uint8_t deviceAddr = 0xA0;//设备地址 写0xA0 读0xA1 A2接地 读写页0x00
uint8_t eepromAddr = 0x00;//读写地址
uint8_t writeData[2] = {0x11, 0x22};
static uint8_t readData[2] = {0x00, 0x00};
uint8_t writeSize = 2;
uint8_t readSize = 2;
static uint8_t a = 0;
static uint32_t lastTick = 0;
if(a == 0)
{
HAL_I2C_Mem_Write_IT(&hi2c1, deviceAddr, eepromAddr, I2C_MEMADD_SIZE_8BIT, writeData, writeSize);
a = 1;
lastTick = GetTick();
}
else if(a == 1)
{
if(pastTickMs(lastTick) > 10)//delay10ms
{
a = 2;
}
}
else if(a == 2)
{
HAL_I2C_Mem_Read_IT(&hi2c1, (deviceAddr+1), eepromAddr, I2C_MEMADD_SIZE_8BIT, readData, readSize);
a = 3;
}
}
逻辑分析仪得到的时序图:
写时序:

读时序:

可以看到,设定的writeData[2] = {0x11, 0x22};,但实际发送的却是0x8E和0x04
经过调试后,发现在writeData的定义上,增加static的静态,那么实际发送的数据就是正确的:
cpp
static uint8_t writeData[2] = {0x11, 0x22};

逻辑分析仪得到的时序图:
写时序:

读时序:

由于程序是通过中断实现非阻塞I2C通讯,会将数组的地址赋值hi2c->pBuffPtr,而writeData如果定义的是局部非静态数组,那么在函数I2C_Test结束后,这一数组的地址就会被释放掉,因此,想要实现中断非阻塞I2C通讯,那么需将写入数组writeData定义为静态变量。
