当前位置: 首页 > news >正文

嵌入式编程学习日记(一)——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 的值。正常情况几乎不用

http://www.jsqmd.com/news/720963/

相关文章:

  • 算法工程师效率工具:用 OpenClaw 自动生成数据集预处理代码、实验报告、调参日志整理
  • Meta、HuggingFace等大佬联手搞的GAIA基准测试,到底在测什么?GPT-4为啥才15%?
  • 实测 DeepSeek V4:为什么真正决定 Coding Agent 上限的,往往不是模型,而是 Harness Engineering
  • 双碳目标下的智慧园区:数字化如何赋能绿色高效运营
  • 【第26期】2026年4月29日 AI日报
  • Windows下用清华源5分钟搞定ONNX全家桶(含CUDA版本匹配避坑指南)
  • 保姆级教程:图形验证码后端核验全流程(多语言实现)
  • Winhance中文版:让你的Windows系统飞起来的免费优化神器
  • 3分钟解锁QQ音乐加密文件:qmcdump终极解密指南
  • 【助睿ETL】实验作业1——订单利润分流数据加工
  • Henghao恒浩HH温度开关原厂一级代理分销经销
  • 揭秘导师不会说:6款AI论文神器,效率飙升200%从此告别拖延 - 麟书学长
  • 在家用显卡上也能生成720P高清视频:Wan2.2-TI2V-5B实战指南
  • YOLO已经不够了:为什么自动驾驶开始转向BEV? ——从“看见物体”到“理解空间”的一次升级
  • Web运行
  • Vue3 + 高德地图JS API v2:手把手教你实现一个带进度条和倍速控制的车辆轨迹回放组件
  • 2025届必备的五大降重复率助手解析与推荐
  • 告别丑图:MapChart 2.32从安装到高级绘图(共线性、LOD曲线)全攻略
  • 定义“具身智造”新范式,海康机器人助推制造业全面升维
  • 我为什么一直看好 RustFS?Beta 发布后,我的判断更坚定了
  • 【Java结构化梳理】泛型-初步了解-上
  • 从‘卖软件’到‘管软件’:一个轻量级License授权系统如何帮你搞定私有化部署后的客户管理
  • 五种IO模型与⾮阻塞IO
  • Python的__complex__库兼容
  • 解决macOS视频缩略图生成效率问题:QuickLookVideo高级配置指南
  • ChampR终极指南:免费开源英雄联盟助手,一键配置出装符文
  • ST Motor Control WorkBench6.4.2 FOC控制代码生成
  • 嵌入式开发自动化:用 OpenClaw 实现交叉编译环境配置、固件版本管理、烧录脚本批量生成
  • 如何快速搭建本地语音识别系统:高效隐私保护的完整指南
  • 全排列问题DFS实现执行示意图