# 初识源码

# 获取源码

MadOS 源码仓库 (opens new window)

TIP

初学者请选择master分支, 产品应用可选择经过严格验证的release版本。

# Windows

WSL2 + Ubuntu 提供的是一个虚拟机环境。将源码放置在 Ubuntu 中,相比放在 Windows 中,编译速度会大幅提升。

  • 启动 Ubuntu WSL2_StartUbuntu
  • 找到 Ubuntu 根路径
    WSL2_UbuntuRoot
  • 建议的源码存放路径
\\wsl.localhost\Ubuntu-22.04\home\lo
1

TIP

  • 上述路径可以在 Github for Windows 中正常使用。
  • 如有安全提示,一律选择"信任"。
  • WindowsMacOS/Ubuntu 采用的换行符不一致,Git Clone 源码之前需修改设置: Git_CfgPath Git_CfgAdd
  • 获取源码 WSL2_CloneCode
  • 查看状态 WSL2_GitHubRdy

# 展开源码

# MacOS & Ubuntu

MadOS 文件夹拖入 VSCode 中: First Code

# Windows

VSCode 连接至 Ubuntu,再通过文件->打开文件夹找到 MadOS WSL2_FindMadOS WSL2_VSCodeRdy

# 目录结构

# 开发者无需关心的内容以 n 标记。
# 开发者可以参考的内容以 - 标记。
# 开发者需要关心的内容以 y 标记。
MadOS
├─ .vscode/        # n : VSCode 相关配置
├─ app/            # y : 项目源码(其中每个子目录为一个项目)
│  ├─ backup       # n : 旧版MadOS中的工程备份。
│  ├─ lesson001/   # - : MadOS应用教程第一课,稍后我们会展开分析它。
│  ├─ LoKernel/    # n : MadOS内核压力测试。
│  ├─ LoNode/      # - : LoBoard开发板配套例程,不断完善中。
│  └─ ...          # - : 未来,我们将开放更多示例供开发者参考。
├─ arch/           # n : MCU架构相关源码
├─ build/          # n : 编译过程文件存放处(源码编译时自动生成)
├─ device/         # n : 设备模块源码
├─ driver/         # n : 驱动源码
├─ kernel/         # n : 内核源码
├─ library/        # n : 第三方库源码
├─ tools/          # n : MadOS环境配置相关工具
├─ .gitattributes  # n : git属性文件
├─ .gitignore      # n : git过滤文件
├─ app_switcher.sh # y : 工作App切换器,详见后述。
├─ elibs.mk        # n : 第三方库配置
├─ LICENSE         # n : 许可证
├─ main.mk         # n : 真实主编译文件(临时生成)
├─ Makefile        # n : 影子主编译文件
└─ rules.mk        # n : 编译规则定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

TIP

  • 如果您是操作系统爱好者,也许对 arch、kernel、device、driver 中的内容会感兴趣。
  • 如果您是初学者、开发者,则只需关心 app ,即您的项目源码,以便快速实现想法。
  • MadOS尚未包含某些驱动或第三方库,如有需要,您可:

# 切换项目

MadOS 根目录下有名为 app_switcher.sh 的脚本,用以切换当前工作项目:

# export MADOS_WORKING_APP=LoKernel
# export MADOS_WORKING_APP=LoNode
export MADOS_WORKING_APP=lesson001
1
2
3

lesson001 前的注释去掉,并注释其他项目,然后在 VSCode 中运行 config 任务:

TIP

终端 -> 运行任务 -> config

MadOS is ready... Enjoy yourself!
1

# 项目结构

lesson001
├─ CfgApp.mk # 项目配置
├─ CfgDevs.c # 设备列表
├─ CfgUser.h # 用户配置
├─ main.c    # 工程源码(主文件)
└─ Makefile  # 编译文件
1
2
3
4
5
6

TIP

  • 如果您是初学者、开发者,只需关心 main.c ,即您的项目源码,以便快速实现想法。
  • 为便于后续学习,我们依然对每个文件进行初步介绍,您可快速浏览,以备不时之需。

# CfgApp.mk

# MCU架构配置
export MCU_ARCH     = armv7-m
export MCU_VER      = cortex-m3
export MCU_PREFIX   = stm32f10x
export MCU_SUFFIX   = cl
export MCU_RAM_SIZE = 0x10000
export MCU_FLS_SIZE = 256K
# 工程特定编译选项
export PRJ_CFLAGS  = -Os
export PRJ_LDFLAGS = --specs=nano.specs
1
2
3
4
5
6
7
8
9
10

# CfgDevs.c

// 第一课中未启用任何设备,设备列表为空。
#include "MadDev.h"
MadDev_t *DevsList[] = {
    MAD_DEVP_END
};
1
2
3
4
5

# CfgUser.h

// MadOS支持最大256个任务优先级,数字越大优先级越低。
// 开发者需根据自身需要进行配置,并避开MadOS内部已使用的优先级。
// 最低优先级为空闲线程使用,次低优先级为统计线程(可选)使用,另建议保留优先级 0。
enum {
   THREAD_PRIO_SYS_RUNNING = 1,  // 标记系统正在运行的线程(周期性闪烁Led)
   THREAD_PRIO_DRIVER_ETH        // 编译网络驱动层所需
};
// 通常,中断优先级都与驱动相关。
// 中断优先级统一于此处枚举,便于高级开发者查阅、更改。
// 优先级1、15为MadOS内部保留,其余优先级可供项目使用。
enum {
    ISR_PRIO_SYSTICK    = 1,
    ISR_PRIO_ARCH_MEM,
    ISR_PRIO_DISK,
	ISR_PRIO_ETH,
    ISR_PRIO_DEV_USART,
    ISR_PRIO_TTY_USART,
    ISR_PRIO_PENDSV     = 15
};
// 堆空间定义(硬件相关)
#define MAD_OS_STACK_SIZE (56 * 1024)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# Makefile

