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

#include "heha_print.h"
#include <signal.h>	// signal()
#include <unistd.h>	// usleep()
#include <fcntl.h>
#ifdef SOFTI2C
# include "heha_gpio.h"
#else
# include <sys/ioctl.h>
# include <linux/i2c-dev.h>
#endif
#include <semaphore.h>
#include <cerrno>	// errno
#include <cstring>	// strerror()
#include <chrono>	// std::chrono
/* Zweites I²C-Abfrage- und Steuerprogramm für
Fernbedien-Adapter der BGA-Fahrzeuge mit ATmega328 (etwa Arduino Uno)
 221219 heha
+230413	Systemglobale Semaphore als Mutex
*/

typedef unsigned char byte;

static volatile bool wantAbort;
static void onSigInt(int) {
 wantAbort=true;
}

// Hexdump: Adresse und bis zu 16 Bytes pro Zeile
static void dump(char const*p,size_t l,unsigned a=0) {
 for (size_t i=0; i<l;a++) {
  if (!(i&15)) heha::print("%04X",a);	// 16 Bytes pro Zeile
  heha::print(" %02X",byte(p[i]));
  if (++i==l || !(i&15)) heha::print("\n");
 }
}

static sem_t*sem;
#ifdef SOFTI2C
static unsigned i2c_addr;

static bool i2c_xfer(const void*s,int sl,void*r,int rl) {
 return gpio::i2c_xfer(i2c_addr,s,sl,r,rl);
}

#else
static int i2c_handle;

static bool i2cwrite(void const*buf,int len) {
 int r=write(i2c_handle,buf,len);
 if (r==len) return true;
 heha::print(stderr,"Schreibfehler! %d!=%d %s\n",
   r,len,strerror(errno));
 return false;
}

static bool i2cread(void*buf,int len) {
 int r=read(i2c_handle,buf,len);
 if (r==len) return true;
 if (r<0 /*&& errno==121*/) {
  r=read(i2c_handle,buf,len);	// Zweiter Versuch
  if (r==len) return true;
 }
 heha::print(stderr,"Lesefehler! %d!=%d %s\n",
   r,len,strerror(errno));
 return false;
}

static bool i2c_xfer(const void*s,int sl,void*r,int rl) {
 if (sl<0 || rl<0 || sl&&!s || rl&!r) return false;
 if (!sl && !rl) return true;
 bool ret=true;
 sem_wait(sem);
 if (sl) ret=i2cwrite(s,sl);
 if (rl && ret) ret=i2cread(r,rl);
 sem_post(sem);
 return ret;
}
#endif

// ASCII-Zeichen in äquivalenten Ziffernwert umwandeln
// Returnwert >= Zahlenbasis bedeutet: Zeichen ist als Ziffer ungültig
static byte digit(signed char c) {
 if (c<='9') return c-'0';	// auch alles negative bleibt ungültig
 c|=0x20;			// in Kleinbuchstabe umwandeln
 if (c<'a') return c;		// ungültig als Ziffer (0x3A..0x3F,0x60)
 return c-'a'+10;		// '[' => '{' => 36 = ungültig
}

static char semname[32];

static void writeaction(unsigned a, char const*pw, unsigned l) {
 char cmd[2+l];
 cmd[0]=char(a>>8);
 cmd[1]=char(a);
 memcpy(cmd+2,pw,l);
 i2c_xfer(cmd,2+l,0,0);
}

