Source file: /~heha/ewa/Reluktanzmotor/maweig-Motor-200831.zip/comport.cpp

/* COM-Port-Kommunikation zum FT2232H bzw. über Optokoppler direkt zum PC
  Mit Interrupts
  Festeinstellung 8-n-1 mit einstellbarer Baudrate
  LSPCLK ist 200 MHz
  Handshake (bspw. XON/XOFF) ist erst mal nicht vorgesehen.
  CPU1 bedient 2 Schnittstellen, CPU2 nur eine
 */

#include "Settings.h"
#include <cwchar>	// wcslen
#include <cmath>	// isinf
#include "regdef2.h"

#if CPU1
static Comport*isr1data;
// Feste Trampoline, siehe: https://de.wikipedia.org/wiki/Trampolin_(Informatik)
RAMFUNC static interrupt void Com1Rx() {
  isr1data->RxIsr();
  PieCtrlRegs.PIEACK.all = PIEACK_GROUP9;
}
RAMFUNC static interrupt void Com1Tx() {
  isr1data->TxIsr();
  PieCtrlRegs.PIEACK.all = PIEACK_GROUP9;
}
#endif

static Comport*isr2data;
RAMFUNC static interrupt void Com2Rx() {
  isr2data->RxIsr();
#if CPU1
  PieCtrlRegs.PIEACK.all = PIEACK_GROUP9;
#else
  PieCtrlRegs.PIEACK.all = PIEACK_GROUP8;
#endif
}
RAMFUNC static interrupt void Com2Tx() {
  isr2data->TxIsr();
#if CPU1
  PieCtrlRegs.PIEACK.all = PIEACK_GROUP9;
#else
  PieCtrlRegs.PIEACK.all = PIEACK_GROUP8;
#endif
}

void Comport::InitAll() {
#if CPU1
  configGpio(42,15,3);	// TxDa, 3 = ohne Synchronisierer
  configGpio(43,15,3);	// RxDa
  configGpio(18,2,3);		// TxDb
  configGpio(19,2,3,false);	// RxDb mit Pullup
  configGpio(56,7,3);		// TxDc
  configGpio(139,6,3,false);	// RxDc mit Pullup
  PieVectTable.SCIA_RX_INT = Com1Rx;
  PieVectTable.SCIA_TX_INT = Com1Tx;
  PieCtrlRegs.PIEIER9.bit.INTx1 = 1;	// SCIA_RX
  PieCtrlRegs.PIEIER9.bit.INTx2 = 1;	// SCIA_TX
  PieVectTable.SCIB_RX_INT = Com2Rx;
  PieVectTable.SCIB_TX_INT = Com2Tx;
  PieCtrlRegs.PIEIER9.bit.INTx3 = 1;	// SCIB_RX
  PieCtrlRegs.PIEIER9.bit.INTx4 = 1;	// SCIB_TX
#else
  PieVectTable.SCIC_RX_INT = Com2Rx;
  PieVectTable.SCIC_TX_INT = Com2Tx;
  PieCtrlRegs.PIEIER8.bit.INTx5 = 1;	// SCIC_RX
  PieCtrlRegs.PIEIER8.bit.INTx6 = 1;	// SCIC_TX
#endif
}

void Comport::init(uint16_t brr) {
// Portpins und Interrupts zuweisen
  if (&regs==&SciaRegs) {
    isr1data=this;
    IER |= M_INT9;	// Enable group 9 interrupts
  }else{
    isr2data=this;
#if CPU1
    IER |= M_INT9;	// Enable group 9 interrupts
#else
    IER |= M_INT8;	// Enable group 8(!!) interrupts
#endif
  }
// Bug im Debugger: Löscht .bss nicht bei erneutem Start
  recvlen = sendlen = 0;
// Schnittstelle initialisieren
  regs.SCICCR.all = 7;	// Modus 8-n-1
  regs.SCICTL1.all = bit(5) | bit(1) | bit(0);	// Freigabe der Leitungen
  regs.SCIHBAUD.all = brr>>8;
  regs.SCILBAUD.all = brr;
  regs.SCIFFTX.all = bit(15) | bit(14) | bit(13) | 8;
  regs.SCIPRI.all = bit(4);	// auch bei DebugBreak weitermachen
}

