std1.0.0-beta-7 SafeC docs

Source hal/spi.h

View in the repository

// SafeC Standard Library — SPI HAL
// Polling-based SPI master driver. Freestanding-safe.
//
// SpiDevice<C> is generic over the SPI block, a compile-time chip type:
//   SpiGeneric    reference layout: DATA +0x00, STATUS +0x04 (bit 0 busy,
//                 bit 1 TX ready), CTRL +0x08 (mode | 0x80 enable),
//                 CS +0x0C (0 = asserted)
//   Stm32F4Spi    STM32F1/F2/F4 SPI as master with software NSS; chip select
//                 is an ordinary GPIO there (cs_assert/cs_deassert do
//                 nothing — drive a GpioPin)
#pragma once

// SPI modes (CPOL/CPHA)
#define SPI_MODE_0 0  // CPOL=0, CPHA=0
#define SPI_MODE_1 1  // CPOL=0, CPHA=1
#define SPI_MODE_2 2  // CPOL=1, CPHA=0
#define SPI_MODE_3 3  // CPOL=1, CPHA=1

namespace std {

// What an SPI controller type provides; SpiDevice<C> requires it.
trait SpiChip {
    void          init(unsigned long base, int mode) const;
    unsigned char transfer(unsigned long base, unsigned char tx) const;
    void          cs(unsigned long base, int asserted) const;
}

struct SpiGeneric {
    int           unused_;
    void          init(unsigned long base, int mode) const;
    unsigned char transfer(unsigned long base, unsigned char tx) const;
    void          cs(unsigned long base, int asserted) const;
};
struct Stm32F4Spi {
    int           unused_;
    void          init(unsigned long base, int mode) const;   // master, fPCLK/16, 8-bit, MSB first
    unsigned char transfer(unsigned long base, unsigned char tx) const;
    void          cs(unsigned long base, int asserted) const;
};

generic<C: SpiChip>
struct SpiDevice {
    &volatile unsigned int base; // MMIO base address
    int          mode;  // SPI mode (0-3)
    C            chip;

    // Initialize an SPI master at `base` with the given mode; the chip type
    // comes from the target.
    static struct SpiDevice<C> new(&volatile unsigned int base, int mode);

    // Transfer one byte (full-duplex). Returns the received byte.
    unsigned char transfer(unsigned char tx);
    // Assert chip select (drive low).
    void          cs_assert();
    // Deassert chip select (drive high).
    void          cs_deassert();
    // Transfer tx (full-duplex); `rx`, when given, receives the incoming
    // bytes (up to rx.len of them).
    void          write([]const unsigned char tx, ?[]unsigned char rx);
    // Fill rx (sends 0xFF as dummy TX).
    void          read([]unsigned char rx);
    unsigned long addr_() const;
};

generic<C: SpiChip>
struct SpiDevice<C> SpiDevice::new(&volatile unsigned int base, int mode) {
    struct SpiDevice<C> s;
    s.base = base;
    s.mode = mode;
    s.chip.init(s.addr_(), mode);
    return s;
}

generic<C: SpiChip>
unsigned long SpiDevice::addr_() const { unsafe { return (unsigned long)self.base; } }
generic<C: SpiChip>
unsigned char SpiDevice::transfer(unsigned char tx) { return self.chip.transfer(self.addr_(), tx); }
generic<C: SpiChip>
void SpiDevice::cs_assert() { self.chip.cs(self.addr_(), 1); }
generic<C: SpiChip>
void SpiDevice::cs_deassert() { self.chip.cs(self.addr_(), 0); }
generic<C: SpiChip>
void SpiDevice::write([]const unsigned char tx, ?[]unsigned char rx) {
    unsigned long i = 0UL;
    while (i < tx.len) {
        unsigned char b = self.transfer(tx[i]);
        match (rx) {
            case none: { }
            case some(r): { if (i < r.len) { r[i] = b; } }
        }
        i = i + 1UL;
    }
}
generic<C: SpiChip>
void SpiDevice::read([]unsigned char rx) {
    unsigned long i = 0UL;
    while (i < rx.len) {
        rx[i] = self.transfer((unsigned char)0xFF);
        i = i + 1UL;
    }
}

} // namespace std