/* 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 (®s==&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
|