STM32百问百答:从硬件到软件全面解析

你是一位具有20年工作经验的嵌入式 硬件工程师和嵌入式软件工程师,请你详细回答以下面试问题,越详细越好,最好让初学者也懂,牢记我的要求:

1.STM32是什么?它基于什么架构?2.ARM Cortex-M0, M3,M4,M7内核的主要区别是什么?

3.什么是CMSIS?它的作用是什么?

4.描述-下STM32从上电到开始执行main函数的过程。

5.什么是ISP和IAP?它们有何不同?

6.STM32的供电引脚(VDD, VDDA, VBAT等)分别有什么作用?

7.什么是时钟树?为什么它在STM32中很重要?8.列举STM32的主要时钟源(HSI, HSE,LSI,LSE, PLL)

9.如何配置PLL以得到最高的系统时钟?10.什么是GPIO?STM32的GPIO有几种工作模式?

11.推挽输出与开漏输出有什么区别?

12.如何将一个GPIO引脚配置为上拉输入模式?13.什么是复用功能?如何将一个PA9引脚配置为USART1_TX?

14.什么是NVIC?它在中断系统中起什么作用?

15.STM32的中断优先级是如何分组和管理的?

16.什么是EXTI?它如何工作?

17.SysTick定时器有什么用途?18.看门狗定时器是什么?独立看门狗和窗口看门狗有何区别?

19.如何从待机模式中唤醒STM32?

20.什么是位带操作?它有什么优势?

21.UART通信的基本原理是什么?

22.如何配置UART以实现115200的波特率?

23.UART如何通过中断方式接收不定长数据?

24.什么是DMA?它有什么好处?

25.如何配置UART使用DMA进行数据发送和接收?

26.SPI有几种工作模式?由什么信号决定?27.如何配置SPI为主机全双工模式?28.12C通信的起始信号和停止信号是如何定义的?

29.如何用软件模拟I2C时序?

30.12C从机地址是如何组成的?

31.STM32的硬件I2C在应用时需要注意什么?32.比较UART、SPI和I2C三种通信协议的特点和适用场景。

33.通用定时器有哪些主要功能?

34.如何配置定时器产生一个1kHz的PWM信号?35.PWM输出的频率和占空比由哪些寄存器决定?36.如何用定时器捕获一个外部脉冲的高电平宽度?

37.什么是定时器的编码器接口模式?如何使用?38.高级定时器(如TIM1)比通用定时器多了哪些功能?

39.如何用定时器触发一个ADC转换?40.ADC的分辨率是什么意思?STM32的ADC通常是多少位?

41.什么是ADC的规则通道和注入通道?

42.如何实现多通道ADC扫描转换?

43.ADC的采样时间如何影响转换结果?

44.如何校准ADC?

45.DAC的主要用途是什么?

46.比较器的工作原理是什么?

47.什么是实时时钟?如何配置RTC并产生一个闹

钟中断?

48.如何将数据备份到RTC的备份寄存器中?

49.芯片唯一ID有什么用途?

50.Flash存储器是如何组织的?什么是页,什么是扇区?

51.如何对内部Flash进行读写操作?

52.什么是选项字节?如何配置它?53.在STM32中如何实现一个简单的引导程序?54.如何将程序从内部Flash搬运到外部RAM中运行以提高速度?

55.什么是内存管理单元?哪些Cortex-M内核拥有它?

56.如何配置MPU以保护不同的内存区域?57.什么是DSP指令集?哪些STM32系列支持它?

58.如何用STM32实现一个简单的FFT?59.浮点单元是什么?哪些STM32系列拥有硬件FPU?

60.使用FPU需要注意什么?

61.以太网MAC和PHY的区别是什么?

62.如何配置LWIP协议栈以实现TCP通信?63.USB有哪几种传输类型?各自适用于什么场景?

64.如何将STM32配置为一个USB HID设备?65.如何将STM32配置为一个USB CDC设备(虚拟串口)?

66.CAN总线的基本帧、扩展帧和远程帧有什么区别?

67.如何配置CAN总线过滤器?

68.如何计算CAN总线的波特率?69.SDIO接口和SPI模式驱动SD卡有何区别?

70.如何通过FSMC接口驱动TFT液晶屏?

71.什么是LTDC?它有什么作用?

72.DCMI接口用于什么场景?

73.如何用STM32驱动一个WS2812B LED?

74.如何读取旋转编码器的值?

75.如何通过STM32驱动一个步进电机?

76.如何实现一个简单的PID控制器?77.在STM32上如何运行FreeRTOS?78.在FreeRTOS中,任务有哪几种状态?79.什么是信号量、互斥量和消息队列?80.如何避免任务间的优先级反转?81.什么是内存堆碎片?如何应对?82.如何使用STM32CubeMX生成初始化代码?83.STM32CubeMX中生成的HAL库和LL库有什么区别?

