文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

使用 Rust 进行嵌入式开发

2024-12-02 19:06

关注

Rust 的高性能、高可靠性和高生产力使它适合于嵌入式系统。

在过去的几年里,Rust 在程序员中获得了热情的追捧。技术潮流来来去去,所以很难将仅仅因为某项新技术而产生的兴奋与对某项技术的优点的兴奋区分开来,但我认为 Rust 是一种真正设计良好的语言。它的目标是帮助开发者建立可靠和高效的软件,而且它从一开始就是为这个目的设计的。你可能听过一些 Rust 的关键特性,在这篇文章中,我会证明这些特性正是 Rust 也恰好适合嵌入式系统的原因。比如:

为什么使用 Rust 进行嵌入式开发?

Rust 的设计是为了保证安全和高性能。嵌入式软件会出现的问题主要是内存的问题。从某种程度上说,Rust 是一种面向编译器的语言,所以你可以确保在编译时安全使用内存。以下是使用 Rust 在嵌入式设备上开发的一些好处:

在这篇文章中,我使用开源的 RT-Thread 操作系统 来演示如何使用 Rust 进行嵌入式开发。

如何在 C 语言中调用 Rust

在 C 代码中调用 Rust 代码时,你必须将 Rust 源代码打包成静态库文件。当 C 代码编译时,将其链接进去。

用 Rust 创建一个静态库

在这个过程中,有两个步骤:

使用 cargo init --lib rust_to_c 在 Clion 中建立一个 lib 库。在 lib.rs 中加入以下代码。下面的函数计算两个类型为 i32 的值的总和并返回结果:

  1. #![no_std]
  2. use core::panic::PanicInfo;
  3.  
  4. #[no_mangle]
  5. pub extern "C" fn sum(a: i32, b: i32) -> i32 {
  6. a + b
  7. }
  8.  
  9. #[panic_handler]
  10. fn panic(_info:&PanicInfo) -> !{
  11. loop{}
  12. }

在你的 Cargo.toml 文件中添加以下代码,以告诉 Rustc 要生成什么类型的库:

  1. [lib]
  2. name = "sum"
  3. crate-type = ["staticlib"]
  4. path = "src/lib.rs"

交叉编译

你可以针对你的目标平台进行交叉编译。假设你的嵌入式系统是基于 Arm 的,步骤很简单:

  1. $ rustup target add armv7a-none-eabi

生成静态库文件:

  1. $ cargo build --target=armv7a-none-eabi --release --verbose
  2. Fresh rust_to_c v0.1.0
  3. Finished release [optimized] target(s) in 0.01s

生成头文件

你也需要头文件:

安装 cbindgencbindgen 工具会从 Rust 库中生成一个 C 或 C++11 的头文件:

  1. $ cargo install --force cbindgen

在你的项目文件夹下创建一个新的 cbindgen.toml 文件。

生成一个头文件:

  1. $ cbindgen --config cbindgen.toml --crate rust_to_c --output sum.h

调用 Rust 库文件

现在你可以对你的 Rust 库进行调用了。

把生成的 sum.h 和 sum.a 文件放到 rt-thread/bsp/qemu-vexpress-a9/applications 目录下。

修改 SConscript 文件并添加一个静态库:

  1.    from building import *
  2.    
  3.    cwd     = GetCurrentDir()
  4.    src     = Glob('*.c') + Glob('*.cpp')
  5.    CPPPATH = [cwd]
  6.    
  7.    LIBS = ["libsum.a"]
  8.    LIBPATH = [GetCurrentDir()]
  9.    
  10.    group = DefineGroup('Applications', src, depend = [''], CPPPATH = CPPPATH, LIBS = LIBS, LIBPATH = LIBPATH)
  11.    
  12.    Return('group')

在主函数中调用 sum 函数,得到返回值,并 printf 该值:

  1. #include <stdint.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <rtthread.h>
  5. #include "sum.h"
  6. int main(void)
  7. {
  8. int32_t tmp;
  9. tmp = sum(1, 2);
  10. printf("call rust sum(1, 2) = %d\n", tmp);
  11. return 0;
  12. }

