Rouille intégrée. Développement pour les processeurs Cortex-M3 utilisant la carte de débogage STM32F103C8T6 (Black Pill)

salut! Je veux vous présenter le projet Rust Embedded . Il nous permet d'utiliser le langage de programmation Rust pour le développement de plateformes embarquées (Embedded Linux / RTOS / Bare Metal).


Dans cet article, nous examinerons les composants nécessaires pour commencer le développement des microprocesseurs Cortex-M3. Après cela, nous écrirons un exemple simple - le clignotement de la LED intégrée.

Pour ce faire, nous avons besoin d'une carte de débogage chinoise abordable et bon marché STM32F103C8T6 ou d'une pilule noire (en raison de la couleur noire et de la petite taille). Il existe également une version de la planche en bleu - Blue Pill. Je ne le recommande pas, car j'ai entendu qu'il a la mauvaise résistance, ce qui pose des problèmes lors de l'utilisation du port USB. Sa deuxième différence avec Black Pill dans la disposition des broches (conclusions). Mais plus là-dessus plus tard.

Nous aurons également besoin d'un programmeur pour les cartes de débogage STM32. Dans cet article, nous utiliserons le programmateur chinois ST-Link V2 bon marché et abordable.


Version du compilateur de rouille


Pour commencer, vous devez vous assurer que la version de votre compilateur est 1.31 ou plus récente. Vous pouvez vérifier la version de votre compilateur en entrant la commande:

> rustc --version

Installation des composants


Nous pouvons maintenant commencer à installer les composants nécessaires.

Chaîne d'outils intégrée GNU Arm


Nous aurons besoin d'un débogueur pour les puces ARM - arm-none-eabi-gdb. Lancez le programme d'installation et suivez les instructions. À la fin de l'installation, assurez-vous de cocher l'option " Ajouter le chemin d'accès à la variable d'environnement ".

Lien de téléchargement

Après l'installation, vous pouvez vérifier si tout est en ordre en entrant ce qui suit sur la ligne de commande:

> arm-none-eabi-gdb -v

Si tout est en ordre, vous verrez la version du composant installé.

Pilote ST-Link


Passons à l'installation du pilote pour le programmeur ST-Link.

Suivez les instructions du programme d'installation et assurez-vous d'installer la version correcte ( soixante-quatre bits ou trente-deux bits ) du pilote, en fonction de la profondeur de bits de votre système d'exploitation.

Lien pour télécharger

Outils ST-Link


L'étape suivante consiste à installer les outils nécessaires au micrologiciel - Outils ST-Link. Téléchargez l'archive et décompressez-la à n'importe quel endroit approprié, vous devez également spécifier le chemin d'accès au sous - dossier bin ( stlink-1.3.0 \ bin ) dans la variable d'environnement " PATH ".

Lien pour télécharger

binutils de cargaison


Enfin, installez le paquet cargo-binutils, cela se fait par deux commandes dans la console.

> cargo install cargo-binutils
> rustup component add llvm-tools-preview

Ceci termine l'installation des composants.

Création et configuration d'un projet


Pour continuer, nous devons créer un nouveau projet avec le nom " stm32f103c8t6 ". Je vous rappelle que cela se fait par la commande:

> cargo new stm32f103c8t6

Dépendances et optimisation du projet


Connectez les bibliothèques nécessaires dans le fichier Cargo.toml :

[package]
name = "stm32f103c8t6"
version = "0.1.0"
authors = ["Nick"]
edition = "2018"

#      Cortex-M3
[dependencies]
cortex-m = "*"
cortex-m-rt = "*"
cortex-m-semihosting = "*"
panic-halt = "*"
nb = "0.1.2"
embedded-hal = "0.2.3"

#       stm32f1
[dependencies.stm32f1xx-hal]
version = "0.5.2"
features = ["stm32f100", "rt"]

#   `cargo fix`!
[[bin]]
name = "stm32f103c8t6"
test = false
bench = false

#   
[profile.release]
codegen-units = 1 #  
debug = true #  ,     Flash 
lto = true #  