84.HAL库中的轮询、中断和DMA三种模式有何不同?

85.如何处理HAL库中的超时错误?

86.如何自定义HAL库的回调函数?

87.如何通过SWD接口调试STM32?

88.断点有哪几种类型?89.如何通过串口打印调试信息?90.如何使用ITM进行更高效的调试?91.如何测量代码的执行时间?92.如何配置STM32进入低功耗模式(睡眠、停

止、待机)?

93.在低功耗模式下,哪些外设可以继续工作?94.如何分析和优化STM32的功耗?95.在电路设计中,STM32的复位电路和晶振电路需要注意什么?

96.什么是去耦电容?为什么要尽可能靠近芯片电源引脚放置?

97.PCB布局布线时,模拟部分和数字部分应如何处理?98.程序跑飞了,可能的原因有哪些?99.如何利用看门狗来增强系统的可靠性?100.在项目开发中,如何进行STM32的固件升级?

下面给出一份"从裸机到RTOS、从硬件到软件、从原理到调试"的STM32百问百答

力求让初学者也能看懂 ,同时把20年项目经验 穿插在"注意事项""实战技巧"里。

篇幅较长,建议收藏后按序号速查。

(所有代码片段均用 HAL/LL 双语法给出,可直接拷贝验证)


1. STM32 是什么?基于什么架构?

  • STM32 是 ST 公司推出的 32 位 ARM Cortex-M 系列 MCU 的总商标。

  • 内核架构:ARMv6-M(M0)、ARMv7-M(M3/M4/M7)。

  • 片内集成 Flash、SRAM、外设总线、时钟、复位、调试模块等,"单片"即可跑系统


2. Cortex-M0/M3/M4/M7 区别(一张表记)

表格

复制

特性 M0 M3 M4 M7
架构 ARMv6-M ARMv7-M ARMv7E-M ARMv7E-M
DSP ✅ 单周期 MAC、SIMD ✅ 双精度 MAC
FPU 单精度可选 单/双精度可选
MPU 可选 标配 标配 标配
Cache I/D Cache
主频 ≤48 MHz ≤200 MHz ≤240 MHz ≤480 MHz
中断数 1-32 1-240 1-240 1-240
指令集 56 条 >200 条 +DSP +DSP+双精度

经验:做电机控制优先选 M4/M7,做超低功耗传感器节点选 M0+。


3. CMSIS 是什么?作用?

Cortex Microcontroller Software Interface Standard

  • ARM 制定的"中间层"API,统一寄存器定义、系统启动、DSP 库、RTOS 接口。

  • 我们写代码时只要 #include "stm32f4xx.h",里面的寄存器名、NVIC 通道号、位定义全部来自 CMSIS,换芯片不换手册


4. 上电 → main() 的 7 步(代码级)

  1. 取复位向量表(0x0800 0000 处 SP,0x0800 0004 处 PC)。

  2. 启动文件 startup_stm32fxxx.s

    • 复制 .data 从 Flash→RAM

    • 清零 .bss

    • 设置系统时钟 SystemInit()

    • __main→最终 main()

  3. 若用 CubeMX,还会先跑 HAL_Init() 配置 Flash 等待周期、SysTick 1 ms。


5. ISP vs IAP

表格

复制

ISP IAP
谁烧录 芯片出厂 BootROM 用户自己程序
接口 UART/USB/CAN 等固定引脚 任意接口(SD卡、以太网、无线)
是否改 Boot 引脚 需要 BOOT0=1 不需要
典型场景 产线第一次烧空片 现场远程升级

6. 供电引脚

  • VDD:数字核、GPIO 1.8-3.6 V。

  • VDDA :ADC/DAC/PLL 模拟供电,必须 ≤ VDD+0.3 V

  • VBAT:RTC+备份寄存器+32 kHz 振荡器,1.65-3.6 V,主电丢失仍可跑。

  • VREF+:ADC 参考,可外接精密 2.5 V/3.0 V;若悬空则等于 VDDA。

经验:VDDA 走线必须 LC 滤波,否则 ADC 噪声大 3-5 LSB。


7. 时钟树 & 重要性

STM32 把"时钟"做成树状 MUX,原因:

  • 不同外设需要不同频率(ADC ≤36 MHz,USB 必须 48 MHz)。

  • 低功耗场景可关闭某一支路。

  • 出错时快速切换安全时钟。


8. 主要时钟源

  • HSI:内部 16 MHz RC,上电默认,±1 % 温漂。

  • HSE:外部 4-26 MHz 晶振,精度 10 ppm。

  • LSI:内部 32 kHz RC,看门狗/自动唤醒。

  • LSE:外部 32.768 kHz,RTC 专用。

  • PLL:乘/除法器,把 HSE/HSI 升到 400 MHz(H7)。


