STM32使用HAL库通过中断实现非阻塞I2C通讯 解决实际发送错误数据的问题 HAL_I2C_Mem_Write_IT

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,出现了设定写入的数据和实际发送的数据不一致,发送错误数据的情况。

目录

24C08

存储大小

设备地址

数据地址

写命令和读命令

STM32G0B1RET6

程序编写


主机为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;
	}

}

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

写时序:

读时序:

可以看到,设定的writeData2 = {0x11, 0x22};,但实际发送的却是0x8E和0x04

经过调试后,发现在writeData的定义上,增加static的静态,那么实际发送的数据就是正确的:

cpp 复制代码
static uint8_t writeData[2] = {0x11, 0x22};

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

写时序:

读时序:

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

相关推荐
Zw-awa18 小时前
从 CPU 搬运到自动搬运:DMA、空闲中断和两块缓冲
stm32
无敌贵点大王18 小时前
从寄存器层面重学STM32(6)——串口USART实战部分
stm32·单片机·嵌入式硬件
庙堂龙吟奈我何18 小时前
二进制和十六进制
stm32·单片机
TST-魚爷20 小时前
小型视频会议室音频踩坑:一体化拾音扩音方案实测分享(易视讯)
stm32·电脑·音视频·钉钉·腾讯会议·zoom会议
十八岁牛爷爷1 天前
STM32H7 长时间运行后 HTTP 断连:从“串口还活着”到 TX 描述符错账
stm32·单片机·嵌入式硬件
Lxx_Zw1 天前
串口命令如何从字符串长成协议:帧头、长度、校验和与状态机
c语言·stm32·单片机·嵌入式硬件
糖糖单片机设计1 天前
基于 STM32 + LU-ASR01 的智能台灯
stm32·单片机·嵌入式硬件·51单片机·语音识别
起个网名真难~2 天前
STM32F103_Bootloader_从零到UART升级成功_博客原稿
stm32·单片机·嵌入式硬件
小李不想当小白2 天前
BKP备份寄存器与RTC实时时钟(STM32标准库学习笔记)
笔记·stm32·单片机·嵌入式硬件·学习·嵌入式
Zw-awa2 天前
串口命令如何从字符串长成协议:帧头、长度、校验和与状态机
stm32