创建 Rust 嵌入式项目 (RISC-V)

四年前 (2019 年),以内存安全 (memory-safe) 闻名的 Rust 语言,最大的特点是:围观的人多,使用的人少;永远在学习 Rust 的路上,学习过程中又持续被劝退。

特别是在嵌入式领域,大家都在想象 (envision) 一个 Rust 替代 C/C++ 的未来,但是项目真要完全用 Rust 替代,走起来一步一个坑,可能 Rust 项目框架还没搭起来,就重新投入了 C 的怀抱。

伪操作系统简介

伪操作系统,它不是mcu上的操作系统,就是一种编程思想,是一种对代码的组织策略,一种问题的解决方案。 主要是解决在资源短缺的mcu平台上运行多任务,不需要操作系统,不需要写复杂的调度系统,只需要在编程时按照固定的格式编程,格式化代码就可以了,也不需要额外编写一些属于操作系统的代码。 伪操作系统的编程要求如下: 为了实现像操作系统一样的多任务,又不能操作PC寄存器进行线程切换和线程管理的情况下, 让多个任务同时进行的办法就是让多个任务运行在一个while循环中,为了让多个任务能同时运行而 又同时在一个while中,必须让每个任务进行重组,每个任务在while中的线程是不能阻塞的,全部 作为状态机存在,每个任务在状态未达到要求时必须返回,在有任务需求时再执行必要的代码。代码比如:

一些在 Rust 和 C 语言中常见的语法元素的对照

以下是一些在 Rust 和 C 语言中常见的语法元素的对照,这有助于理解两种语言之间的主要区别:

1. 变量定义

C:

1
2
int a = 10;
char b = 'b';

Rust:

1
2
3

let a = 10;       // 不可变变量
let mut b = 'b';  // 可变变量

2. 函数定义

C:

1
2
3
4

int add(int x, int y) {
    return x + y;
}

Rust:

1
2
3
4

fn add(x: i32, y: i32) -> i32 {
    x + y  // 注意:Rust 中不需要 return 语句
}

3. 条件语句

C:

1
2
3
4
5
6

if (a > b) {
    // do something
} else {
    // do something else
}

Rust:

1
2
3
4
5
6

if a > b {
    // do something
} else {
    // do something else
}

4. 循环

C:

1
2
3
4

for (int i = 0; i < 10; i++) {
    // do something
}

Rust:

1
2
3
4

for i in 0..10 {
    // do something
}

5. 结构体

C:

1
2
3
4
5
6

struct Point {
    int x;
    int y;
};
struct Point p = {10, 20};

Rust:

1
2
3
4
5
6

struct Point {
    x: i32,
    y: i32,
}
let p = Point { x: 10, y: 20 };

6. 指针和引用

C:

1
2
3
4

int x = 10;
int *ptr = &x;
*ptr = 20;

Rust:

1
2
3
4

let mut x = 10;
let ptr = &mut x;  // 可变引用
*ptr = 20;

7. 数组

C:

1
2

int arr[3] = {1, 2, 3};

Rust:

1
2

let arr: [i32; 3] = [1, 2, 3];

8. 字符串

C:

1
2

char *str = "Hello, world!";

Rust:

1
2
3

let str = "Hello, world!";  // 不可变字符串切片
let mut string = String::from("Hello, world!");  // 可变字符串对象

9. 错误处理

C:

1
2

// 通常通过返回特定的错误码或使用全局变量 errno

Rust:

1
2
3
4
5
6

// 使用 Result 类型或 panic! 宏
fn do_something() -> Result<(), String> {
    // ...
    Ok(())
}

10. 内存管理

C:

1
2
3
4

int *ptr = malloc(sizeof(int));
// 使用 ptr
free(ptr);

Rust:

1
2
3
4

let mut ptr = Box::new(10);  // 自动管理内存
// 使用 ptr
// 内存自动释放

总结

这些对照表展示了 Rust 和 C 语言在语法和内存管理方面的一些基本区别。 Rust 的设计注重安全性和现代化,其语法倾向于表达明确性和类型安全。 虽然这些差异可能需要一定的学习曲线,但 Rust 的特性(如所有权系统、模式匹配、安全的并发编程等)为编写高效、可靠和安全的代码提供了强大的支持。