在 RT-Thread Env 环境中,使用 scons 来编译项目并运行:

  1. $ scons -j6
  2. scons: Reading SConscript files ...
  3. scons: done reading SConscript files.
  4. scons: Building targets ...
  5. [...]
  6. scons: done building targets.
  7.  
  8. $ qemu.sh
  9. \ | /
  10. - RT - Thread Operating System
  11. / | \ 4.0.4 build Jul 28 2021
  12. 2006 - 2021 Copyright by rt-thread team
  13. lwIP-2.1.2 initialized!
  14. [...]
  15. call rust sum(1, 2) = 3

加、减、乘、除

你可以在 Rust 中实现一些复杂的数学运算。在 lib.rs 文件中,使用 Rust 语言来实现加、减、乘、除:

  1. #![no_std]
  2. use core::panic::PanicInfo;
  3.  
  4. #[no_mangle]
  5. pub extern "C" fn add(a: i32, b: i32) -> i32 {
  6. a + b
  7. }
  8.  
  9. #[no_mangle]
  10. pub extern "C" fn subtract(a: i32, b: i32) -> i32 {
  11. a - b
  12. }
  13.  
  14. #[no_mangle]
  15. pub extern "C" fn multiply(a: i32, b: i32) -> i32 {
  16. a * b
  17. }
  18.  
  19. #[no_mangle]
  20. pub extern "C" fn divide(a: i32, b: i32) -> i32 {
  21. a / b
  22. }
  23.  
  24. #[panic_handler]
  25. fn panic(_info:&PanicInfo) -> !{
  26. loop{}
  27. }

构建你的库文件和头文件,并把它们放在应用程序目录中。使用 scons 来编译。如果在链接过程中出现错误,请在官方 Github 页面 中找到解决方案。

修改 rtconfig.py 文件,并添加链接参数 --allow-multiple-definition

  1. DEVICE = ' -march=armv7-a -marm -msoft-float'
  2. CFLAGS = DEVICE + ' -Wall'
  3. AFLAGS = ' -c' + DEVICE + ' -x assembler-with-cpp -D__ASSEMBLY__ -I.'
  4. LINK_SCRIPT = 'link.lds'
  5. LFLAGS = DEVICE + ' -nostartfiles -Wl,--gc-sections,-Map=rtthread.map,-cref,-u,system_vectors,--allow-multiple-definition'+\
  6. ' -T %s' % LINK_SCRIPT
  7. CPATH = ''
  8. LPATH = ''

编译并运行 QEMU 来看看你的工作。

在 Rust 中调用 C 语言

Rust 可以在 C 代码中调用,但是如何在你的 Rust 代码中调用 C 呢?下面是一个在 Rust 代码中调用 rt_kprintf C 函数的例子。

首先,修改 lib.rs 文件:

  1. // The imported rt-thread functions list
  2. extern "C" {
  3. pub fn rt_kprintf(format: *const u8, ...);
  4. }
  5. #[no_mangle]
  6. pub extern "C" fn add(a: i32, b: i32) -> i32 {
  7. unsafe {
  8. rt_kprintf(b"this is from rust\n" as *const u8);
  9. }
  10. a + b
  11. }

接下来,生成库文件:

  1. $ cargo build --target=armv7a-none-eabi --release --verbose
  2. Compiling rust_to_c v0.1.0
  3. Running `rustc --crate-name sum --edition=2018 src/lib.rs --error-format=json --json=diagnostic-rendered-ansi --crate-type staticlib --emit=dep-info,link -C opt-level=3 -C embed-bitcode=no -C metadata=a
  4. Finished release [optimized] target(s) in 0.11s

而现在,要运行代码,将 Rust 生成的库文件复制到应用程序目录中,然后重新构建:

  1. $ scons -j6 scons: Reading SConscript files ... scons: done reading SConscript files. [...]
  2. scons: Building targets ... scons: done building targets.

再次运行 QEMU,可以在你的嵌入式镜像中看到结果。

你可以拥有这一切

在你的嵌入式开发中使用 Rust,你可以获得 Rust 的所有功能,而不需要牺牲灵活性或稳定性。今天就在你的嵌入式系统上试试 Rust 吧。关于嵌入式 Rust 的过程(以及 RT-Thread 本身)的更多信息,请查看 RT-Thread 项目的 YouTube 频道。请记住,嵌入式也可以是开放的。 

来源:Linux中国内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