前言:
看本篇博客前,清先看一下这篇博客:衡山派D133EBS入门笔记-CSDN博客
不让就会觉得不知道下面写的是什么东西
1. 今天主要学了什么
今天主要弄明白了 4 件事:
-
SConscript这种 SCons 构建脚本到底在干什么。 -
USER_LED_ON这种功能开关从哪里来、怎么影响编译。 -
RT-Thread 的
PIN文档主要讲了什么。 -
RT-Thread 的线程和
Kconfig分别是什么、起什么作用。
2. SConscript 到底是在做什么
之前看到的那段 SConscript,本质上不是普通业务代码,而是在告诉构建系统:
-
当前这个目录要不要参与编译
-
当前目录下哪些
.c文件要加入工程 -
编译这个模块时头文件路径在哪里
-
最后把这个模块作为一个"构建组"返回给上层工程
关键理解
SConscript 的作用不是"运行功能逻辑",而是"描述怎么编译这个功能模块"。
例如:
if GetDepend('USER_LED_ON'):
src = Glob(os.path.join(cwd, '*.c'))
这段代码的意思是:
-
如果
USER_LED_ON这个开关打开了 -
就把当前目录下所有
.c文件加入编译
如果没打开,就不编译这一组文件。
3. 什么是构建组
"构建组"可以理解成:
把某个功能模块相关的源码、头文件路径、依赖条件、编译选项打包成一个构建单元,交给构建系统统一管理。
例如一个 user_led 模块,它可以包含:
-
user_led.c -
user_led.h -
当前目录头文件路径
-
功能开关依赖,例如
USER_LED_ON
然后通过:
DefineGroup('user-led', src, ...)
把它注册到整个工程中。
你可以这样理解
-
一个工程 = 很多个构建组拼起来
-
每个构建组 = 一个功能模块
4. USER_LED_ON 是什么
USER_LED_ON 通常不是随便写出来的名字,而是一个"配置开关"。
它的作用通常有两个:
-
控制某些源码文件要不要参与编译
-
控制某些代码分支要不要生效
它一般怎么用
在构建脚本里:
if GetDepend('USER_LED_ON'):
意思是:
-
构建系统检查
USER_LED_ON有没有打开 -
如果打开,就编译这个模块
在 C 代码里:
#ifdef USER_LED_ON
user_led_init();
#endif
意思是:
-
如果这个宏存在
-
就编译这段代码
关键理解
同一个配置项,既可能影响构建系统,也可能影响 C 代码。
5. Kconfig 是什么
Kconfig 是"配置规则文件"。
它不是业务代码,也不是驱动代码,而是用来定义:
-
这个工程有哪些选项
-
这些选项是什么类型
-
默认值是什么
-
它们之间有什么依赖关系
-
在配置界面中怎么显示
可以把它理解成
-
Kconfig:定义"能选什么" -
menuconfig / pyconfig:决定"你选了什么" -
rtconfig.h:记录"最终结果"
一个最简单的例子
config USER_LED_ON
bool "Enable user LED"
default n
这段的意思是:
-
定义一个叫
USER_LED_ON的配置项 -
类型是布尔型,也就是开/关
-
默认关闭
6. Kconfig 和 CubeMX 的关系怎么理解
今天已经纠正过一个很重要的理解:
Kconfig 和 CubeMX 有一点像,但不能完全等同。
相似的地方
-
都有"配置"的感觉
-
都能让用户通过界面选择一些选项
不同的地方
CubeMX 更偏向:
-
外设配置
-
时钟配置
-
自动生成初始化代码
Kconfig 更偏向:
-
软件功能裁剪
-
模块开关管理
-
宏配置和条件编译
更准确的理解
可以把 Kconfig 理解成:
更像"软件工程里的配置规则系统",不是主要用来生成外设初始化代码的工具。
7. USER_LED_ON 的完整链路
今天最重要的收获之一,就是把这条完整链路串起来了:
Kconfig
->
menuconfig / pyconfig
->
配置结果
->
rtconfig.h
->
SConscript 判断是否编译 .c 文件
->
C 代码用 #ifdef 控制分支是否生效
把它翻译成人话
-
先在
Kconfig里定义这个开关。 -
再在配置界面里选择开还是关。
-
系统把结果写到
rtconfig.h。 -
构建脚本读取这个配置,决定要不要编译对应源码。
-
C 代码也可以通过这个宏决定是否启用某些逻辑。
一句话总结
Kconfig 决定"有什么选项",rtconfig.h 记录"最后选了什么",SConscript 决定"编不编",C 代码决定"跑不跑"。
8. RT-Thread 的 PIN 文档讲了什么
PIN 文档主要讲的是:
RT-Thread 如何把 GPIO 封装成统一接口供应用层使用。
最常见的 API 有:
-
rt_pin_mode() -
rt_pin_write() -
rt_pin_read() -
rt_pin_attach_irq() -
rt_pin_irq_enable()
最关键的使用流程
-
先确定引脚编号
-
再设置引脚模式
-
输出引脚用
write -
输入引脚用
read -
需要中断时先
attach_irq -
然后再
irq_enable
重点记忆
-
PIN是 RT-Thread 对 GPIO 的统一抽象 -
引脚编号要看 BSP 的定义,不一定直接等于
PA0这种写法 -
中断不是只
attach就完成了,还要enable
9. RT-Thread 线程文档讲了什么
线程文档主要讲的是:
RT-Thread 里线程是什么、系统怎么调度线程、如何创建和管理线程。
核心概念
-
线程是 RT-Thread 的最小调度单位
-
每个线程有自己的栈
-
每个线程有优先级
-
同优先级线程按时间片轮转
-
高优先级线程可以抢占低优先级线程
最重要的结论
在 RT-Thread 里:
优先级数字越小,优先级越高
这是今天专门确认过的一个关键点。
常见 API
-
rt_thread_create() -
rt_thread_init() -
rt_thread_startup() -
rt_thread_delay() -
rt_thread_yield() -
rt_thread_suspend() -
rt_thread_resume()
重点记忆
-
创建线程后还要
startup -
栈大小很重要
-
优先级不要理解反
10. 今天最容易混淆、但现在已经搞清楚的点
1. Import 和 import 不是一回事
-
Import(...):SCons 的导入机制 -
import ...:Python 的导入机制
2. GetDepend('USER_LED_ON') 不一定是在查某个 .c 文件
它通常是在查工程配置项。
3. USER_LED_ON 既可能影响构建,也可能影响代码
-
构建脚本用它决定编不编文件
-
C 代码用它决定编译哪些分支
4. Kconfig 不是业务代码
它是配置规则文件。
5. menuconfig 不等于 CubeMX
它们都有界面,但定位不同:
-
CubeMX 偏外设和初始化代码生成
-
menuconfig 偏软件组件和功能开关管理
6. RT-Thread 线程优先级不是"数字越大越高"
正确结论是:
数字越小,优先级越高
11. 今天建立起来的工程思维
今天不只是学了几个知识点,更重要的是开始建立"工程分层"的理解。
一个 RT-Thread 工程大致可以分成这些层:
配置层
Kconfig
负责定义有哪些功能选项。
配置结果层
rtconfig.h
负责保存最终配置结果。
构建层
-
SConstruct -
SConscript
负责决定哪些文件参与编译。
功能实现层
-
.c -
.h
负责真正实现功能逻辑。
一个很重要的收获
以后看到一个功能不生效,不能只盯着 .c 文件看了,还要想到:
-
配置是不是没开
-
rtconfig.h有没有生成宏 -
构建脚本有没有把文件编进去
-
C 代码是不是被
#ifdef屏蔽了
12. 今天内容的最短复习版
如果以后只花 1 分钟复习,就看这一段:
SCons / SConscript
-
用来描述"怎么编译模块"
-
不是业务逻辑代码
构建组
- 某个功能模块的源码、路径、依赖、选项的打包单元
Kconfig
-
定义有哪些配置项
-
定义依赖、默认值、帮助信息
menuconfig
- 让用户选择这些配置项
rtconfig.h
-
保存最终配置结果
-
通常会生成宏
USER_LED_ON
-
是功能开关
-
可影响构建系统和 C 代码
线程
-
RT-Thread 最小调度单位
-
优先级数字越小,优先级越高
PIN
-
RT-Thread 对 GPIO 的统一接口
-
常用 API:
mode / write / read / irq
13. 最后一句总结
今天最大的收获是:
你已经开始从"只看代码片段"转向"看整个 RT-Thread 工程是怎么通过配置、构建和代码协同工作的"。
这一步非常关键。