【嵌入式学习笔记】工程模板建立

前言

本系列学习笔记是本人跟随米醋电子工作室学习嵌入式的学习笔记,自用为主,不是教学或经验分享,若有误,大佬轻喷,同时欢迎交流学习,侵权即删。

一、基本概念

什么是嵌入式开发?

嵌入式开发是为特定目的而设计的计算系统编写软件的过程。这些系统通常具有受限的资源(处理能力、内存、能源等),并且需要可靠地执行特定任务。在嵌入式开发中,有两种主要的编程范式:裸机编程和基于调度器的开发。

1.裸机执行

控制流:线性执行

任务切换:无

执行模式:阻塞式

特点:简单、可预测

比喻理解

裸机开发:单车道公路

想象一条单车道公路,车辆必须按照严格的顺序依次通过。这就像裸机编程:

顺序执行:就像车辆必须一辆接一辆地通过,裸机代码也是按照固定顺序一步步执行

阻塞问题:如果一辆车停下来(例如任务卡住),后面所有车辆都必须等待

简单可靠:单车道设计简单,没有车辆需要变换车道,不会发生碰撞

2.调度器执行

  • 优先级驱动调度:会对任务划分高 / 中 / 低优先级(如 "无线通信" 为高优先级),优先处理高优先级任务。
  • 自动化任务切换:无需手动操作,系统自动完成不同任务之间的切换。
  • 支持并发执行:采用 "并发" 模式处理任务,提升 CPU 资源的利用效率。
  • 流程化管理:遵循 "任务创建→优先级分配→调度决策→上下文切换→任务执行" 的流程,有序推进任务处理。
  • 集中式优先级管理:调度器承担 "优先级管理" 职责,统一协调任务队列的执行顺序。

比喻理解

调度器开发:繁忙的餐厅

想象一家繁忙的餐厅,有一位餐厅经理(调度器)协调服务员和厨师的工作:

优先级处理:餐厅经理根据订单的紧急程度分配任务,如VIP客人的订单优先处理

资源分配:厨师(CPU)被分配给不同的任务,而餐厅经理确保资源得到最有效利用

任务切换:服务员可以在多个任务之间切换,如一个服务员可以同时照顾多桌客人

3.裸机开发与调度器开发的对比

对比维度 裸机开发 (Bare-Metal) 调度器开发 (Scheduler-Based)
定义 直接在硬件上编程,无操作系统 / 调度器支持;代码直接控制硬件资源,按严格顺序执行 使用实时操作系统 (RTOS) 或任务调度器管理多任务执行;由调度器决定任务的执行时机
优势 - 直接控制硬件:精确控制硬件资源,获得最佳性能- 低资源消耗:无调度器 / 操作系统的额外开销- 确定性执行:执行时间可精确预测,每次运行结果一致 - 多任务支持:可并发处理多个任务,类似多车道公路同时容纳多辆车- 优先级管理:重要任务可优先执行,类似紧急服务车辆的优先通行权- 资源管理:协调共享资源以避免冲突,类似餐厅经理协调厨房资源- 扩展性更好:新功能可作为独立任务添加,无需重构整个系统
劣势 - 复杂性增加:功能增多后管理困难,如同单人管理整条流水线- 难处理并发:顺序执行,一个任务阻塞会影响整个系统- 响应延迟:紧急任务需等待当前任务完成,如同急救车卡在单车道 - 额外开销:调度器本身消耗系统资源,如同交通管理系统需要额外设施- 机制复杂:需理解调度机制、任务优先级及同步等问题

二、工程模板搭建

1.时钟配置

1.1 时钟基本概念

在电子学和微控制器领域,时钟信号是一种周期性变化的电子信号,用于同步系统中的操作和数据传输。时钟信号通常是方波,并具有特定的频率,频率决定了系统的运行速度。

比方:时钟是乐队的节拍器

在微控制器中,时钟就像是乐队中的节拍器。节拍器以固定的频率发出滴答声,指导乐队成员的演奏节奏。同样,时钟信号以固定的频率引导微控制器内部各种操作的执行。

时钟的作用

同步操作

时钟信号用于同步微控制器内部的各种操作,例如CPU指令的执行、数据的读取和写入等。

确定运行速度

时钟频率直接影响系统的运行速度。较高的时钟频率意味着更快的处理速度,但也意味着更高的功耗。

驱动外设

许多外设(如定时器、USART、SPI等)需要时钟信号来工作。正确的时钟配置确保外设按照预期的速度运行。

1.2 时钟配置流程

配置时钟的主要步骤

1选择时钟源

确定使用内部时钟(HSI/LSI)还是外部时钟(HSE/LSE)。内部时钟提供便捷性,外部时钟提供更高的精度。(一般都是用外部)

2配置PLL

通过相位锁定环(PLL)调整时钟频率,生成所需的系统时钟。PLL能够倍频或分频输入时钟,产生更高或更低的频率。

补充:PLL的作用

本质上 PLL 是解决「基础时钟源频率无法满足系统与外设多样化需求」的核心桥梁,具体意义可以从这几个实用角度理解

