超级常见的外设通信方式,一般叫做I方C。
大部分图片来源:正点原子HAL库课程
专栏目录:记录自己的嵌入式学习之路-CSDN博客
目录
1 基本概念
IIC:Inter Integrated Circuit,集成电路总线,是一种同步、串行、半双工通信总线。
1.1 总线结构
- 一般来说IIC总线接3个设备就差不多了。
- IIC的时钟信号是由主机发出的。
1.2 IIC协议
- 主机通过SDA传输数据时,数据在SCL为高电平时有效。
- 在主机传输完毕一个字节的数据后,其需要释放SDA,使得从机可以通过拉低SDA来应答确认数据收到。
1.3 软件模拟IIC逻辑
由于初期ST官方说自己的IIC有问题,口碑没做上来,导致大家都使用软件IIC代替其内部的硬件IIC实现。但其实硬件IIC应该没啥大问题,详见:
关于STM32的I2C(IIC)问题的讨论 (stmicroelectronics.cn)
2 AT24C02
EEPROM是一种掉电后数据不丢失的储存器,常用来存储一些配置信息,在系统重新上电时就可以加载。AT24C02是一个2K bit的EEPROM存储器,使用IIC通信方式。
2.1 设备地址与通信地址
- 通讯地址指定了操作的写和读;
- 而设备地址不包括读写位;
- 编程时需要发送出去的是通讯地址,不是设备地址;
- 对于4K、8K、16K的存储芯片,由于IIC发送一个字节是8位,而4K是9位,因此需要在通讯地址处借1位进行数据发送,8K和16K也是以此类推。但是这样一来通讯地址的可用枚举就变少了,因为A0、A1、A2的位置被拿去做内存扩展了。最终的结果就是,IIC总线上可搭载的该类设备的数量会大打折扣,如16K的就只能搭载一个了。
2.2 读写操作模式
- 写操作
- AT24C02支持字节写模式和页写模式,其实也并不是什么字节写,实际上就是在IIC主机发送了数据地址信号后到发送停止信号前,最多可以写到页尾,仅此而已;
- 其本质就是:收到每个数据字后,数据字地址的低三位(1K/2K)或四位(4K、8K、16K)在内部递增。较高的数据字地址位不递增,保留存储器页面行位置。当内部生成的字地址到达页面边界时,随后的字节被放置在同一页面的开头。如果超过八个(1K/2K)或十六个(4K、8K、16K)数据字传输到EEPROM,数据字地址将“翻转”,先前的数据将被覆盖。
- 字节写模式就是一个地址一个数据进行写入;
- 页写模式就是连续写入数据。只需要写一个地址,连续写入数据时地址会自增,但存在页的限制,超出一页时,超出数据覆盖原先写入的数据。但读会自动翻页。因此,若需要使用页写模式,就需要手动在写完一页后进行翻页。
- 读操作
- AT24C02支持当前地址读模式,随机地址读模式和顺序读模式;
- 当前读模式是基于上一次读/写操作的最后位置继续读出数据;
- 随机地址读模式是指定地址读出数据;
- 顺序读模式是连续读出数据。
2.3 写时序
2.4 读时序
2.5 驱动步骤(例程中使用的是软件IIC)
2.6 SDA的GPIO使用开漏输出的原因
3 硬件IIC
(正点原子没讲硬件IIC,这个是参考野火的HAL库教程的)
STM32的I2C片上外设专门负责实现I2C通讯协议,只要配置好该外设,它就会自动根据协议要求产生通讯信号,收发数据并缓存起来,CPU只要检测该外设的状态和访问数据寄存器,就能完成数据收发。这种由硬件外设处理I2C协议的方式减轻了CPU的工作,且使软件设计更加简单。
3.1 概述
STM32F103具有2个I2C总线接口,能够工作于多主模式或从模式,支持标准(100kHz)和快速模式(400kHz)。I2C接口支持7位或10位寻址,7位从模式时支持双从地址寻址。内置了硬件CRC发生器/校验器。它们可以使用DMA操作并支持SMBus总线2.0版/PMBus总线。
- 时钟
- STM32F103的两个IIC接口外设都挂载在APB1时钟总线上;
- 为了产生正确的时序,必须在I2C_CR2寄存器中设定该模块的输入时钟。输入时钟的频率必须至少是(但APB1一般都会满足吧):
- 标准模式下为:2MHz;
- 快速模式下为:4MHz;
3.2 配置
由于正点原子和野火都没讲硬件IIC的配置,因此此处的配置步骤是我通过网络上的CubeMX教程总结来的(没错,就连正点原子的CubeMX教程都是用模拟IIC的)。
- CubeMX方式
- 根据需要控制的IIC外部原件连接的GPIO其对应的IIC外设进行使能,例如正点原子F103板子上的AT24C02接在PB6、PB7上,因此是I2C1:
- 配置IIC的设置,对于AT24C02,除了速率外基本不需要变:
- 在高速模式下,有一个Fast Mode Duty,是用来调节高速模式下的占空比的,据野火教程说两个选项没太大差别,一般使用可以随便选;
- 根据需要控制的IIC外部原件连接的GPIO其对应的IIC外设进行使能,例如正点原子F103板子上的AT24C02接在PB6、PB7上,因此是I2C1:
- HAL库函数手动编写方式
- 初始化IIC:HAL_I2C_Init
- 初始化时钟和GPIO:HAL_I2C_MspInit
- 在需要的位置使用HAL_I2C_Mem_Read或者HAL_I2C_Mem_Write进行读写。这两个函数直接可以一次性指定目标从机地址、寄存器地址、要写入寄存器的数据等,超级方便:
- hi2c:IIC句柄;
- DevAddress:从机通讯读地址(注意是通讯地址,不是设备地址,分读写地址不同那个);
- MemAddress:操作的从机寄存器地址;
- MemAddSize:从机寄存器数据宽度;
- pData:准备写入的数据的地址或指针;
- Size:准备写入的数据的字节数;
- Timeout:超时时间;
- pData:返回的数据将要写入的缓存地址或指针(一般为变量地址或数组地址);
- 其余与上一个类似;
- 除了上述两个函数外,还有两个函数可以实现IIC通信:HAL_I2C_Master_Receive和HAL_I2C_Master_Transmit。这两个只能同时发送从机地址和数据,适合写入和读取那种从机内无需寄存器地址就能读到数据的从机,如传感器之类的。
- 有博主提出使用HAL_I2C_Mem_Write等于先使用HAL_I2C_Master_Transmit传输第一个寄存器地址,再用HAL_I2C_Master_Transmit传输写入第一个寄存器的数据。我没有试验过,不知道对不对。
3.3 注意事项
- 在使用HAL_I2C_Mem_Write的过程中,需要注意AT24C02一页只有8个字节的数据,所以一次Write的长度最多是8,要在循环写32次才能写完整片。循环过程记住要改变MemAddress(每次+8);
- 在Write的循环中,请务必务必在每次Write后delay一段时间,具体的时间可根据EEPROM手册上说的时间设置,最好设置大一点;
- 在读写过程中设置的超时时间也应设置大一点;
- 不过在HAL_I2C_Mem_Read过程中就不需要分页,直接能读256个字节;
4 关于AT24C04、AT24C08、AT24C16
前面提到,这几个性高的通讯地址中有一定的位数是寄存器地址的借位:
由于其数据地址的借位,在发送通讯地址的时候,要结合数据地址对通讯地址进行处理。这篇文章讲得很好:
AT24C04、AT24C08、AT24C16系列EEPROM芯片单片机读写驱动程序-CSDN博客
这里截取一下其对写入的处理:
其中,第一个IIC_WriteByte,实际上是将设备地址DEV_ADDR和写地址WRITE_CMD(即0)或操作组成通讯的写地址;其次,对数据地址进行右移8位,并与0x07进行与运算,实际上就是取出数据地址中高8位的低3位,即通讯地址中的P2、P1、P0(若有的话对应位就是1,所以用或运算很安全)。然后还对取出的低三位进行了左移1位运算,再与通讯地址做与运算,这是因为通讯地址的最低位是读/写设置位。
第二个IIC_WriteByte就是将剩下的8位数据地址也发过去,发完就可以开始发数据了。