Rust para Embebidos?
Tags: rust, arm, electronic, programing
Published: 2020-07-20 04:00:38

---
title: Rust para Embebidos?
published: true
description: Aprende como progrmaar uC ARM con Rust muy facil
tags: rust, arm, electronic, programing
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/i3ht45i8d252lwes1pic.jpg
---

Durante los días pasados estuve leyendo sobre el tema para preparar este post, es mucha información y que lo mas seguro termine siendo toda una serie.

Quiero agradecer tanto al equipo de [Electronic cats](https://electroniccats.com) como al equipo de de [nrf-rs](https://github.com/nrf-rs) que estuvieron ayudándome a resolver dudas sobre el hardware y el funcionamiento de las bibliotecas.

## Material
Para la practica de hoy requerimos una placa con `nrf52840` como lo es la `Bast BLE` y un debuger, yo usare el [Black magic Probe](https://dev.to/iddar/crea-un-black-magic-probe-con-un-stm32f103-5gnj) que vimos como programar en post anteriores.

### Software
En tu sistema debes contar con el [toolchain de ARM](https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads) instalado y agregado al path, [Rust](https://www.rust-lang.org/) para este post se usa la versión `1.45.0` para el proyecto final usare GNU Make para cargar el firmware.

### Estructura de un proyecto.
En este ejemplo quiero platicar como iniciar un proyecto de Rust para embebidos desde cero. Por lo cual debo comentar varias partes previas a código de nuestro uC.

```shell
$ mkdir nfr-blik
$ cd nrf-blink
$ cargo init
```

Creamos la carpeta para nuestro proyecto y dentro ejecutamos `cargo init`, **cargo** es un el manejador de paquetes de Rust y con el comando `init` le indicamos que cree un proyecto nuevo. El resultado de este comando se debe ver algo así:

```
.
├── Cargo.toml
└── src
    └── main.rs

```

Esta es la estructura base de cualquier proyecto en Rust: 

- `src`: este directorio almacena todos el codigo de nuestro proyecto, si vemos incluye por defecto un `Hello world` en el archivo `main.rs`
- `Cargo.toml`: este archivo incluye la descripción de nuestro proyecto y las dependencias del mismo.

### Dependencias

Para nuestro proyecto requerimos tres dependencias, las cuales nos facilitan el trabajo. Abre el archivo `Cargo.toml` en tu editor favorito y agrega las dependencias justo debajo de`[dependancies]` ver el ejemplo a continuación:

```toml
[dependencies]
nrf52840-hal = "0.11.0"
cortex-m-rt = "0.6.12"
panic-halt = "0.2.0"
```

- `cortex-m-rt`: esta el la biblioteca base para trabajar con ARM Cortex-M incluye las configuraciones base del sistema manejo de memoria, registro etc, que son comunes en toda la familia microcontroladores.

- `panic-halt`: Siempre debemos indicar un handler en caso de que algo salga mal, este paquete nos ahorra este paso dotándonos de una función genérica para esto.

- `nrf52840-hal`: esta es una capa de abstracción creada por el equipo de `nrf-rs` la cual tiene mapeados todos los registro del uC al igual de dotarnos de algunas funciones y utilerias que veremos mas adelante.

### Target
Debemos indicar al compilador de Rust cual es el target para nuestro binario, en este caso la plataforma es ARM; para esto crearemos un carpeta `.cargo` en la raiz de nuestro proyecto y dentro un archivo de nombre `config` en el colocaremos las derectiva, aprovecharemos para agregar el soporte para arm con el comando `rustup`.

```shell
$ mkdir .cargo # el nombre de la carpeta inicia con un punto
$ touch .cargo/confg # con este comando creamos el archivo
$ rustup target add thumbv7em-none-eabihf
```
Al archivo `.cargo/confg` agregamos las siguientes lineas
```
[build]
target = "thumbv7em-none-eabihf"
```

### Hora del código

El momento esperado llego veamos como podemos controlar nuestro uC. Colocare el código y luego explicare cada parte

```rust
#![no_std]
#![no_main]

extern crate panic_halt;
extern crate nrf52840_hal;
use cortex_m_rt::entry;

use nrf52840_hal::prelude::*;
use nrf52840_hal::pac::Peripherals;
use nrf52840_hal::gpio::*;
use nrf52840_hal::Timer;

#[entry]
fn main() -> ! {
    let board = Peripherals::take().unwrap();
    let p0 = p0::Parts::new(board.P0);
    let mut led1 = p0.p0_24
        .into_push_pull_output(Level::High);

    let mut timer = Timer::new(board.TIMER0);

    loop {
        led1.set_low().unwrap();
        timer.delay(1_000_000);
        led1.set_high().unwrap();
        timer.delay(1_000_000);
    }
}
```

Como puedes ver son muy pocas lineas las que se requieren para esta tarea. Veamos cada una a detalle:

- `#![no_std]` y `#![no_main]` la primera de ellas le dice al compilador que el programa debe compilarse sin la biblioteca entandar ya que no corre sobre un sistema operativo (no tenemos files ni sockets), la segunda que no tiene un punto de entrada definido.

- Luego tenemos la sección de dependencias que ya hablamos antes.

- `#[entry]` justo antes de la definición de nuestra función `main` vemos esta instrucción que lo que le indica al compilador es que esta sera principal de nuestro programa.

- `fn main() -> ! {}`: si ya habías programado en rust antes seguro esto te llama la atención; el tipo de retorno `!` indica que esta función nunca terminara, esto porque dentro tenemos un bucle infinito.

- `Peripherals::take()` aquí creamos una instancia de nuestro uC esta es la forma en la que funcionan todos los Cortex-M en rust y desde aquí podemos interactuar e inicializar los periféricos.

- `p0::Parts::new(board.P0)` aquí inicializamos el reloj de puerto 0, veremos mas sobre como funciona los relojes y los periféricos en ARM en aproximas entregas.

- `p0.p0_24.into_push_pull_output(Level::High)`con figuramos el pin como `push_pull` (te recomiendo ver el esquema de los pines si tienes dudas sobre como funciona esto.) este nos regresa una instancia `Pin` que usaremos para configurar el estado del mismo. En este ejemplo el pin que utilizamos es el `24` del puerto `0`

- `Timer::new(board.TIMER0)` para poder generar un retraso para poder parpadear el led usaremos un instancia del Timer `0`.

- `loop {}` este es un bucle infinito equivalente a cuando usamos el metodo loop en un sketch de arduino.

- `led1.set_low()` y `led1.set_high()` son métodos utilitarios para cambiar el estado de los pines. 

- `timer.delay(1_000_000);` utilizando la instancia del Timer `0` podemos ejecutar su método `delay` el cual genera un retardo.


### Cargar el programa

Antes de grabar el programa debemos generar el binario para esto nos ayudamos de `cargo` como vemos a continuación:

```
$ cargo build --release
```

Esto nos genera un archivo en binario en la ruta

```
target/thumbv7em-none-eabihf/release/nrf-blink
```
Este archivo lo usaremos para grabar nuestro micro. Tal como vimos en articulos anteriores conectamos nuestra placa al depurador y ejecutamos `arm-none-eabi-gdb`

```shell
$ arm-none-eabi-gdb
# Con esto entramos a la consola para debug
GNU gdb (GNU Arm Embedded Toolchain)
For help, type "help".
(gdb)
(gdb) target extended-remote /dev/ttyACM0
(gdb) monitor swdp_scan
  > Target voltage: 2.9V
  > Available Targets:
  > No. Att Driver
  >   1      Nordic nRF52 M4
  >   2      Nordic nRF52 Access Port
(gdb) attach 1
(gdb) load target/thumbv7em-none-eabihf/release/nrf-blink
(gdb) compare-sections
(gdb) detach
# ctrl + d para salir
```

Listo tenemos nuestro Blink compilado y cargado en nuestro ARM felicidades. 
En los próximos artículos hablaremos sobre los puertos seriales y otros periféricos.