void Comport::wait(uint8_t mask) {
  if (mask&TxStop) {
    regs.SCIFFTX.bit.TXFFIENA = 0;
    regs.SCIFFTX.bit.TXFIFORESET = 0;
    regs.SCIFFTX.bit.TXFIFORESET = 1;
    sendlen = 0;
  }
  if (mask&RxStop) {
    regs.SCIFFRX.bit.RXFFIENA = 0;
    regs.SCIFFRX.bit.RXFIFORESET = 0;
    regs.SCIFFRX.bit.RXFIFORESET = 1;
    recvlen = 0;
  }
  if (mask&TxDataOut) while (sendlen);
  if (mask&RxComplete) while (recvlen);
  if (mask&TxComplete) while (regs.SCICTL2.bit.TXEMPTY);
}

void Comport::recv(uint8_t*rx,size_t len,uint8_t binary) {
  if (!len) return;
  regs.SCIFFRX.bit.RXFFIENA = 0;	// Interrupt aus, darf hier nicht hineinfunken
  recvptr=rx;
  recvlen=len;
  recvbin=binary;
// Interrupt erst wenn entweder alle oder die ersten 8 Bytes eingetroffen sind,
// um die Interruptlast zu minimieren.
  regs.SCIFFRX.bit.RXFFIL = len<8 ? len : 8;
  regs.SCIFFRX.bit.RXFFIENA = 1;	// Interrupt aktivieren, ISR kümmert sich
}

void Comport::send(const uint8_t*tx,size_t len,uint8_t binary) {
  if (!len) return;
// Problem: Kein definierter Weg um TXFFINT initial zu setzen!!
  wait(TxDataOut);			// schaltet (über die ISR) Interrupts aus
  regs.SCIFFTX.bit.TXFFIL = 0;
  regs.SCIFFTX.bit.TXFFIL = 8;		// Aktiviert den Interrupt
  sendptr=tx;
  sendlen=len;
  sendbin=binary;
  regs.SCIFFTX.bit.TXFFIENA = 1;	// Interrupt aktivieren, ISR kümmert sich
}

// Die Leseroutine belässt im Binärmodus nicht zu setzende Halbwörter unverändert.
// Empfangsfehler (hier: BREAK-Signalisierung) werden nicht beachtet.
RAMFUNC void Comport::RxIsr() {
  regs.SCIFFRX.bit.RXFFINTCLR = 1;
  while (recvlen && regs.SCIFFRX.bit.RXFFST) {
    uint8_t b=regs.SCIRXBUF.all;
    if (recvbin&1)
    if ((recvbin^=2)&2) *recvptr = *recvptr&0xFF00 | b&0xFF;	// Lo-Byte (zuerst)
    else *recvptr++ = *recvptr&0xFF | b<<8;			// Hi-Byte
    else *recvptr++ = b;
    if (!--recvlen) regs.SCIFFRX.bit.RXFFIENA = 0;
// Was dann noch kommt, verbleibt in der RxFIFO, kann man mit wait(RxStop) vor dem nächsten Empfang löschen.
  }
  if (recvlen && recvlen<8) regs.SCIFFRX.bit.RXFFIL = recvlen;
}

RAMFUNC void Comport::TxIsr() {
  regs.SCIFFTX.bit.TXFFINTCLR = 1;
// Die Interruptschwelle ist auf 8 festgelegt, sodass die ISR hier 8 Runden drehen kann,
// um ständiges Rein-Raus in/aus der ISR zu vermeiden.
  while (sendlen && !(regs.SCIFFTX.all&bit(12))) {	// Bit 12 ist bei TxFifoFull gesetzt
    regs.SCITXBUF.all = sendbin&1 ? (sendbin^=2)&2 ? *sendptr : *sendptr++>>8 : *sendptr++;
    if (!--sendlen) regs.SCIFFTX.bit.TXFFIENA = 0;
  }
}

/********************/
/** Ohne Interrupt **/
/********************/

void Comport::send(char tx) const{
  while (regs.SCIFFTX.all&bit(12));	// warten bis kein TxFifoFull
  regs.SCITXBUF.all = tx;		// Das ist alles
}