核心意义可概括为 3 点:

  1. 倍频生成高频系统时钟,提升 STM32 芯片的运算效率;
  2. 分频产生多路精准时钟,适配不同外设的差异化需求;
  3. 兼顾时钟精度、硬件成本与功耗平衡,适配多样化应用场景。

3设置时钟树

配置系统时钟、AHB时钟、APB1时钟和APB2时钟,确保各外设时钟频率正确。这是分配时钟资源的关键步骤。

4使能外设时钟

为需要使用的外设使能时钟信号。只有使能了相应的时钟,外设才能正常工作。

2.SYS配置

SYS配置主要涉及调试接口的选择和设置,正确的配置可以确保芯片与调试器的稳定通信,是开发过程中的重要一环

常用调试接口

Serial Wire (SW-DP) (一般无脑选他)

  • **引脚需求:**仅需2个引脚 (SWCLK, SWDIO)

  • **适用于:**ST-Link调试器

  • **优势:**引脚占用少,速度快

  • **常用性:**使用频率高,推荐首选

SW调试接口(SW-DP)仅需要两个引脚:

  • **SWCLK:**主机到从机的时钟信号
  • **SWDIO:**双向数据信号

这种模式下,ST-Link工作最稳定,占用引脚少,是日常开发的首选。

JTAG (JTAG-DP)

  • **引脚需求:**4个引脚(4pin)或5个引脚(5pin,含复位)

  • **适用于:**J-Link等高级调试器

  • **优势:**功能更完整,支持高级调试

  • **常用性:**在复杂应用中使用

JTAG调试接口(JTAG-DP)需要4或5个引脚:

  • **TCK:**测试时钟
  • **TMS:**测试模式选择
  • **TDI:**测试数据输入
  • **TDO:**测试数据输出
  • **TRST:**测试复位(可选,5pin模式才有)

JTAG适合复杂的调试场景,尤其是在使用J-Link调试器时。

3.工程配置

工程配置是创建一个结构良好、可维护的嵌入式项目的关键步骤。它定义了项目的整体结构、代码生成方式和中间件配置等重要参数。

4.NVIC配置

中断控制与优先级管理

4.1为什么选择SysTick作为时间基准源

在STM32微控制器项目中,许多与计时相关的功能通常依赖于软件定时器,即默认的SysTick定时器。如果SysTick定时器的优先级设置过低,它将频繁地被其他任务抢占,导致计时精度下降和系统响应延迟。

原因分析

软件定时器依赖

许多计时功能(如`HAL_Delay()`函数和各种超时机制)依赖于SysTick定时器提供的时间基准。

优先级的重要性

SysTick定时器的中断优先级决定了它在系统中断和任务调度中的地位。如果优先级过低,其他高优先级的中断和任务将频繁抢占SysTick定时器,影响其计时准确性。

系统稳定性

为了确保系统的计时功能稳定运行,必须合理设置SysTick定时器的优先级,以避免频繁的中断抢占。

推荐配置

在一般应用中,建议将SysTick的优先级设置为较高级别(0-2),以确保系统时基的准确性。如果项目中有更关键的实时响应需求,可根据具体情况调整,但应保证SysTick不会频繁被抢占。

4.2 NVIC配置要点

1中断分组设置

根据应用需求,选择合适的中断分组方式,以平衡抢占优先级和子优先级的分配。常用配置为"4 bits for pre-emption priority, 0 bits for subpriority"。

补充:中断分组理解

  1. 中断分组的核心是拆分 4 位优先级寄存器,分配「抢占优先级」和「子优先级」的位数占比;
  2. 抢占优先级决定中断能否嵌套,子优先级决定同抢占等级中断的执行顺序;
  3. "4 位抢占、0 位子优先级" 是分组 4,支持最多 16 级抢占嵌套,适用于复杂中断场景。

2优先级分配原则

为不同的中断源分配合适的优先级,通常遵循"响应时间越严格,优先级越高"的原则。例如,安全相关的中断应具有高优先级,而后台处理任务可以分配较低的优先级。

3中断嵌套考虑

在设计中断处理系统时,需要考虑中断嵌套的可能性。过度使用中断嵌套可能导致栈溢出和系统不稳定,应当谨慎设计。

4中断处理函数优化

中断处理函数应尽量简短,只执行必要的操作。可以使用标志位方式,在主循环中处理耗时操作,减少中断处理的时间。

5.Keil配置

Keil MDK是STM32开发中常用的集成开发环境,了解其工程文件结构对项目开发和维护至关重要。本节将介绍STM32CubeMX生成的Keil工程主要文件和目录的作用。

.mxproject

包含项目的元数据和配置。它通常是由STM32CubeMX等工具生成的,用于描述工程的整体设置和生成代码的选项。

Core

包含核心代码,如主程序、系统初始化和中断服务例程等。包括主程序入口、系统初始化和中断处理代码。

Drivers

包含设备驱动程序,通常包括外设的初始化和控制代码,以及HAL/LL库的硬件抽象层。

