/*
Projekt: Photovoltaik-Präsentation "solar" mit MSP430
Detail: Zweit-Anzeige des Displays und der Tableaus via HyperTerminal
061106 erstellt
*/
#include "solar.h"
#include <stdio.h>
#include <string.h> // strlen
char AnsiGetKey(void) {
//Tastenabfrage mit Konvertierung von Kursortasten u.ä., blockierend
char c;
rep1:
c=(char)getchar(); // blockierendes getchar()!
switch (c) {
case 0: goto rep1; // HyperTerminal-Müll
case 033: {
rep2:
c=getchar();
switch (c) {
case '[':
case 'O': goto rep2;
case 033: c=0x03; break; // 2x ESC -> ^C
case 'H': c=0x01; break; // Pos1 -> ^A
case 'K': c=0x05; break; // Ende -> ^E
case 'A': c=0x10; break; // hoch -> ^P
case 'B': c=0x0E; break; // runter -> ^N
case 'F': c=0x06; break; // rechts -> ^F
case 'D': c=0x02; break; // links -> ^B
case 'P':
case 'Q':
case 'R':
case 'S': c-='P'-0x1C; // F1..F4 -> ^]..^_
}
}
}
return c;
}
char Visible(char c) {
if ((unsigned char)c<' ' || c==0x7F) c='_';
return c;
}
void AnsiGotoXY(INT x, INT y) {
printf(CSI "%d;%dH",y+1,x+1);
}
static BYTE CurrentColor;
void AnsiSetColor(BYTE NewColor) {
bool bSendSemi=false; // Flag zum Aussenden eines Semikolons
void SendSemi(void) { // lokale Hilfsprozedur
if (bSendSemi) putchar(';');
bSendSemi=true;
}
// Setze Farbe für folgende Ausgaben, kürzestmögliche Sequenz mit Cache
// Bit3:0 = Vordergrund (Bit 3 = Intensitätsbit)
// Bit6:4 = Hintergrund
// Bit7 = Blinken
// Farb-Bitzuordnung anders als in DOS!! Bit 0=rot, Bit 1=grün, Bit2=blau
// Unterstreichung wird hier nicht gehandhabt.
if (NewColor!=CurrentColor) {
putchar(0x1B);
putchar('[');
//Das "Rücksetzen von Attributen" ist nur notwendig, wenn ein Intensitäts-
//oder ein Blinkbit verschwindet - oder kurzerhand wenn grau-auf-weiß gewünscht
if (NewColor==0x07 || ~NewColor&CurrentColor&0x88) {
putchar('0');
bSendSemi=true;
CurrentColor=0x07;
}
if ((NewColor^CurrentColor)&0x80) { // neues Blink-Bit
SendSemi();
putchar('5');
}
if ((NewColor^CurrentColor)&0x70) { // neue Hintergrundfarbe
SendSemi();
putchar('4');
putchar(((NewColor>>4)&7)+'0');
}
if ((NewColor^CurrentColor)&0x08) { // neues Intensitätsbit
SendSemi();
putchar('1');
}
if ((NewColor^CurrentColor)&0x07) { // neue Textfarbe
SendSemi();
putchar('3');
putchar((NewColor&7)+'0');
}
putchar('m');
CurrentColor=NewColor;
}
}
// vor der Ausgabe von Strings in Fenstern aufrufen!
void AnsiSaveCursor(void) {
printf(CSI"s");
}
// Cursor in Ausgabefenster zurückstellen
void AnsiRestoreCursor(void) {
printf(CSI"u");
AnsiSetColor(0x07);
}
// hier als Funktionen statt als ANSI-Inlines, zwecks Anpassung an
// (möglicherweise) andere Terminal-Sequenzen
void AnsiUnderlineOn(void) {
printf(CSI"4m");
}
void AnsiUnderlineOff(void) {
printf(CSI"24m");
}
// <count> gleiche Zeichen ausgeben
void AnsiDrawChars(char c, INT count) {
if (count>0) do putchar(c); while(--count);
}
// zur vertikalen Zeichenausgabe in AnsiDrawBox
static void vert(void) {
// if (aktuelle Spalte!=79)
putchar(0x08); // nach links
putchar(0x0A); // nach unten
}
// Rand in aktueller Farbe mit den angegebenen boxdrawing-Zeichen malen
// [0]=waag., [1]=senkr., [2]=l.o., [3]=r.o., [4]l.u., [5]=r.u.
// Kann sein, dass diese Routine am rechten Rand versagt;
// dafür wird ein Minimum an Steuerzeichen verwendet!
// <title> ist im ANSI-, <boxdrawing> im OEM-Zeichensatz
void AnsiDrawBox(INT x,INT y,INT w,INT h,const char* title,const char*boxdrawing) {
INT i,j;
BYTE SaveFlags=Ser0Flags;
AnsiGotoXY(x+1,y);
i=strlen(title); if (i>w-2) i=w-2; // String kürzen bei Ausgabe
j=w-2-i; // verbleibende waagerechte Striche
if (i>0) do putchar(*title++); while (--i);
Ser0Flags&=~(fCRLF|fOutConv); // 0Ah (LF) und OEM nicht umsetzen!
AnsiDrawChars(boxdrawing[0],j);
putchar(boxdrawing[3]);
i=h-2; if (i>0) do {
vert(); // rechts runter, ohne Cursorpositionierung
putchar(boxdrawing[1]);
}while (--i);
AnsiGotoXY(x,y);
putchar(boxdrawing[2]);
vert();
i=h-2; if (i>0) do {
putchar(boxdrawing[1]);
vert(); // links runter
}while (--i);
putchar(boxdrawing[4]);
AnsiDrawChars(boxdrawing[0],w-2);
putchar(boxdrawing[5]);
Ser0Flags=SaveFlags;
}
// Ausgefülltes Rechteck mit aktueller Farbe zeichnen
void AnsiDrawRectangle(INT x, INT y, INT w, INT h, char c) {
if (h>0) do{
AnsiGotoXY(x,y);
AnsiDrawChars(' ',w);
y++;
}while (--h);
}
// Vergleicht (Anfang des) Eingabedatenstroms mit der angegebenen Zeichenkette,
// liefert 0 wenn OK. Es wird <ms> gewartet, bis die Daten kommen.
// Mindestens 1 Idle()-Aufruf erfolgt.
static char AnsiCompareInput(const char*comparer, unsigned ms) {
unsigned tic=GetTickCount();
// Eine Zeit lang versuchen, Zeichen zu empfangen
do{
Idle();
if (!*comparer) break;
if (kbhit()) {
if (getchar()!=*comparer) break; // Vergleich falsch: raus!
comparer++; // nächstes Zeichen
}
}while (GetTickCount()-tic<ms);
// Rest der Zeit warten, um den (HyperTerminal-)Müll zu empfangen
// Nicht via Sleep() und ClearInputBuffer(), um XON/XOFF zu vermeiden!
do{
Idle();
if (kbhit()) getchar();
}while (GetTickCount()-tic<ms);
return *comparer;
}
/******************************************
* Ab hier anwendungsspezifische Routinen *
******************************************/
static const char boxDouble[6]={0xCD,0xBA,0xC9,0xBB,0xC8,0xBC};
// Garniert die Wert- und Balkenausgabe mit Beschriftung
static void TermDrawUIP(INT y, const char*desc) {
static const char Formelz[3]={'U','I','P'};
static const char Einheit[3]={'V','A','W'};
AnsiGotoXY(1,y);
AnsiSetColor(0x20);
puts(desc);
AnsiSetColor(0x24);
for (y=0;y<3;y++) printf(CSI "2C%c =" CSI "7C%c\n",Formelz[y],Einheit[y]);
}
typedef struct{
int val;
unsigned balken;
int hex; // wenn hex=0xFFFF dann alles neu zeichnen
}TDrawValueCache;
// Komprimiert 2 Bits von "bar" zu einem Bit von "balken"
unsigned bar2balken(unsigned long bar) {
INT i;
unsigned ret/*blindes gcc:*/=0;
bar&=0x7FFFFFFEUL;
for (i=0; i<16; i++) {
ret>>=1;
if (bar&3) ret|=0x8000U;
bar>>=2;
}
return ret;
}
int MSB(unsigned x) { // liefert Nummer des höchsten gesetzten Bits in <x>
int i;
if (!x) return -1;
for (i=15; !(x&0x8000U); x<<=1, i--);
return i;
}
int LSB(unsigned x) { // liefert Nummer des niedrigsten gesetzten Bits in <x>
int i;
if (!x) return -1;
for (i=0; !(x&1); x>>=1, i++);
return i;
}
static TDrawValueCache DrawValueCache[9];
// Macht eine Wert- und Balkenausgabe
void TermDrawValue(INT t, int val, INT nk, unsigned long bar, int hex) {
// Zeilennummern im HyperTerminal
static const BYTE Term_line[9]={2,3,4,6,7,8,10,11,12};
// Cache, weil serielle Schnittstelle elend langsam
INT i,j;
char buf[8];
unsigned balken,bch;
TDrawValueCache*pCache=DrawValueCache+t;
t=Term_line[t]; // ab jetzt Zeilennummer
// Wert ausgeben (ohne Gleitkomma-printf)
if (val!=pCache->val || pCache->hex==-1) {
nk++;
AnsiGotoXY(5,t);
AnsiSetColor(0x2F); // weiß auf grün
i=snprintf(buf,7,"%0*d",nk+(val<0?1:0),val); // max. 6 Stellen: -32768
for (j=5;j>i;j--) putchar(' ');
for (i=0;j;j--,i++) {
putchar(buf[i]);
if (j==nk) putchar(',');
}
pCache->val=val;
}
// "bar" ausgeben:
balken=bar2balken(bar); // Zu zeichnende Bits
bch=balken^pCache->balken; // geänderte Bits
if (pCache->hex==-1) bch=0xFFFFU; // "alles geändert" ansetzen
if (bch) {
AnsiSetColor(0x09); // hellrot auf schwarz
i=LSB(bch); // "unterstes" geändertes Bit
j=MSB(bch); // "oberstes" geändertes Bit
AnsiGotoXY(15+i,t); // auf notwendige Position
for (; i<=j; i++) { // notwendige Zeichen ausgeben
putchar(balken&(unsigned)SHLD[i]?'I':' ');
}
pCache->balken=balken;
}
// ADU-Wert (in Hex) ausgeben (nicht bei "Leistung")
if (hex!=-1) {
if (hex!=pCache->hex) {
AnsiGotoXY(33,t);
AnsiSetColor(0x20); // schwarz auf grün
printf("%03X",hex);
pCache->hex=hex;
}
}else pCache->hex=0; // Cache gültig machen
}
#define DISP_ORG_X (80-TEXT_X-1)
#define DISP_ORG_Y 1
void TermDrawDisplayMirror(INT x, INT y, const char*text) {
AnsiGotoXY(x+DISP_ORG_X,y+DISP_ORG_Y);
puts(text);
}
static const char KeyBit2Char[8]="81234967";
// entsprechend der Anordnung auf dem numerischen Tastenblock
// Zeichen in Bit-Nummer (für Taster-Simulation) umwandeln
// -1 wenn kein gültiges Zeichen dafür
int TermChar2KeyBit(char c) {
char*p=memchr(KeyBit2Char,c,sizeof(KeyBit2Char));
if (p) return p-KeyBit2Char;
return -1;
}
BYTE TermTaster; // Virtueller(!) Status der 8 Tasten
BYTE TermSchalter; // Virtueller(!) Status Nockenschalter
void TermDrawTaster(INT i) {
//Bit 0 1 2 3 4 5 6 7
//Taste 8 1 2 3 4 9 6 7
static const BYTE PosX[8]={ 6, 1, 6,11, 4,11, 8, 1};
static const BYTE PosY[8]={14,16,16,16,15,14,15,14};
if (!TermMode) return;
AnsiGotoXY(PosX[i],PosY[i]);
AnsiSetColor(TermTaster&SHL[i] ? 0x1F : 0x6F);
if (i+'0'==KeyBit2Char[i]){ // Bitnummer gleich NumPad-Ziffer?
putchar(' ');
AnsiUnderlineOn();
putchar(i+'0');
AnsiUnderlineOff();
putchar(' ');
}else{
putchar(i+'0'); // andere Beschriftung wenn ungleich
putchar('=');
AnsiUnderlineOn();
putchar(KeyBit2Char[i]);
AnsiUnderlineOff();
}
}
// TermTaster setzen und Veränderungen im Terminalfenster anzeigen
void TermSetTaster(BYTE NewState) {
INT i;
NewState^=TermTaster; // Änderungen
TermTaster^=NewState;
if (NewState) for (i=0; i<8; i++) {
if (NewState&SHL[i]) TermDrawTaster(i);
}
}
void TermDrawSchalter(void) {
INT i;
if (!TermMode) return;
AnsiSetColor(0x60);
for (i=0; i<3; i++) {
AnsiGotoXY(18,14+i); putchar(TermSchalter==i ? 254 : ' ');
}
}
void TermSetSchalter(BYTE NewState) {
if (TermSchalter==NewState) return;
TermSchalter=NewState;
TermDrawSchalter();
}
void TermDrawRelais(INT i) {
if (!TermMode) return;
AnsiSetColor(0x60);
AnsiGotoXY(31,14+i); putchar(Rel_GetState(i) ? 'X' : ' ');
}
static void TermDrawSchalterStatic(INT i) {
static const char* const Text[]={"links ","Mitte ","rechts"};
AnsiSetColor(0x60);
AnsiGotoXY(17,14+i);
printf("( ) ");
AnsiUnderlineOn();
putchar(Text[i][0]);
AnsiUnderlineOff();
printf(Text[i]+1);
}
static void TermDrawRelaisStatic(INT i) {
AnsiSetColor(0x60);
AnsiGotoXY(30,14+i);
printf("[ ] ");
putchar(i+'0');
putchar('=');
AnsiUnderlineOn();
putchar(i+'X'); // ergibt X, Y und Z
AnsiUnderlineOff();
}
// Anzeige für D/A-Wandler (Strom-Einstellung)
static void TermDrawDAU0Static(void) {
AnsiGotoXY(39,17);
printf("D/A-Umsetzer:");
}
void TermDrawDAU0(void) {
AnsiSetColor(0x76);
AnsiGotoXY(53,17);
printf("%03X",DAU_Get(0));
}
void TermDrawDrehstrich(void) {
static const char Drehstrich[4]="\\|/-";
static INT index;
AnsiGotoXY(78,0); AnsiSetColor(0x74); // blau auf grau
putchar(Drehstrich[index=(index+1)&3]);
}
BYTE TermMode; // vorgefundener Terminal-Typ
void TermInit(void) {
INT i;
TermMode=0;
CurrentColor=0xFF;
AnsiSetColor(0x07); // auf ANSI schalten, grau auf schwarz
printf(CSI"2J" // Bild löschen
CSI"H" // Cursor links oben
CSI"6n" // Cursorposition abfragen
CSI"19;24r"); // Roll-Bereich (18..23) setzen
AnsiGotoXY(0,18); // Cursor in Rollbereich setzen
if (!AnsiCompareInput(CSI"1;1R",100)) { // 0 wenn OK
printf(CSI"c");
if (!AnsiCompareInput(CSI"?1;",100)) { // 0 wenn VT100
TermMode|=TERMMODE_VT100;
printf("VT100-");
}else{
TermMode|=TERMMODE_ANSI;
printf("ANSI-");
}
}else{
printf("\rdummes "); // sichtbaren Schrott mit '\r' überschreiben
}
puts("Terminal");
if (!TermMode) return;
for(i=0; i<9; i++) DrawValueCache[i].hex=-1;
AnsiSaveCursor(); // hier: (0,19) mit grau auf schwarz
AnsiGotoXY(0,0);
// obere "Bildhälfte" grauer Hintergrund, unten schwarz (Meldungen)
AnsiSetColor(0x70/*schwarz auf grau*/);
for(i=0; i<18; i++) puts(CSI"K"); // Zeile löschen, nächste Zeile
AnsiGotoXY(1,0); printf("Tableaus:");
AnsiGotoXY(39,0); printf("Display:");
AnsiSetColor(0x76);
AnsiGotoXY(17,0); printf("Firmware: " __DATE__);
AnsiGotoXY(59,0); printf("Matthias Olescher");
// Kopie der Tableaus hinterlegen
AnsiSetColor(0x2F/*weiß auf grün*/);
AnsiDrawRectangle(1,1,37,12,' ');
TermDrawUIP(1,"Solarzelle");
TermDrawUIP(5,"Last");
TermDrawUIP(9,"Akku");
// Box für Taster, Schalter und Relais
AnsiSetColor(0x7F);
AnsiDrawBox(0,13,15,5,"Taster",boxDouble);
for (i=0; i<8; i++) TermDrawTaster(i);
AnsiSetColor(0x7F);
AnsiDrawBox(16,13,12,5,"Schalter",boxDouble);
for (i=0; i<3; i++) TermDrawSchalterStatic(i);
TermDrawSchalter();
AnsiSetColor(0x7F);
AnsiDrawBox(29,13,9,5,"Relais",boxDouble);
TermDrawDAU0Static();
TermDrawDAU0();
for (i=0; i<3; i++) TermDrawRelaisStatic(i);
for (i=0; i<3; i++) TermDrawRelais(i);
// Kopie des Grafikdisplays vorbereiten (natürlich nur für Textdarstellung!)
AnsiSetColor(0x47/*grau auf blau*/);
AnsiDrawRectangle(DISP_ORG_X,DISP_ORG_Y,TEXT_X,TEXT_Y,' ');
AnsiRestoreCursor();
}
Vorgefundene Kodierung: UTF-8 | 0
|