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),防篡改


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

祝面试通关,源码常伴!

相关推荐
三品吉他手会点灯2 小时前
STM32F103学习笔记-16-RCC(第3节)-使用HSE配置系统时钟并使用MCO输出监控系统时钟
c语言·笔记·stm32·单片机·嵌入式硬件·学习
朱嘉鼎3 小时前
GPIO中断编程
单片机·嵌入式硬件
straw_hat.3 小时前
32HAL——万年历
stm32·单片机·学习
敬往事一杯酒哈3 小时前
第3节 STM32 串口通信
stm32·单片机·嵌入式硬件
XH1.4 小时前
学习RT-thread(项目一:基于RT-thread的multi_button控制灯闪烁)
stm32·单片机·学习
Jerry丶Li5 小时前
二十五、STM32的DMA(数据转运)
stm32·单片机·嵌入式硬件
李boyang6 小时前
I2C通信
单片机·嵌入式硬件·i2c
d111111111d7 小时前
STM32外设学习--ADC模数转换器--笔记
笔记·stm32·单片机·嵌入式硬件·学习
xiaotianyun889 小时前
NCP13992中文版手册 工作原理
单片机·嵌入式硬件·ncp13992