Óxido incrustado. Desarrollo para procesadores Cortex-M3 utilizando la placa de depuración STM32F103C8T6 (Black Pill)

¡Hola! Quiero presentarte el proyecto Rust Embedded . Nos permite utilizar el lenguaje de programación Rust para el desarrollo de plataformas integradas (Linux incorporado / RTOS / Bare Metal).


En este artículo, consideraremos los componentes necesarios para comenzar el desarrollo de los microprocesadores Cortex-M3. Después de eso, escribiremos un ejemplo simple: parpadeo del LED incorporado.

Para hacer esto, necesitamos una placa de depuración china asequible y barata STM32F103C8T6 o Black Pill (debido al color negro y al tamaño pequeño). También hay una versión del tablero en azul: Blue Pill. No lo recomiendo, ya que escuché que tiene una resistencia incorrecta, lo que causa problemas al usar el puerto USB. Su segunda diferencia de la píldora negra en la disposición de los pines (conclusiones). Pero más sobre eso más tarde.

También necesitaremos un programador para placas de depuración STM32. En este artículo, utilizaremos el programador chino ST-Link V2 barato y asequible.


Versión del compilador de óxido


Para comenzar, debe asegurarse de que su versión del compilador sea 1.31 o más reciente. Puede verificar la versión de su compilador ingresando el comando:

> rustc --version

Instalación de componentes


Ahora podemos comenzar a instalar los componentes necesarios.

GNU Arm Embedded Toolchain


Necesitaremos un depurador para chips ARM: arm-none-eabi-gdb. Inicie el instalador y siga las instrucciones. Al final de la instalación, asegúrese de marcar la opción " Agregar ruta a la variable de entorno ".

Enlace de descarga

Después de la instalación, puede verificar si todo está en orden ingresando lo siguiente en la línea de comando:

> arm-none-eabi-gdb -v

Si todo está en orden, verá la versión del componente instalado.

Controlador ST-Link


Pasemos a instalar el controlador para el programador ST-Link.

Siga las instrucciones del instalador y asegúrese de instalar la versión correcta ( sesenta y cuatro bits o treinta y dos bits ) del controlador, dependiendo de la profundidad de bits de su sistema operativo.

Enlace para descargar

Herramientas ST-Link


El siguiente paso es instalar las herramientas necesarias para el firmware: Herramientas ST-Link. Descargue el archivo comprimido y descomprímalo en cualquier lugar conveniente, también debe especificar la ruta a la subcarpeta bin ( stlink-1.3.0 \ bin ) en la variable de entorno " PATH ".

Enlace para descargar

carga-binutils


Finalmente, instale el paquete cargo-binutils, esto se realiza mediante dos comandos en la consola.

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

Esto completa la instalación de componentes.

Crear y configurar un proyecto


Para continuar, necesitamos crear un nuevo proyecto con el nombre " stm32f103c8t6 ". Déjame recordarte que esto se hace mediante el comando:

> cargo new stm32f103c8t6

Dependencias y Optimización de Proyectos


Conecte las bibliotecas necesarias en el archivo 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 #  

Además, puede ver que, al final del archivo, se ha habilitado la optimización del código.

La biblioteca cortex-m-rt requiere que creemos un archivo en el directorio raíz del proyecto, debe llamarse " memory.x ". Indica cuánta memoria tiene nuestro dispositivo y su dirección:

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

Establecer la plataforma objetivo y los objetivos de compilación predeterminados


Es necesario establecer la plataforma de destino para el compilador, esto se hace mediante el comando:

> rustup target add thumbv7m-none-eabi

Después de eso, cree la carpeta " .cargo " en el directorio raíz y el archivo " config " en él .

El contenido del archivo de configuración :

[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

En él, identificamos el objetivo de compilación predeterminado, esto nos permite compilar nuestro código en un archivo ARM .elf con un comando simple y familiar:

> cargo build --release

Ejemplo


Para asegurarse de que funciona, considere el ejemplo más simple: parpadear un LED.

El contenido del archivo 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();
    }
}

En la primera línea, usando el atributo - #![deny(unsafe_code)], eliminamos la posibilidad de usar código inseguro.

En la segunda línea, colocamos el atributo #![no_std], es obligatorio, porque estamos creando una aplicación para hierro puro, y la biblioteca estándar necesita un sistema operativo.

