std1.0.0-beta-7 SafeC docs

Source hal/i2c.h

View in the repository

// SafeC Standard Library — I2C HAL
// Polling-based I2C master driver. Freestanding-safe.
//
// I2cBus<C> is generic over the I2C block, a compile-time chip type:
//   I2cGeneric    reference layout: DATA +0x00, STATUS +0x04 (bit 0 busy,
//                 bit 1 ACK, bit 2 error), CTRL +0x08 (1 START, 2 STOP,
//                 4 SEND, 8 READ+ACK, 16 READ+NACK), ADDR +0x0C, SPEED +0x10
//   Stm32F4I2c    STM32F1/F2/F4 I2C ("v1": CR1/CR2/DR/SR1/SR2/CCR/TRISE),
//                 standard (<= 100 kHz) and fast (<= 400 kHz) mode
// Every transfer ends with STOP, also when it fails, so the bus is released.
#pragma once

// I2C result codes
#define I2C_OK     0
#define I2C_NACK   1
#define I2C_ERROR  2

namespace std {

// What an I2C controller type provides; I2cBus<C> requires it.
trait I2cChip {
    void init(unsigned long base, unsigned int speed, unsigned int clock_hz) const;
    int  write(unsigned long base, unsigned char addr, []const unsigned char data) const;
    int  read(unsigned long base, unsigned char addr, []unsigned char data) const;
    int  probe(unsigned long base, unsigned char addr) const;
}

struct I2cGeneric {
    int unused_;
    void init(unsigned long base, unsigned int speed, unsigned int clock_hz) const;
    int  write(unsigned long base, unsigned char addr, []const unsigned char data) const;
    int  read(unsigned long base, unsigned char addr, []unsigned char data) const;
    int  probe(unsigned long base, unsigned char addr) const;
};
struct Stm32F4I2c {
    int unused_;
    void init(unsigned long base, unsigned int speed, unsigned int clock_hz) const;
    int  write(unsigned long base, unsigned char addr, []const unsigned char data) const;
    int  read(unsigned long base, unsigned char addr, []unsigned char data) const;
    int  probe(unsigned long base, unsigned char addr) const;
};

generic<C: I2cChip>
struct I2cBus {
    &volatile unsigned int base; // MMIO base address
    unsigned int speed;  // bus speed in Hz
    C            chip;

    // Initialize an I2C master at `base` for `speed` Hz, from a `clock_hz`
    // peripheral clock (STM32: the APB1 clock; ignored by I2cGeneric). The chip
    // type comes from the target.
    static struct I2cBus<C> new(&volatile unsigned int base, unsigned int speed, unsigned int clock_hz);

    // Write `data` to the 7-bit address `addr`. Returns I2C_OK on success.
    int          write(unsigned char addr, []const unsigned char data);
    // Read data.len bytes into `data` from device at `addr`. Returns I2C_OK on success.
    int          read(unsigned char addr, []unsigned char data);
    // Write, then read (e.g. a register address, then its value). Returns I2C_OK on success.
    int          write_read(unsigned char addr, []const unsigned char wr, []unsigned char rd);
    // Probe: address the device with no data. Returns 1 if it acknowledges.
    int          probe(unsigned char addr);
    unsigned long addr_() const;
};

generic<C: I2cChip>
struct I2cBus<C> I2cBus::new(&volatile unsigned int base, unsigned int speed, unsigned int clock_hz) {
    struct I2cBus<C> bus;
    bus.base = base;
    bus.speed = speed;
    bus.chip.init(bus.addr_(), speed, clock_hz);
    return bus;
}

generic<C: I2cChip>
unsigned long I2cBus::addr_() const { unsafe { return (unsigned long)self.base; } }
generic<C: I2cChip>
int I2cBus::write(unsigned char addr, []const unsigned char data) {
    return self.chip.write(self.addr_(), addr, data);
}
generic<C: I2cChip>
int I2cBus::read(unsigned char addr, []unsigned char data) {
    return self.chip.read(self.addr_(), addr, data);
}
generic<C: I2cChip>
int I2cBus::write_read(unsigned char addr, []const unsigned char wr, []unsigned char rd) {
    int r = self.chip.write(self.addr_(), addr, wr);
    if (r != I2C_OK) { return r; }
    return self.chip.read(self.addr_(), addr, rd);
}
generic<C: I2cChip>
int I2cBus::probe(unsigned char addr) { return self.chip.probe(self.addr_(), addr); }

} // namespace std