9. 配置最高系统时钟(以 F407 为例,168 MHz)

c

复制

复制代码
// CubeMX 图形界面只需 3 步:
// 1) HSE 选 8 MHz 晶振
// 2) PLLSRC = HSE
// 3) N=336, P=2, Q=7 → 168 MHz
// 代码级:
RCC_OscInitTypeDef osc = {0};
osc.OscillatorType = RCC_OSCILLATORTYPE_HSE;
osc.HSEState       = RCC_HSE_ON;
osc.PLL.PLLState   = RCC_PLL_ON;
osc.PLL.PLLSource  = RCC_PLLSOURCE_HSE;
osc.PLL.PLLM       = 8;      // VCO 输入 1 MHz
osc.PLL.PLLN       = 336;    // VCO 输出 336 MHz
osc.PLL.PLLP       = RCC_PLLP_DIV2;  // 系统时钟 168 MHz
HAL_RCC_OscConfig(&osc);

10. GPIO 是什么?工作模式

General Purpose Input/Output ,双向引脚。

STM32 每脚 8 种模式:

  1. 输入浮空

  2. 输入上拉

  3. 输入下拉

  4. 模拟(ADC/DAC)

  5. 推挽输出

  6. 开漏输出

  7. 复用推挽

  8. 复用开漏


11. 推挽 vs 开漏

  • 推挽 :内部上下管交替导通,能输出高/低电平,驱动 LED、USART。

  • 开漏 :下管导通只能拉低,上管关闭,需外加上拉电阻,可做"线与"、电平转换(5 V→3.3 V)。


12. 上拉输入配置(寄存器级)

c

复制

复制代码
// HAL
GPIO_InitTypeDef g = {0};
g.Pin  = GPIO_PIN_0;
g.Mode = GPIO_MODE_INPUT;
g.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOA, &g);

// LL
LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_0, LL_GPIO_MODE_INPUT);
LL_GPIO_SetPinPull(GPIOA, LL_GPIO_PIN_0, LL_GPIO_PULL_UP);

13. 复用功能 & PA9→USART1_TX

复用 :GPIO 不再做普通 IO,而交给片上外设。

步骤:

  1. __HAL_RCC_GPIOA_CLK_ENABLE();

  2. __HAL_RCC_USART1_CLK_ENABLE();

  3. 配置 PA9 为复用推挽,上拉,速度高。

  4. 选 AF7:GPIO_InitStruct.Alternate = GPIO_AF7_USART1;


14. NVIC 作用

Nested Vectored Interrupt Controller,ARM 内核自带。

  • 管理 240 条外设中断线,提供 4-16 级优先级。

  • 负责抢占+尾链+迟到优化,中断响应 12 周期以内。


15. 中断优先级分组

Cortex-M 把 8 位优先级拆成 组优先级+子优先级

STM32 只用高 4 位,通过 NVIC_PriorityGroupConfig() 选 5 种分配方式:

例:Group2 → 2 位组+2 位子,共 4 组,每组 4 级。

经验:RTOS 场景全部设 Group4(全抢占),避免子优先级带来的反转。


16. EXTI 外部中断

  • 把 GPIO 边沿映射到 NVIC 线 0-15。

  • 配置流程:
    SYSCFG→EXTIx 选端口 → 上升/下降沿触发 → NVIC 使能 → 写 ISR 清挂起位。


17. SysTick 用途

  • 24 位递减计数器,地址固定 0xE000E010。

  • HAL 拿它做 1 ms 时基;RTOS 拿它做时间片轮转。

  • 也可自己摘出来做软件定时器。


18. 看门狗

  • 独立看门狗 IWDG :内部 32 kHz LSI,一旦启动软件无法关闭,复位时间 0.1-32 s。

  • 窗口看门狗 WWDG :APB1 时钟,必须在"窗口期"喂狗,提前或滞后都复位,可检测跑飞+时钟失效


19. 从待机模式唤醒

待机电流 2 µA(L476)。

唤醒源:RTC 闹钟、WKUP 引脚上升沿、NRST、IWDG。

代码:

c

复制

复制代码
HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1);
HAL_PWR_EnterSTANDBYMode();   // 到此停机
// 唤醒后自动复位,重新执行 main()

20. 位带操作

Cortex-M 把 1 M 字节的 SRAM 和外设区映射到 32 M 字节别名区,一次写 1 bit 变成写 32 bit 的原子操作

优势:无需读-改-写,无竞争,速度快 ,在多任务共享标志时神器。

示例:

c

复制

复制代码
#define BITBAND_SRAM(addr, bit) \
    (*(__IO uint32_t*)(0x22000000 + ((addr)-0x20000000)*32 + bit*4))