void Comport::send(wchar_t tx) const{	// nur für BMP0, sonst kommt CESU-8 raus
  if (tx>=0x80) {			// 0000 0000 0xxx xxxx -> 0xxx xxxx
    char chr2=tx>>6|0xC0;		// 0000 0yyy yyxx xxxx -> 110y yyyy  10xx xxxx
    if (tx>=0x800) {			// zzzz yyyy yyxx xxxx -> 1110 zzzz  10yy yyyy  10xx xxxx
      send(char(unsigned(tx)>>12|0xE0));
      chr2&=~bit(6);
    }
    send(chr2);
    tx&=~bit(6); tx|=bit(7);		// 10xx xxxx
  }
  send(char(tx));			// unverändert im Bereich 0..0x7F
}
/*
void Comport::send(const char*tx) const{	// hier: synchron ohne Interrupt
  while(*tx) send(*tx++);
}
*/
void Comport::send(const char*tx,size_t len) const{
  if (len) do send(*tx++); while(--len);
}

void Comport::send(const wchar_t*tx) const{	// hier: synchron ohne Interrupt
  while(*tx) send(*tx++);
}

void Comport::send(const wchar_t*tx,size_t len) const{
  if (len) do send(*tx++); while(--len);
}

/**************************************************
 ** printf-Nachbau mit einigen nützlichen Extras **
 **************************************************/
/* War erforderlich, weil das originale printf bei %f abstürzt
und damit ziemlich unbrauchbar ist.
Beim Debuggen gibt es ein Reset in fcvt(), sodass wohl auch die
Laufzeitfunktionen ecvt() und gcvt() unbrauchbar sind.

Die Features sind:
- Ausgabe Dezimaltrennzeichen Komma oder Punkt ("," statt "." angeben)
- Dezimaltrennzeichen auch bei Ganzzahlen (via Genauigkeitsangabe)
- Ausgabe ±Inf und NaN
- Unterdrückung ± vor Null und NaN
- "%#X" gibt 0x-Präfix aus, nicht 0X
- "%#,1d" unterdrückt ",0" am Ende
- "% #,1i" ersetzt ",0" am Ende durch Leerzeichen
- Zifferngruppierung mit "`", gibt '`' aus, hex/binär: Vierergruppe, sonst Dreiergruppe
- %j wie %i aber mit Inf/NaN-Ausgabe

Dieses printf zählt nicht die Ausgabezeichen (= Vereinfachung)
und ist zurzeit nicht für beliebige Ausgabeströme verwendbar.
*/

#define PRINTF64 0

struct printf_frame{
  int width,nk;
  unsigned flags;
  int e,base;
  union{
    unsigned long arg;
    double floatarg;
    void* ptrarg;
  };
  printf_frame():width(0),nk(-1),flags(bit(11)|bit(5)|bit(1)),e(0),base(10) {}
  printf_frame(int exponent):nk(0),flags(2),base(10),arg(exponent) {}	// nur für Exponentenausgabe
  void detect_nan_inf(unsigned);
  void fmtfloat(char c);
  void add_digit(int a) {int&w=((int*)&width)[flags&1]; w=w>0?w*10+a:a;}
  unsigned killsizeinfo() {unsigned r=flags; flags&=~(bit(7)|bit(8)|bit(9)); return r;}
  void makepositive() {if ((signed long)arg<0) {arg=-arg; flags|=bit(15);}}
  wchar_t*emitnumber(wchar_t*,int=0);
};

void printf_frame::detect_nan_inf(unsigned f) {
#if PRINTF64
  if (f&bit(9)) {
    if (arg==0x8000000000000000ULL) flags|=bit(9);
    if (arg==0x7FFFFFFFFFFFFFFFULL) flags|=bit(8);
  }else
#endif
  if (f&bit(8)) {
    if (arg==0x80000000UL) flags|=bit(9);
    if (arg==0x7FFFFFFFUL) flags|=bit(8);
  }else{
    if (arg==0x8000U) flags|=bit(9);
    if (arg==0x7FFFU) flags|=bit(8);
  }
}

