STM32的IIC串口通信_读写EEPROM实验

IIC简介

IIC通信原理

  • IIC原理简述

    IIC又称I2C或I2C,全称Inter IC总线,实现IC集成芯片之间的同步串行通信

    IIC与异步的UART一样也可以只用两根通讯线:数据线SDA与时钟线SCL,但只有一根数据线情况下实现的是半双工通信

    对IIC总线通信系统,可以有多个IIC总线器件同时接在IIC总线上,通过器件地址来区分各个设备,能够实现一主多从以及多主的通信系统,在蓝桥杯开发板上IIC连接了24C02与MCP4017两个IC

  • IIC工作流程
    1. 主机发送起始信号启动总线

    2. 发送从机地址(7位长)与读写控制位(决定传输方向,读1写0)

      因为读写控制位占用一位,因此一个8位地址的IIC总线理论可挂载的从机数为27-1=128-1=127个(纯0不能作地址)

    3. 等待从机回复一个应答位

    4. 发送数据,每发送一个字节(8位长)等待从机回复一个应答位

    5. 发送完成,主机发送停止位,释放总线

    更具体的工作原理参阅本人STM8教程的I2C章节

  • 24C02储存器原理

    在IIC读写实验中,我们使用24C02芯片,这是一款使用I2C通信协议的EEPROM非易失性储存芯片,掉电后数据不丢失

    其设备地址由A0/A1/A2三个引脚配置,在开发板上这三个引脚都接地,为000

    具体应该发送的第一个字节是:MSB:1-0-1-A0-A1-A2-R/W:LSB

IIC的初始化配置

  • IIC的引脚配置

    PA6为SCL,PA7为SDA,两个引脚配置为GPIO_Output

    在后续导入的IIC驱动中已经配置好了这两个引脚,但为了防止出错,建议在CubeMX中配置好

  • IIC的驱动导入

    从将硬件配置为GPIO功能可看出,我们不使用芯片上的硬件I2C,而是使用GPIO输出模拟I2C,但我们不需要像在STM8教程中那样亲自配置读写的每一个位,而是使用提供的驱动文件中的函数来完成对IIC时序的模拟

    在工程中导入蓝桥杯嵌入式资源包提供的i2c_hal.c并在主函数中引入i2c_hal.h,导入驱动的方法见LCD一章

  • 编译器优化对IIC的影响

    在使用之前,在工程的Options(魔法棒按钮)C/C++(AC6)标签页中的Optimization选为-O0

    把编译器的优化等级调低,这样IIC才能正常工作

    这样因为IIC驱动中的延时函数使用了原始的CPU空转延时,会被编译器优化掉,导致IIC工作出错

IIC的使用

IIC使用的库函数

  • 初始化函数I2CInit

    在使用之前,要先对I2C进行初始化

    void I2CInit(void);
    
  • 封装IIC读取函数

    虽然驱动提供了各个库函数让我们不必关注各个不同的工作时序,但每次进行通信操作都要频繁调用发送起始信号、发送字节数据、等待答应信号一系列函数,过程依旧繁琐

    因此我们封装一个功能为读取IIC的函数,只要给出读取到数据的缓冲区,要读取的内存地址和读取字节数,就能自动完成IIC的读取数据流程

    //将函数写到i2c_hal.c中,记得将函数原型放到i2c_hal.h
    //因为是半双工通信,所以只需要一个公共的缓冲区
    //通过使用不同的函数决定发送区中字节还是接收字节存到区中
    void MEM_Read(uint8_t* pucBuf, uint8_t ucAddr, uint8_t ucNum)
    {
    	I2CStart();//I2C起始信号
    	I2CSendByte(0xa0);//1010 0000,后三位地址为000,控制位为0,写模式
    	I2CWaitAck();//等答应信号
    	  
    	I2CSendByte(ucAddr);//先把要读取的地址发送给24C02
    	I2CWaitAck();
    	  
    	I2CStart();
    	I2CSendByte(0xa1);//控制位为1,切换为读模式
    	I2CWaitAck();
    	  
    	while(ucNum--)//逐个字节地接收
    	{
    		*pucBuf++=I2CReceiveByte();//把接收到的数据存放到缓冲区
    		if(ucNum)//如果还未读到最后一个,应该给24C02回复答应信号
    			I2CSendAck();
    		else//读完最后一个就不必发送答应信号了
    			I2CSendNotAck();
    	}
    	I2CStop();//发送停止位
    }
    
  • 封装IIC写入数据

    同上,也能封装一个用于IIC写入的函数

    void MEM_Write(uint8_t* pucBuf, uint8_t ucAddr, uint8_t ucNum)
    {
    	I2CStart();
    	I2CSendByte(0xa0);
    	I2CWaitAck();
    	  
    	I2CSendByte(ucAddr);
    	I2CWaitAck();
    	  
    	while(ucNum--)//逐个字节地发送
    	{
    		I2CSendByte(*pucBuf++);//发送一个字节后偏移指针以发送下一个字节
    		I2CWaitAck();
    	}
    	I2CStop();
    	delay1(500);//增加延时避免写入太快导致出错
    	//delay1是i2c_hal.c定义的延时
    }
    /* USER CODE END 2 */
    

代码实现

  • 写入与读取实验

    进行一个简单的实验同时检测刚刚封装好的I2C函数以及EEPROM掉电后保存数据的能力

    实验设计如下:声明一个变量uint8_t cnt,每次上电后对其读取,并将其数值加1后再写入,烧录后开关单片机,如果能观察到这个变量(借助LCD)在每次开关后加1,实验成功

    MEM_Read((uint8_t *)&cnt,0,1);
    cnt++;
    MEM_Write((uint8_t *)&cnt,0,1);
    /* USER CODE END 2 */
    
  • 注意:一个地址只能存8bit,也就是一个uint8_t类型的变量,如果是16位或32位数据类型需要按数值或者按位进行分割

posted on 2025-07-22 21:52  无术师  阅读(151)  评论(0)    收藏  举报