Embedded Rust: A Camada de Abstração

PACs, HALs e Boad Crates. Como o Rust organiza o acesso ao Hardware.

No mundo Rust, não se escreve registradores na mão (a menos que necessário). Usa-se uma pirâmide de abstração.

1. PAC (Peripheral Access Crate) - A Base

Gerada automaticamente (via svd2rust) a partir do arquivo SVD do fabricante do chip. Dá acesso seguro aos registradores brutos.

// Acesso bruto (unsafe-ish feel, mas type safe)
let dp = pac::Peripherals::take().unwrap();
dp.GPIO.out_set.write(|w| w.pin5().set_bit());

É verboso, mas garante que você não escreva o bit errado no registrador errado.

2. HAL (Hardware Abstraction Layer) - O Meio

Implementa as traits do embedded-hal. Transforma registradores brutos em objetos de alto nível.

// Transformando um pino bruto em um Saída Push-Pull
let mut led = gpioa.pa5.into_push_pull_output();

// Agora 'led' tem métodos de alto nível
led.set_high(); // Acende

O legal é o Type State Programming: Um pino configurado como Input não tem o método .set_high(). Se você tentar ligar um pino de entrada, o código não compila. Erro de hardware pego na compilação!

3. Board Crate (BSP) - O Topo

Configuração pronta para sua placa específica (Arduino Uno, STM32 Discovery, ESP32 DevKit). Já define quem é o LED, quem é o botão.

embedded-hal: O Driver Universal

O poder real do Rust. Se eu escrevo um driver para um sensor (ex: MPU6050) usando as traits genéricas I2c do embedded-hal:

  • Esse driver funciona no ESP32.
  • Funciona no STM32.
  • Funciona no Raspberry Pi (Linux).
  • Funciona no nRF52.

Sem mudar uma linha de código do driver. Apenas trocando a implementação da HAL que é passada para ele.

// Driver genérico
pub struct Mpu6050<I2C> { i2c: I2C }

impl<I2C, E> Mpu6050<I2C>
where I2C: WriteRead<Error = E>
{
    pub fn new(i2c: I2C) -> Self { ... }
}
Progresso do Tópico