Luego viene un atributo #![no_main]que le dice al compilador que no estamos usando la función predeterminada predeterminada con el vector de argumento y el tipo de retorno. Esto no tiene sentido, ya que no tenemos un sistema operativo u otro tiempo de ejecución que llame a la función y procese el valor de retorno.

Después de los atributos, conectamos los módulos necesarios en el ámbito.

Esto es seguido por una función, en nuestro caso, por costumbre, la llamamos:main(). Este es el punto de entrada al programa, está determinado por el atributo #[entry].

Dentro de la función principal, creamos un identificador para un objeto periférico que "posee" todos los dispositivos periféricos.

Después de eso, podemos configurar el pin responsable de la salida del LED integrado en la placa. Como en mi caso es una placa de píldora negra , el LED está conectado al pin B12 . Si tiene una placa de píldora azul , entonces el pin C13 es responsable del LED incorporado . Por lo tanto, configuramos el pin B12 como una salida push-pull y lo asignamos a una variable led.

Ahora podemos establecer el tiempo de activación del evento de actualización para el temporizador del sistema y guardarlo en una variabletimer. En nuestro ejemplo, esta vez es un segundo.

Luego, en un bucle sin fin, podemos describir la lógica de nuestro programa. Es muy simple, primero timerbloquea la ejecución del programa hasta que el tiempo especificado en él haya pasado. El siguiente paso es configurar el pin del LED incorporado en alto , para que se ilumine. Después de eso, la ejecución del programa se bloquea nuevamente por el mismo temporizador, durante un segundo. Y el pin del LED está configurado en bajo , por lo tanto se apaga. Además, el programa se repite cíclicamente.

Compilacion


Ahora que hemos descubierto el código y la lógica del programa, podemos compilarlo en un archivo .elf con el comando:

> cargo build --release

Este formato contiene no solo código binario, sino también algunos encabezados y más. Esto es útil cuando el archivo se inicia con otro software, como el sistema operativo o el gestor de arranque.

Pero, para ejecutar el programa en el bare metal, no necesitamos un archivo .elf , sino un archivo .bin . Un archivo .bin es una imagen de software que puede escribirse en bytes en la memoria del microcontrolador. El archivo .elf se puede convertir a un archivo .bin usando el comando:

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

Ahora nuestro archivo binario está listo para el firmware.

Conexión


Vamos a flashear la placa usando el programador ST-Link V2. Para hacer esto, primero debe conectarlo a la placa.

En la caja del programador hay una disposición de pines del conector. Debe prestar atención al recorte en el conector, también se muestra en el diagrama, lo que le permite comprender cómo realizar la conexión.

Conecte los pines 3.3V y GND del programador con los pines correspondientes en la placa. Haga ping a SWDIO connect para fijar el DIO y pin SWCLK para fijar el CLK .

Después de conectar la placa al programador, podemos conectar el ST-Link a la computadora.

¡Atención!Antes de conectar, desconecte todas las demás fuentes de alimentación de la placa, si las hay, de lo contrario podría dañar la placa o la computadora.

Ahora podemos verificar la conexión:

> st-info --descr

En la consola, deberíamos ver la línea " F1 Dispositivo de densidad media ".

Firmware


Opcionalmente, antes de flashear, puede borrar la memoria de la pizarra si hay algo escrito en ella:

> st-flash erase

Finalmente, podemos iniciar el firmware:

> st-flash write stm32f1.bin 0x8000000

Si todo se hace correctamente, debería ver un LED parpadeante. ¡Felicidades!

Conclusión


Entonces, para actualizar el tablero, debe usar este conjunto de comandos ingresados ​​secuencialmente:

> 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 nuestra vida, podemos crear un archivo .bat con estos comandos y ejecutarlo desde la consola.

Del autor


Si está interesado en estas guías, bienvenido a mi canal de YouTube .
Además, recientemente, comencé un canal de Telegram donde publico varias traducciones de manuales y libros, noticias, humor y otras cosas relacionadas con el lenguaje de programación Rust. Allí puede encontrar enlaces a varias salas de chat para hacer sus preguntas y obtener ayuda, enviar sus proyectos o simplemente chatear con personas de ideas afines.

¡Gracias por la atención!

All Articles