BITBAND_SRAM(&flag, 7) = 1;   // 原子置位 bit7

21. UART 原理

异步串行:起始位(低)+8 数据位+可选奇偶+停止位(高)。

双方约定波特率 ,允许 2-3 % 误差。

TTL 电平:0-3.3 V;外接 RS-232 需 MAX3232;外接 RS-485 需 SP3485。


22. 配置 115200 baud(F407,APB1=42 MHz)

c

复制

复制代码
huart1.Instance = USART1;
huart1.Init.BaudRate   = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits   = UART_STOPBITS_1;
huart1.Init.Parity     = UART_PARITY_NONE;
huart1.Init.Mode       = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl  = UART_HWCONTROL_NONE;
HAL_UART_Init(&huart1);
// 寄存器级:USART_BRR = 42 MHz / 115200 ≈ 0x1A1

23. 中断接收不定长数据

3 种主流做法:

  1. 空闲中断(IDLE)+DMA:CubeMX 勾选 USART1 global interrupt,

    USART1_IRQHandler() 里检测 __HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE),清标志后计算 DMA_RX_LEN = BUF_SIZE - __HAL_DMA_GET_COUNTER(hdma_usart1_rx)

  2. 字符超时+循环缓冲。

  3. 特殊帧尾(\n)中断拼接。


24. DMA 好处

  • 无需 CPU 搬运,省 10-20 倍时间。

  • 双缓冲可零拷贝串口流。

  • 低功耗:DMA 跑时内核可睡。


25. UART+DMA 双工

c

复制

复制代码
HAL_UART_Transmit_DMA(&huart1, txBuf, len);
HAL_UART_Receive_DMA(&huart1, rxBuf, BUF_LEN);
// 完成后分别进入 HAL_UART_TxCpltCallback / HAL_UART_RxCpltCallback

26. SPI 四种模式

CPOL (时钟极性)和 CPHA(时钟相位)决定:

  • Mode0: CPOL=0, CPHA=0 → 上升沿采样

  • Mode1: CPOL=0, CPHA=1 → 下降沿采样

  • Mode2: CPOL=1, CPHA=0

  • Mode3: CPOL=1, CPHA=1

经验:先读从机手册,90% flash/W25Qxx 支持 Mode0/3


27. 配置 SPI 主机全双工

c

复制

复制代码
hspi1.Init.Direction      = SPI_DIRECTION_2LINES;
hspi1.Init.Mode           = SPI_MODE_MASTER;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4; // 84/4=21 MHz
hspi1.Init.CLKPhase       = SPI_PHASE_1EDGE;   // Mode0
hspi1.Init.CLKPolarity    = SPI_POLARITY_LOW;
HAL_SPI_Init(&hspi1);
// NSS 建议软件管理:GPIO 输出片选

28. I²C 起/止信号

  • 起始 S:SCL 高期间 SDA 下降沿。

  • 停止 P:SCL 高期间 SDA 上升沿。

必须先把 SDA 设为开漏,外加上拉 4.7 kΩ。


29. 软件模拟 I²C(位 bang)

c

复制

复制代码
void I2C_Delay(void) { for(int i=0;i<80;i++) __NOP(); }
void I2C_Start(void){
    SDA_H; SCL_H; I2C_Delay();
    SDA_L; I2C_Delay();   // 下降沿
    SCL_L;
}

适合引脚不够用或硬件 I²C 有硅 bug 的系列(如 F103 部分批次)。


30. I²C 从机地址

7 位地址左移 1 位 + 读写位 → 8 位头字节。

例:MPU6050 地址 0x68,写 0xD0,读 0xD1。

10 位地址模式很少用。


31. 硬件 I²C 注意点

  • 必须开模拟滤波器(ANOFF=0)。

  • 时钟拉伸要允许。

  • 多主机会死锁,加超时回调 HAL_I2C_ErrorCallback() 里发停止信号恢复。

  • 上电时 SDA/SCL 不要被外设拉高,否则锁死,需 Clock out 9 个脉冲 解锁。


32. UART vs SPI vs I²C

表格

复制

线数 速度 多主机 距离 成本
UART 2(TX/RX) Mbps 级 中等
SPI 4(+CS) 50+ Mbps
I²C 2 0.4/1/3.4 Mbps 最低

33. 通用定时器功能

  • 时基中断

  • 输入捕获(测脉冲宽/频率)

  • PWM 输出(带死区)

  • 编码器接口

  • 单脉冲模式

  • 触发 ADC/DAC


34. 1 kHz PWM(F103,TIM2,72 MHz)

c

复制

