LV16-19-I2C-01-基础知识
本文主要是STM32开发——I2C的一些基础知识的一些相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。
点击查看使用工具及版本
Windows | windows11 |
Ubuntu | Ubuntu16.04的64位版本 |
VMware® Workstation 16 Pro | 16.2.3 build-19376536 |
SecureCRT | Version 8.7.2 (x64 build 2214) - 正式版-2020年5月14日 |
开发板 | 正点原子 i.MX6ULL Linux阿尔法开发板 |
uboot | NXP官方提供的uboot,NXP提供的版本为uboot-imx-rel_imx_4.1.15_2.1.0_ga(使用的uboot版本为U-Boot 2016.03) |
linux内核 | linux-4.15(NXP官方提供) |
STM32开发板 | 正点原子战舰V3(STM32F103ZET6) |
点击查看本文参考资料
- 通用
分类 | 网址 | 说明 |
官方网站 | https://www.arm.com/ | ARM官方网站,在这里我们可以找到Cotex-Mx以及ARMVx的一些文档 |
https://www.st.com/content/st_com/zh.html | ST官方网站,在这里我们可以找到STM32的相关文档 | |
https://www.stmcu.com.cn/ | 意法半导体ST中文官方网站,在这里我们可以找到STM32的相关中文参考文档 | |
http://elm-chan.org/fsw/ff/00index_e.html | FatFs文件系统官网 | |
教程书籍 | 《ARM Cortex-M3权威指南》 | ARM公司专家Joseph Yiu(姚文祥)的力作,中文翻译是NXP的宋岩 |
《ARM Cortex-M0权威指南》 | ||
《ARM Cortex-M3与Cortex-M4权威指南》 | ||
开发论坛 | http://47.111.11.73/forum.php | 开源电子网,正点原子的资料下载及问题讨论论坛 |
https://www.firebbs.cn/forum.php | 国内Kinetis开发板-野火/秉火(刘火良)主持的论坛,现也做STM32和i.MX RT | |
https://www.amobbs.com/index.php | 阿莫(莫进明)主持的论坛,号称国内最早最火的电子论坛,以交流Atmel AVR系列单片机起家,现已拓展到嵌入式全平台,其STM32系列帖子有70W+。 | |
http://download.100ask.net/index.html | 韦东山嵌入式资料中心,有些STM32和linux的相关资料也可以来这里找。 | |
博客参考 | http://www.openedv.com/ | 开源网-原子哥个人博客 |
http://blog.chinaaet.com/jihceng0622 | 博主是原Freescale现NXP的现场应用工程师 | |
cortex-m-resources | 这其实并不算是一个博客,这是ARM公司专家Joseph Yiu收集整理的所有对开发者有用的官方Cortex-M资料链接(也包含极少数外部资源链接) |
- STM32
STM32 | STM32 HAL库开发实战指南——基于F103系列开发板 | 野火STM32开发教程在线文档 |
STM32库开发实战指南——基于野火霸道开发板 | 野火STM32开发教程在线文档 |
- SD卡
SD Association | 提供了SD存储卡和SDIO卡系统规范 |
点击查看相关文件下载
STM32F103xx英文数据手册 | STM32F103xC/D/E系列的英文数据手册 |
STM32F103xx中文数据手册 | STM32F103xC/D/E系列的中文数据手册 |
STM32F10xxx英文参考手册(RM0008) | STM32F10xxx系列的英文参考手册 |
STM32F10xxx中文参考手册(RM0008) | STM32F10xxx系列的中文参考手册 |
Arm Cortex-M3 处理器技术参考手册-英文版 | Cortex-M3技术参考手册-英文版 |
STM32F10xxx Cortex-M3编程手册-英文版(PM0056) | STM32F10xxx/20xxx/21xxx/L1xxxx系列Cortex-M3编程手册-英文版 |
SD卡相关资料——最新版本 | 有关SD卡的一些资料可以从这里下载 |
SD卡相关资料——历史版本 | 有关SD卡的一些历史版本资料可以从这里下载,比如后边看的SD卡2.0协议 |
SD 2.0 协议标准完整版 | 这是一篇关于SD卡2.0协议的中文文档,还是比较有参考价值的,可以一看 |
一、I2C简介
这一部分可以查看笔记《通信协议-03-I2C通信》,这篇笔记主要是关于I2C的物理层协议,读写时序以及各个信号的说明包括对各信号的模拟。
二、STM32的物理I2C
1. 物理I2C框图
这一部分我们可以查看[STM32中文参考手册](https://www.stmcu.com.cn/Designresource/detail/localization_document /710001)的24 I2C接口一节:
2. ①通信引脚
通信引脚在框图中有三根:
(1)SDA——数据线;
(2)SCL——时钟线;
(3)SMBALERT——用于系统管理总线(SMBus),SMBus是一个双线接口。通过它,各设备之间以及设备与系统的其他部分之间 可以互相通信。它基于I 2 C操作原理。SMBus为系统和电源管理相关的任务提供一条控制总线。 一个系统利用SMBus可以和多个设备互传信息,而不需使用独立的控制线路。
STM32芯片有多个I2C外设,它们的I2C通讯信号引出到不同的GPIO引脚上,使用时必须配置到这些指定的引脚,主要是看STM32数据手册的3 Pinouts and pin descriptions。首先我们看STM32的存储映像会发现,STM32F103ZET6有两个I2C:
它们对应的通信引脚分别为:
3. ②时钟控制逻辑
SCL线的时钟信号,由I2C接口根据时钟控制寄存器(CCR)控制,控制的参数主要为时钟频率。
(1)可选择I2C通讯的“标准/快速”模式,这两个模式分别I2C对应100/400Kbit/s的通讯速率。
(2)在快速模式下可选择SCL时钟的占空比,可选Tlow/Thigh=2或Tlow/Thigh=16/9模式。
(3)CCR寄存器中12位的配置因子CCR,它与I2C外设的输入时钟源共同作用,产生SCL时钟。
(4)STM32的I2C外设输入时钟源为PCLK1。
那怎么计算时钟的频率呢?
1 | 标准模式: Thigh = CCR*TPCKL1 Tlow = CCR*TPCLK1 |
例如,我们的PCLK1=36MHz,想要配置400Kbit/s的速率,计算方式如下:
1 | PCLK时钟周期: TPCLK1 = 1/36000000 |
计算出来的CCR值写入到寄存器即可,向该寄存器位写入此值则可以控制 IIC 的通讯速率为 400KHz,其实即使配置出来的 SCL 时钟不完全等于标准的 400KHz, IIC 通讯的正确性也不会受到影响,因为所有数据通讯都是由 SCL 协调的,只要它的时钟频率不远高于标准即可。
4. ③数据控制逻辑
I2C的SDA信号主要连接到数据移位寄存器上,数据移位寄存器的数据来源及目标是数据寄存器(DR)、地址寄存器(OAR)、 PEC寄存器以及SDA数据线。
(1)当向外发送数据的时候,数据移位寄存器以“数据寄存器”为数据源,把数据一位一位地通过SDA信号线发送出去;
(2)当从外部接收数据的时候,数据移位寄存器把SDA信号线采样到的数据一位一位地存储到“数据寄存器”中。
5. ④整体控制逻辑
整体控制逻辑负责协调整个I2C外设,控制逻辑的工作模式根据我们配置的“控制寄存器(CR1/CR2)”的参数而改变。
在外设工作时,控制逻辑会根据外设的工作状态修改“状态寄存器(SR1和SR2)”,只要读取这些寄存器相关的寄存器位,就可以了解I2C的工作状态。
三、I2C通讯过程
使用I2C外设通讯时,在通讯的不同阶段它会对“状态寄存器(SR1及SR2)”的不同数据位写入参数,通过读取这些寄存器标志来了解通讯状态。 STM32的物理I2C有以下四种模式,接口可以下述4种模式中的一种运行:
(1)从发送器模式
(2)从接收器模式
(3)主发送器模式
(4)主接收器模式
1. 主发送器模式
(1)控制产生起始信号(S),当发生起始信号后,它产生事件“EV5”,并会对SR1寄存器的“SB”位置1,表示起始信号已经发送;
(2)发送设备地址并等待应答信号,若有从机应答,则产生事件“EV6”及“EV8”,这时SR1寄存器的“ADDR”位及“TXE”位被置1, ADDR 为1表示地址已经发送, TXE为1表示数据寄存器为空;
(3)往I2C的“数据寄存器DR”写入要发送的数据,这时TXE位会被重置0,表示数据寄存器非空, I2C外设通过SDA信号线一位位把数据发送出去后,又会产生“EV8”事件,即TXE位被置1,重复这个过程,可以发送多个字节数据;
(4)发送数据完成后,控制I2C设备产生一个停止信号(P),这个时候会产生EV2事件, SR1的TXE位及BTF位都被置1,表示通讯结束。
2. 主接收器模式
(1)起始信号(S)是由主机端产生的,控制发生起始信号后,它产生事件“EV5”,并会对SR1寄存器的“SB”位置1,表示起始信号已经发送;
(2)发送设备地址并等待应答信号,若有从机应答,则产生事件“EV6”这时SR1寄存器的“ADDR”位被置1,表示地址已经发送。
(3)从机端接收到地址后,开始向主机端发送数据。当主机接收到这些数据后,会产生“EV7”事件, SR1寄存器的RXNE被置1,表示接收数据
寄存器非空,读取该寄存器后,可对数据寄存器清空,以便接收下一次数据。此时可以控制I2C发送应答信号(ACK)或非应答信号(NACK),若应答,则重复以上步骤接收数据,若非应答,则停止传输;
(4)发送非应答信号后,产生停止信号(P),结束传输。
四、相关寄存器
这一部分我们可以查看 [STM32中文参考手册](https://www.stmcu.com.cn/Designresource/detail/localization_document /710001)的24.6 I 2 C寄存器描述。
五、HAL库函数分析
1. I2C_InitTypeDef
1 | typedef struct { |
(1)ClockSpeed:设置的是 I2C 的传输速率,在调用初始化函数时,函数会根据我们输入的数值写入到 I2C的时钟控制寄存器 CCR。
(2)DutyCycle:设置的是 I2C 的 SCL 线时钟的占空比。该配置有两个选择,分别为低电平时间比高电平时间为 2: 1 (I2C_DUTYCYCLE_2) 和 16: 9 (I2C_DUTYCYCLE_16_9)。其实这两个模式的比例差别并不大,一般要求都不会如此严格,这里随便选就可以了。
(3)OwnAddress1:配置的是 STM32 的 I2C 设备自身地址 1,每个连接到 I2C 总线上的设备都要有一个自己的地址,作为主机也不例外。地址可设置为 7 位或 10 位 (受下面 (3) AddressingMode 成员决定),只要该地址是 I2C 总线上唯一的即可。STM32 的 I2C 外设可同时使用两个地址,即同时对两个地址作出响应,这个结构成员 OwnAddress1 配置的是默认的、 OAR1 寄存器存储的地址,若需要设置第二个地址寄存器 OAR2,可使用 DualAddressMode 成员使能,然后设置 OwnAddress2 成员即可, OAR2 不支持 10 位地址。
(4)AddressingMode:选择 I2C 的寻址模式是 7 位还是 10 位地址。这需要根据实际连接到 I2C 总线上设备的地址进行选择,这个成员的配置也影响到 OwnAddress1 成员,只有这里设置成 10 位模式时, OwnAddress1 才支持 10 位地址。
(5)DualAddressMode:配置的是 STM32 的 I2C 设备自己的地址,每个连接到 I2C 总线上的设备都要有一个自己的地址,作为主机也不例外。地址可设置为 7 位或 10 位 (受下面 I2C_dual_addressing_mode 成员决定),只要该地址是 I2C 总线上唯一的即可。STM32 的 I2C 外设可同时使用两个地址,即同时对两个地址作出响应,这个结构成员I2C_OwnAddress1 配置的是默认的、 OAR1 寄存器存储的地址,若需要设置第二个地址寄存器OAR2,可使用 I2C_OwnAddress2Config 函数来配置, OAR2 不支持 10 位地址。
(6)OwnAddress2:本成员配置的是 STM32 的 I2C 设备自身地址 2,每个连接到 I2C 总线上的设备都要有一个自己的地址,作为主机也不例外。地址可设置为 7 位,只要该地址是 I2C 总线上唯一的即可。
(7)GeneralCallMode:本成员是关于 I2C 从模式时的广播呼叫模式设置。
(8)NoStretchMode:本成员是关于 I2C 禁止时钟延长模式设置,用于在从模式下禁止时钟延长。它在主模式下必须保持关闭。配置完这些结构体成员值,调用库函数 HAL_I2C_Init 即可把结构体的配置写入到寄存器中。
2. 读取与写入
其实HAL_I2C_Mem_Write 就是等价于先用HAL_I2C_Master_Transmit传输第一个寄存器地址,再用HAL_I2C_Master_Transmit传输写入第一个寄存器的数据。所以,如果只是往某个外设中读/写数据,则用Master_Receive/Master_Transmit就OK了,如果是外设里面还有子地址(像E2PROM),有设备地址,还有每个数据的寄存器存储地址,则用Mem_Write。(Mem_Write是两个地址,Master_Transmit只有从机地址)。
2.1 HAL_I2C_Master_Transmit()
1 | HAL_StatusTypeDef HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c,uint16_t DevAddress,uint8_t *pData,uint16_t Size,uint32_t Timeout); |
2.2 HAL_I2C_Master_Receive()
1 | HAL_StatusTypeDef HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c,uint16_t DevAddress,uint8_t *pData,uint16_t Size,uint32_t Timeout); |
2.3 HAL_I2C_Mem_Write()
1 | 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); |
2.4 HAL_I2C_Mem_Read()
1 | 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); |