MDK-ARM

用于存放ARM MDK (Keil) 集成开发环境相关的文件,包含工程文件如.uvprojx和.uvoptx,以及编译设置。

MyAPP

用户自定义的应用程序代码目录,用于存放用户编写的应用逻辑代码和功能模块实现。

Yz_V1.ioc

I/O配置文件,用于描述微控制器的引脚和外设配置,定义外设的初始化参数和工作模式。

补充知识点:头文件保护

"#ifndef SCHEDULER_H #define SCHEDULER_H"这是什么东西,为什么要这么干?

这是 C 语言中非常基础且核心的头文件保护机制(也叫 "防止头文件重复包含宏")

这组指令是 C 语言的预处理指令 (编译前先执行的指令,不参与最终代码运行),三者是成对出现的(#ifndef#endif必须配套,#define在中间):

指令 含义(通俗解释)
#ifndef SCHEDULER_H if not defined 的缩写,意思是 "检查SCHEDULER_H这个宏是否未被定义 "→ 若未定义:执行后续代码(直到#endif)→ 若已定义:直接跳过后续所有代码(直到#endif
#define SCHEDULER_H 定义SCHEDULER_H这个宏(无需给它赋值,仅作为 "标记" 使用),标记该头文件已被包含过
#endif 结束#ifndef的条件判断块,相当于 "条件判断的结束符"

简单类比:这就像图书馆的 "借书登记本"------

  1. #ifndef SCHEDULER_H:检查登记本上有没有 "scheduler.h 已借出" 的记录;

  2. 若没有记录(未定义):先做登记(#define SCHEDULER_H),再把书借给你(执行头文件内容);

  3. 若已有记录(已定义):直接拒绝重复借书(跳过头文件内容);

  4. #endif:登记流程结束。

为什么必须要加这组指令?(核心用途:解决 "头文件重复包含" 问题)

在 C 语言项目中,头文件(.h)被重复包含是很常见的情况(比如你的代码中就存在:scheduler.h包含mydefine.hmydefine.h又包含scheduler.h,这是 "循环包含";还有main.c同时包含mydefine.hscheduler.h,也会导致scheduler.h被重复包含),而重复包含会引发严重的编译错误,具体危害如下:

  1. 直接导致编译报错的场景
  • 类型 / 结构体重复定义 :如果头文件中定义了task_t结构体,重复包含会让编译器看到两次struct task_t的定义,报 "redefinition of struct task_t"(结构体重定义)错误;

  • 宏定义重复 :如果头文件中有#define MAX_TASK 10,重复包含会让编译器看到两次该宏定义,报 "redefinition of macro MAX_TASK"(宏重定义)错误;

  • 变量重复声明 :如果头文件中声明了全局变量(如uint8_t task_num;,注:全局变量建议在.c文件定义,.h文件用extern声明),重复包含会导致 "multiple definition of task_num"(变量多重定义)错误。

  1. 间接影响:增加编译时间

即使某些重复包含不会报错(比如仅包含函数声明,函数声明允许重复),也会让编译器重复解析相同的头文件内容,浪费编译时间,项目越大,影响越明显。

而头文件保护宏的核心作用,就是确保任意一个头文件,在整个项目的编译过程中,只会被编译器解析一次,从根源上避免上述问题。

结合代码举例(帮你规避隐藏问题)

你的代码中存在 "循环包含":

  • scheduler.h → 包含 mydefine.h

  • mydefine.h → 包含 scheduler.h

这种循环包含即使有头文件保护宏,也可能导致 "某些声明未定义" 的报错(比如编译器解析时,还没读到scheduler_init的声明就先解析mydefine.h,进而找不到对应函数)。

相关推荐
科技林总5 分钟前
【系统分析师】3.4 指令系统
学习
li星野18 分钟前
OpenCV4.X学习-视频相关
学习·音视频
恶魔泡泡糖39 分钟前
51单片机独立按键
单片机·嵌入式硬件·51单片机
万事可爱^1 小时前
LangChain v1.0学习笔记(4)—— 核心组件Models
人工智能·笔记·学习·langchain·大模型
卡布叻_星星2 小时前
笔记之光盘刻录
笔记
寄思~2 小时前
Excel 数据匹配工具 -笔记
笔记·python·学习·excel
朔北之忘 Clancy2 小时前
2020 年 6 月青少年软编等考 C 语言二级真题解析
c语言·开发语言·c++·学习·青少年编程·题解·尺取法
知识分享小能手2 小时前
Ubuntu入门学习教程,从入门到精通,Ubuntu 22.04 中安装 Docker 容器 —— 知识点详解(26)
学习·ubuntu·docker
炸膛坦客2 小时前
Cortex-M3-STM32F1 开发:(五十)软件模拟 IIC 和硬件 IIC 的区别,以及软件 IIC 配置步骤及相关函数,以及相关问题
stm32·单片机·嵌入式硬件
数据轨迹0012 小时前
CVPR Efficient ViM:视觉 Mamba 的轻量化
经验分享·笔记·facebook·oneapi·twitter