目录
[1. 传感器用的哪些型号?](#1. 传感器用的哪些型号?)
[2. 一个平台能跑起来最小系统要哪些?](#2. 一个平台能跑起来最小系统要哪些?)
[3. DDR 是多大的?](#3. DDR 是多大的?)
[4. Flash 是多大的?](#4. Flash 是多大的?)
[5. ADC 的采样频率是多少?](#5. ADC 的采样频率是多少?)
[6. ADC 不够的话,怎样去组合?](#6. ADC 不够的话,怎样去组合?)
[7. SPI 有几根线?](#7. SPI 有几根线?)
[8. SPI 的主从模式区别?](#8. SPI 的主从模式区别?)
[9. 你们使用的板子的主频是多大的?](#9. 你们使用的板子的主频是多大的?)
[10. 你有自己写过 makefile 么?CFLAGS 能设置哪些内容?makefile 怎么实现指令传参?](#10. 你有自己写过 makefile 么?CFLAGS 能设置哪些内容?makefile 怎么实现指令传参?)
[11. KEIL 的话,你看过编译的中间文件么,比如那个.map 文件你看过么?看那个.map 你会关注什么,最底下那几个代码段说一下它各代表什么意思?](#11. KEIL 的话,你看过编译的中间文件么,比如那个.map 文件你看过么?看那个.map 你会关注什么,最底下那几个代码段说一下它各代表什么意思?)
[12. FreeRTOS 的优先级是只与单片机有关么?](#12. FreeRTOS 的优先级是只与单片机有关么?)
[13. 任务句柄是用来干吗的?](#13. 任务句柄是用来干吗的?)
[14. 消息队列和信号量有什么区别?什么情况下用消息队列了?](#14. 消息队列和信号量有什么区别?什么情况下用消息队列了?)
[15. 如果两个任务间要传递一个比较大的数据,队列创建时你会把队列的元素长度给他规定的很大么?](#15. 如果两个任务间要传递一个比较大的数据,队列创建时你会把队列的元素长度给他规定的很大么?)
[16. IIC 的话,主机怎么去控制对一个寄存器是读操作还是写操作?](#16. IIC 的话,主机怎么去控制对一个寄存器是读操作还是写操作?)
1. 传感器用的哪些型号?
项目中根据监测需求选用了三类核心传感器,型号与用途对应明确:
- 温湿度传感器:采用 SHT30(工业级),支持 I2C 接口,测量范围 - 40℃~125℃、0%~100% RH,精度 ±0.3℃(25℃时),适配原料存储区与生产车间的温湿度采集需求;
- 空气质量传感器:选用 MQ135,模拟量输出(0~5V),可检测 CO₂、烟雾等有害气体,通过 STM32 的 ADC 模块采样,用于原料仓库的气体安全监测;
- 压力传感器:采用 MPX5700DP(工业级),模拟量输出(0~5V),测量范围 0~700kPa,适配成型设备的压力参数采集,确保生产加工环节的压力稳定;
- 设备状态检测:搭配 E18-D80NK 红外光电传感器(开关量输出),用于检测辅助设备(如输送泵)的运行状态,判断是否存在卡停故障。
2. 一个平台能跑起来最小系统要哪些?
基于 STM32F103C8T6 的最小系统,核心组成需包含 4 类基础模块,满足 "上电运行 + 核心功能支撑":
- 核心控制单元:STM32F103C8T6 芯片(ARM Cortex-M3 内核),提供运算与外设控制能力;
- 电源模块:5V 转 3.3V 稳压电路(如 AMS1117-3.3 芯片),搭配滤波电容(0.1μF+10μF),确保供电稳定;
- 复位电路:10kΩ 上拉电阻 + 0.1μF 电容组成的外部复位电路,支持手动复位与上电自动复位;
- 时钟电路:外部 8MHz 晶振 + 2 个 22pF 负载电容,为芯片提供主时钟信号(可倍频至 72MHz);
- 下载接口:SWD 接口(2 线:SWDIO、SWCLK),用于程序烧录与在线调试(替代传统 JTAG 接口,简化布线)。以上模块构成 "最小系统" 基础,若需扩展功能(如通信、存储),可额外接入 Wi-Fi/Bluetooth 模块、Flash 芯片等。
3. DDR 是多大的?
STM32F103C8T6 属于无片外 DDR 的 MCU(微控制器),其 "内存" 依赖芯片内置的 SRAM(静态随机存取存储器),容量为20KB(地址范围 0x20000000~0x20004FFF),主要用于存储程序运行中的临时数据(如传感器采样值、任务栈、队列缓存)。项目中通过 FreeRTOS 的任务栈大小优化(如核心任务栈设为 512 字节、次要任务设为 256 字节),20KB SRAM 可满足 "6-8 个任务 + 多传感器数据缓存" 的需求,无需额外扩展片外 DDR(工业级嵌入式场景中,MCU 多依赖内置 SRAM,DDR 多用于高性能处理器如 STM32H7 系列)。
4. Flash 是多大的?
项目中使用两类 Flash,分别承担 "程序存储" 与 "数据存储" 功能:
- MCU 内置 Flash :STM32F103C8T6 内置64KB Flash(地址范围 0x08000000~0x0800FFFF),用于存储程序代码(如 FreeRTOS 内核、传感器驱动、通信协议逻辑),64KB 容量可满足项目中 "嵌入式软件 + 轻量级应用" 的存储需求;
- 外部扩展 Flash:通过 SPI 接口接入 W25Q64 芯片(8MB 容量,即 64Mbit),用于存储历史传感器数据(如近 7 天的温湿度日志)、设备配置参数(如告警阈值、Wi-Fi 账号),避免内置 Flash 容量不足导致的数据无法持久化问题。
5. ADC 的采样频率是多少?
STM32F103C8T6 的 ADC 为 12 位逐次逼近型,项目中根据传感器类型设置差异化采样频率,平衡 "实时性" 与 "资源占用":
- 温湿度传感器(SHT30):I2C 数字接口,无需 ADC 采样,数据刷新频率设为 1 次 / 秒;
- 模拟量传感器(MQ135、MPX5700DP) :ADC 采样频率设为10kHz(即每 100μs 采样 1 次),单通道采样时间约 1.5μs(配置 ADC 采样周期为 1.5 个时钟周期),支持 4 个模拟量通道轮询采样(总采样频率 40kHz,满足实时监测需求);
- 采样频率计算:ADC 时钟由 APB2 总线提供(最高 72MHz),项目中设为 12MHz(72MHz/6 分频),单通道采样时间 = 1.5+12.5=14 个时钟周期(12.5 为转换周期),即采样频率 = 12MHz/14≈857kHz,实际配置为 10kHz 是通过 "定时器触发采样 + 软件间隔控制" 实现,避免高频采样占用过多 CPU 资源。
6. ADC 不够的话,怎样去组合?
若 STM32 内置 ADC 通道(STM32F103C8T6 共 16 个外部通道)不足以覆盖传感器需求,项目中可通过 "硬件扩展 + 软件优化" 两种方式组合:
- 硬件扩展:增加外部 ADC 芯片:通过 SPI/I2C 接口接入外部 12 位 / 16 位 ADC 芯片(如 ADS1115,I2C 接口,4 通道),扩展采样通道数量,外部 ADC 的采样数据通过通信接口传输至 STM32,不占用内置 ADC 资源;
- 软件优化:通道分时复用:对 "非实时性需求的传感器"(如环境光照传感器),采用 "分时轮询采样"------ 例如将内置 ADC 的 1 个通道分时连接 2 个传感器(通过 GPIO 控制模拟开关如 CD4051,切换采样对象),采样间隔设为 500ms,既节省通道,又不影响监测精度;
- 优先级调度:核心通道优先采样:对 "高实时性传感器"(如设备压力传感器)分配固定 ADC 通道,低优先级传感器(如空气质量传感器)通过软件轮询复用剩余通道,确保核心数据采集不延迟。
7. SPI 有几根线?
SPI(串行外设接口)为全双工同步串行通信协议,项目中使用4 根核心信号线(标准 SPI 模式),部分场景可简化为 3 根(无 CS 线),具体如下:
- SCK(Serial Clock):时钟线,由主机(如 STM32)输出,同步数据传输;
- MOSI(Master Out Slave In):主机输出 / 从机输入线,主机向从机发送数据(如 STM32 向 W25Q64 发送写指令);
- MISO(Master In Slave Out):主机输入 / 从机输出线,从机向主机返回数据(如 W25Q64 向 STM32 返回读取的数据);
- CS(Chip Select):片选线,由主机控制(低电平有效),用于选择待通信的从机(若仅 1 个从机,可固定拉低,省略 CS 线,但多从机场景必须保留);项目中 STM32 与 W25Q64 Flash 的通信采用 4 线 SPI(SCK、MOSI、MISO、CS),确保多设备(如同时接入 SPI 接口的显示屏)时可独立控制。
8. SPI 的主从模式区别?
SPI 主从模式的核心区别在于 "时钟控制权" 与 "数据流向主导权",项目中 STM32 始终作为主机,外设(如 W25Q64、ADC 芯片)作为从机,具体差异如下:
| 对比维度 | 主机(Master) | 从机(Slave) |
|---|---|---|
| 时钟来源 | 主动生成 SCK 时钟信号 | 被动接收主机的 SCK 时钟 |
| 通信触发 | 主动发起通信(发送指令 / 读取数据) | 仅在被 CS 选中后,响应主机指令 |
| 数据流向控制 | 主导数据发送 / 接收的时序 | 按主机时序同步发送 / 接收数据 |
| 硬件配置 | 需配置 SCK 输出模式、时钟极性 / 相位 | 需配置 SCK 输入模式,无需时钟配置 |
| 应用场景 | 控制器(如 STM32、MCU) | 外设(如 Flash、ADC、显示屏) |
例如项目中 "STM32 读取 W25Q64 数据":STM32(主机)拉低 CS 选中从机,输出 SCK 时钟,通过 MOSI 发送 "读数据指令 + 地址",W25Q64(从机)按 SCK 时序,通过 MISO 返回对应地址的数据。
9. 你们使用的板子的主频是多大的?
项目中核心控制板(STM32F103C8T6)的最高主频为 72MHz,由外部 8MHz 晶振经 PLL(锁相环)倍频得到(8MHz×9=72MHz),实际运行中根据功能需求配置为 72MHz(满频),原因如下:
- 72MHz 是 STM32F103 系列的稳定最高主频,可满足 "多任务调度(FreeRTOS)+ 多外设并发(ADC、SPI、UART)" 的算力需求;
- 外设时钟与主频联动:ADC 时钟最高 14MHz(72MHz/6 分频)、SPI 时钟最高 18MHz(72MHz/4 分频),72MHz 主频可确保外设工作在最优速率,避免因主频不足导致的通信延迟。
10. 你有自己写过 makefile 么?CFLAGS 能设置哪些内容?makefile 怎么实现指令传参?
项目中基于 Linux 环境开发 C++ 服务器端代码(如视频监控系统的 Reactor 服务器)时,独立编写过 Makefile,核心细节如下:
-
是否写过 Makefile:是,用于管理 "服务器端代码 + 交叉编译嵌入式程序",实现 "一键编译、链接、清理",避免手动输入复杂编译指令;
-
CFLAGS 可设置的内容 :
- 编译选项:
-c(仅编译不链接)、-o(指定输出文件名)、-Wall(开启所有警告,如未定义变量、类型不匹配); - 优化级别:
-O0(无优化,用于调试)、-O2(中度优化,平衡性能与编译速度,项目中默认使用); - 头文件路径:
-I./include(指定头文件所在目录,如包含 FFmpeg、MySQL 的头文件); - 宏定义:
-DDEBUG(定义 DEBUG 宏,用于条件编译调试代码,如#ifdef DEBUG printf("日志信息"); #endif); - 标准指定:
-std=c++11(启用 C++11 标准,支持智能指针、lambda 表达式等);
- 编译选项:
-
Makefile 实现指令传参 :通过
make 变量名=值的方式传参,Makefile 中使用$(变量名)引用,例如:makefile
# Makefile内容 CC=g++ CFLAGS=-std=c++11 -Wall $(OPT) # $(OPT)为外部传入参数 target=server $(target): main.cpp $(CC) $(CFLAGS) -o $@ $^ # 指令传参示例:make OPT=-O2(启用O2优化)、make OPT=-g(生成调试信息) # 执行make OPT=-O2时,CFLAGS实际为"-std=c++11 -Wall -O2"
11. KEIL 的话,你看过编译的中间文件么,比如那个.map 文件你看过么?看那个.map 你会关注什么,最底下那几个代码段说一下它各代表什么意思?
项目中使用 KEIL MDK 编译 STM32 嵌入式代码时,经常查看编译生成的.map文件(链接器输出的内存分配报告),核心关注点与代码段含义如下:
- 是否看过.map 文件:是,主要用于排查 "内存溢出、代码段分布异常" 问题;
- 关注的核心信息 :
- Code(代码段)大小 :查看程序代码占用的 Flash 空间(如
Total RO Size (Code + RO Data)),判断是否超出 STM32 内置 Flash 容量(64KB); - RAM 占用 :关注
Total RW Size (RW Data + ZI Data)(RW 为已初始化数据,ZI 为未初始化数据),避免超出 20KB SRAM; - 函数 / 变量地址:定位关键函数(如 FreeRTOS 任务函数)的内存地址,用于调试时查看函数调用栈;
- 未定义符号(Undefined symbols):排查链接错误(如函数声明未实现、库文件未添加);
- Code(代码段)大小 :查看程序代码占用的 Flash 空间(如
- 最底部核心代码段含义 (以 STM32F103 为例,地址与芯片相关):
- Code:存放程序指令(如函数代码),位于 Flash(0x08000000 开始),只读;
- RO Data(Read-Only Data) :存放只读数据(如
const定义的常量、字符串常量),位于 Flash,与 Code 段连续; - RW Data(Read-Write Data) :存放已初始化的全局变量 / 静态变量(如
int a=10),编译时存于 Flash,上电后复制到 SRAM(0x20000000 开始); - ZI Data(Zero-Initialized Data) :存放未初始化的全局变量 / 静态变量(如
int b),编译时不占用 Flash,上电后由系统初始化为 0,位于 SRAM,紧跟 RW Data; - Stack(栈):存放函数局部变量、函数调用参数,位于 SRAM 高地址,由编译器自动分配(可在 KEIL 中配置栈大小);
- Heap(堆) :用于动态内存分配(如
malloc),位于 SRAM,栈的下方,大小可在 KEIL 中配置(项目中 FreeRTOS 的内存管理优先使用堆)。
12. FreeRTOS 的优先级是只与单片机有关么?
FreeRTOS 的优先级不仅与单片机有关,还与 FreeRTOS 内核配置、任务调度机制直接相关,单片机仅决定 "优先级数量的上限",具体如下:
- 单片机的影响 :依赖 MCU 的内核中断控制器(如 STM32 的 NVIC),FreeRTOS 的任务调度基于 "PendSV(可悬起系统调用)" 和 " SysTick(系统滴答定时器)" 中断,NVIC 支持的中断优先级位数(STM32F103 为 4 位,即 16 级优先级)决定了 FreeRTOS 可配置的最大优先级数量(项目中配置为 16 级,0 为最低,15 为最高);
- FreeRTOS 内核配置的影响 :通过
FreeRTOSConfig.h中的宏定义调整优先级特性:configMAX_PRIORITIES:配置支持的最大优先级数(如设为 16,即 0-15 级),需≤MCU NVIC 支持的最大级别;configUSE_PORT_OPTIMISED_TASK_SELECTION:启用硬件优化的任务选择(仅部分 MCU 支持),影响高优先级任务的响应速度;
- 任务调度机制的影响:FreeRTOS 采用 "抢占式调度"(默认),高优先级任务可抢占低优先级任务,优先级的 "数值大小"(而非绝对值)决定调度顺序 ------ 例如优先级 10 的任务始终比优先级 5 的任务先执行,与单片机型号无关,仅需内核配置支持该优先级范围。综上,单片机决定 "优先级数量上限",FreeRTOS 配置与调度机制决定 "优先级的实际使用规则"。
13. 任务句柄是用来干吗的?
FreeRTOS 中的 "任务句柄(Task Handle)" 是一个指向 "任务控制块(TCB,Task Control Block)" 的指针,核心作用是作为任务的 "唯一标识",供内核函数操作任务,具体用途包括:
- 任务操作 :通过句柄调用 FreeRTOS API 控制任务,如:
vTaskSuspend(handle):挂起指定句柄的任务;vTaskResume(handle):恢复挂起的任务;vTaskDelete(handle):删除任务(需句柄确认目标任务);
- 任务间通信 / 同步 :作为参数传递给同步机制函数,标识 "操作目标",如:
xQueueSendToBack(queue, &handle, 0):将任务句柄发送到队列,实现 "任务通知";xSemaphoreGiveFromISR(sem, &xHigherPriorityTaskWoken):在中断中释放信号量时,通过句柄判断是否需要唤醒高优先级任务;
- 任务状态查询 :通过
eTaskGetState(handle)获取任务当前状态(如运行、就绪、阻塞),用于调试或状态监控;项目中每个任务创建时都会保存句柄(如TaskHandle_t xAdcTaskHandle;),例如 "ADC 采集任务" 挂起 / 恢复、查询状态,均通过该句柄操作,确保精准控制目标任务。
14. 消息队列和信号量有什么区别?什么情况下用消息队列了?
FreeRTOS 中消息队列与信号量均用于 "任务间同步 / 通信",但核心定位与用途差异显著,具体区别及消息队列的适用场景如下:
| 对比维度 | 消息队列(Message Queue) | 信号量(Semaphore) |
|---|---|---|
| 核心功能 | 传递 "具体数据"(如传感器采样值、指令) | 传递 "事件通知"(无具体数据,仅表示 "资源可用 / 事件发生") |
| 数据存储 | 可存储多个数据元素(队列深度可配置) | 仅存储 "可用计数"(二进制信号量计数 0/1,计数信号量可设 N) |
| 操作方式 | 发送(xQueueSend)/ 接收(xQueueReceive)数据 | 释放(xSemaphoreGive)/ 获取(xSemaphoreTake)信号量 |
| 资源消耗 | 占用 RAM 较多(需存储数据元素) | 占用 RAM 较少(仅需存储计数与控制信息) |
消息队列的适用场景:当任务间需要传递 "具体数据内容" 时使用,例如:
- 项目中 "ADC 采集任务" 向 "数据处理任务" 传递传感器采样值(如温湿度、压力的具体数值),通过队列存储数据,确保数据不丢失;
- "云平台通信任务" 向 "本地告警任务" 传递远程配置指令(如修改温湿度告警阈值的数值),通过队列传递指令参数;
- 多任务向同一任务发送数据(如 3 个传感器采集任务向 1 个数据上传任务发送数据),队列的 "FIFO(先进先出)" 特性可确保数据有序处理。
15. 如果两个任务间要传递一个比较大的数据,队列创建时你会把队列的元素长度给他规定的很大么?
不会直接将队列元素长度设为 "大数据尺寸",而是通过 "传递数据指针 + 内存管理" 的方式优化,避免队列占用过多 RAM 资源,具体方案如下:
- 核心问题:若数据较大(如 1024 字节的视频帧数据、512 字节的历史日志),直接将队列元素长度设为 1024 字节,若队列深度为 10,则需占用 10×1024=10KB RAM,而 STM32F103 仅 20KB SRAM,会严重挤占其他任务的内存空间;
- 优化方案:传递数据指针 :
- 提前在堆内存(如 FreeRTOS 的堆、或外部 Flash)中为 "大数据" 分配存储空间,获取数据的指针(如
uint8_t *pBigData = pvPortMalloc(1024);); - 队列元素类型设为 "指针类型"(如
QueueHandle_t xBigDataQueue = xQueueCreate(5, sizeof(uint8_t *));),队列仅存储 "数据指针"(4 字节,32 位 MCU),而非数据本身; - 发送任务将数据指针写入队列(
xQueueSend(xBigDataQueue, &pBigData, 0);),接收任务读取指针后,通过指针访问大数据,处理完成后释放内存(vPortFree(pBigData););
- 提前在堆内存(如 FreeRTOS 的堆、或外部 Flash)中为 "大数据" 分配存储空间,获取数据的指针(如
- 注意事项:需确保 "数据指针指向的内存空间在传递期间不被释放"(如发送任务分配内存,接收任务释放内存),避免野指针问题;若数据需长期保存,可将数据存入外部 Flash(如 W25Q64),队列传递 "Flash 地址 + 数据长度",进一步减少 RAM 占用。
16. IIC 的话,主机怎么去控制对一个寄存器是读操作还是写操作?
IIC(Inter-Integrated Circuit)通过 "从机地址 + 读写控制位" 的组合,控制对从机寄存器的 "读 / 写操作",具体流程基于 IIC 的通信时序(起始信号、地址字节、应答信号、数据字节、停止信号),以项目中 SHT30 温湿度传感器(I2C 从机)为例,步骤如下:
- I2C 地址字节的结构 :IIC 从机地址为 7 位(部分从机为 10 位,项目中 SHT30 为 7 位),第 8 位为 "读写控制位",用于标识操作类型:
- 第 8 位 = 0:表示 "写操作"(主机向从机发送数据,如写入寄存器地址、配置参数);
- 第 8 位 = 1:表示 "读操作"(主机从从机读取数据,如读取寄存器中的温湿度值);
- 写操作流程(主机→从机,如向 SHT30 发送 "读取温湿度" 指令) :
- 主机发送 IIC 起始信号(SDA 从高变低,SCK 为高);
- 主机发送 SHT30 的 7 位从机地址(0x44)+ 第 8 位 = 0(写操作),即地址字节 0x44<<1 | 0 = 0x88;
- 从机发送应答信号(ACK,SDA 拉低);
- 主机向从机发送 "读取温湿度" 的指令字节(如 0x2C、0x06),从机每次接收后发送 ACK;
- 主机发送停止信号(SDA 从低变高,SCK 为高);
- 读操作流程(从机→主机,如读取 SHT30 的温湿度数据) :
- 主机发送起始信号;
- 主机发送 SHT30 的 7 位从机地址(0x44)+ 第 8 位 = 1(读操作),即地址字节 0x44<<1 | 1 = 0x89;
- 从机发送 ACK,随后向主机发送温湿度数据字节(如先发送温度高 8 位,再发送温度低 8 位);
- 主机接收数据后,根据是否需要继续读取,发送 ACK(继续读)或 NACK(停止读);
- 主机发送停止信号,完成读操作;综上,主机通过 "地址字节的第 8 位" 控制读 / 写类型,再结合后续的 "数据发送 / 接收" 时序,实现对从机寄存器的精准操作。
17. F103C8T6其中的各个字段表示什么意思
ST 公司 32 位微控制器,属于 F1 系列中容量产品线(103),48 引脚(C),内置 64KB Flash(8),采用 LQFP 封装(T),工作温度 - 40℃~85℃(6)。
18. 你知道IIC的工作原理吗
IIC(Inter-Integrated Circuit,集成电路间总线)是一种同步串行通信协议,由飞利浦(NXP)开发,主要用于短距离、低速设备间的通信(如传感器、存储器、显示屏等与 MCU 的连接)。其工作原理可通过 "物理层""协议层" 和 "通信时序" 三部分拆解,具体如下:
一、物理层:两根线实现双向通信
IIC 仅需两根信号线即可实现多设备间的双向通信,核心硬件组成如下:
| 信号线 | 作用 | 特性 |
|---|---|---|
| SDA(Serial Data) | 串行数据线,用于传输数据 | 双向线,开漏输出(需上拉电阻,通常 4.7kΩ) |
| SCK(Serial Clock) | 串行时钟线,用于同步数据传输 | 由主机(如 MCU)产生,控制通信速率 |
- 多设备支持:同一 IIC 总线上可连接多个主机和多个从机,通过 "从机地址" 区分不同设备(无需片选线);
- 上拉电阻:SDA 和 SCK 均需外接上拉电阻到电源(如 3.3V),当总线空闲时,两根线均为高电平;设备输出低电平时将总线拉低,释放后由上拉电阻恢复高电平。

二、协议层:通信规则与数据格式
IIC 通信遵循严格的协议规则,核心包括 "地址机制""数据帧格式" 和 "应答机制":
-
从机地址
- 每个 IIC 从机设备有唯一的 7 位或 10 位地址(主流为 7 位),用于主机识别目标设备;
- 主机发送数据时,首先发送 "从机地址 + 读写位"(8 位):第 8 位为 "读写控制位"------0 表示写操作(主机→从机),1 表示读操作(从机→主机)。
-
数据帧格式
- 每帧数据为 8 位(1 字节),高位在前(MSB);
- 通信时按 "起始信号→地址帧→数据帧(n 个)→停止信号" 的顺序传输。
-
应答机制(ACK/NACK)
- 主机发送 1 字节数据后,会释放 SDA 线,等待从机的应答信号(ACK):
- 从机在第 9 个时钟周期将 SDA 拉低(低电平),表示 "已接收"(ACK);
- 若从机未拉低 SDA(高电平),表示 "未接收"(NACK),主机可终止通信或重试;
- 读操作时,主机接收完 1 字节后,需向从机发送 ACK/NACK:ACK 表示 "继续接收",NACK 表示 "接收结束"。
- 主机发送 1 字节数据后,会释放 SDA 线,等待从机的应答信号(ACK):

在I2C通信中,一个时钟周期被分成了两个部分:
- 工作时间:一个时钟周期中的高电平部分属于工作部分,此时SDA输出的电平决定了传输数据是0还是1。
- 休息时间:一个时钟周期中的低电平部分属于休息部分,此时SDA输出的电平是无效数据,这段时间是留给设备,来准备下一个工作时间发送数据的。
三、核心通信时序(以 "主机写数据到从机" 为例)
-
起始信号(S)
- 主机控制:SCK 为高电平时,SDA 从高电平跳变为低电平(下降沿),表示通信开始。
-
发送地址帧
- 主机通过 SDA 发送 8 位 "从机地址 + 读写位"(如写操作时为
0x48),SCK 同步提供时钟; - 从机识别到自身地址后,在第 9 个时钟周期发送 ACK(SDA 拉低)。
- 主机通过 SDA 发送 8 位 "从机地址 + 读写位"(如写操作时为
-
发送数据帧
- 主机依次发送 n 字节数据(如传感器配置参数),每发送 1 字节后,等待从机 ACK;
- 数据按字节传输,高位在前,每个 bit 对应一个 SCK 时钟脉冲。
-
停止信号(P)
- 主机控制:SCK 为高电平时,SDA 从低电平跳变为高电平(上升沿),表示通信结束。

四、关键特性总结
| 特性 | 说明 |
|---|---|
| 通信速率 | 标准模式(100kbps)、快速模式(400kbps)、高速模式(3.4Mbps) |
| 拓扑结构 | 多主多从,通过地址区分设备 |
| 信号完整性 | 开漏输出 + 上拉电阻 ,支持线与逻辑(多设备同时拉低时总线为低) |
| 应用场景 | 近距离低速通信(如温湿度传感器、EEPROM、OLED 屏等) |
例如在 "新能源材料智能监测系统" 中,STM32(主机)通过 IIC 与 SHT30 温湿度传感器(从机)通信:主机发送地址 + 写指令,传感器返回 ACK 后,主机发送 "读取温湿度" 命令,传感器再通过 IIC 将数据回传给主机,整个过程依赖上述时序和协议规则实现可靠数据传输。
19. 你知道GPIO的输入模式和输出模式吗
在 STM32 等微控制器中,GPIO(General-Purpose Input/Output,通用输入 / 输出)的输入模式和输出模式用于适配不同的外设交互场景,每种模式的电气特性和适用场景有明确差异。以下是具体分类及说明:
一、GPIO 输入模式(4 种)
输入模式下,GPIO 引脚用于 "接收外部信号",根据是否有内部上拉 / 下拉电阻、是否支持模拟信号输入,分为 4 种:
| 模式名称 | 核心特性 | 适用场景 |
|---|---|---|
| 浮空输入(Floating Input) | 引脚内部无上下拉电阻,电平由外部信号决定(高阻态)。 | 需外部电路提供确定电平的场景,如连接按键(需外部上拉 / 下拉电阻)、接收外部数字信号(如红外接收模块)。 |
| 上拉输入(Pull-Up Input) | 引脚内部接弱上拉电阻(约 40kΩ),无外部信号时默认电平为高(VDD)。 | 外部信号为低电平有效时使用,如按键接地(按下时为低,松开时内部上拉为高),无需额外外部电阻。 |
| 下拉输入(Pull-Down Input) | 引脚内部接弱下拉电阻(约 40kΩ),无外部信号时默认电平为低(GND)。 | 外部信号为高电平有效时使用,如检测外部高电平触发信号(如传感器输出高电平报警)。 |
| 模拟输入(Analog Input) | 引脚连接到 ADC(模数转换器),用于接收模拟信号(如电压变化),内部上下拉无效。 | 需采集模拟量的场景,如连接 MQ135 空气质量传感器(输出 0~5V 模拟电压)、 potentiometer(电位器)。 |
二、GPIO 输出模式(4 种,按输出驱动方式分类)
输出模式下,GPIO 引脚用于 "输出高低电平或 PWM 信号",根据输出驱动能力和是否支持复用功能,分为 4 种:
| 模式名称 | 核心特性 | 适用场景 |
|---|---|---|
| 推挽输出(Push-Pull Output) | 输出级采用 N-MOS 和 P-MOS 管,高低电平均有强驱动能力(高电平输出 VDD,低电平输出 GND)。 | 需要强驱动能力的场景,如驱动 LED、继电器、蜂鸣器(高低电平均可有效驱动负载)。 |
| 开漏输出(Open-Drain Output) | 仅 N-MOS 管有效,高电平时引脚浮空(需外部上拉电阻才能输出高电平),低电平时输出 GND。 | 需实现 "线与" 逻辑(多设备共享总线,如 IIC 的 SDA/SCK 线)、电平转换(通过外部上拉电阻适配 5V 设备)。 |
| 复用推挽输出(Alternate Function Push-Pull) | 引脚功能复用为外设(如 UART 的 TX、SPI 的 MOSI),输出驱动方式为推挽。 | 外设输出信号时使用,如 UART 发送数据(TX 引脚)、TIM 定时器输出 PWM(需强驱动能力)。 |
| 复用开漏输出(Alternate Function Open-Drain) | 引脚功能复用为外设,输出驱动方式为开漏(需外部上拉)。 | 复用为 IIC、SMBus 等总线外设时使用(如 IIC 的 SDA/SCK 引脚复用,依赖开漏特性实现多设备共享)。 |
三、关键区别与选型原则
- 输入模式 vs 输出模式:输入模式用于 "接收信号"(如读按键、传感器),输出模式用于 "发送信号"(如控制 LED、驱动外设);
- 上下拉电阻:输入模式中,上拉 / 下拉用于 "无外部信号时确定引脚电平",避免浮空导致的不确定状态;
- 推挽 vs 开漏:推挽输出驱动能力强(高低电平均有源驱动),开漏输出适合总线共享(需外部上拉);
- 复用功能:当 GPIO 作为外设引脚(如 UART、SPI)时,需配置为 "复用输出",由外设控制引脚电平,而非软件直接写 GPIO 寄存器。
例如在项目中:
- 连接按键采用 "上拉输入"(无需外部电阻,按下为低,松开为高);
- 驱动 LED 采用 "推挽输出"(直接输出高低电平控制亮灭);
- IIC 的 SDA/SCK 引脚采用 "复用开漏输出"(配合外部上拉电阻实现多设备通信);
- 连接模拟传感器采用 "模拟输入"(接入 ADC 采集电压)。
哈弗结构和冯诺伊曼结构的区别?
施密特触发器是什么?
讲一下I/o端口的基本结构?