RTT_IIC
272.3.8RTT_IIC
#include <rtthread.h> // 引入 RT-Thread 内核接口,本例使用毫秒延时和 RT_EOK 返回值。
#define DBG_TAG "main" // 设置 RT-Thread 日志模块标签为 main。
#define DBG_LVL DBG_LOG // 设置日志过滤级别为普通日志级别。
#include <rtdbg.h> // 引入 RT-Thread 日志接口;最终有效业务代码没有再输出日志。
#include "oled_config.h" // 引入 OLED 初始化函数和温度显示函数的声明。
#include "GY906.h" // 引入 GY-906 初始化函数和红外温度读取函数的声明。
float temp; // 定义全局浮点变量,保存传感器返回的物体温度。
int main(void) // 定义应用程序入口函数。
{ // main 函数体开始。
int count = 1; // 定义循环计数变量并置为 1,使第一次循环条件成立。
DisplayCfg_Init(); // 初始化 U8g2、SSD1306 OLED 及其 PB6/PB7 软件 I²C 接口。
GY906_Init(); // 将 GY-906 使用的 PB6/PB7 配置为开漏输出。
while (count++) // 先判断 count 的旧值是否非零,再将其加 1;课程用它持续循环。
{ // 温度采集与显示循环开始。
temp = SMBus_ReadTemp(); // 通过自定义软件 SMBus 驱动读取 MLX90614 的物体温度。
Display_normal(temp); // 将本次温度按两位小数绘制到 OLED 缓冲区并发送到屏幕。
rt_thread_mdelay(1000); // 当前线程延时 1000 ms,使温度约每秒刷新一次。
} // 温度采集与显示循环结束。
return RT_EOK; // 保留课程源码的成功返回语句;在定义良好的持续循环中不会执行到此处。
} // main 函数体结束。
上方代码来自课程配套工程
14IO_DEVICE2/applications/main.c的最终有效业务代码。有效语句、变量、函数名和执行顺序均保持原样,只移除了文件头旧注释及被注释掉的LOG_D(),并补充逐行说明。课程还展示了oled_config.c/.h与GY906.c/.h,它们属于显示适配层和传感器驱动,不在这里重复抄入库/驱动实现。
一、I²C组件使用方法
I²C(Inter-Integrated Circuit,集成电路总线)用两根信号线连接控制器和多个目标器件:
SCL是时钟线,由控制器产生时钟;SDA是双向数据线,地址、读写位、数据和应答都从这条线传输;- 每个目标器件通常用地址区分,因此多个器件可以共享同一对 SCL/SDA;
- 总线空闲时两条线都为高电平,传输由 START 开始、STOP 结束,每发送 8 位后还有一个 ACK/NACK 应答位。
SDA、SCL 标准上采用开漏/开集电极方式:器件只主动拉低线路,释放线路后由上拉电阻把它恢复为高电平。这样多个器件共享线路时不会出现一个器件强推高、另一个器件强拉低的直接冲突。若模块上没有合适的上拉电阻,就要给 SDA、SCL 外接上拉;上拉电压必须与 STM32 的输入电平和所有总线器件兼容,不能因为模块标称可用 5 V 就直接把总线拉到 5 V。
本节演示由两部分组成:GY-906 红外温度模块读取物体温度,SSD1306 OLED 显示温度。手靠近传感器时,传感器视场内接收到的红外辐射发生变化,显示值随之升高;移开后数值回落。视频还演示了超温蜂鸣提示,但课程归档的最终 main.c 只包含“读取温度—显示—延时”,没有蜂鸣器判断或驱动代码,因此不能从本节源码推导出其阈值和实现。
需要特别区分 I²C 与 SMBus:两者都是两线串行总线,电气和基本传输形式相近,但 SMBus 对时钟范围、超时、PEC 等有附加约束。MLX90614 数据手册称其接口为 SMBus;课程的 GY-906 驱动也实现了 START、STOP、ACK 和 PEC 校验,所以笼统说成“I²C 传感器”便于理解,但并不意味着两个协议的全部细节完全相同。
1.1 构建目标
若按 RT-Thread 的标准 I²C 设备框架开发,通常会从 drivers/board.h 的 I²C 配置区开始,依次启用通用 I²C 框架、BSP 总线、引脚/硬件初始化,再通过 rt_i2c_transfer() 等统一接口访问设备。
本节刻意没有采用这条路径。配套工程的 .config 明确显示 CONFIG_RT_USING_I2C 未启用,U8g2 的硬件 I²C 选项也未启用。实际链路是:
| 外设 | 使用的实现 | 是否经过 RT-Thread I²C 总线设备 |
|---|---|---|
| SSD1306 OLED | U8g2 的 u8x8_byte_sw_i2c 软件模拟 I²C | 否 |
| GY-906/MLX90614 | GY906.c 自行翻转 GPIO 的软件 SMBus | 否 |
因此,视频中提到的 board.h“四步配置”是常规设备框架路线的回顾,并不是本实验的实际配置步骤。这里仍然使用了 RT-Thread PIN 接口和线程延时,但没有注册或查找 i2c1 设备。
1.2 RT-Thread Studio组件库
课程口语中的“组件”实际包含两个容易混淆的概念:
- RT-Thread 组件:随系统源码提供、通过配置裁剪的内核或服务功能,例如设备框架、DFS、网络协议栈等。
- RT-Thread 软件包:软件包中心管理的可复用外部代码,保存后下载到工程的
packages目录;软件包可以声明对某些组件或其他软件包的依赖。
本节选择的是软件包中心里的 u8g2-official。U8g2 是面向单色显示器的图形库,支持多种控制器和通信方式。工程中的 oled_config.c 在 U8g2 与当前硬件之间做了薄封装:选择 SSD1306 128×64 I²C 屏、指定 PB6/PB7、完成初始化,并提供显示浮点温度的函数。
这与此前从 51 单片机再移植到 STM32 的专用 OLED 驱动思路不同:应用不必重新实现字体、缓冲区和绘图函数,而是复用 U8g2,再编写少量板级适配代码。软件包节省的是通用功能开发量,具体屏幕型号、接口、引脚、电源和地址仍要由使用者确认。
1.3 I²C组件使用
1.3.1 添加软件包
在 RT-Thread Studio 中添加 U8g2 的流程是:
- 双击工程根目录的 RT-Thread Settings。
- 进入 添加软件包,在搜索框输入
u8g2。 - 在搜索结果中选择
u8g2-official,不要把视频语音识别出的“office”当成包名。 - 单击添加并关闭窗口,按
Ctrl+S保存配置,等待 Studio 下载软件包并更新工程。 - 在
packages/u8g2-official-latest下确认包已经出现,再编译工程。
配套工程最终启用了 CONFIG_PKG_USING_U8G2_OFFICIAL 和 latest 版本。视频出现多个 U8g2 搜索结果,是因为软件包中心同时存在旧移植包、官方上游包等不同入口;课程使用的是官方上游版本。
1.3.2 选择U8g2软件包
oled_config.c 中的关键配置为 u8g2_Setup_ssd1306_i2c_128x64_noname_f(),函数名已经编码了本实验的显示参数:
| 名称片段 | 含义 |
|---|---|
ssd1306 | 显示控制器为 SSD1306 |
i2c | 屏幕使用 I²C 接口 |
128x64_noname | 选择常见的 128×64 非特定厂牌面板变体 |
_f | 使用完整帧缓冲模式 |
128×64 单色画面仅像素缓冲就需要 128×64÷8 = 1024 字节 RAM。完整帧缓冲便于先在内存中完成清屏、字体和文本绘制,再由 u8g2_SendBuffer() 一次发送;RAM 更紧张的平台可考虑分页模式,但那不是本节源码的配置。
课程适配文件把 OLED 时钟线定义为 GET_PIN(B, 6),数据线定义为 GET_PIN(B, 7)。DisplayCfg_Init() 建立 U8g2 对象、设置两根引脚、初始化屏幕并退出省电模式;Display_normal(float temp) 清空缓冲区,绘制 TEMP TEST,把温度格式化为两位小数,再发送完整缓冲区。
1.3.3 淘宝搜索红外温度传感器
课程展示的是常见 GY-906 模块,其核心器件和驱动行为对应 Melexis MLX90614。视频展示了 BAA、BCC、DCI 等后缀产品,并用“检测距离”作直观区分;更严谨的指标是视场角(FOV),不是一个固定的最大测距值。官方数据手册给出的典型视场宽度包括 xAA 的 90°、xCC 的 35°、xCI 的 5°。视场越窄,同样距离下覆盖的目标区域越小,但实际读数仍受目标尺寸、距离、发射率、环境反射和安装结构影响。
MLX90614 得到的是视场内所有物体辐射贡献形成的平均温度。它读取手部表面温度,不等于无需校准即可得到医学体温;要用于人体测温,还需选对医疗精度型号,并处理测量距离、环境温度、发射率、光学窗口和整机校准。
供电也不能只凭商家页面的“3~5 V”概括。裸 MLX90614 的 Axx 型号工作电压为 4.5~5.5 V,Bxx/Dxx 型号为 2.6~3.6 V;市售 GY-906 模块可能额外带稳压和上拉电路。接线前应核对模块原理图、板上器件和芯片完整后缀,并确保 SDA/SCL 的上拉电压对 STM32F103 的 3.3 V 逻辑安全。
1.3.4 添加温度传感器驱动文件
视频复制到 applications 的四个自定义文件实际是:
GY906.c/.h:GPIO 模拟 SMBus 的 MLX90614 驱动;oled_config.c/.h:对 U8g2 初始化和温度显示流程的封装。
它们不是软件包中心自动下载的“温度传感器组件包”。其中 U8g2 本体位于 packages/u8g2-official-latest,自定义文件则位于 applications。刷新工程树后才能在 IDE 中看到刚复制的文件。
GY906.h 将 SCL、SDA 分别映射到 PB6、PB7;GY906_Init() 把两者配置为开漏输出。驱动读取 MLX90614 的 RAM 0x07,即物体温度 TOBJ1,再按下式把原始值转换为摄氏度:
物体温度(℃) = 原始值 × 0.02 - 273.15
这里 0.02 表示每个最低有效位对应 0.02 K,减去 273.15 是开尔文到摄氏度的换算。它读取的不是芯片自身环境温度;后者位于 RAM 0x06。
课程驱动把从地址写为 0x00。在 MLX90614 中,0x00 是通用调用地址,单个器件时可以得到响应;芯片默认的普通 7 位地址是 0x5A。如果同一总线上放置多个 MLX90614,通用调用会造成同时响应,不能照搬本节的单器件做法。
还应注意自定义驱动的延时实现:Coarse_delay_us() 用 rt_tick_get() / 4000000 * us 计算循环次数,但 rt_tick_get() 返回的是系统启动后的节拍计数,不是 CPU 频率或周期计数。本工程每秒只有 1000 个节拍,程序启动后的很长时间内这段整数除法通常得到 0,因此它不能可靠地产生所请求的微秒延时。课堂实物可能因 GPIO 调用本身的开销、较低的实际总线速度而碰巧通信成功,但该时序会随运行时间和编译优化变化,不能直接视为稳健驱动;正式工程应使用经过验证的微秒延时、硬件定时器或统一 I²C/SMBus 驱动。这里不改写配套源码,只明确其风险。
1.3.5 应用案例
1.3.5.1 例题: 红外温度与屏幕显示
最终程序的执行顺序为:
- 包含
oled_config.h与GY906.h,让编译器获得函数的正确返回类型和参数声明。 - 调用
DisplayCfg_Init()初始化 OLED。 - 调用
GY906_Init(),把共用的 PB6/PB7 最终设为开漏输出。 - 循环调用
SMBus_ReadTemp()取得float型物体温度。 - 把温度传给
Display_normal();该函数用%0.2f显示两位小数。 rt_thread_mdelay(1000)让当前线程休眠约 1 秒,再刷新下一次。
视频先用常量 11.2 验证显示链路,屏幕显示为 11.20;随后才接入真实温度返回值。这种“先验证输出,再接入输入”的分步调试法能快速判断问题位于屏幕端还是传感器端。
第一次读取动态温度时屏幕出现异常,原因是漏掉了 GY906.h。在旧 C 编译模式或宽松编译选项下,未声明函数可能只产生警告,编译器却按错误的默认返回类型处理;SMBus_ReadTemp() 实际返回 float,调用约定或返回值解释不匹配就会造成乱码或异常数值。不能把“编译没有红色错误”当成程序正确,黄色警告也应逐项处理。
两套软件总线都占用 PB6/PB7:U8g2 先初始化引脚,随后 GY906_Init() 将其改成开漏模式;之后 OLED 与传感器在同一个线程中顺序访问,所以课程演示可以运行。若扩展为多个线程同时访问,必须用互斥锁保护完整事务,否则一方可能在另一方传输中途改变 SCL/SDA。更稳妥的工程化方案是让所有器件统一挂在同一个 RT-Thread I²C 总线上,由总线驱动负责互斥与传输,但本节没有这样改写。
初学者接线可按下面理解:
| 开发板 | OLED | GY-906 | 说明 |
|---|---|---|---|
| PB6 | SCL | SCL | 共用时钟线 |
| PB7 | SDA | SDA | 共用双向数据线 |
| GND | GND | GND | 所有器件必须共地 |
| 合适电源 | VCC | VCC | 分别按模块规格供电,不凭线色或商品标题猜电压 |
还要确认总线上已有且仅有合适等效值的上拉电阻。两个模块都自带上拉时,它们会并联,使等效阻值变小;完全没有上拉时,开漏线路无法可靠回到高电平。发生无显示、读数固定或随机错误时,应依次检查共地、供电、SCL/SDA 是否接反、上拉电压、器件地址,再用示波器或逻辑分析仪观察 START、地址、ACK 和数据,而不是只反复烧写代码。
课程原样使用 while (count++)。它表达了持续运行的意图,但有符号 int 不断递增最终会发生 C 语言未定义的溢出,因而不是严谨的永久循环写法;精校稿依据要求不改写源码,只指出这一事实。在每秒一次的循环中,这属于极长期运行问题,却仍应在正式产品代码评审时处理。
1.4 软件包中心
RT-Thread Studio 软件包中心按物联网、外设、系统、工具、语言、杂类等组织可复用代码。搜索比逐层浏览更直接:输入器件或协议名称,打开详情核对维护者、版本、许可证、依赖和适用平台,再决定是否加入工程。
保存配置后,Studio 会解析软件包声明的依赖,必要时自动启用相关组件或加入其他包。视频用机智云相关包演示时看到 lwIP、SAL、DFS 等项目被联动启用;这说明依赖被自动解析,不代表这些功能是 U8g2 或当前温度实验所必需。若网络慢,下载阶段可能长时间占用带宽;应等待状态完成并查看下载/构建日志,不能只根据图标是否高亮判断安装成功。
软件包的价值包括版本化、复用和依赖管理,但不能取代数据手册与板级适配。加入软件包后仍要确认:
- 配置是否真正保存并反映到
.config、rtconfig.h; - 下载目录和源码是否已经出现;
- 依赖是否与当前 RT-Thread/BSP 版本兼容;
- 示例使用的是软件总线还是硬件外设;
- 引脚、地址和显示控制器是否与手中硬件一致。
1.5 淘宝搜索AT24C02
AT24C02 是常见的两线串行 EEPROM。课程通过商品页面引出这个器件,但器件容量、电压、速度和寿命应以具体制造商及完整料号的数据手册为准,不能用商品标题代替技术规格。
1.5.1 AT24C02的存储功能及特性
原稿把 AT24C02 写成“2 KB”,这是数量级错误。型号中的 02 通常表示 2 Kbit,Microchip AT24C02C 的内部组织为 256 × 8 bit,所以实际容量是 256 字节,不是 2048 字节。它适合保存少量配置、校准值、编号或掉电保持状态,不能按大容量存储卡理解。
EEPROM 的读取通常不会消耗写入寿命,擦写则有次数限制;但“只能写约 1 万次”不是所有 AT24C02 的统一结论。以 Microchip AT24C02C 数据手册为例,在规定测试条件下给出至少 1,000,000 次写周期和 100 年数据保持。其他厂商、温度与工作条件可能不同,设计必须查实际器件规格。
所谓“每次写不同地址”是磨损均衡的基本思想,但不是简单换地址就结束。完整方案还要记录最新数据位置、处理掉电时的半写入、校验数据,并考虑页写边界。还可采用以下方法减少磨损:只在值确实变化时写、合并频繁更新、延迟批量保存、循环使用多个记录槽,并利用 WP 写保护脚防止误写。
1.5.2 RT-Thread软件包与组件介绍
软件包中心可以搜索 AT24Cxx 相关驱动。典型驱动会提供初始化、按地址读取、按地址写入等接口,并处理设备地址、页大小、写周期等待和总线传输。是否选择软件包,取决于它能否匹配具体器件、当前 I²C 总线接口和工程版本。
课程只是浏览软件包并建议课后实践,没有把 AT24C02 加入当前工程,也没有编写读写业务代码,因此顶部源码不能凭空加入 EEPROM 示例。练习时可以从“写入一个字节—复位—读回验证”开始,再扩展到页写、边界条件、写保护和掉电一致性。
1.5.3 例题1:RT-Thread Studio项目构建与资源管理
添加软件包后出现多个新配置项,通常源于依赖关系:上层包声明自己需要网络栈、文件系统或其他服务,配置工具据此联动选择。应从包详情、Kconfig 与最终 .config 验证依赖,而不是仅凭界面颜色猜测。
本节工程本身形成了清楚的对照:
- OLED 使用
u8g2-official软件包,但选择其软件 I²C 回调; - 红外温度传感器使用手工复制的
GY906.c/.h; CONFIG_RT_USING_I2C未启用,所以 RT-Thread 自带的软件 I²C 设备项没有高亮,也没有参与本实验;- 在
board.h打开相应总线开关并保存配置后,相关驱动配置才可能生效。
软件包、应用文件和 RT-Thread 设备框架可以同时存在,但它们不是同一个层次。判断某段代码走哪条路径,应追踪它实际调用的是 rt_i2c_transfer()、U8g2 回调,还是直接 rt_pin_write()/rt_pin_read()。
1.5.4 电脑性能对项目运行的影响
视频中软件包下载缓慢时,任务管理器显示网络接收速率较高而 CPU 占用较低,因此本次等待主要是网络下载,不应直接归因于 CPU、内存或硬盘。电脑卡顿应根据指标定位:
- 网络持续占满:检查下载源、代理、网络质量和后台下载;
- 内存接近耗尽并频繁换页:关闭大型程序或评估扩容;
- 磁盘活动时间长期 100% 且响应很慢:检查占用进程、剩余空间和磁盘健康;
- CPU 长期满载:查看具体进程与编译并行度。
“8 GB 或 16 GB 一定太小”“哪里慢就换哪里”都不是可靠诊断。先看任务管理器和 Studio 日志,区分下载、解压、索引与编译阶段,再决定是否需要更换硬件。
1.5.5 文件搜索技巧
在 Windows 文件资源管理器的文件列表中,把输入法切到英文后按某个字母,会跳到以该字母开头的项目;连续按同一个字母可在匹配项之间移动。这个方法适合在文件较多的目录中快速定位 S、A 等首字母文件,但它不是全文搜索,也不会按文件内容查找。
在 RT-Thread Studio 中找代码时,还可使用工程搜索功能按文件名、函数名或宏名搜索。例如本节可搜索 SMBus_ReadTemp 追到 GY906.c,搜索 Display_normal 追到 oled_config.c,比只在工程树中逐项翻找更可靠。
二、结束
本节的核心不是完整讲解 I²C 设备框架,而是学会从软件包中心加入 U8g2,并把现成显示库、自定义 GY-906 驱动和少量业务代码组合起来。最终数据流是:
MLX90614 红外测量 → PB6/PB7 软件 SMBus → SMBus_ReadTemp() → float temp → Display_normal() → PB6/PB7 软件 I²C → SSD1306 OLED
下一节将复盘 RT-Thread 设备模型,解释应用层、设备管理层和驱动层的关系,以及设备如何注册、应用为何能通过名称查找设备。这是课程预告,不代表本节的自定义软件总线已经使用了该设备模型。
从学习顺序看,应先掌握线程创建与调度、线程间通信和同步,再理解设备模型与驱动分层;如果裸机 GPIO、定时器、UART、I²C 等基础仍不牢,可以结合小项目继续练习。本节尤其适合对照两条路径:当前“直接操作 PIN 的软件总线”和后续“通过统一 I²C 设备接口访问总线”,理解复用代码与分层管理各自解决的问题。