Olá! Quero apresentá-lo ao projeto Rust Embedded . Ele nos permite usar a linguagem de programação Rust para desenvolvimento de plataformas embarcadas (Embedded Linux / RTOS / Bare Metal).
Neste artigo, consideraremos os componentes necessários para iniciar o desenvolvimento dos microprocessadores Cortex-M3. Depois disso, escreveremos um exemplo simples - piscando no LED embutido.Para fazer isso, precisamos de uma placa de depuração chinesa acessível e barata STM32F103C8T6 ou Black Pill (devido à cor preta e tamanho pequeno). Há também uma versão do quadro em azul - Blue Pill. Não recomendo, pois ouvi dizer que o resistor está errado, o que causa problemas ao usar a porta USB. Sua segunda diferença em relação à pílula negra no arranjo dos pinos (conclusões). Mas mais sobre isso mais tarde.Também precisaremos de um programador para placas de depuração STM32. Neste artigo, usaremos o programador chinês ST-Link V2 barato e acessível.
Versão do Rust Compiler
Para começar, você precisa garantir que sua versão do compilador seja 1.31 ou mais recente. Você pode verificar sua versão do compilador digitando o comando:> rustc --version
Instalação de componentes
Agora podemos começar a instalar os componentes necessários.Cadeia de ferramentas incorporada GNU Arm
Vamos precisar de um depurador para chips ARM - arm-none-eabi-gdb. Inicie o instalador e siga as instruções. No final da instalação, verifique a opção " Adicionar caminho à variável de ambiente ".Download linkApós a instalação, você pode verificar se está tudo em ordem digitando o seguinte na linha de comando:> arm-none-eabi-gdb -v
Se tudo estiver em ordem, você verá a versão do componente instalado.Driver ST-Link
Vamos seguir para a instalação do driver do programador ST-Link.Siga as instruções do instalador e certifique-se de instalar a versão correta ( sessenta e quatro bits ou trinta e dois bits ) do driver, dependendo da profundidade de bits do seu sistema operacional.Link para downloadFerramentas ST-Link
O próximo passo é instalar as ferramentas necessárias para o firmware - ST-Link Tools. Faça o download do arquivo morto e descompacte em qualquer local conveniente. Você também precisa especificar o caminho para a subpasta bin ( stlink-1.3.0 \ bin ) na variável de ambiente " PATH ".Link para downloadbinutils de carga
Por fim, instale o pacote cargo-binutils, isso é feito por dois comandos no console.> cargo install cargo-binutils
> rustup component add llvm-tools-preview
Isso completa a instalação dos componentes.Criando e configurando um projeto
Para continuar, precisamos criar um novo projeto com o nome " stm32f103c8t6 ". Deixe-me lembrá-lo, isso é feito pelo comando:> cargo new stm32f103c8t6
Dependências e otimização do projeto
Conecte as bibliotecas necessárias no arquivo 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 #
Além disso, você pode ver que, no final do arquivo, a otimização de código foi ativada.A biblioteca cortex-m-rt exige que criemos um arquivo no diretório raiz do projeto, e ele deve ser chamado " memory.x ". Indica quanta memória nosso dispositivo possui e seu endereço:MEMORY
{
FLASH : ORIGIN = 0x08000000, LENGTH = 64K
RAM : ORIGIN = 0x20000000, LENGTH = 20K
}
Definindo a plataforma de destino e as metas de compilação padrão
É necessário definir a plataforma de destino para o compilador, isso é feito pelo comando:> rustup target add thumbv7m-none-eabi
Depois disso, crie a pasta " .cargo " no diretório raiz e o arquivo " config " nele .O conteúdo do arquivo de configuração :[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
Nele, identificamos o objetivo de compilação padrão, isso nos permite compilar nosso código em um arquivo .elf do ARM com um comando simples e familiar:> cargo build --release
Exemplo
Para garantir que funcione, considere o exemplo mais simples: piscar um LED.O conteúdo do arquivo 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);
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();
}
}
Na primeira linha, usando o atributo - #![deny(unsafe_code)]
, removemos a possibilidade de usar código não seguro.Na segunda linha, colocamos o atributo #![no_std]
, é necessário, porque estamos criando um aplicativo para o ferro puro e a biblioteca padrão precisa de um sistema operacional.Em seguida, vem um atributo #![no_main]
que informa ao compilador que não estamos usando a função padrão padrão com o vetor de argumento e o tipo de retorno. Isso não faz sentido, pois não temos um sistema operacional ou outro tempo de execução que chame a função e processe o valor de retorno.Após os atributos, conectamos os módulos necessários ao escopo.Isso é seguido por uma função, no nosso caso, por hábito, que chamamos demain()
. Este é o ponto de entrada para o programa, ele é determinado pelo atributo #[entry]
.Dentro da função principal, criamos um identificador para um objeto periférico que "possui" todos os dispositivos periféricos.Depois disso, podemos definir o pino responsável pelo LED embutido na placa na saída. Como no meu caso é uma placa Black Pill , o LED está conectado ao pino B12 . Se você possui uma placa Blue Pill , o pino C13 é responsável pelo LED embutido . Portanto, configuramos o pino B12 como uma saída push-pull e o atribuímos a uma variável led
.Agora podemos definir o tempo de disparo do evento de atualização para o timer do sistema e salvá-lo em uma variáveltimer
. No nosso exemplo, esse tempo é de um segundo.Então, em um loop infinito, podemos descrever a lógica do nosso programa. É muito simples, primeiro timer
bloqueia a execução do programa até que o tempo especificado nele tenha passado. O próximo passo é definir o pino do LED embutido para alto , para que acenda. Depois disso, a execução do programa é novamente bloqueada pelo mesmo timer, por um segundo. E o pino do LED está definido como baixo e , portanto, apaga-se. Além disso, o programa é repetido ciclicamente.Compilação
Agora que descobrimos o código e a lógica do programa, podemos compilá-lo em um arquivo .elf com o comando:> cargo build --release
Este formato contém não apenas código binário, mas também alguns cabeçalhos e muito mais. Isso é útil quando o arquivo é iniciado por outro software, como o sistema operacional ou o carregador de inicialização.Mas, para executar o programa no bare metal, não precisamos de um arquivo .elf , mas de um arquivo .bin . Um arquivo .bin é uma imagem de software que pode ser escrita em bytes na memória do microcontrolador. O arquivo .elf pode ser convertido em um arquivo .bin usando o comando:> cargo objcopy --bin stm32f103c8t6 --target thumbv7m-none-eabi --release -- -O binary stm32f103c8t6.bin
Agora nosso arquivo binário está pronto para o firmware.Conexão
Vamos piscar o quadro usando o programador ST-Link V2. Para fazer isso, você deve primeiro conectá-lo à placa.No gabinete do programador, há um arranjo de pinos do conector. Você precisa prestar atenção ao recorte no conector, que também é exibido no diagrama, o que permite entender como fazer a conexão.Conecte os pinos 3.3V e GND do programador com os pinos correspondentes na placa. Faça o ping no SWDIO para fixar o DIO e fixe o SWCLK para fixar o CLK .Após conectar a placa ao programador, podemos conectar o ST-Link ao computador.Atenção!Antes de conectar, desconecte todas as outras fontes de energia da placa, se houver, caso contrário, poderá danificar a placa ou o computador.Agora podemos verificar a conexão:> st-info --descr
No console, devemos ver a linha " Dispositivo de média densidade F1 ".Firmware
Opcionalmente, antes de piscar, você pode limpar a memória do quadro se algo estiver escrito nele:> st-flash erase
Por fim, podemos iniciar o firmware:> st-flash write stm32f1.bin 0x8000000
Se tudo for feito corretamente, você verá um LED piscando. Parabéns!Conclusão
Portanto, para exibir o quadro, você deve usar este conjunto de comandos digitados sequencialmente:> 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
Para simplificar nossa vida, podemos criar um arquivo .bat com esses comandos e executá-lo no console.Do autor
Se você está interessado nesses guias, bem-vindo ao meu canal do YouTube .Recentemente, iniciei um canal Telegram, onde publico várias traduções de manuais e livros, notícias, humor e outras coisas relacionadas à linguagem de programação Rust. Lá você pode encontrar links para várias salas de bate-papo para fazer suas perguntas e obter ajuda, enviar seus projetos ou apenas conversar com pessoas que pensam da mesma forma.Obrigado pela atenção!