FreeRTOS9.0内存申请函数与LVGL9.0内存配合使用。

介绍

FreeRTOS 9.0 提供了一系列用于内存管理的函数。这些函数允许您在任务或中断服务例程中动态地分配和释放内存。下面是一些主要的内存管理函数:

pvPortMalloc: 这个函数用于申请内存。它类似于标准 C 库中的 malloc 函数。您可以使用此函数分配指定大小的内存块。

嵌入式项目结构生成脚本(python)

需求说明

嵌入式项目结构生成脚本 CreateHAL.py

代码内容

  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
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import os

# 架构目录列表
directories = [
    ("firmware", "嵌入式固件相关代码"),
    ("firmware/core", "核心功能代码"),
    ("firmware/drivers", "驱动程序"),
    ("firmware/ui", "用户界面相关代码"),
    ("firmware/simulator", "模拟器代码"),
    ("firmware/business_logic", "业务逻辑代码"),
    ("firmware/config", "配置文件"),
    ("firmware/config/hardware_config", "硬件配置文件"),
    ("firmware/config/system_config", "系统配置文件"),
    ("firmware/sdks", "软件开发工具包"),
    ("firmware/sdks/chip1_sdk", "芯片1的SDK"),
    ("firmware/sdks/chip2_sdk", "芯片2的SDK"),
    ("firmware/scripts", "脚本工具"),
    ("firmware/scripts/build_scripts", "构建脚本"),
    ("firmware/scripts/debug_tools", "调试工具脚本"),
    ("firmware/tests", "测试代码"),
    ("firmware/tests/unit_tests", "单元测试"),
    ("firmware/tests/integration_tests", "集成测试"),
    ("firmware/tests/system_tests", "系统测试"),
    ("firmware/third_party", "第三方库"),
    ("firmware/projects", "项目文件"),
    ("firmware/projects/eclipse", "Eclipse项目文件"),
    ("firmware/projects/codeblocks", "CodeBlocks项目文件"),
    ("firmware/projects/keil", "Keil项目文件"),
    ("firmware/projects/gcc", "GCC项目文件"),
    ("mechanical_design", "机械设计文件"),
    ("ui_design", "UI设计文件"),
    ("pc_application", "上位机程序相关代码"),
    ("communication_interface", "通讯接口说明文档"),  # 新增通讯接口说明文档目录
    ("documentation", "文档"),
    ("documentation/requirements", "需求文档"),
    ("documentation/test_reports", "测试报告"),
    ("documentation/coding_standards", "开发规范"),
    ("documentation/design", "设计文档"),  # 新增设计文档目录
    ("documentation/design/system", "系统设计文档"),
    ("documentation/design/hardware", "硬件设计文档"),
    ("documentation/design/software", "软件设计文档"),
    ("documentation/test", "测试文档"),
    ("documentation/test/unit", "单元测试文档"),
    ("documentation/test/integration", "集成测试文档"),
    ("documentation/test/system", "系统测试文档"),
    ("documentation/user_manual", "用户手册"),
    ("documentation/api_reference", "API参考文档"),
    ("documentation/release_notes", "发布说明"),
    ("documentation/project_management", "项目管理文档"),
    ("documentation/project_management/project_plan", "项目计划"),
    ("documentation/project_management/risk_management", "风险管理文档"),
    ("documentation/coding_standards", "开发规范"),
    ("documentation/hardware_reference", "硬件参考文档"),  # 新增硬件参考文档目录
    ("documentation/software_reference", "软件参考文档"),  # 新增软件参考文档目录
]

# 基础路径 - 根据需要调整
base_path = "HAL_Architecture"

# 生成目录树文本
def generate_tree_text(dirs, prefix=""):
    tree_text = ""
    for i, (dir, desc) in enumerate(dirs):
        is_last = i == len(dirs) - 1
        tree_text += f"{prefix}{'`-- ' if is_last else '|-- '}{dir}/ ({desc})\n"
        if os.path.isdir(dir):
            tree_text += generate_subtree_text(dir, is_last, prefix + ("    " if is_last else "|   "))
    return tree_text