复制代码
TimHandle.Instance = TIM2;
TimHandle.Init.Prescaler = 72-1;      // 1 MHz
TimHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
TimHandle.Init.Period = 1000-1;       // 1 kHz
HAL_TIM_PWM_Init(&TimHandle);
sConfigOC.Pulse = 500;                // 50 % 占空
HAL_TIM_PWM_ConfigChannel(&TimHandle, &sConfigOC, TIM_CHANNEL_1);

35. PWM 频率/占空寄存器

  • 频率 = TIM_CLK / (PSC+1) / (ARR+1)

  • 占空 = CCR / (ARR+1)


36. 捕获高电平宽度

c

复制

复制代码
// 通道1捕获上升沿,通道2捕获下降沿,开启"直连"
__HAL_TIM_SET_CAPTUREPOLARITY(&htim, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_RISING);
__HAL_TIM_SET_CAPTUREPOLARITY(&htim, TIM_CHANNEL_2, TIM_INPUTCHANNELPOLARITY_FALLING);
// 在 CCR1/CCR2 中断里计算差值
width = (capture2 - capture1) * 1 us;  // 假设 1 MHz 计数

37. 编码器接口

定时器把 TI1/TI2 当成正交信号,硬件自动加减计数

配置:

c

复制

复制代码
sEncoderMode.EncoderMode = TIM_ENCODERMODE_TI12;
HAL_TIM_Encoder_Init(&htim, &sEncoderMode);

__HAL_TIM_GET_COUNTER(&htim) 即位置,零软件开销


38. 高级定时器 TIM1 额外功能

  • 6 通道 PWM,带互补输出(驱动三相全桥)

  • 死区时间可编程(ns 级)

  • 刹车输入(紧急关 PWM,保护 MOSFET)

  • 重复计数器(更新事件可 N 次后才中断)

  • 可生成中心对齐 PWM(对称,降低谐波)


39. 定时器触发 ADC

c

复制

复制代码
ADC_ExternalTrigConvConfig(ADC1, ADC_EXTERNALTRIGCONV_T1_CC1, ENABLE);

ADC 的 JEXTEN/JEXTSEL 位选触发源,实现同步采样,用于电机电流环。


40. ADC 分辨率

分辨率 = 量化台阶数,12 位 → 4096 级。

STM32 主流 12 位,部分系列 16 位过采样(G4/H7)。


41. 规则通道 vs 注入通道

  • 规则:正常扫描,最多 16 通道,结果进 DR。

  • 注入高优先级"打断"规则组 ,最多 4 通道,结果进 JDR,适合电机相电流双采样


42. 多通道扫描

c

复制

复制代码
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank    = 1;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank    = 2;
...
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adcBuf, N*Ch);

43. 采样时间影响

采样时间越长,输入阻抗允许越大 ;否则电荷没建立,读数偏低。

公式:RAIN < (Tsamp / (Cadc * ln(2^(12+1)))) -- Radc

经验:源阻抗 >10 kΩ 必须加运放缓冲。


44. ADC 校准

c

复制

复制代码
HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED);

每颗芯片出厂增益/偏移不同,校准后典型 ENOB 提高 0.3-0.5 位。


45. DAC 用途

  • 波形发生器(正弦、梯形)

  • 参考电压

  • 音频输出(带 DMA 双缓冲)


46. 比较器

模拟比较器 1 µs 内比较两路电压,输出可接定时器刹车、产生中断、唤醒 Stop 模式。

用于过流保护比 ADC 轮询快 10 倍。


47. RTC 闹钟

c

复制

复制代码
RTC_TimeTypeDef sTime = {0};
sTime.Hours = 6;
RTC_SetTime(&hrtc, &sTime);
HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BIN);

闹钟事件可唤醒待机,精度 1 ppm(外接 32.768 kHz LSE)。


48. 备份寄存器

c

复制

复制代码
HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR0, 0x1234);

VBAT 不掉电,存 128 字节,可存掉电前状态、错误码。


49. 唯一 ID

96 位 UID,每颗芯片不同,可做:

  • 加密密钥种子

  • USB 序列号

  • 防抄板


50. Flash 组织

  • 页 Page:最小擦除单位,F1 系列 1 KB/页,F4 16 KB/页。

  • 扇区 Sector:F4 分 12 扇区,前 4 个 16 KB,接着 64 KB,最后 128 KB。

  • 块 Bank:H7 双 Bank 可 RWW 读-写并行。


51. 内部 Flash 读写

c

复制

复制代码
HAL_FLASH_Unlock();
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_WRPERR);
FLASH_Erase_Sector(FLASH_SECTOR_5, VOLTAGE_RANGE_3);
HAL_FLASH_Program(TYPEPROGRAM_WORD, addr, data);
HAL_FLASH_Lock();

必关中断,擦写时不能执行代码,可在 RAM 中跑。


52. 选项字节

存放:读保护 RDP、BOR 电压、独立看门狗是否硬件启动、复位模式。

