一、core文件夹
存储上电后第一个执行的文件,负责初始化堆栈、中断向量表、跳转到main()。标准库工程里这个文件是固定的,别动它。
二、FWLIB文件夹
存储 STM32 官方提供的标准外设库(固件库),里面包含所有外设的驱动文件(如stm32f4xx_gpio.c、stm32f4xx_usart.c、stm32f4xx_tim.c等)。这些是官方提供的底层库文件,修改会导致各种不可预期的错误,也会影响后续库的升级。
三、CMSIS文件夹
存储ARM Cortex-M 内核的标准支持包,包含内核寄存器定义、NVIC 配置、编译器相关的头文件。是工程的基础依赖文件,不需要修改。
四、README文件夹
存储工程模板的说明文档,告诉你模板的使用方法、注意事项。不用改,只是参考说明。
五、SYSTEM文件夹
一、文件夹介绍
| 文件 | 作用 | 是否需要修改 |
|---|---|---|
sys.c | 系统底层配置,比如位带操作、中断优先级分组、时钟使能封装 | ⚠️基本不用改,除非你要调整中断分组或时钟策略 |
delay.c | 延时函数(微秒 / 毫秒级延时),基于 SysTick 实现 | ⚠️不用频繁改,若需要调整延时精度可修改 |
usart.c | 串口初始化和printf重定向,方便串口调试打印 | ⚠️首次配置波特率时修改一次,后续不用动 |
二、可以变动的部分
1、delay.c
delay中的代码绝大多数不能修改,但是有少部分的代码可以修改。接下来一一列举并解析其作用:
1.delay_init 传入的时钟参数(最常改)
delay_init(168); // 168 = 系统时钟 168MHz- 作用:告诉延时函数你的单片机主频是多少
- 改了会怎样:
- 改成 84 → 延时时间会变长一倍
- 改成 100 → 延时不准
- 什么时候改:你换了主频、换了芯片、超频时才改
- 正常情况保持芯片的主频(STM32F4 默认168)
2.SysTick 时钟源(不建议改,但能改)
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);- 作用:设置 systick 时钟 = 主频 / 8
- 能改成
SysTick_CLKSource_HCLK // 不分频,直接用主频 - 改了作用:延时精度更高,但计时范围变小。以STM32F4为例,本来计数器可以计数
次的时长,现在仅能计数
次的时长,即次数未变,但是前面时间更长。
3.uCOS 延时节拍(用操作系统才改)
#define OS_TICKS_PER_SEC 1000 // 1ms 一个节拍在includes.h或os_cfg.h
- 作用:设置 uCOS 系统时钟节拍
- 改了作用:
- 改 100 → 10ms 一个节拍
- 改 500 → 2ms 一个节拍
- 不用 uCOS 完全不用管
4.delay_ms 最大单次延时(可改)
u8 repeat=nms/540;这里 540 代表单次最大延时 540ms
- 作用:防止溢出
- 能改:比如改成 500、400 都可以
- 改了作用:只是分段方式变了,不影响延时精度
5.fac_us、fac_ms(底层参数,懂了才能改)
fac_us=SYSCLK/8; fac_ms=(u16)fac_us*1000;- 作用:计算 1us / 1ms 需要多少个时钟周期
- 改了会直接导致延时不准
- 新手绝对不要改
公式:SysTick时钟频率 = 系统主频 / 8
以STM32F4为例,168M / 8 =21MHz
代表:SysTick 一秒跳动 21000000 次
1微秒跳动次数 = 21000000 / 1000000 =21次
仅针对无UCOS场景,你的源码公式:
fac_ms=(u16)fac_us*1000;含义:1ms = 1000us,1毫秒需要的节拍数 = 微秒节拍 × 1000
以STM32F4为例,带入数值:21 × 1000 =21000
2、usart.c
1. printf 功能总开关(低频可改)
#if 1- 作用:开启或关闭串口printf重定向功能,决定工程能否使用printf打印调试信息。
- 改了会怎样:
改成 #if 0 → 关闭printf功能,节省闪存空间,无法串口打印调试
保持 #if 1 → 开启printf,支持串口打印调试,最常用
- 什么时候改:项目最终成品、不需要调试打印时关闭;开发调试阶段保持开启
- 正常情况:默认1,开启printf调试功能
2. 串口初始化波特率(最常改)
uart_init(u32 bound)- 作用:设置串口通信波特率,决定串口数据传输速率,是串口通信匹配上位机的核心参数。
- 改了会怎样:
改成9600/38400:通信速度慢,抗干扰更强,适合远距离通信
改成115200:通信速度快,调试最常用,近距离标准波特率
和串口助手波特率不匹配:全部数据乱码、收发失败
- 什么时候改:上位机波特率变更、适配传感器串口波特率、通信距离变化时修改
- 正常情况:默认115200,通用调试波特率
3. 串口停止位配置(低频可改)
USART_InitStructure.USART_StopBits = USART_StopBits_1;- 作用:设置一帧数据的停止位位数,用于数据帧同步,提升通信稳定性。
- 能改成:
USART_StopBits_2 // 2位停止位
- 改了会怎样:
1位停止位(默认):传输速度快,近距离稳定,适合板载调试
2位停止位:数据帧同步更稳定,抗干扰强,降低丢包概率,传输速度略低
- 什么时候改:串口长线通信、工业干扰环境、外设要求2位停止位时修改
- 正常情况:默认1位停止位
4. 串口校验位配置(低频可改)
USART_InitStructure.USART_Parity = USART_Parity_No;- 作用:开启/关闭数据校验,检测串口传输数据是否出错,保障通信可靠性。
- 能改成:
USART_Parity_Even /*偶校验*/ USART_Parity_Odd //奇校验- 改了会怎样:
无校验(默认):传输效率最高,速度最快,普通调试足够使用
增加奇偶校验:可以校验传输错误,数据更安全,但收发双方必须一致,否则全部报错
- 什么时候改:精密数据采集、传感器通信、工业串口设备通信时修改
- 正常情况:默认无校验
5. 串口中断优先级(中频修改)
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3; NVIC_InitStructure.NVIC_IRQChannelSubPriority =3;- 作用:设置串口中断的抢占优先级、子优先级,用于多中断共存时,决定中断执行顺序。
- 改了会怎样:
数值改小(0/1):优先级变高,优先响应串口中断,不易丢数据
数值改大:优先级变低,容易被定时器、外部中断抢占,导致串口丢包
- 什么时候改:工程存在多个中断、出现串口数据丢失、中断抢占冲突时修改
- 正常情况:默认3,普通调试无冲突
6. 串口接收缓冲区大小(中频修改)
#define USART_REC_LEN 200- 作用:定义串口单次接收数据最大字节数,限制接收缓存数组大小。
- 改了会怎样:
调大数值:可以接收更长的报文、大数据帧,占用更多内存
调小数值:节省单片机内存,无法接收长数据报文,过长数据会丢失截断
- 什么时候改:接收传感器长数据、上位机长指令时调大;简单指令调试可以调小
- 正常情况:默认200,满足绝大多数调试场景
7. 串口接收中断总开关(中频修改)
#define EN_USART1_RX 1- 作用:开启或关闭串口接收中断功能,控制单片机是否可以接收上位机数据。
- 改了会怎样:
为1:开启接收中断,既能发数据也能收数据,双向通信
为0:关闭接收中断,仅能printf发送,无法接收上位机指令
- 什么时候改:只需要打印调试、不需要接收指令时关闭;需要串口交互、设备控制时开启
- 正常情况:默认开启
三、调用方法
一、delay.c 在 main 中的调用方法
1. 初始化(必须放在最前面)
delay_init(168);作用:初始化延时模块,告诉系统主频是 168M。
2. 毫秒延时
delay_ms(500);作用:延时 500ms,可填 1~65535。
3. 微秒延时
delay_us(10);作用:延时 10us,用于精密时序、驱动模拟。
二、sys.c 在 main 中的调用方法
1. 关闭全部中断
INTX_DISABLE();2. 开启全部中断
INTX_ENABLE();3. 进入休眠模式
WFI_SET();三、usart.c 在 main 中的调用方法
1. 串口初始化
uart_init(115200);作用:打开串口 1,波特率 115200。
2. 串口打印数据(直接用)
printf("Hello World!\r\n");printf("温度 = %d\r\n", temp);3. 判断串口接收完成
if(USART_RX_STA & 0x8000) { // 收到一帧数据 printf("收到数据:%s\r\n", USART_RX_BUF); USART_RX_STA = 0; }六、USER文件夹(用户主战场)
一、文件夹介绍
| 文件 | 作用 | 是否需要修改 |
|---|---|---|
main.c | 程序入口,所有业务逻辑、初始化调用的核心 | ✅高频修改 |
stm32f4xx_it.c | 所有外设的中断服务函数(定时器、串口、外部中断等) | ✅用到哪个中断就改哪个 |
system_stm32f4xx.c | 系统时钟初始化文件,配置单片机主频(如 168MHz) | ⚠️配置时钟时修改一次,之后基本不动 |
二、可以修改的部分
1、system_stm32f4xx.c
1. 外部晶振频率 HSE_VALUE(最常改!)
位置:stm32f4xx.h中(本文件依赖它)本文件关联:SetSysClock()计算时钟
作用:告诉单片机你的外部晶振是多少 MHz(常见 8M / 25M)。
改了会怎样:
- 你的板子是8M 晶振→ 必须改成8000000
- 你的板子是25M 晶振→ 必须改成25000000
- 写错 →系统时钟跑飞、主频错误、串口乱码、延时不准
什么时候改:换不同硬件板子必须改
正常情况:根据你开发板晶振填写(正点原子 F407 通常是8MHz)
2. PLL_M 分频系数(高频修改)
#define PLL_M 8作用:对外部晶振分频,给 PLL 提供稳定输入时钟。
改了会怎样:
- 晶振 8MHz →PLL_M = 8
- 晶振 25MHz →PLL_M = 25
- 填错 →主频直接错误,系统不工作
什么时候改:必须和外部晶振匹配
3. PLL_N 倍频系数(中频修改)
#define PLL_N 336作用:PLL 核心倍频,决定系统能跑到多高频率。
改了会怎样:
- F407 标准主频:336 → 168MHz
- 改小 → 降频、降低性能、省电
- 改大 → 超频、不稳定、容易死机
什么时候改:降频省电、超频测试时修改。
正常情况:保持336(168M 最稳定)
4. PLL_P 分频系数(低频修改)
#define PLL_P 2作用:最终分频,决定系统主频 SYSCLK。
改了会怎样:
- PLL_P=2 → 168MHz
- PLL_P=4 → 84MHz
什么时候改:需要降频到 84MHz 时改。
正常情况:保持2。
5. PLL_Q 分频系数(USB 专用)
#define PLL_Q 7作用:生成 USB / SDIO / RNG 时钟(必须 = 48MHz)。
改了会怎样:
- 改对 → USB 正常
- 改错 → USB 不能用
什么时候改:使用 USB 功能时必须校准。
正常情况:保持7。
6. AHB 预分频器(几乎不改)
RCC_CFGR_HPRE_DIV1作用:HCLK 系统时钟分频。
改了会怎样:
- DIV1 → 168M
- DIV2 → 84M降低系统性能,减少功耗。
什么时候改:低功耗项目。
正常情况:DIV1。
7. APB1 / APB2 预分频器(不改)
RCC_CFGR_PPRE1_DIV4 RCC_CFGR_PPRE2_DIV2作用:外设时钟分频(APB1 最大 42M,APB2 最大 84M)。
改了会怎样:
- 改错 → 定时器、串口时钟错误
- 外设驱动全部异常
什么时候改:几乎不改。
正常情况:保持默认。
8. 中断向量表偏移 VECT_TAB_OFFSET(Bootloader 专用)
#define VECT_TAB_OFFSET 0x00作用:设置中断向量表存放位置。
改了会怎样:
- 0x00 → 正常程序
- 非 0 → IAP 升级、Bootloader 跳转
什么时候改:做 Bootloader、双程序区时。
正常情况:保持0x00。
9. 向量表位置 VECT_TAB_SRAM(极少改)
/* #define VECT_TAB_SRAM */作用:把中断向量表放到 RAM 里。
改了会怎样:
- 打开 → 中断放 RAM
- 关闭 → 中断放 Flash(默认)
什么时候改:高级动态更新中断时。
正常情况:关闭。
10. 外部 SRAM / SDRAM 宏(极少改)
/* #define DATA_IN_ExtSRAM */ /* #define DATA_IN_ExtSDRAM */作用:使用外部内存时开启。
改了会怎样:
- 开启 → 初始化 FMC 接口
- 关闭 → 不使用外部 RAM
什么时候改:板子带外部 RAM 时。
正常情况:关闭。
11. SystemCoreClock 系统主频(自动计算,一般不改)
uint32_t SystemCoreClock = 168000000;作用:保存当前系统主频 HCLK。
改了会怎样:
- 错误填写 → SysTick、延时、RTOS 时钟全部错误
什么时候改:不要手动改,由SystemCoreClockUpdate()自动更新。
2、stm32f4xx_it.c
1. SysTick_Handler 系统滴答中断(最常改)
void SysTick_Handler(void) { }作用:系统定时中断,1ms 进入一次,用于延时、系统时钟、操作系统心跳。
改了会怎样:
- 里面写定时逻辑 → 每隔 1ms 自动执行
- 不写 → 不使用系统滴答定时
- 如果用 UCOS/FreeRTOS → 必须在这里写系统调度
什么时候改:需要定时任务、延时、系统心跳时加代码。
正常情况:可空,可加定时计数。
2. HardFault_Handler 硬件错误中断(高频调试修改)
void HardFault_Handler(void) { while(1); }作用:程序崩溃、数组越界、指针错误、地址非法访问时进入。
改了会怎样:
- 默认死循环 → 程序死机重启
- 加打印 / 重启代码 → 死机后自动重启或上报错误
- 加调试代码 → 快速定位死机位置
什么时候改:调试程序崩溃、死机、重启问题时。
3. USART1_IRQHandler 串口 1 中断(必须在这里声明)
注意:你在 usart.c 里已经写了,这里不能重复!
作用:如果 usart.c 没有写中断服务函数,就必须在这里写。
改了会怎样:
- 不写 → 串口接收中断失效
- 重复写 → 编译报错
什么时候改:不用改,保持注释即可。
4. NMI_Handler 非屏蔽中断(几乎不改)
作用:极端紧急硬件中断(时钟故障)。
改了会怎样:一般不改,改了也用不上。
5. MemManage_Handler 内存管理异常(不改)
作用:内存访问违规进入。
改了会怎样:默认死循环,调试时可加重启。
6. BusFault_Handler 总线异常(不改)
作用:硬件总线错误。
7. UsageFault_Handler 使用异常(不改)
作用:执行非法指令。
8. SVC_Handler / PendSV_Handler(操作系统专用)
作用:UCOS/FreeRTOS 系统调度专用。
改了会怎样:
- 裸机:保持空即可
- 操作系统:必须写调度代码
9. DebugMon_Handler 调试中断(不改)
作用:调试器断点使用。
10. PPP_IRQHandler 外设中断模板(可添加)
/*void PPP_IRQHandler(void) { }*/作用:添加定时器、SPI、I2C、ADC 等中断函数。
改了会怎样:
- 去掉注释 + 写代码 → 对应外设中断生效
什么时候改:使用新外设中断时添加。
三、调用方法
system_stm32f4xx.c
可调用 / 可使用的只有 2 个
1. SystemInit()
调用方式:不需要在 main 里写!作用:开机自动配置系统时钟(168MHz)。注意:启动文件自动调用,用户永远不用手动调用。
2. SystemCoreClock
调用方式:直接当变量用
SystemCoreClock作用:存放当前系统主频(例如 168000000)。用途:计算时钟、串口、定时器时使用。可在 main 里直接读。
3. SystemCoreClockUpdate()
调用方式:
SystemCoreClockUpdate();作用:时钟改变后,更新 SystemCoreClock 的值。正常情况几乎不用。