#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-8 | 0
|