Source file: /~heha/ewa/Reluktanzmotor/maweig-Motor-190927.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 <cstring>	// strlen
#include <cwchar>	// wcslen
#include <cmath>	// pow

static Comport*isr1data,*isr2data;

// 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;
}
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::init(uint16_t brr) {
// Portpins und Interrupts zuweisen
  if (&regs==&SciaRegs) {
    isr1data=this;
    GpioCtrlRegs.GPBGMUX1.all |= 0b11UL<<20 | 0b11UL<<22;	// GPIO42 -> TxDa,
    GpioCtrlRegs.GPBMUX1 .all |= 0b11UL<<20 | 0b11UL<<22;	// GPIO43 -> RxDa
    GpioCtrlRegs.GPBQSEL1.all |= 0b11UL<<20 | 0b11UL<<22;	// keine Synchronisation
    PieVectTable.SCIA_RX_INT = Com1Rx;
    PieVectTable.SCIA_TX_INT = Com1Tx;
    PieCtrlRegs.PIEIER9.bit.INTx1 = 1;	// SCIA_RX
    PieCtrlRegs.PIEIER9.bit.INTx2 = 1;	// SCIA_TX
    IER |= M_INT9;	// Enable group 9 interrupts
  }else{
    isr2data=this;
#if CPU1
    GpioCtrlRegs.GPAMUX2 .all |= 0b10UL<<4 | 0b10UL<<6;	// GPIO18 -> TxDb, GPIO19 = RxDb
    GpioCtrlRegs.GPAQSEL2.all |= 0b11UL<<4 | 0b11UL<<6;	// keine Synchronisation
    GpioCtrlRegs.GPAPUD.bit.GPIO19 = 0;		// Pullup aktivieren
    PieVectTable.SCIB_RX_INT = Com2Rx;
    PieVectTable.SCIB_TX_INT = Com2Tx;
    PieCtrlRegs.PIEIER9.bit.INTx3 = 1;	// SCIB_RX
    PieCtrlRegs.PIEIER9.bit.INTx4 = 1;	// SCIB_TX
    IER |= M_INT9;	// Enable group 9 interrupts
#else
    GpioCtrlRegs.GPBGMUX2.all |= 0b01UL<<16;	// GPIO56 -> TxDc
    GpioCtrlRegs.GPBMUX2 .all |= 0b11UL<<16;
    GpioCtrlRegs.GPEGMUX1.all |= 0b01UL<<22;	// GPIO139 = RxDc
    GpioCtrlRegs.GPEMUX1 .all |= 0b10UL<<22;
    GpioCtrlRegs.GPEQSEL1.all |= 0b11UL<<22;
    GpioCtrlRegs.GPEPUD.bit.GPIO139 = 0;	// Pullup aktivieren
    PieVectTable.SCIC_RX_INT = Com2Rx;
    PieVectTable.SCIC_TX_INT = Com2Tx;
    PieCtrlRegs.PIEIER8.bit.INTx5 = 1;	// SCIC_RX
    PieCtrlRegs.PIEIER8.bit.INTx6 = 1;	// SCIC_TX
    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

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;
  int flags,e;
  union{
    unsigned long arg;
    double floatarg;
    void* ptrarg;
  };
  printf_frame():width(0),nk(-1),flags(bit(11)|bit(5)|bit(1)),e(0) {}
  void fmtfloat(char c);
  void add_digit(int a) {int&w=((int*)&width)[flags&1]; w=w>0?w*10+a:a;}
  void killsizeinfo() {flags&=~(bit(7)|bit(8)|bit(9));}
  void makepositive() {if ((signed long)arg<0) {arg=-arg; flags|=bit(15);}}
  static wchar_t*emitnumber(wchar_t*,unsigned long,unsigned=10,int=0,int=0,int=0);
  static bool group_before(int nk, unsigned G) {return nk>0 && !((unsigned)nk%G);}
  static bool group_after(int nk, unsigned G) {return nk<0 && !((unsigned)-nk%G);}
};

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
  for (int e=0; e<nk; e++) floatarg*=10;
  arg=floatarg;		// in Integer umwandeln
// TODO: Exponentialausgabe
}

// 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,unsigned long arg,unsigned base,int flags,int nk,int width) {
  if (!arg) flags&=~bit(2);	// Niemals '+' für "0" (Erweiterung)
  if (nk<0) nk=0;
  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') {flags&=~bit(14); goto savec;}	// !=0 detektiert: Unterdrückungsbit weg
      else if (flags&bit(3)) {c=' '; goto savec;}
    }else{ savec:
      if (flags&bit(6) && group_after(nk,G)) *--sp='`';
      *--sp=c;		// abspeichern
      if (flags&bit(6) && group_before(nk,G)) *--sp='`';
    }
    if (!--nk && !(flags&bit(14))) {	// Wenn nur Nullen hinter'm Komma, kein Komma ausgeben
      *--sp=','|flags&bit(1);		// Punkt/Komma einfügen
    }
  }while(--width>=0 || 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 char*fmt,va_list va) const{
  vprintf((const wchar_t*)fmt,va);
}
*/
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'`': fr.flags|=bit(6); break;		// *Zifferngruppierung
	case L'h': fr.flags|=bit(7); break;		// Short-Argument (hier ungenutzt)
	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 goto breakloop;
      }
    }
breakloop:
    unsigned base=10;
    wchar_t*sp; int len;
    switch (c) {
      case 0: return;
      case L'i':
      case L'd':
	fr.arg = fr.flags&bit(9) ? va_arg(va,long long)
		:fr.flags&bit(8) ? va_arg(va,long)	// Vorzeichenerweiterung
				 : va_arg(va,int);
	fr.killsizeinfo();	// Größeninfo weg: erledigt
	fr.makepositive();
      break;
      case L'o': base=8; goto unsig;
      case L'b': 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': 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;
      default: {send(c); continue;}
    }
// Zahlenkonvertierung
    if (fr.nk>16) fr.nk=16;
    wchar_t buffer[36];
    sp=buffer+sizeof buffer;	// hinten anfangen mit dem Füllen
    if (fr.flags&bit(7)) {
      sp=fr.emitnumber(sp,fr.e);
      *--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,fr.arg,base,fr.flags,fr.nk,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: UTF-80