void printf_frame::fmtfloat(char c) {
  if (floatarg!=floatarg) {flags|=bit(9); return;}	// NaN-Flag
  if (floatarg<0) {flags|=bit(15); floatarg=-floatarg;}	// floatarg positiv
  if (isinf(floatarg)) {flags|=bit(8); return;}		// Inf-Flag
  if (nk<0) nk=6;	// Nachkommastellen bei fehlender Angabe
  c|=' ';		// Kleinbuchstabe
  if (c=='g') flags^=bit(14);	// Nachkommaunterdrückung umkehren
  if (floatarg) {
    if (c=='e' || c=='g' && (floatarg>1E7 || floatarg<1E-3)) {
      flags|=bit(7);	// Normalisieren zur Basis 10 (ohne exp/log/pow)
      while (floatarg<1) {floatarg*=10; --e;}
      while (floatarg>=10) {floatarg*=0.1; ++e;}	// avoid float division
    }
    for (int e=0; e<nk; e++) floatarg*=10;
  }
  arg=floatarg+0.5;	// in Integer umwandeln und runden
}

// Ziffernfolge rückwärts in Puffer ausgeben, für Basis (mit Komma) und Exponent (dann ohne Komma)
// Flags: Bit 1: Punkt statt Komma ausgeben
//	  Bit 2: Positives Vorzeichen ('+') ausgeben, Vorrang vor Bit 3
//	  Bit 3: Unterdrückte Stellen (Vorzeichen, Nachkommastellen) mit Leerzeichen auffüllen
//	  Bit 5: Kleinbuchstabige Hexadezimalziffern
//	  Bit 6: Zifferngruppierung mit '`'
//	  Bit 8: "Inf" ausgeben, arg ignorieren
//	  Bit 9: "NaN" ausgeben, arg ignorieren
//	  Bit 12: Präfix ausgeben ("0b", "0x" oder "0")
//	  Bit 13: Immer Punkt/Komma ausgeben (TODO)
//	  Bit 14: Nullen in Nachkommastellen unterdrücken, ggf. Punkt/Komma unterdrücken
//	  Bit 15: Negatives Vorzeichen ('-') ausgeben, Vorrang vor Bit 2
wchar_t*printf_frame::emitnumber(wchar_t*sp,int width) {
  if (!arg) flags&=~bit(2);	// Niemals '+' für "0" (Erweiterung)
  if (nk<0) nk=0;
  if (flags&(bit(15)|bit(2)|bit(3))) --width;	// Platz für Vorzeichen weg
//  if (flags&bit(12) && base!=10) {		// Platz für 0-Präfix weg
//   --width;
//   if (base!=8) --width;			// Platz für 0x- bzw. 0b-Präfix weg
//  }
  wchar_t*end=sp;
  unsigned G = base==2|base==16 ? 4 : 3;	// Gruppengröße
  if (flags&bit(9)) {
    *--sp='N';*--sp='a';*--sp='N'; flags&=~(bit(15)|bit(2));
  }else if (flags&bit(8)) {
    *--sp='f';*--sp='n';*--sp='I';
  }else do{
    wchar_t c=arg%base+'0'; arg/=base;
    if (c>'9') {
      c+='A'-('9'+1);
      c|=flags&bit(5);		// Kleinbuchstabe
    }
    if (nk>0 && flags&bit(14)) {
      if (c=='0') {
        if (!(flags&bit(3))) {--nk; continue;}	// keine Nachkommastellen und ggf. kein Komma
        c=' ';
      }else flags&=~bit(14);		// !=0 detektiert: Unterdrückungsbit weg
    }
    if (flags&bit(6) && nk && sp!=end && !(unsigned(abs(nk))%G)) *--sp='`';
    *--sp=c;		// abspeichern
    if (!--nk) *--sp=','|flags&bit(1);		// Punkt(0x2E)/Komma(0x2C) einfügen
  }while(int(end-sp)<width || arg || nk>=0);
  if (flags&bit(12) && base!=10) {
    if (base==16) *--sp='x';
    if (base==2) *--sp='b';
    *--sp='0';
  }
  if (flags&bit(15)) *--sp='-';
  else if (flags&bit(2)) *--sp='+';
  else if (flags&bit(3)) *--sp=' ';
  return sp;
}

