STM32HAL库学习笔记七——I2C通信

HAL库快速部署I2C

本文主要介绍如何使用STM32CubeMX快速部署I2C通信,并与EEPROM进行数据收发。


文章目录

  • HAL库快速部署I2C
  • I2C简介
  • EEPROM简介
  • HAL库部署IIC通信实现多字节写入
    • 一、CubeMX配置
    • 二、代码编写


I2C简介

I2C是一种串行同步半双工通信方式。
I2C物理层是由一条双向数据总线SDA和一条双向时间总线SCL组成,I2C总线上可以挂载多个从机设备。

stm32f103的引脚说明,来自leung——STM32CubeMX学习笔记(9)PB8 PB9 为重映射。这里是引用

I2C协议层定义了通讯的起始和停止信号、数据有效性、响应、仲裁、时钟同步和地址广播等环节。通信流程如下:
在这里插入图片描述
起始信号发出后,先发出设备地址决定与哪个从机通信,WRITE位为0表示写入为1表示读出。主机每发送完一个字节数据,都要等待从机的应答信号 (ACK),只有接收到应答信号后,主机才能继续发送或接收数据。然后开始写入/读出数据,对于EEPROM写入/读出的第一个数据是写入/读出内存的地址。若接收端希望结束数据传输,则向对方发送“非应答 (NACK)”信号,发送方接收到该信号后会产生一个停止信号,结束信号传输。


EEPROM简介

EEPROM 是一种掉电后数据不丢失的存储器,最常用的通讯方式就是 I2C 协议,STM32F103开发板中使用的 EEPROM 芯片型号是AT24C02。

AT24C02 EEPROM 的 7 位设备地址是:101 0000b ,即 0xA0。由于 I2C 通讯时常常是地址跟读写方向连在一起构成一个 8 位数,且当 R/W 位为 0 时,表示写方向,所以加上 7 位地址,其值为“0xA0”,常称该值为 I2C 设备的“写地址”;当 R/W 位为 1时,表示读方向,加上 7 位地址,其值为“0xA1”,常称该值为“读地址”。

AT24C02有2K的存储容量,2K=2*1024=2048位,所以最多可以往AT24C02存储器里面写2048bit,可以写256个8位字节,写或者读的地址是0X00–0X7FF。

值得一提的是根据AT24C02的规格说明书,写入ROM是比较耗时的,写周期最大5ms,没写入完就指令写入下一组数据会出现错误,这点在编程时需要注意。

EEPROM的页写入:虽然I2C通信是串行通信,一次传输一个字节,但对于EEPROM定义了一种页写入的时序。我们希望向连续地址写入多个数据的时候,只要告诉 EEPROM 第一个内存地址 address1,后面的数据按次序写入到address2、address3…这样可以节省通讯的内容,加快速度。AT24C02 型号的芯片页写入时序最多可以一次发送 8 个数据 (即 n = 8 ),该值也称为页大小,某些型号的芯片每个页写入时序最多可传输 16 个数据。

24系列的eeprom提供缓存的技术,写入一页的数据,它先保存起来,当你停止对它操作时(stop信号后),eeprom再把缓存的数据写好。所以页操作的最大值时受限于IC的缓存区大小的。 ——来自:EEPROM的写入操作解析
当写入数据溢出页大小限制时,溢出数据会从页起始地址再写入,如果该地址原本有数据,会被覆盖,导致写入异常。
很好的分析AT24C02的博客:stm32专题十七:AT24C02

EEPROM的页写入

HAL库部署IIC通信实现多字节写入

AT24C02 型EEPROM一页最多写入8个数据,所以一般来说我们不能写入超过8个数据,但是通过合理的程序编程我们可以实现跨页写入,从而一次写入多个数据。

一、CubeMX配置

1.新建工程,常规配置调试模式、时钟树、项目环境;
2.选择PB0、PB1、PB5配置为GPIO_Out,初始为高电平,用于测试程序的指示;
3.配置I2C模式、传输速度,需要中断时开中断。
在这里插入图片描述
4.GENERATE CODE

二、代码编写

1.函数介绍
HAL库部署I2C通信主要使用HAL_I2C_Mem_Write函数和HAL_I2C_Mem_Read函数。

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)

*hi2c是指向设备特定I2C配置信息的结构体的指针;
DevAddress是从机设备地址;
MemAddress是写入或者读出数据的地址;
MemAddSize是MemAddress的地址长度,注意hal库I2C函数中地址长度是以字节为单位的
*pData是指向待写入的数据内存或者是存放读出数据的内存的指针;
Size是待写入的数据或者是待读出数据的数据大小;
Timeout是超时时间,超过该时间函数返回错误值。

