stm32逐步开发

使用cubemx生成初始化工程

选择MCU型号

打开 STM32CubeMX 后,依次点击工具栏中的 “File” -> “New Project …” 或通过快捷键 Ctrl + N 来新建一个 STM32CubeMX 工程,软件会打开一个选择 MCU 型号的界面

在界面左侧添加 你所需要的开发板芯片型号 的信息,然后在界面右侧找到并选中 该型号,然后点击右上角的 Start Project

配置工程信息

进入配置界面后,打开 Project Manager 配置工程的基础信息

Project Structure:选择需要的工程结构,这里仅勾选 Appli

Project Structure介绍

FSBL:FSBL(First Stage Boot Loader)工程主要用于开发 FSBL,FSBL 是 STM32N6 结束硬件复位后自动从外部 Flash 加载到内部 SRAM 并运行的第一段用户程序,主要完成相应的初始化工作,并启动 Appli 程序。

Appli:Appli 工程主要用于开发用户的应用程序,应用程序主要用于实现用户功能。

ExtMemLoader:ExtMemLoader 工程主要用于开发 External loader,External loader 主要用于 STM32CubeProgrammer、STM32CubeIDE 等工具访问、操作 MCU 外部 Flash

Toolchain / IDE:选择所使用的工具链或 IDE,这里选择 STM32CubeIDE,并取消勾选Generate Under Root,因为后续需要使用 STM32CubeIDE 打开工程。

MCU引脚和外设配置

接下来打开Pinout&Configuration配置MCU引脚和外设

Cache 管理

概述

STM32N6 使用 Cortex-M55。
这类内核为了提高性能,会使用数据缓存 D-Cache
CPU 访问内存时,并不总是直接去 SRAM 取数据,很多时候先访问 Cache。

这本身是性能优化,但只要系统里用了 DMA,就会带来一个非常典型的问题:

CPU 看到的数据,不一定和 DMA 刚写入的数据一致

这引入了Cache管理的需求

原因在于 CPU 和 DMA 访问内存的路径不一样。

  • CPU 常常先走 Cache
  • DMA 直接访问物理 SRAM

这很可能导致数据处理和发送时都使用了旧数据
在实时的检测时会存在延时和数据不更新的问题

解决

DMA 接收完成后,CPU 读取前要失效,对应 Cache

也就是常说的 Invalidate
意思是告诉 CPU缓存内容不可信,下次重新从 SRAM 读取

invalidate是在DMA接收完成后CPU读取前,典型位置在ADC/SPI的DMA回调里面

1
2
3
4
5
6
7
8
volatile uint8_t adc_ready = 0;

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
dcache_invalidate_range(adc_buf, sizeof(adc_buf));
adc_ready = 1;
}

适用场景:

  • ADC DMA 接收完成
  • SPI DMA 接收完成
  • 某块接收缓冲区即将交给 DSP 处理

CPU 修改完待发送数据后,DMA 启动前要回写 Cache

也就是常说的 Clean
意思是把 Cache 里的最新数据强制同步回 SRAM,CPU 改完发送缓冲后,先 Clean,再启动 DMA 发送

1
2
3
4
5
void send_frame_dma(void)
{
dcache_clean_range(tx_buf, sizeof(tx_buf));
HAL_UART_Transmit_DMA(&huart1, tx_buf, sizeof(tx_buf));
}

适用场景:

  • CPU 算完一帧特征后要发出去
  • CPU 组织好一段 SPI 发送缓冲后交给 DMA

给 DMA 缓冲区单独留非缓存区

如果项目里 DMA 缓冲区固定,而且你不想每次都手工刷 Cache,可以考虑 MPU 方案:

  • 划一块 SRAM
  • 配成 Non-Cacheable
  • 把所有 DMA 交互缓冲区都放进去

这样做的好处是逻辑更稳定:

  • DMA 写进去,CPU 读到的就是实际内存
  • CPU 写进去,DMA 读到的也是实际内存

代价是:

  • 这块区域访问性能会差一些
  • 内存规划要提前做

32b对齐

CPU 读写 Cache 时,并不像很多人想象的那样“一次只搬 1 个字节”或“一次只搬 1 个 int”,这太慢了。
Cache 的管理是按块(Block)进行的,这个块就是 Cache Line。

Cache line,Cache的最小单位,有32字节,当 CPU 想读取地址 0x00 处的一个字节时,硬件实际上会把从 0x00 ~ 0x1F全部从 SRAM 一口气打包拉进 Cache 里
因此刷新invalidate的时候会刷新32字节的区域,所以DMA buffer最好和32b对齐

如果不对齐,可能造成数据存储的挤压和混乱,导致清除缓存的时候把某个文件的头和尾也清洗掉了

1
2
__attribute__((aligned(32)))
uint16_t adc_buffer[256];

算法buffer要尽量连续,这样cache命中率高
由于 Cache 的工作原理是“要一个字,给你32字节”。

如果光谱特征数组像链表、散列或者是东挑一块西找一块的数据。CPU 每次去 SRAM 抓 32 字节上来,结果你只用 1 个,下次又要去另一个远处的地址抓新 Cache,这叫**“Cache 命中率低”**,反复读 SRAM 极大拖慢性能。
如果你的滤波数组是一大块连续的浮点或定点大容量数组(数组天然是内存连续的),CPU 读第一个数时,后边的几个数也顺带被打包拉进了超快的 Cache 里。后面的读取瞬间变成内部光速响应,这就是“命中率高”,也是 CMSIS 算法能跑飞快的原因。

  • CPU 为什么会优先读 Cache

  • DMA 为什么绕过 Cache 直接访问 SRAM

CORTEX_M55_S 配置页中,将 CPU ICacheCPU DCache 都配置为 Enable

(此处留白)

使用cubeide进行开发

打开main.c编辑main函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
int main(void)
{

/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

/* Enable the CPU Cache */

/* Enable I-Cache---------------------------------------------------------*/
SCB_EnableICache();

/* Enable D-Cache---------------------------------------------------------*/
SCB_EnableDCache();

/* MCU Configuration--------------------------------------------------------*/

/* Update SystemCoreClock variable according to RCC registers values. */
SystemCoreClockUpdate();
HAL_Init();

/* USER CODE BEGIN Init */

/* USER CODE END Init */

/* Update SystemCoreClock variable */
SystemCoreClockUpdate();

/* USER CODE BEGIN SysInit */

/* USER CODE END SysInit */

/* Initialize all configured peripherals */
SystemIsolation_Config();
MX_GPIO_Init();
/* USER CODE BEGIN 2 */

/* USER CODE END 2 */

/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
HAL_GPIO_TogglePin(GPIOG, GPIO_PIN_10);
HAL_Delay(200);
HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_10);
HAL_Delay(200);
/* USER CODE END WHILE */

/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}

上述代码中,实现了跑马灯效果的功能

对于 STM32CubeMX 生成的代码,强烈建议仅在 xxx CODE BEGIN xxx 和 xxx CODE END xxx 之间添加或修改代码,否则在后续使用 STM32CubeMX 重新生成代码时,添加或修改的代码会被覆盖。


stm32逐步开发
https://rubbishbro.github.io/2026/03/24/stm32-stepbystep/
Author
John Doe
Posted on
March 24, 2026
Licensed under