void Comport::vprintf(const wchar_t*fmt,va_list va) const{
  for(;;) {
    wchar_t c=*fmt++;
    if (!c) return;
    if (c!=L'%') {send(c); continue;}
    printf_frame fr;
    for(;;) {
      c=*fmt++;
      switch (c) {
	case L'.': fr.flags|=bit(0); fr.flags&=~bit(1); break;	// Kommastellen *auch für Dezimalzahlen
	case L',': fr.flags|=bit(0); break;		// *Dezimalkomma statt Punkt
	case L'+': fr.flags|=bit(2); break;		// Zwangs-Plus
	case L' ': fr.flags|=bit(3); break;		// 1 Leerzeichen bei positiven Zahlen; *Leerzeichen statt Dezimalnullen
	case L'\'':
	case L'`': fr.flags|=bit(6); break;		// *Zifferngruppierung
	case L'H':
	case L'h': fr.flags|=bit(7); break;		// Short-Argument (hier ungenutzt)
	case L'L': fr.flags|=bit(9); break;		// LongLong-Argument
	case L'l': if (!(fr.flags&bit(9))) fr.flags+=bit(8); break;	// Long- oder LongLong-Argument
	case L'_': fr.flags|=bit(10)|bit(0); break;	// *Signifikante Stellen statt Kommastellen (LabVIEW)
	case L'-': fr.flags&=~bit(11); break;		// linksbündig
	case L'#': fr.flags|=bit(12)|bit(14); break;	// alternativ: Nullen am Ende weg, 0/0x/0b-Präfix *nie 0X
	case L'*': fr.add_digit(va_arg(va,int)); break;
	case L'0': if (!(fr.flags&1)) {fr.flags|=bit(4); break;}	// Nullen statt Leerzeichen auffüllen
	default: if (L'0'<=c && c<=L'9') fr.add_digit(c-L'0');
	else /*if ('A'<=c && c<='Z' || 'a'<=c && c<='z' || c=='%' || c>=0x80)*/ goto breakloop;
      }
    }
breakloop:
    wchar_t*sp; int len;
    wchar_t buffer[16];
    switch (c) {
      case 0: return;
      case L'd':
      case L'i':
      case L'j':					// *mit Inf/NaN
	fr.arg = fr.flags&bit(9) ? va_arg(va,long long)
		:fr.flags&bit(8) ? va_arg(va,long)	// Vorzeichenerweiterung
				 : va_arg(va,int);
	{ unsigned f=fr.killsizeinfo();	// Größeninfo weg: (fast) erledigt
	  fr.makepositive();
	  if (c=='j') fr.detect_nan_inf(f);	// Hier wird Größeninfo noch mal gebraucht
	}
      break;
      case L'A': fr.flags&=~bit(5);	// Großbuchstaben
      case L'a': fr.flags|=bit(8); goto hex;	// Hex-Float
	// Hex-Float geht eigentlich anders! 0x1.<Mantisse-hex>p±<Exponent-dez>
      case L'o': fr.base=8; goto unsig;
      case L'b': fr.base=2; goto unsig;
      case L'p': fr.flags|=bit(8)|bit(4); fr.flags&=~bit(9); fr.width=6; fr.nk=0;	// fest 3FE6D0 hexadezimal (wg. 22 Bit)
      case L'X': fr.flags&=~bit(5);	// Großbuchstaben
      case L'x': hex: fr.base=16;
      case L'u':
unsig:	fr.arg = fr.flags&bit(9) ? va_arg(va,unsigned long long)
		:fr.flags&bit(8) ? va_arg(va,unsigned long)	// Null-Erweiterung
				 : va_arg(va,unsigned);
	fr.killsizeinfo();
      break;
      case L'G': 
      case L'E': fr.flags&=~bit(5);	// Großbuchstaben
      case L'e':
      case L'f':
      case L'g':
	fr.floatarg = fr.flags&bit(8) ? va_arg(va,long double)
				      : va_arg(va,double);
	fr.killsizeinfo();
	fr.fmtfloat(char(c));	// ab jetzt: Bit 7 = Exp-Flag
      break;
      case L'S': fr.flags&=~bit(5);	// *UTF8-String
      case L's': sp = va_arg(va,wchar_t*);
//    len = fr.nk>=0 ? wcsnlen(sp,fr.nk) : wcslen(sp);		// so wäre es ganz richtig
      len = wcslen(sp); if (fr.nk>=0 && len>fr.nk) len=fr.nk;	// so muss s ein nullterminierter String sein
      goto outstring;
      case L'C': fr.flags&=~bit(5);	// 8-Bit-Zeichen
      case L'c': buffer[0] = va_arg(va,wchar_t);
      sp = buffer; len = 1;
      if (fr.flags&bit(14) && !*sp) len=0;	//* alternative Form: NUL-Zeichen unterdrücken
      goto outstring;
      default: {send(c); continue;}
    }
// Zahlenkonvertierung
    if (fr.nk>8) fr.nk=8;
    sp=buffer+sizeof buffer;	// hinten anfangen mit dem Füllen
    if (fr.flags&bit(7)) {
      printf_frame fr2(fr.e);
      sp=fr2.emitnumber(sp);
      *--sp='E'|fr.flags&bit(5);
    }
// Minimale Ziffernzahl (wenn links Nullen gewünscht), Vorzeichen kommt davor (d.h. Präfixe stehen vor den Nullen)
// Hingegen bei normaler rechtsbündiger Ausgabe werden Leerzeichen _vor_ allen Präfixen ausgegeben
    { int numberlen = fr.flags&bit(11) && fr.flags&bit(4)?fr.width-(buffer+sizeof buffer-sp):0;
      if (fr.flags&(bit(15)|bit(2)|bit(3))) --numberlen;	// Platz für Vorzeichen
      sp=fr.emitnumber(sp,numberlen);
    }
    len=buffer+sizeof buffer-sp;
    fr.flags&=~bit(5);	// ohne Transkodierung ausgeben lassen (alles Plain ASCII)
// Stringausgabe, entweder aus Puffer oder vom %s-Argument
outstring:
    int fill=fr.width-len;	// Platz zum Auffüllen
    if (fr.flags&bit(11) && fill>0) do send(char(' '|fr.flags&bit(4))); while(--fill);
    if (fr.flags&bit(5)) send(sp,len); else send((char*)sp,len);	// mit oder ohne UTF-8-Transkodierung
    fr.flags|=' ';
    if (fill>0) do send(char(fr.flags&'0')); while(--fill);
  }
}
/*
void Comport::printf(const char*fmt,...) const{
  va_list va;
  va_start(va,fmt);
  vprintf(fmt,va);
  va_end(va);
}
*/
void Comport::printf(const wchar_t*fmt,...) const{
  va_list va;
  va_start(va,fmt);
  vprintf(fmt,va);
  va_end(va);
}

