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