De plus, vous pouvez voir qu'à la fin du fichier, l'optimisation du code a été activée.

La bibliothèque cortex-m-rt nous oblige à créer un fichier dans le répertoire racine du projet, il doit s'appeler " memory.x ". Il indique la quantité de mémoire de notre appareil et son adresse:

MEMORY
{
 FLASH : ORIGIN = 0x08000000, LENGTH = 64K
 RAM : ORIGIN = 0x20000000, LENGTH = 20K
}

Définition de la plate-forme cible et des objectifs de compilation par défaut


Il est nécessaire de définir la plate-forme cible pour le compilateur, cela se fait par la commande:

> rustup target add thumbv7m-none-eabi

Après cela, créez le dossier " .cargo " dans le répertoire racine et le fichier " config " dedans .

Le contenu du fichier de configuration :

[target.thumbv7m-none-eabi]

[target.'cfg(all(target_arch = "arm", target_os = "none"))']

rustflags = ["-C", "link-arg=-Tlink.x"]

[build]
target = "thumbv7m-none-eabi"  # Cortex-M3

Dans celui-ci, nous avons identifié l'objectif de compilation par défaut, cela nous permet de compiler notre code dans un fichier ARM .elf avec une commande simple et familière:

> cargo build --release

Exemple


Pour vous assurer que cela fonctionne, considérez l'exemple le plus simple - faire clignoter une LED.

Le contenu du fichier main.rs :

#![deny(unsafe_code)]
#![no_std]
#![no_main]

use panic_halt as _;

use nb::block;

use stm32f1xx_hal::{
    prelude::*,
    pac,
    timer::Timer,
};
use cortex_m_rt::entry;
use embedded_hal::digital::v2::OutputPin;

//   .
#[entry]
fn main() -> ! {
    
    //     
    let cp = cortex_m::Peripherals::take().unwrap();
    let dp = pac::Peripherals::take().unwrap();
    let mut flash = dp.FLASH.constrain();
    let mut rcc = dp.RCC.constrain();

    let clocks = rcc.cfgr.freeze(&mut flash.acr);
    let mut gpiob = dp.GPIOB.split(&mut rcc.apb2);

    //   b12   .
    //  "crh"      .
    //   0-7,    "crl".
    let mut led = gpiob.pb12.into_push_pull_output(&mut gpiob.crh);
    //        .
    let mut timer = Timer::syst(cp.SYST, &clocks)
    .start_count_down(1.hz());

    //     
    //    .
    loop {
        block!(timer.wait()).unwrap();
        led.set_high().unwrap();
        block!(timer.wait()).unwrap();
        led.set_low().unwrap();
    }
}

Dans la première ligne, en utilisant l'attribut - #![deny(unsafe_code)], nous supprimons la possibilité d'utiliser du code dangereux.

Dans la deuxième ligne, nous avons placé l'attribut #![no_std], il est obligatoire, car nous créons une application pour le fer nu, et la bibliothèque standard a besoin d'un système d'exploitation.

Vient ensuite l'attribut #![no_main]qui indique au compilateur que nous n'utilisons pas la fonction par défaut par défaut avec le vecteur d'argument et le type de retour. Cela n'a pas de sens, car nous n'avons pas de système d'exploitation ou autre runtime qui appelle la fonction et traite la valeur de retour.

Après les attributs, nous connectons les modules nécessaires à la portée.

Ceci est suivi d'une fonction, dans notre cas, par habitude, nous l'avons appelé -main(). Il s'agit du point d'entrée du programme, il est déterminé par l'attribut #[entry].

À l'intérieur de la fonction principale, nous créons une poignée vers un objet périphérique qui «possède» tous les périphériques.

Après cela, nous pouvons définir la broche responsable de la LED intégrée dans la carte à la sortie. Étant donné que dans mon cas, il s'agit d'une carte Black Pill , la LED qu'elle contient est connectée à la broche B12 . Si vous avez une carte Blue Pill , la broche C13 est responsable de la LED intégrée . Par conséquent, nous définissons la broche B12 comme sortie push-pull et l'affectons à une variable led.