/******************************************
 ** Ende printf; Tasten- und Mauseingabe **
 ******************************************/

int Comport::recv() const{
  if (!regs.SCIFFRX.bit.RXFFST) return -1;
  return regs.SCIRXBUF.all;		// mit Frame-Error ebenfalls negativ
}
/*
int Comport::recv(char*rx) const{
  int r=regs.SCIFFRX.bit.RXFFST;	// minimal 0, maximal 16
  for (int i=0; i<r; i++) rx[i]=regs.SCIRXBUF.bit.SAR;	// ohne Frame-Error
  return r;
}
*/
char Comport::getchr() const{
  while (!regs.SCIFFRX.bit.RXFFST);
  return regs.SCIRXBUF.bit.SAR;	// ohne Frame-Error
}

wchar_t Comport::getwchr() const{	// Fehlerhaftes UTF-8 wird etwas toleriert.
  wchar_t r=getchr();
  if (r>=0xC2 && r<0xF0) {	// mindestens 2 Byte?
    if (r>=0xE0) {		// 3 Byte?
      r<<=6;			// 0000 0000 1110 zzzz  ->  0011 10zz zz00 0000
      char c=getchr();		// 10yy yyyy
      r|=c&0x3F;		//			->  0011 10zz zzyy yyyy
      if (!(c&0x80) || c&0x40) return r;// Bei falschem Trailbyte raus
    }else r&=0x01F;		// 0000 0000 110y yyyy  ->  0000 0000 000y yyyy
    r<<=6;			// zzzz yyyy yy00 0000 oder 0000 0yyy yy00 0000 
    r|=getchr()&0x3F;		// zzzz yyyy yyxx xxxx oder 0000 0yyy yyxx xxxx
  }
  return r;
}

int Comport::recv(wchar_t*rx) const{
  int r=0;
  while (regs.SCIFFRX.bit.RXFFST) {
    *rx++=getwchr();
    if (++r==16) break;		// könnte sonst überlaufen, wenn zwischendurch weitere Bytes eintrudeln
  }
  return r;			// minimal 0, maximal 16
}
Detected encoding: ASCII (7 bit)8