2.单字节和页写入
这两种写入方式没有特别大差异,之间调用HAL_I2C_Mem_Write函数就可以实现。EEPROM 的单字节时序规定,向它写入数据的时候,第一个字节为内存地址,第二个字节是要写入的数据内容。
单字节写入一次写一个字节,把Size设为1就可以;页写入一次最多写入8个字节,在MemAddress模8为0时Size最大为8,参考代码如下:

#define EEPROM_ADDRESS          0xA0
#define I2C_MEMADD_SIZE_8BIT    0x00000001UI2C_HandleTypeDef  I2C_Handle;uint32_t I2C_EE_ByteWrite(uint8_t* pBuffer, uint8_t WriteAddr)
{HAL_StatusTypeDef status = HAL_OK;status = HAL_I2C_Mem_Write(&I2C_Handle, EEPROM_ADDRESS, (uint16_t)WriteAddr, I2C_MEMADD_SIZE_8BIT, pBuffer, 1, 100); /* Check the communication status */if(status != HAL_OK){/* Execute user timeout callback *///I2Cx_Error(Addr);}while (HAL_I2C_GetState(&I2C_Handle) != HAL_I2C_STATE_READY){}/* Check if the EEPROM is ready for a new operation */while (HAL_I2C_IsDeviceReady(&I2C_Handle, EEPROM_ADDRESS, EEPROM_MAX_TRIALS, I2Cx_TIMEOUT_MAX) == HAL_TIMEOUT);/* Wait for the end of the transfer */while (HAL_I2C_GetState(&I2C_Handle) != HAL_I2C_STATE_READY){}return status;
}uint32_t I2C_EE_PageWrite(uint8_t* pBuffer, uint8_t WriteAddr, uint8_t NumByteToWrite)
{HAL_StatusTypeDef status = HAL_OK;/* Write EEPROM_PAGESIZE */status=HAL_I2C_Mem_Write(&I2C_Handle, EEPROM_ADDRESS,WriteAddr, I2C_MEMADD_SIZE_8BIT, (uint8_t*)(pBuffer),NumByteToWrite, 100);while (HAL_I2C_GetState(&I2C_Handle) != HAL_I2C_STATE_READY){}/* Check if the EEPROM is ready for a new operation */while (HAL_I2C_IsDeviceReady(&I2C_Handle, EEPROM_ADDRESS, EEPROM_MAX_TRIALS, I2Cx_TIMEOUT_MAX) == HAL_TIMEOUT);/* Wait for the end of the transfer */while (HAL_I2C_GetState(&I2C_Handle) != HAL_I2C_STATE_READY){}return status;
}

3.多字节写入
多字节写入主要针对多页写入设计,引入变量Addr(写入地址模8) count(第一页写入数据长度) NumOfPage(需要页数) NumOfSingle (去除整页剩余数据长度),先把写入第一页的起始地址后的部分写完再写入后面的数据。

void I2C_EE_BufferWrite(uint8_t* pBuffer, uint8_t WriteAddr, uint16_t NumByteToWrite)
{uint8_t NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0;Addr = WriteAddr % EEPROM_PAGESIZE;count = EEPROM_PAGESIZE - Addr;NumOfPage =  NumByteToWrite / EEPROM_PAGESIZE;NumOfSingle = NumByteToWrite % EEPROM_PAGESIZE;/* 写入起始地址是8的整数倍  */if(Addr == 0) {/* If NumByteToWrite < I2C_PageSize */if(NumOfPage == 0) {I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);}/* If NumByteToWrite > I2C_PageSize */else  {while(NumOfPage--){I2C_EE_PageWrite(pBuffer, WriteAddr, EEPROM_PAGESIZE); WriteAddr +=  EEPROM_PAGESIZE;pBuffer += EEPROM_PAGESIZE;}if(NumOfSingle!=0){I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);}}}/* 写入起始地址非8的整数倍  */else {/* If NumByteToWrite < I2C_PageSize */if(NumOfPage== 0) {I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);}/* If NumByteToWrite > I2C_PageSize */else{NumByteToWrite -= count;     //去掉第一页要写入的字节数NumOfPage =  NumByteToWrite / EEPROM_PAGESIZE;		NumOfSingle = NumByteToWrite % EEPROM_PAGESIZE;	/*如果起始地址与页不对齐,需要先写满当页剩下的字节*/if(count != 0){  I2C_EE_PageWrite(pBuffer, WriteAddr, count);WriteAddr += count;pBuffer += count;} while(NumOfPage--){I2C_EE_PageWrite(pBuffer, WriteAddr, EEPROM_PAGESIZE);WriteAddr +=  EEPROM_PAGESIZE;pBuffer += EEPROM_PAGESIZE;  }if(NumOfSingle != 0){I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle); }}}  
}


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部