Maintenant, nous pouvons définir l'heure de déclenchement de l'événement de mise à jour pour le minuteur système et l'enregistrer dans une variabletimer. Dans notre exemple, ce temps est d'une seconde.

Ensuite, dans une boucle sans fin, nous pouvons décrire la logique de notre programme. C'est très simple, timerbloque tout d' abord l'exécution du programme jusqu'à ce que le temps spécifié dans celui-ci soit écoulé. L'étape suivante consiste à régler la broche de la LED intégrée sur haute , afin qu'elle s'allume. Après cela, l'exécution du programme est à nouveau bloquée par le même temporisateur, pendant une seconde. Et la broche de la LED est réglée sur faible , elle s'éteint donc. De plus, le programme est répété cycliquement.

Compilation


Maintenant que nous avons compris le code et la logique du programme, nous pouvons le compiler dans un fichier .elf avec la commande:

> cargo build --release

Ce format contient non seulement du code binaire, mais aussi quelques en-têtes et plus encore. Ceci est utile lorsque le fichier est lancé par un autre logiciel, tel que le système d'exploitation ou le chargeur de démarrage.

Mais, pour exécuter le programme sur du métal nu, nous n'avons pas besoin d'un fichier .elf , mais d'un fichier .bin . Un fichier .bin est une image logicielle qui peut être écrite en octets dans la mémoire du microcontrôleur. Le fichier .elf peut être converti en fichier .bin à l'aide de la commande:

> cargo objcopy --bin stm32f103c8t6 --target thumbv7m-none-eabi --release -- -O binary stm32f103c8t6.bin

Maintenant, notre fichier binaire est prêt pour le firmware.

Connexion


Nous flasherons la carte à l'aide du programmateur ST-Link V2. Pour ce faire, vous devez d'abord le connecter à la carte.

Sur le boîtier du programmateur, il y a une disposition de broches du connecteur. Vous devez faire attention à la découpe dans le connecteur, elle est également affichée sur le schéma, ce qui vous permet de comprendre comment effectuer la connexion.

Connectez les broches 3,3 V et GND du programmateur aux broches correspondantes sur la carte. Ping SWDIO se connecte pour épingler le DIO , et pin SWCLK pour épingler le CLK .

Après avoir connecté la carte au programmateur, nous pouvons connecter le ST-Link à l'ordinateur.

Attention!Avant de vous connecter, déconnectez toutes les autres sources d'alimentation de la carte, le cas échéant, sinon cela pourrait endommager la carte ou l'ordinateur.

Maintenant, nous pouvons vérifier la connexion:

> st-info --descr

Dans la console, nous devrions voir la ligne " F1 Appareil à densité moyenne ".

Firmware


En option, avant de flasher, vous pouvez effacer la mémoire de la carte si quelque chose y est écrit:

> st-flash erase

Enfin, nous pouvons démarrer le firmware:

> st-flash write stm32f1.bin 0x8000000

Si tout est fait correctement, vous devriez voir une LED clignotante. Toutes nos félicitations!

Conclusion


Donc, pour flasher le tableau, vous devez utiliser cet ensemble de commandes entrées séquentiellement:

> cargo build --release
> cargo objcopy --bin stm32f103c8t6 --target thumbv7m-none-eabi --release -- -O binary stm32f103c8t6.bin
> st-flash erase
> st-flash write stm32f1.bin 0x8000000

Pour simplifier notre vie, nous pouvons créer un fichier .bat avec ces commandes et l'exécuter à partir de la console.

De l'auteur


Si vous êtes intéressé par ces guides, bienvenue sur ma chaîne YouTube .
De plus, récemment, j'ai lancé une chaîne Telegram où je publie diverses traductions de manuels et de livres, des actualités, de l'humour et d'autres choses liées au langage de programmation Rust. Vous y trouverez des liens vers plusieurs salons de discussion afin de poser vos questions et obtenir de l'aide, soumettre vos projets ou simplement discuter avec des personnes partageant les mêmes idées.

Merci pour l'attention!

All Articles