STM32 CubeMx(六)I2C同步串行通信与EEPROM 24C02的读写

1.I2C简单介绍

以下内容来自原子哥SM324F发指南HAL库版本

在这里插入图片描述

2.实现 EEPROM 24C02 的读写

EEPROM (Electrically Erasable Programmable read only memory)是指带电可擦可编程只读存储器。是一种掉电后数据不丢失的存储芯片。

2.1 硬件与IO口配置

在这里插入图片描述

在这里插入图片描述

2.2 工程配置

1.开启串口

在这里插入图片描述
2.开启I2C
在这里插入图片描述
3.检查串口是否正确,不正确需要修改,我这里就需要修改。
IO口一定要检查!!!!直接影响后面的实验。
在这里插入图片描述

2.3 代码配置

【STM32Cube_13】使用硬件I2C读写EEPROM(AT24C02)

2.3.1 修改一下配置的小bug。

在这里插入图片描述

2.3.2 I2C函数补充知识

## 1.2 I2C函数
I2C读写函数

```cpp
 HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi21, uint16_t DevAddress, 
 uint8_t *pData, uint16_t Size, uint32_t Timeout);
 HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi21, uint16_t DevAddress, 
 uint8_t *pData, uint16_t Size, uint32_t Timeout);

/* 第1个参数为I2C操作句柄
   第2个参数为写入的地址 设置写入数据的地址
   第3个参数为需要写入的数据
   第4个参数为要发送的字节数
   第5个参数为操作超时时间,超过传输时间将自动退出传输函数 */

I2C读写多个数据

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_I2C_Mem_Read(参数和上面一样);
/* 第1个参数为I2C操作句柄
   第2个参数为从机设备地址 device—adress
   第3个参数为从机寄存器地址 memory-adress
   第4个参数为从机寄存器地址长度 memory-adress-size
   第5个参数为发送的数据的起始地址
   第6个参数为传输数据的大小
   第7个参数为操作超时时间   */

参数配置在stm32f4xx_hal_i2c.h里

2.3.3 I2C函数读写一个字节

#define	ADDR_WRITE	0xA0	//写地址
#define ADDR_READ	0xA1	//读地址

/**
 * @brief		24C02任意地址写一个字节数据
 * @param		addr —— 写数据的地址(0-255)
 * @param		dat  —— 存放写入数据的地址
 * @retval		成功 —— HAL_OK
*/
uint8_t Write_Byte(uint16_t addr, uint8_t* dat)
{
	return HAL_I2C_Mem_Write(&hi2c1, ADDR_WRITE, addr, I2C_MEMADD_SIZE_8BIT, dat, 1, 0xFFFF);
	//翻译:向一个 起始地址为ADDR_WRITE,寄存器起始地址为addr,内存8bit的设备。发送起始地址为dat,1个字节的信息。
}

/**
 * @brief		24C02任意地址读一个字节数据
 * @param		addr —— 读数据的地址(0-255)
 * @param		read_buf —— 存放读取数据的地址
 * @retval		成功 —— HAL_OK
*/
uint8_t Read_Byte(uint16_t addr, uint8_t* read_buf)
{
	return HAL_I2C_Mem_Read(&hi2c1, ADDR_READ, addr, I2C_MEMADD_SIZE_8BIT, read_buf, 1, 0xFFFF);
}

函数返回值在stm32f4xx hal i2c.c

return HAL_BUSY;      
return HAL_ERROR;
return HAL_OK;

测试:

  /* USER CODE BEGIN 2 */
	if(HAL_OK == Write_Byte(10,&write_dat))		printf("Write ok!\r\n");
	else	printf("Write fail!\r\n");
	HAL_Delay(50);		//写一次和读一次之间需要短暂的延时
	if(HAL_OK == Read_Byte(10,&receive_buf))		printf("Read ok, receive_buf = 0x%02X!\r\n", receive_buf);
	else	printf("Read fail!\r\n");
  /* USER CODE END 2 */

结果:

在这里插入图片描述

AT24C02的IIC每次写之后要延时一段时间才能继续写 每次写之后要delay 5ms左右 不管硬件IIC采用何种形式(DMA,IT),都要确保两次写入的间隔大于5ms;

2.3.4 I2C函数读写多个字节

驱动代码来源

uint8_t Read_Amount_Byte(uint16_t addr, uint8_t* recv_buf, uint16_t size)
{
	return HAL_I2C_Mem_Read(&hi2c1, ADDR_READ, addr, I2C_MEMADD_SIZE_8BIT, recv_buf, size, 0xFFFF);
}

/**
 * @brief		AT24C02任意地址连续写多个字节数据
 * @param		addr —— 写数据的地址(0-255)
 * @param		dat  —— 存放写入数据的地址
 * @retval		成功 —— HAL_OK
*/
uint8_t Write_Amount_Byte(uint16_t addr, uint8_t* dat, uint16_t size)
{
    uint8_t i = 0;
    uint16_t cnt = 0;		//写入字节计数
    
    /* 对于起始地址,有两种情况,分别判断 */
    if(0 == addr % 8 )
    {
        /* 起始地址刚好是页开始地址 */
        
        /* 对于写入的字节数,有两种情况,分别判断 */
        if(size <= 8)
        {
            //写入的字节数不大于一页,直接写入
            return HAL_I2C_Mem_Write(&hi2c1, ADDR_WRITE, addr, I2C_MEMADD_SIZE_8BIT, dat, size, 0xFFFF);
        }
        else
        {
            //写入的字节数大于一页,先将整页循环写入
            for(i = 0;i < size/8; i++)
            {
                HAL_I2C_Mem_Write(&hi2c1, ADDR_WRITE, addr, I2C_MEMADD_SIZE_8BIT, &dat[cnt], 8, 0xFFFF);
                addr += 8;
                cnt += 8;
            }
            //将剩余的字节写入
            return HAL_I2C_Mem_Write(&hi2c1, ADDR_WRITE, addr, I2C_MEMADD_SIZE_8BIT, &dat[cnt], size - cnt, 0xFFFF);
        }
    }
    else
    {
        /* 起始地址偏离页开始地址 */
        /* 对于写入的字节数,有两种情况,分别判断 */
        if(size <= (8 - addr%8))
        {
            /* 在该页可以写完 */
            return HAL_I2C_Mem_Write(&hi2c1, ADDR_WRITE, addr, I2C_MEMADD_SIZE_8BIT, dat, size, 0xFFFF);
        }
        else
        {
            /* 该页写不完 */
            //先将该页写完
            cnt += 8 - addr%8;
            HAL_I2C_Mem_Write(&hi2c1, ADDR_WRITE, addr, I2C_MEMADD_SIZE_8BIT, dat, cnt, 0xFFFF);
            addr += cnt;
            
            //循环写整页数据
            for(i = 0;i < (size - cnt)/8; i++)
            {
                HAL_I2C_Mem_Write(&hi2c1, ADDR_WRITE, addr, I2C_MEMADD_SIZE_8BIT, &dat[cnt], 8, 0xFFFF);
                addr += 8;
                cnt += 8;
            }
            
            //将剩下的字节写入
            return HAL_I2C_Mem_Write(&hi2c1, ADDR_WRITE, addr, I2C_MEMADD_SIZE_8BIT, &dat[cnt], size - cnt, 0xFFFF);
        }			
    }
}

测试:

	for(i = 0;i < 30; i++)
	{
		write_dat[i] = i;
		printf("%02X ", write_dat[i]);
		if((i+1) % 5 == 0)	printf("\r\n");
	}
	if(HAL_OK == Write_Amount_Byte(10,write_dat,30))		printf("Write ok!\r\n");
	else	printf("Write fail!\r\n");
	HAL_Delay(50);		//写一次和读一次之间需要短暂的延时
	if(HAL_OK == Read_Amount_Byte(ADDR_READ,receive_buf,30))
	{
		printf("Read ok!\r\n");
		for(i = 0; i < 30; i++)
		{
			printf("0x%02X ", receive_buf[i]);
			if((i+1) % 5 == 0)	printf("\r\n");
		}
	}		
	else	printf("Read fail!\r\n");

结果:
在这里插入图片描述

3. I2C总结

参考:

UM10204 I2C-bus specification and user manual
I2C很简单,但很多人却不了解其通信原理和协议
UART - I2C - SPI - 串行通信协议_视频

1980年,飞利浦公司为了让各种低速设备(飞利浦芯片)连接起来,就开始着手研发通信的总线。
I2C:Inter-Integrated Circuit,字面意思是集成电路之间,是I2C Bus简称。

I2C只需要两根电线,这两根线最多可支持1008个从设备,实现同步串行通信

I2C是目前使用较多的一种总线,一般用于连接各种从设备,比如:EEPROM存储器、温湿度传感器、角速度计等。

3.1 收发基本原理

I2C的两个总线:SCL时钟信号,SDA数据信号。SCL由主机产生,SDA由主机或者从机产生。

I2C是同步串行通信,同时它属于半双工,也就是说同一时间SDA只能由一个设备发送信号。

这样,你就会发现,SDA上的信号(数据),有时候是主机的,有时候是从机的。

3.2 基本协议:7/10位地址

参见参考资料。
7位地址传输:

在这里插入图片描述

在这里插入图片描述
10位地址传输:
在这里插入图片描述

在这里插入图片描述

3.3 开始和停止

SDA数据线由高 -> 低 为总线开始条件;

SDA数据线由低 -> 高 为总线结束条件;
在这里插入图片描述

3.4 应答(ACK)和非应答(ACK)

应答和非应答发生在每个字节之后,是由接收方向发送方发出确认信号,表明“数据”已成功接收,并且可以继续发送下一字节数据。
可以机器翻译一下下面的内容。

Acknowledge (ACK) and Not Acknowledge (NACK)
The acknowledge takes place after every byte. The acknowledge bit allows the receiver to signal the transmitter that the byte was successfully received and another byte may be sent. The master generates all clock pulses, including the acknowledge ninth clock pulse.
The Acknowledge signal is defined as follows: the transmitter releases the SDA line during the acknowledge clock pulse so the receiver can pull the SDA line LOW and it remains stable LOW during the HIGH period of this clock pulse (see Figure 4). Set-up and
hold times (specified in Section 6) must also be taken into account.
When SDA remains HIGH during this ninth clock pulse, this is defined as the Not Acknowledge signal. The master can then generate either a STOP condition to abort the transfer, or a repeated START condition to start a new transfer. There are five conditions that lead to the generation of a NACK:

  1. No receiver is present on the bus with the transmitted address so there is no device to respond with an acknowledge.
  2. The receiver is unable to receive or transmit because it is performing some real-time function and is not ready to start
    communication with the master.
  3. During the transfer, the receiver gets data or commands that it does not understand.
  4. During the transfer, the receiver cannot receive any more data bytes.
  5. A master-receiver must signal the end of the transfer to the slave transmitter.

确认(ACK)和不确认(NACK)
确认发生在每个字节之后。
应答位允许接收方向发送方发出信号,表示已成功接收该字节,并可发送另一个字节。
主机产生所有时钟脉冲,包括确认的第9个时钟脉冲。
确认信号的定义如下:发射机在确认时钟脉冲期间释放SDA线,因此接收机可以将SDA线拉低,并在此时钟脉冲的高周期内保持稳定的低(见图4)
持有时间(第6节规定)也必须考虑在内。
当SDA在这第9个时钟脉冲期间保持高时,这被定义为不确认信号。
然后,主程序可以生成一个STOP条件来中止传输,或者生成一个重复的START条件来启动新的传输。
有五种情况导致NACK的生成:
1.总线上没有接收端,因此没有应答设备。
2.接收器无法接收或发送,因为它正在执行一些实时功能,而且还没有准备好启动与主机沟通。
3.在传输过程中,接收端获取它不理解的数据或命令。
4. 在传输过程中,接收端无法接收更多的数据字节。
5. 主接收机必须向从发射机发出传输结束的信号。

3.5 简短的总结

在I2C通信中,发送器可以发送各种数据,但只有特定的接收器才能接收到相应的数据。能做到这一点,是因为每一个接收器都有一个特定的地址所以发送器会首先发送地址,然后发送数据。这样只有特定地址的接收器才会保存来自发送器的数据到接收器。所以这种通信方式是一对一的,可以连接多个接收器。

后续会更新SPI通信,并对UART、I2C和SPI做总结~~
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值