Ferrugem incorporada. Desenvolvimento para processadores Cortex-M3 usando a placa de depuração STM32F103C8T6 (Black Pill)

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 link

Apó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 download

Ferramentas 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 download

binutils 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);

    //   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();
    }
}

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 timerbloqueia 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!

All Articles