修改:ST-Link Utility 或 HAL_FLASHEx_OBProgram()

RDP 从 0xAA→0xCC 会全局擦除,慎用!


53. 简易 Bootloader(1 KB 搞定)

  1. 上电检查 GPIO 按键,按住进升级模式

  2. 接收 UART 新固件(带 CRC)。

  3. 写 App 区(0x08004000 起)。

  4. 校验 OK 后跳 ((void(*)())(0x08004000 + 4))();

中断向量表重定位:SCB->VTOR = 0x08004000;


54. 程序搬至外部 RAM 运行

  • H7/F7 带 FMC/SDRAM,

    startup.s 里把 .data/.bss 链接到 0xC000 0000,

    系统启动时复制 RO 段→SDRAM,CPU 通过 I-Cache 取指,速度提升 2-3 倍。

注意 MPU 把 SDRAM 设为 Normal, Write-back, no write allocate,否则 Cache 一致性问题。


55. MMU & MPU

  • MMU :只有 Cortex-M33/M55/M7 可选 ,提供虚拟地址,跑 Linux 才需要

  • MPU :所有 M3/M4/M7 都有,把 4 GB 地址划 8/16 区 ,设特权/缓存/执行禁止,防止野指针跑飞


56. 配置 MPU 保护

c

复制

复制代码
MPU_Region_InitTypeDef mpu = {0};
mpu.Enable           = MPU_REGION_ENABLE;
mpu.BaseAddress      = 0x20000000;
mpu.Size             = MPU_REGION_SIZE_256KB;
mpu.AccessPermission = MPU_REGION_FULL_ACCESS;
mpu.TypeExtField     = MPU_TEX_LEVEL0;
mpu.IsBufferable     = 1;
mpu.IsCacheable      = 1;
mpu.IsShareable      = 0;
HAL_MPU_ConfigRegion(&mpu);

57. DSP 指令集

M4/M7 提供:

  • 单周期 32×32→64 MAC

  • SIMD 16×2/8×4

  • 硬件除法、开方

跑 FIR 比 M3 快 5-7 倍。


58. 用 STM32 做 FFT

c

复制

复制代码
#include "arm_math.h"
#define N 1024
float32_t in[N], out[N];
arm_rfft_fast_instance_f32 S;
arm_rfft_fast_init_f32(&S, N);
arm_rfft_fast_f32(&S, in, out, 0);
arm_cmplx_mag_f32(out, mag, N/2);

开 FPU 后 1024 点仅需 0.8 ms(F407 168 MHz)。


59. FPU

Floating Point Unit ,单精度符合 IEEE-754。

M4F/M7/M33 有,M0/M3 无

编译选项:-mfloat-abi=hard -mfpu=fpv4-sp-d16


60. 使用 FPU 注意

  1. 上电默认关,SCB->CPACR |= (0xF << 20) 使能。

  2. 中断里用浮点需 FPU->FPCCR |= FPU_FPCCR_ASPEN_Msk; 保存上下文。

  3. 双精度变量会调用软件库,拖慢 100 倍 ,尽量用 float


61. 以太网 MAC vs PHY

  • MAC:STM32 内部,负责帧打包、DMA、FIFO。

  • PHY :外部芯片(LAN8720、KSZ8081),负责 MII/RMII 串并转换、电压驱动

  • 通过 MDIO 接口 配置 PHY 寄存器,LED 灯、自协商


62. 配置 LWIP TCP

  1. CubeMX 选 ETH、LWIP,DMA 描述符长度 4

  2. low_level_init() 里设置 MAC 地址。

  3. tcp_new()tcp_bind()tcp_listen() → 在 accept 回调里 tcp_write()

内存池改 LWIP_MEM_SIZE 到 16 KB,否则大数据包丢包


63. USB 传输类型

  • 控制:枚举/命令,必支持。

  • 中断:HID 鼠标/键盘,1 ms 轮询。

  • 批量 :U 盘、CDC,带宽大、可延迟

  • 等时 :USB 音频/摄像头,固定带宽、无重传


64. 配置 USB HID

CubeMX:

  • MiddleWares → USB_DEVICE → Class → HID。

  • 报告描述符改 64 字节,实现双向通信

  • USBD_HID_SendReport() 发数据,HID_OutEvent() 收数据


65. 配置 USB CDC 虚拟串口

CubeMX 选 CDC,PC 端免驱

  • 发送:CDC_Transmit_FS(buf, len);

  • 接收:在 CDC_Receive_FS() 回调里把数据抛给队列。

注意 Win10 首次需 INF,Linux/Mac 免驱


66. CAN 帧类型

  • 标准帧:11 位 ID,数据场 0-8 字节。

  • 扩展帧:29 位 ID。

  • 远程帧:无数据,用于请求。