# 递归生成子目录树文本
def generate_subtree_text(base, is_last, prefix):
    tree_text = ""
    entries = sorted(os.listdir(base))
    for i, entry in enumerate(entries):
        entry_path = os.path.join(base, entry)
        is_entry_last = i == len(entries) - 1
        tree_text += f"{prefix}{'`-- ' if is_last and is_entry_last else '|-- '}{entry}/\n"
        if os.path.isdir(entry_path):
            tree_text += generate_subtree_text(entry_path, is_entry_last, prefix + ("    " if is_entry_last else "|   "))
    return tree_text

# 创建目录
def create_directories(dirs):
    for dir, desc in dirs:
        path = os.path.join(base_path, dir)
        os.makedirs(path, exist_ok=True)
        print(f"已创建目录: {path} ({desc})")

# 创建 README.md
def create_readme(dirs):
    readme_path = os.path.join(base_path, "README.md")
    with open(readme_path, "w", encoding="utf-8") as readme_file:
        readme_file.write("# HAL Architecture\n\n这是用于嵌入式系统的 HAL 架构。\n\n## 目录结构\n")
        readme_file.write("```plaintext\n")
        readme_file.write(generate_tree_text(dirs))
        readme_file.write("```\n请参考每个目录的 README 文件以获取更多详细信息。\n")
        readme_file.write("\n## 嵌入式开发流程\n")
        readme_file.write("1. **需求分析:** 在项目开始阶段,明确定义系统的功能和性能需求。\n")
        readme_file.write("2. **系统设计:** 根据需求进行系统设计,包括硬件和软件的设计。\n")
        readme_file.write("3. **硬件开发:** 实现硬件设计,包括电路设计、PCB设计和原型制作。\n")
        readme_file.write("4. **嵌入式软件开发:** 开发嵌入式系统的固件和软件。\n")
        readme_file.write("5. **集成与测试:** 将硬件和软件集成到最终系统中,并进行各种测试,包括单元测试、集成测试和系统测试。\n")
        readme_file.write("6. **验证与调试:** 验证系统的正确性和性能,解决可能出现的问题。\n")
        readme_file.write("7. **认证与合规性:** 确保系统符合相关法规和标准,进行必要的认证。\n")
        readme_file.write("8. **生产与部署:** 生产系统并部署到目标环境。\n")
        readme_file.write("\n**注意:** 每个目录的具体内容和用途,请参考相应目录的 README 文件。\n")
        readme_file.write("\n---\n")
        readme_file.write("感谢使用 HAL Architecture!")


# 创建目录
create_directories(directories)

# 创建 README.md
create_readme(directories)

print("HAL architecture directories and README created successfully.")

Golang中小数点后三位的四舍五入

需求

需要将29.995四舍五入成30.00 29.991 -> 29.99 29.992 -> 29.99 29.993 -> 29.99 29.997 -> 30.00

实现代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
package main

import (
    "fmt"
    "math"
)

func main() {
    for _, a := range []float64{29.995, 29.991, 29.992, 29.997} {
        fmt.Printf("OK: %f =》 %0.2f\n", a, math.Floor((a+0.005)*100)/100)
    }
}

结果如下

1
2
3
4
OK: 29.995000 =》 30.00
OK: 29.991000 =》 29.99
OK: 29.992000 =》 29.99
OK: 29.997000 =》 30.00

修改Codeblocks主题,让编辑器看的更舒服。

Codeblocks是一款跨平台的C/C++集成开发环境。

下载主题配置文件

Codeblocks 自带的主题太难看了,于是去git上搜了下,找到下面的主题分享:

1
git clone https://github.com/virtualmanu/Codeblocks-Themes

windows下的配置

将配置文件下载回来后,进入到主题配置的文件夹

ncurses库的介绍与安装

介绍

ncurses(new curses)是一套编程库,它提供了一系列的函数以便使用者调用它们去生成基于文本的用户界面。 ncurses名字中的n意味着“new”,因为它是curses的自由软件版本。由于AT&T“臭名昭著”的版权政策,人们不得不在后来用ncurses去代替它。 ncurses是GNU计划的一部分,但它却是少数几个不使用GNU GPL或LGPL授权的GNU软件之一。