news 2026/4/29 20:24:24

嵌入式编程学习日记(一)——C语言篇(文件分析库函数版)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
嵌入式编程学习日记(一)——C语言篇(文件分析库函数版)

一、core文件夹

存储上电后第一个执行的文件,负责初始化堆栈、中断向量表、跳转到main()。标准库工程里这个文件是固定的,别动它。

二、FWLIB文件夹

存储 STM32 官方提供的标准外设库(固件库),里面包含所有外设的驱动文件(如stm32f4xx_gpio.cstm32f4xx_usart.cstm32f4xx_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.hos_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 的值。正常情况几乎不用

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/29 20:21:36

AI无所不能的时代,我们该学什么?

原文:AI小揭秘 当AI能写代码、作诗歌、解难题,很多人开始迷茫:“我们的学习还有意义吗?” 在这个AI狂飙的时代,何楚涵博士的一场演讲,或许能为我们拨开迷雾——AI再厉害,也替代不了人的思考、温…

作者头像 李华
网站建设 2026/4/29 20:14:39

如何快速掌握TMD Matlab Toolbox:潮汐模型驱动完整指南

如何快速掌握TMD Matlab Toolbox:潮汐模型驱动完整指南 【免费下载链接】TMD_Matlab_Toolbox_v2.5 项目地址: https://gitcode.com/gh_mirrors/tm/TMD_Matlab_Toolbox_v2.5 TMD Matlab Toolbox v2.5(潮汐模型驱动工具箱)是Earth and …

作者头像 李华
网站建设 2026/4/29 20:13:34

LLM代理安全防御系统AGENTSYS的内存管理创新

1. 项目概述 AGENTSYS是一个针对大型语言模型(LLM)代理的安全防御系统,其核心创新点在于通过精细化的内存管理机制来提升LLM代理的安全性。这个系统解决了当前LLM代理在运行过程中面临的两大核心挑战:一是内存使用缺乏有效隔离导致…

作者头像 李华
网站建设 2026/4/29 20:13:26

ABC-Bench:大语言模型全生命周期评估框架解析

1. 项目背景与核心价值ABC-Bench的诞生源于当前大语言模型(LLM)评估体系中的一个关键缺口——现有基准测试大多集中在代码生成或单点任务上,而忽略了真实后端开发中从需求分析到系统维护的全流程验证。我在参与多个企业级LLM落地项目时发现&a…

作者头像 李华