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