std1.0.0-beta-7 SafeC docs

Source hal/gpio.h

View in the repository

// SafeC Standard Library — GPIO HAL
// Memory-mapped GPIO pin abstraction. Freestanding-safe.
//
// GpioPin<C> is generic over the chip's GPIO block, a compile-time type
// whose methods do the register work:
//   GpioGeneric   reference layout: DIR +0x00, OUT +0x04, IN +0x08,
//                 PULL_EN +0x0C, PULL_SEL +0x10 (1 = up), one bit per pin
//   Stm32F4Gpio   STM32F2/F4/F7/L4/G0/H7-style port: MODER, PUPDR, IDR,
//                 ODR, BSRR (atomic set/reset)
//
//   struct GpioPin<Stm32F4Gpio> led = std::GpioPin::new(gpioa, 5, GPIO_OUTPUT);
//   led.write(1);
#pragma once

// Pin direction
#define GPIO_INPUT  0
#define GPIO_OUTPUT 1

// Pin pull configuration
#define GPIO_PULL_NONE 0
#define GPIO_PULL_UP   1
#define GPIO_PULL_DOWN 2

namespace std {

// What a GPIO block type provides (GpioGeneric, Stm32F4Gpio below, or
// your own): GpioPin<C> requires it, so a type missing one is rejected
// where the pin type is named.
trait GpioChip {
    void set_direction(unsigned long base, int pin, int direction) const;
    void write(unsigned long base, int pin, int value) const;
    int  read(unsigned long base, int pin) const;
    void toggle(unsigned long base, int pin) const;
    void set_pull(unsigned long base, int pin, int pull) const;
}

struct GpioGeneric {
    int  unused_;
    void set_direction(unsigned long base, int pin, int direction) const;
    void write(unsigned long base, int pin, int value) const;
    int  read(unsigned long base, int pin) const;
    void toggle(unsigned long base, int pin) const;
    void set_pull(unsigned long base, int pin, int pull) const;
};

struct Stm32F4Gpio {
    int  unused_;
    void set_direction(unsigned long base, int pin, int direction) const;
    void write(unsigned long base, int pin, int value) const;
    int  read(unsigned long base, int pin) const;
    void toggle(unsigned long base, int pin) const;
    void set_pull(unsigned long base, int pin, int pull) const;
};

generic<C: GpioChip>
struct GpioPin {
    &volatile unsigned int base; // MMIO base address of the GPIO port
    int          pin;        // pin number within the port
    unsigned int pin_mask;   // 1 << pin
    int          direction;  // GPIO_INPUT or GPIO_OUTPUT
    C            chip;

    // Initialize a GPIO pin: `base` = port base, `pin` = pin number (0-31; 0-15
    // on STM32). The chip type comes from the target:
    //   struct GpioPin<GpioGeneric> p = std::GpioPin::new(base, 3, GPIO_INPUT);
    static struct GpioPin<C> new(&volatile unsigned int base, int pin, int direction);

    // Set pin direction (GPIO_INPUT or GPIO_OUTPUT).
    void         set_direction(int direction);
    // Write 1 (high) or 0 (low) to an output pin.
    void         write(int value);
    // Read pin state. Returns 0 or 1.
    int          read() const;
    // Toggle output pin.
    void         toggle();
    // GPIO_PULL_NONE, GPIO_PULL_UP or GPIO_PULL_DOWN.
    void         set_pull(int pull);
    unsigned long addr_() const;
};

generic<C: GpioChip>
struct GpioPin<C> GpioPin::new(&volatile unsigned int base, int pin, int direction) {
    struct GpioPin<C> p;
    p.base = base;
    p.pin = pin;
    p.pin_mask = (unsigned int)1 << pin;
    p.direction = direction;
    p.chip.set_direction(p.addr_(), pin, direction);
    return p;
}

generic<C: GpioChip>
unsigned long GpioPin::addr_() const { unsafe { return (unsigned long)self.base; } }
generic<C: GpioChip>
void GpioPin::set_direction(int direction) {
    self.direction = direction;
    self.chip.set_direction(self.addr_(), self.pin, direction);
}
generic<C: GpioChip>
void GpioPin::write(int value) { self.chip.write(self.addr_(), self.pin, value); }
generic<C: GpioChip>
int GpioPin::read() const { return self.chip.read(self.addr_(), self.pin); }
generic<C: GpioChip>
void GpioPin::toggle() { self.chip.toggle(self.addr_(), self.pin); }
generic<C: GpioChip>
void GpioPin::set_pull(int pull) { self.chip.set_pull(self.addr_(), self.pin, pull); }

} // namespace std