Utiliser Rust pour le développement embarqué

Au cours des dernières années, Rust a gagné un public passionné parmi les programmeurs. Les tendances technologiques vont et viennent, il peut donc être difficile de séparer l’enthousiasme simplement parce que quelque chose est nouveau et l’enthousiasme pour les mérites d’une technologie, mais je pense que Rust est un langage vraiment bien conçu. Il vise à aider les développeurs à créer des logiciels fiables et efficaces, et il a été conçu à cet effet dès le départ. Il existe des fonctionnalités clés dont vous entendrez parler à propos de Rust, et dans cet article, je démontre que bon nombre de ces fonctionnalités expliquent exactement pourquoi Rust est également idéal pour les systèmes embarqués. Voici quelques exemples:
- Hautes performances : c’est rapide, avec une utilisation élevée de la mémoire
- Fiabilité : les erreurs de mémoire peuvent être éliminées lors de la compilation
- Productivité : excellente documentation, un compilateur convivial avec des messages d’erreur utiles et des outils de premier ordre. Il existe un gestionnaire de packages et un outil de construction intégrés, une prise en charge multi-éditeurs intelligente avec saisie automatique et inspections de type, un formateur automatique, etc.
Contents
Pourquoi utiliser Rust pour le développement embarqué ?
Rust est conçu pour garantir à la fois la sécurité et des performances élevées. Les logiciels embarqués peuvent avoir des problèmes, principalement dus à la mémoire. Rust est, d’une certaine manière, un langage orienté compilateur, vous pouvez donc être sûr que vous utilisez la mémoire en toute sécurité lors de la compilation. Voici quelques-uns des avantages de l’utilisation de Rust pour développer sur des appareils embarqués :
- Analyse statique puissante
- Mémoire flexible
- Concurrence intrépide
- Interopérabilité
- Portabilité
- Axé sur la communauté
Dans cet article, j’utilise l’open source Système d’exploitation RT-Thread pour montrer comment utiliser Rust pour le développement embarqué.
Comment appeler Rust en C
Lorsque vous appelez du code Rust en code C, vous devez empaqueter le code source Rust en tant que fichier de bibliothèque statique. Lorsque le code C se compile, associez-le.
Créer une bibliothèque statique avec Rust
Il y a deux étapes dans ce processus.
1. Utiliser cargo init --lib rust_to_c
pour construire une bibliothèque lib dans Clion. Ajoutez le code suivant au lib.rs
. La fonction suivante évalue la somme de deux valeurs de type i32 et renvoie le résultat :
#![no_std]
use core::panic::PanicInfo;#[no_mangle]
pub extern "C" fn sum(a: i32, b: i32) -> i32 {
a + b
}#[panic_handler]
fn panic(_info:&PanicInfo) -> !{
loop{}
}
2. Ajoutez le code suivant à votre Cargo.toml
fichier pour indiquer à Rustc quel type de bibliothèque générer :
[lib]
name = "sum"
crate-type = ["staticlib"]
path = "src/lib.rs"
Compilation croisée
Vous pouvez effectuer une compilation croisée pour votre cible. En supposant que votre système embarqué soit basé sur Arm, les étapes sont simples :
$ rustup target add armv7a-none-eabi
2. Générez le fichier de bibliothèque statique :
$ cargo build --target=armv7a-none-eabi --release --verbose
Fresh rust_to_c v0.1.0
Finished release [optimized] target(s) in 0.01s
Générer un fichier d’en-tête
Vous avez également besoin de fichiers d’en-tête.
1. Installer cbindgen. Les cbindgen
L’outil génère un fichier d’en-tête C ou C++11 à partir de la bibliothèque Rust :
$ cargo install --force cbindgen
2. Créez un nouveau cbindgen.toml
fichier sous votre dossier de projet.
3. Générez un fichier d’en-tête :
$ cbindgen --config cbindgen.toml --crate rust_to_c --output sum.h
Appelez le fichier de bibliothèque Rust
Vous pouvez désormais appeler vos bibliothèques Rust.
1. Mettez le généré sum.h
et sum.a
fichiers dans le rt-thread/bsp/qemu-vexpress-a9/applications
annuaire.
2. Modifiez le SConscript
fichier et ajoutez une bibliothèque statique :
from building import *
cwd = GetCurrentDir()
src = Glob('*.c') + Glob('*.cpp')
CPPPATH = [cwd]
LIBS = ["libsum.a"]
LIBPATH = [GetCurrentDir()]
group = DefineGroup('Applications', src, depend = [''], CPPPATH = CPPPATH, LIBS = LIBS, LIBPATH = LIBPATH)
Return('group')
3. Appelez le somme fonction dans la fonction principale, obtenez la valeur de retour, et printf
la valeur.
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <rtthread.h>
#include "sum.h"
int main(void)
{
int32_t tmp;
tmp = sum(1, 2);
printf("call rust sum(1, 2) = %dn", tmp);
return 0;
}
4. Dans le fil RT Env environnement, utilisation scons
pour compiler le projet et exécuter :
$ scons -j6
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
[...]
scons: done building targets.$ qemu.sh
| /
- RT - Thread Operating System
/ | 4.0.4 build Jul 28 2021
2006 - 2021 Copyright by rt-thread team
lwIP-2.1.2 initialized!
[...]
call rust sum(1, 2) = 3
Additionner, soustraire, multiplier et diviser
Vous pouvez implémenter des mathématiques compliquées dans Rust. Dans le lib.rs
fichier, utilisez le langage Rust pour implémenter l’addition, la soustraction, la multiplication et la division :
#![no_std]
use core::panic::PanicInfo;#[no_mangle]
pub extern "C" fn add(a: i32, b: i32) -> i32 {
a + b
}#[no_mangle]
pub extern "C" fn subtract(a: i32, b: i32) -> i32 {
a - b
}#[no_mangle]
pub extern "C" fn multiply(a: i32, b: i32) -> i32 {
a * b
}#[no_mangle]
pub extern "C" fn divide(a: i32, b: i32) -> i32 {
a / b
}#[panic_handler]
fn panic(_info:&PanicInfo) -> !{
loop{}
}
Créez vos fichiers de bibliothèque et vos fichiers d’en-tête et placez-les dans le répertoire de l’application. Utilisation scons
compiler. Si des erreurs apparaissent lors de la liaison, trouvez la solution sur le site officiel Page Github.
Modifier le rtconfig.py
fichier et ajoutez le paramètre de lien --allow-multiple-definition
:
DEVICE = ' -march=armv7-a -marm -msoft-float'
CFLAGS = DEVICE + ' -Wall'
AFLAGS = ' -c' + DEVICE + ' -x assembler-with-cpp -D__ASSEMBLY__ -I.'
LINK_SCRIPT = 'link.lds'
LFLAGS = DEVICE + ' -nostartfiles -Wl,--gc-sections,-Map=rtthread.map,-cref,-u,system_vectors,--allow-multiple-definition'+
' -T %s' % LINK_SCRIPT
CPATH = ''
LPATH = ''
Compilez et exécutez QEMU pour voir votre travail.
Appelez C dans Rust
Rust peut être appelé en code C, mais qu’en est-il d’appeler C dans votre code Rust ? Ce qui suit est un exemple d’appel du rt_kprintf
Fonction C en code Rust.
Tout d’abord, modifiez le lib.rs
déposer:
// The imported rt-thread functions list
extern "C" {
pub fn rt_kprintf(format: *const u8, ...);
}
#[no_mangle]
pub extern "C" fn add(a: i32, b: i32) -> i32 {
unsafe {
rt_kprintf(b"this is from rustn" as *const u8);
}
a + b
}
Ensuite, générez le fichier de bibliothèque :
$ cargo build --target=armv7a-none-eabi --release --verbose
Compiling rust_to_c v0.1.0
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
Finished release [optimized] target(s) in 0.11s
Et maintenant, pour exécuter le code, copiez les fichiers de bibliothèque générés par Rust dans le répertoire de l’application et reconstruisez :
$ scons -j6 scons: Reading SConscript files ... scons: done reading SConscript files. [...]
scons: Building targets ... scons: done building targets.
Exécutez à nouveau QEMU pour voir les résultats dans votre image intégrée.
Tu peux tout avoir
L’utilisation de Rust pour votre développement intégré vous offre toutes les fonctionnalités de Rust sans sacrifier la flexibilité ou la stabilité. Essayez Rust sur votre système embarqué dès aujourd’hui. Pour plus d’informations sur le processus de Rust intégré (et sur RT-Thread lui-même), consultez le projet RT-Thread Chaîne Youtube. Et n’oubliez pas que l’embarqué peut aussi être ouvert.