Source file: /~heha/mb-iwp/Bergwerk/fba-rpi-230421.zip/heha_gpio.cpp

#include "heha_gpio.h"
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <chrono>

/****************************
 * Konstruktor / Destruktor *
 ****************************/
Gpiomem::Gpiomem():handle(-1),base(0) {
  handle = ::open("/dev/gpiomem",O_RDWR|O_SYNC);
  if (handle>=0) {
   base = (volatile uint32_t*)mmap(NULL,0xB4,PROT_READ|PROT_WRITE,MAP_SHARED,handle,0);
  }
 }
Gpiomem::~Gpiomem() {::close(handle); handle=-1; base=0;}

/***********************
 * gpio-Minifunktionen *
 ***********************/
namespace gpio{

byte fsel(byte gpio,byte func) {
 byte index = gpio/10;	// max. 6 also 64 Portpins
 byte bit = gpio%10*3;
// Problem: Read-Modify-Write, hier darf niemand dazwischenfunken
// (der gleichzeitig andere Bits bearbeitet)
 uint32_t f = gpiomem(index);
 gpiomem[index] = f & ~(7<<bit) | (func&7)<<bit;
 return f>>bit&7;
}

void set(byte gpio) {
 byte index = gpio>>5; gpio&=31;
 gpiomem[7+index] = 1<<gpio;
}

void clr(byte gpio) {
 byte index = gpio>>5; gpio&=31;
 gpiomem[10+index] = 1<<gpio;
}

bool get(byte gpio) {
 byte index = gpio>>5; gpio&=31;
 return gpiomem(13+index)>>gpio&1;
}

// Nach Installation von enable_ccr.ko (make install)
// Liest CPU-Zykluszähler, hoffentlich ohne CPU-Wechsel zwischendrin
uint32_t cycle() {
 uint32_t ret = ret;
 __asm__ volatile ("mrc p15, 0, %0, c9, c13, 0":"=r" (ret));
 return ret;
}

/*******
 * I²C *
 *******/

//__sync_synchronize() brachte absolut keine Verbesserung, es wurde nur langsamer.
//Jetzt helfe ich mir mit Kontroll-Lesezugriffen

inline bool scl() {return get(SCL_PIN);}
inline bool sda() {return get(SDA_PIN);}
inline void scll() {fsel(SCL_PIN,1); while ( scl());}
inline void sclh() {fsel(SCL_PIN,0); while (!scl());}	// clock-stretch abwarten
inline void sdal() {fsel(SDA_PIN,1);}
inline void sdah() {fsel(SDA_PIN,0);}
inline void delay() {
 auto start = cycle();
 while (cycle()-start < 1200);	// 1 µs für Taktfrequenz 1,2 GHz
}

// Bit senden oder empfangen: SCL ist low getrieben, SDA beliebig
// Zeitpunkt: Direkt nach der fallenden Flanke von SCL
static bool bit(bool b=true) {
 if (b) sdah(); else sdal();
 delay();
 sclh();
 bool ret=sda();	// Nach der L-H-Taktflanke abtasten
 delay();
 scll();
 return ret;
}

// Startsequenz ausgeben
static void start() {
 if (!sda()) {		// falls low
  sdah();
  while (!sda());	// (sollte H werden)
  delay();
 }
 sclh();
 delay();
 sdal();
 delay();
 scll();
}

// Stoppsequenz ausgeben:  SCL ist low getrieben, SDA beliebig
// Zeitpunkt: Direkt nach der fallenden Flanke von SCL
static void stop() {
 sdal();
 delay();
 sclh();
 delay();
 sdah();
// while (!sda());	// (sollte H werden)
}

static bool problem;

static byte recv(bool nak) {
 byte b=0, c=8;
 do b = b<<1 | bit(); while(--c);	// MSB zuerst
 problem = bit(nak)!=nak;
 return b;
}

static bool send(byte b) {
 byte c=8;
 problem=false;	// Auf mögliche Kollision (Multi-Master) testen
 do{
  bool sendbit = problem || b&0x80;	// MSB zuerst
  if (bit(sendbit)!=sendbit) problem=true;	// fortan SDA hochohmig belassen
  b<<=1;
 }while(--c);
 return bit() || problem;		// false bei ACK und true bei NAK oder Kollision
}

// Hier: <addr> = 7-Bit-Adresse.
// 10-Bit-Adressierung kann mittels addr = 0b111100xx (Hi-2-Bits)
// und s[0] = 0byyyyyyyy (Lo-8-Bits) zusammengestellt werden.
// Lesen solo ist bei 10-Bit-Adresse ohnehin nicht möglich.
bool i2c_xfer(unsigned addr,const void*s,int sl,void*r,int rl) {
 if (!sl && !rl) return true;	// Nichts zu tun
 if (sl<0 || sl&&!s || rl<0 || rl&&!r) return false;
 byte saveSclFunc = fsel(SCL_PIN,0);	// Eingang
 byte saveSdaFunc = fsel(SDA_PIN,0);
 clr(SCL_PIN);
 clr(SDA_PIN);
 bool ret=false;
 if (scl() && sda()) {		// Bus frei?
  const byte*sd = reinterpret_cast<const byte*>(s);
  byte*rd = reinterpret_cast<byte*>(r);
  if (sl) {
   unsigned k=10;
   do start(); while (send(addr<<1) && --k);	// Nur 7-Bit-Adresse (Schreiben), ggf. wiederholen
   if (k) do --sl; while (!send(*sd++) && sl);	// Das letzte Byte darf mit NAK entgegengenommen werden
  }
  if (!sl && rl) {
   unsigned k=10;
   do start(); while (send((addr<<1)+1) && --k);// (Lesen)
   if (k) do *rd++=recv(!--rl); while (rl);	// Das letzte Byte mit NAK empfangen
  }
  stop();		// hinterlässt beide Portpins als Eingang
  ret=!sl && !rl;	// true wenn alles erledigt ist
 }
 fsel(SCL_PIN,saveSclFunc);
 fsel(SDA_PIN,saveSdaFunc);
 return ret;
}

}//namespace
Detected encoding: UTF-80