static void readaction(unsigned a, unsigned l) {
 char cmd[]={char(a>>8),char(a)};
 if (a==0x6000 && l>6) l=6;	// Uhr
 char buf[l];
 if (i2c_xfer(cmd,2,buf,l)) {
//    usleep(100);	// Dem ATmega etwas Zeit zum Verdauen geben
  dump(buf,l,a);
  if ((a>>8 & 0xE0)==0x60) switch (a>>8 & 0x0F) {
   case 0: if (!(char)a && l>=6) {
    byte s100 = buf[0];
    long long s = 0;
    memcpy(&s,buf+1,5);
    std::time_t t = s;
    heha::print("%lld,%02u %s",s,s100,std::ctime(&t));
   }break;
   case 1:
   case 2:
   case 3: if ((byte)a+l<=13) for (int i=0; i<l;++i) {
    heha::print("%4d%c",(signed char)buf[i],i+1==l?'\n':' ');
   }break;
   case 4: if (!((a|l)&1) && (byte)a+l<=32) for (int i=0; i<l;i+=2) {
    short z=*(short*)(buf+i);	// Vektor aus int16 (avr-gcc: int)
    if (z==short(0x8000)) heha::print("iNaN");
    else if ((byte)a+i<14) heha::print("%,2d",z);
    else heha::print("%d",z);
    heha::print("%c",i+2>=l?'\n':' ');
   }break;
   case 5:
   case 6: if (!(byte)a && l==12) for (int i=0; i<3; i++) {
    heha::print("%7f%c",*(float*)(buf+4*i),i+1==3?'\n':'\t');
   }break;
  }
 }
//  delete[]buf;
}

/* hier geht's los */
int main(int argc,char**argv) {
 unsigned a=0x6000,l=8;	// Adresse und Länge
 char*pw=0;
 if (argc>=2) {
  if (!strcmp(argv[1],"settime")) {
// Argument der Form "settime"
   using namespace std::chrono;
   system_clock::time_point t = system_clock::now();
   milliseconds ms = duration_cast<milliseconds>(t.time_since_epoch());
   seconds sec= duration_cast<seconds>(ms);
   long long s = sec.count();
   char buf[6];
   buf[0] = ms.count()/10;	// 0..99
   memcpy(buf+1,&s,5);
   pw=buf; l=6;
  }else if (pw=strchr(argv[1],'W')) {
// Argument der Form "0x2000W123456789ABCDEF0" (Hex-Bytes)
   sscanf(argv[1],"%i",&a);
   char buf[64],i;
   for (i=0; i<sizeof buf;) {
    byte j=digit(*++pw);
    if (j>=16) break;
    byte k=digit(*++pw);
    if (k<16) j = j<<4|k;
    buf[i++]=j;
    if (k>=16) break;
   }
   pw=buf; l=i;
  }else if (pw=strchr(argv[1],'F')) {
// Argument der Form "0x5000F1.23,456.7,8E-9"
   sscanf(argv[1],"%i",&a);
   char buf[64],i;
   for (i=0; i<sizeof buf; i+=sizeof(float)) {
    float f;
    int k,j=sscanf(++pw,"%f%n",&f,&k);
    if (!j) break;
    pw+=k;	// auf das Komma oder sonstiges Trennzeichen
    *(float*)(buf+i)=f;
   }
   pw=buf; l=i;
  }else sscanf(argv[1],"%iL%i",&a,&l);
// Argument der Form "0x8000L16" = Lesemodus (ohne "L" 16 Bytes)
 }

 unsigned i2cnr = 1;
 if (argc>=3) sscanf(argv[2],"%i",&i2cnr);
#ifndef SOFTI2C
 char devname[32];
 heha::snprint(devname,sizeof devname,"/dev/i2c-%u",i2cnr);
 i2c_handle=open(devname,O_RDWR);
 if (i2c_handle<0) {
  fprintf(stderr,"Kann %s nicht öffnen: %s\n",
    devname,strerror(errno));
  return 1;
 }
#endif
 unsigned i2cslave = 0x5d;	// 7-Bit-Adresse in Linux-Schreibweise
 if (argc>=4) sscanf(argv[3],"%u",&i2cslave);
#ifdef SOFTI2C
 i2c_addr = i2cslave;
#else
 if (ioctl(i2c_handle,I2C_SLAVE,i2cslave)<0) {
  heha::print(stderr,"Kann Slave %#02x nicht setzen: %s\n",
    i2cslave,strerror(errno));
  return 2;
 }
#endif
 heha::snprint(semname,sizeof semname,"/i2c%u.%02x",i2cnr,i2cslave);
 sem = sem_open(semname,O_CREAT,0660,1);
 if (pw) writeaction(a,pw,l);
 else{
//  signal(SIGINT,onSigInt);
//  do {
   readaction(a,l);
//   if (wantAbort) break;
//   usleep(499900);
//  }while(!wantAbort);
 }
 sem_unlink(semname);
#ifndef SOFTI2C
 close(i2c_handle);
#endif
}
Detected encoding: UTF-80