67. CAN 过滤器

c

复制

复制代码
sFilterConfig.FilterBank = 0;
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
sFilterConfig.FilterIdHigh     = 0x321 << 5;
sFilterConfig.FilterMaskIdHigh = 0x7FF << 5;
HAL_CAN_ConfigFilter(&hcan, &sFilterConfig);

28 个过滤器,可组双 16 位,灵活屏蔽。


68. 计算 CAN 波特率

复制

复制代码
CAN 位时间 = 1 + BS1 + BS2
波特率 = PCLK / (Prescaler * (1 + BS1 + BS2))

例:PCLK=42 MHz, Prescaler=6, BS1=11, BS2=2 → 42 M/6/(1+11+2)=500 kbit/s。

采样点 70-87.5 % 符合 ISO。


69. SDIO vs SPI 模式 SD 卡

表格

复制

SDIO SPI
速度 48 MHz, 24 MB/s 25 MHz, 3 MB/s
引脚 6 根 4 根
功耗
代码 需 DMA 多块 简单

70. FSMC 驱动 TFT

  • NOR/SRAM 模式,A0 接 RS 脚 ,写命令 *(__IO uint16_t*)(0x60000000)=cmd;

  • 读数据 data = *(volatile uint16_t*)(0x60000002);

时序配 10 ns 级,RGB565 刷满屏 20 ms


71. LTDC

LCD-TFT Display Controller ,H7 自带,直接驱动 24 位 RGB 屏 ,最高 1024×768。

带两层图层、Alpha 混合、色键,无需 CPU 刷屏


72. DCMI

Digital Camera Interface ,8-14 位并口,接 OV2640、MT9M114 ,支持 JPEG 压缩数据流,DMA 直接进 SRAM


73. 驱动 WS2812B

单总线 800 kHz,时序 0: 400 ns 高 + 850 ns 低;1: 850 ns 高 + 400 ns 低

方案:

  • PWM+DMA(F4 用 TIM1,72 MHz 倍频)

  • SPI 移位(M0 用 24×8=192 位模拟一像素)

关中断,否则时序抖动颜色错


74. 读旋转编码器

定时器编码器模式A/B 相中断

加 1 ms 软件消抖,防止机械抖动误判


75. 驱动步进电机

c

复制

复制代码
// 定时器 1 kHz 中断,四拍
const uint8_t seq[8] = {0x09,0x0A,0x06,0x05};
HAL_GPIO_WritePort(GPIOA, seq[stepIdx++] & 0xF);

电流大用 DRV8825,加斜率控制降噪


76. PID 控制器

c

复制

复制代码
typedef struct{
    float Kp, Ki, Kd;
    float err, integral, prev;
} PID;
float PID_Update(PID *p, float set, float fb){
    float e = set - fb;
    p->integral += e;
    float out = p->Kp*e + p->Ki*p->integral + p->Kd*(e - p->prev);
    p->prev = e;
    return out;
}

采样周期固定,积分限幅+抗饱和


77. 跑 FreeRTOS

  1. CubeMX 勾选 FreeRTOS,选 CMSIS_V2。

  2. 默认堆 10 KB,任务栈 128 word

  3. 生成后 osKernelStart() 自动调 vTaskStartScheduler()


78. FreeRTOS 任务状态

  • Running

  • Ready

  • Blocked(等事件/队列)

  • Suspended(显式挂起)

  • Deleted(待清理)


79. IPC 机制

  • 信号量:资源计数。

  • 互斥量:优先级继承,防反转。

  • 消息队列 :拷贝数据,解耦 ISR 与任务


80. 避免优先级反转

  1. 互斥量(自带优先级继承)。

  2. 天花板优先级:任务先升优先级再进临界区。


81. 内存堆碎片

  • 使用 heap_4.c(带合并算法)。

  • 静态分配 xTaskCreateStatic()

  • 应用层对象池


82. CubeMX 生成代码

图形化配外设 → 自动生成:

  • 时钟树

  • GPIO 初始化

  • HAL/LL 句柄

  • FreeRTOS 任务框架

不要把用户代码写到 "USER CODE BEGIN" 之外,否则重新生成被覆盖。


83. HAL vs LL

表格

复制

HAL LL
抽象度 寄存器级
可移植
效率 高(单周期)
代码体积

中断频繁建议 LL+DMA


84. HAL 三种模式

  • 轮询:阻塞,简单。

  • 中断:后台进行,需回调。

  • DMA :零拷贝,最省功耗


85. 超时错误处理

c

复制

复制代码
if(HAL_UART_Transmit(&huart1, buf, len, 100) != HAL_OK){
    // 超时 100 ms
    HAL_UART_Abort(&huart1);
}

86. 自定义 HAL 回调

stm32f4xx_hal_msp.c 里重写:

c

复制

复制代码
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart){
    if(huart->Instance==USART1) LED_Toggle();
}

87. SWD 调试

  • 2 线:SWDIO + SWCLK,占脚少

  • 速度 4 MHz,下载 256 KB 仅需 2 s

长线加 22 Ω 串阻,防信号反射


88. 断点类型

  • 软件断点 :改 Flash 插 0xBE00,数量无限制

  • 硬件断点 :内核 COMP 寄存器,M4 仅 6 个

  • 数据观察点 :监测某地址读写,抓野指针神器


89. 串口打印调试

c

复制

复制代码
#define DEBUG_UART &huart1
int __io_putchar(int ch){
    HAL_UART_Transmit(DEBUG_UART, (uint8_t*)&ch, 1, 10);
    return ch;
}
printf("Temp=%.2f\r\n", temp);

封装 macro 开关,量产关闭减体积


90. ITM 调试

SWO 引脚输出,0 开销 printf ,速度 2 Mbit/s。

Keil 里勾选 Use ITM,代码:

c

复制

复制代码
ITM_SendChar('A');

可插 32 路虚拟端口,不占 UART


91. 测量代码执行时间

c

复制

复制代码
uint32_t t0 = DWT->CYCCNT;
run_my_algo();
uint32_t cycles = DWT->CYCCNT - t0;
float us = cycles / 168.0f;  // 168 MHz

先开 CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;


92. 低功耗模式

表格

复制

模式 电流 唤醒源 上下文
Sleep mA 级 任何中断 保持
Stop 10-50 µA EXTI、RTC、NRST SRAM 保持
Standby 1-2 µA RTC、WKUP、NRST 丢失,复位

93. 低功耗下仍工作外设

  • LSE、LSI

  • RTC、独立看门狗

  • WKUP 引脚

  • 部分 USART(低功耗 UART)


94. 功耗优化技巧

  • 降主频:168 → 16 MHz,电流降 60 %

  • 关 GPIO 模拟输入:每脚省 20 µA

  • Run-Stop 交替占空比 1 % 平均 50 µA。


95. 复位 & 晶振电路

  • NRST 引脚 100 nF→地10 kΩ→VDD防误复位

  • HSE 晶振 8 MHz负载电容 12-20 pF走线差分、包地、远离高频


96. 去耦电容

  • 0.1 µF + 4.7 µF 并联,环路面积 < 1 cm²

  • 降低 高频瞬态电流防止电源塌陷导致内部复位


97. PCB 模拟/数字分区

  • 模拟地 AGND 单独画,单点接地

  • ADC 走线 远离数字高速线包地+ guard ring

  • 供电 π 型滤波(磁珠 + 电容)。


98. 程序跑飞原因

  • 数组越界

  • 中断向量表未重定位

  • 时钟未配置即跑 Flash

  • 堆栈溢出

  • 看门狗未喂

  • 外设指针未开时钟


99. 看门狗增强可靠性

  • IWDG 在初始化早期就启动窗口值 80 %

  • 关键任务置标志主循环统一喂狗可检测死循环


100. 固件升级方案

  1. Bootloader + 双 Bank(H7 硬件支持)。

  2. YMODEM/XMODEM 串口升级。

  3. USB DFU (Device Firmware Update),PC 免驱动

  4. OTA :Wi-Fi/4G 下 断点续传 + CRC32 + 版本回滚

量产时 加密签名 (AES-CTR + ECDSA),防篡改


------ 完 ------

祝面试通关,源码常伴!

相关推荐
清风66666615 小时前
基于单片机与DAC0832的双路波形信号发生系统设计
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
azwsm16 小时前
电路元器件和GPIO控制器
单片机·嵌入式硬件
kebidaixu19 小时前
FreeRTOS 移植到 STM32F407VETX 记录(一)
stm32·单片机·嵌入式硬件
CSDN官方博客20 小时前
「谁说嵌入式只是调包和焊板子?」—— 2026嵌入式全栈技术征锋令
嵌入式硬件·物联网·embedding
点灯小铭20 小时前
基于单片机的数码管定时插座设计与定时开关功能实现
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
云栖梦泽21 小时前
玩转RK3506SDK
linux·嵌入式硬件
数智工坊1 天前
机器人四大主控板系统分层选型指南:树莓派、ESP32、STM32与Arduino的能力边界与实战定位
stm32·嵌入式硬件·机器人
进击的小头1 天前
第8篇:IGBT 从零到精通:核心原理、关键参数、选型指南与工业级应用要点
经验分享·嵌入式硬件·学习
点灯小铭1 天前
基于单片机的多模式智能洗衣机设计
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
项目題供诗1 天前
STM32-AD单通道&AD多通道(十九)
stm32·单片机·嵌入式硬件