# 将项目路径下所有.c文件输入编译系统。
export TEMP = $(BUILD_DIR)/app
ASMS =
SRCS = $(wildcard *.c)
include $(RULES)
after_all:
	$(LD) $(wildcard $(BUILD_DIR)/app/*.o) $(LDFLAGS) -o $(TARGET).elf
	$(OCPY) -O ihex $(TARGET).elf $(TARGET).hex
1
2
3
4
5
6
7
8

# main.c

#include "MadOS.h"   // MadOS核心头文件
#include "CfgUser.h" // 用户配置头文件

// 运行时堆栈 (8Bytes-Align for Float)
MadAligned_t MadStack[MAD_OS_STACK_SIZE / MAD_MEM_ALIGN] = { 0 };

// 函数声明
static void madStartup(MadVptr exData);

// 初始化
int main()
{
    ...   
}

// 线程
static void madStartup(MadVptr exData)
{
    ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 运行时堆栈

运行时堆栈的空间由硬件决定。
任何项目的运行时堆栈皆可由下式定义:

MadAligned_t MadStack[MAD_OS_STACK_SIZE / MAD_MEM_ALIGN] = { 0 };
1

# 初始化

下述初始化流程可适用于任何项目。
启动MadOS后,程序会跳转至新建线程中运行。
第一课中,我们只建立一个线程,意在简明,线程优先级取 0。

int main()
{
    madCopyVectorTab();                         // 将中断向量表复制到RAM中
    madOSInit(MadStack, MAD_OS_STACK_SIZE);     // MadOS初始化
    madThreadCreate(madStartup, 0, MAD_OS_STACK_SIZE / 2, 0); // 新建线程
    madOSRun();                                 // 启动MadOS
    while(1);                                   // !永远不该运行至此!
} // 以上是MadOS的启动过程,初学者不必深究,随后的学习中会逐步了解其原理
1
2
3
4
5
6
7
8

# 线程

开发者在线程中实现想法,每个线程都是一个无限循环。

TIP

使用线程管理API,可以新建、挂起、恢复、删除线程。

static void madStartup(MadVptr exData)
{
    GPIO_InitTypeDef pin;  // GPIO临时变量
    MadBool flag = MFALSE; // LED状态标志
	(void)exData;          // 防止编译器产生警告

    // 初始化GPIOE-1,用于控制LED开关。
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);
    pin.GPIO_Mode  = GPIO_Mode_Out_PP;
	pin.GPIO_Pin   = GPIO_Pin_1;
	pin.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOE, &pin);

    // 初始化SysTick,脉动间隔1ms。
    madInitSysTick(DEF_SYS_TICK_FREQ, DEF_TICKS_PER_SEC);

    // 线程主循环
    while(1) {
        madTimeDly(500); // 延时500ms
        flag = !flag;    // LED状态取反
        if(flag) GPIO_ResetBits(GPIOE, GPIO_Pin_1); // 开灯
        else     GPIO_SetBits(GPIOE, GPIO_Pin_1);   // 关灯
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

TIP

  • 无OS环境,通常使用空循环方式延时:
    • 无法精准延时
    • 大量浪费MCU时间
void delay(int i) { while(i--); }
1
  • MadOS环境,SysTick 初始化后提供了精准的心跳,根据心跳的频率可得精准延时。
    • 运行30天,实测误差小于1微秒(1us=106s1 us = 10^{-6} s)
madTimeDly(1000 * 3600 * 24 * 30); // 通常,madTimeDly以ms为单位。
1

# 编译项目

# 首次编译

在VSCode中运行 rebuild 任务

TIP

终端 -> 运行任务 -> rebuild

# 普通编译

在VSCode中运行 build 任务

TIP

终端 -> 运行任务 -> build

# 编译结果

...
Building ... Done.
1
2

编译成功后,MadOS根目录下会生成 build 文件夹:

build
├─ app/         # 存放项目相关编译过程文件
├─ arch/        # 存放芯片相关编译过程文件
├─ dev/         # 存放设备相关编译过程文件
├─ drv/         # 存放驱动相关编译过程文件
├─ kernel/      # 存放内核相关编译过程文件
├─ HiMadOS.elf  # 调试文件
├─ HiMadOS.hex  # 烧录文件
├─ HiMadOS.ld   # 链接脚本
├─ libarch.a    # 芯片库文件
├─ libdev.a     # 设备库文件
├─ libdrv.a     # 驱动库文件
└─ libkernel.a  # 内核库文件
1
2
3
4
5
6
7
8
9
10
11
12
13

TIP

build 内生成的目录 / 文件与项目配置有关,开发者无需关心。

# 调试项目

STLink 分别与 LoBoard(开发板)、电脑连接,并给 LoBoard 通电:
Hardware Connection

TIP

VSCode 中启动调试:
Start Debug
正常启动调试后,程序会暂停在 main 函数起始位置: Debugging

TIP

F5 或 点击调试工具栏中的 继续 按钮,程序继续执行。
DebugTools