Source file: /~heha/ewa/Kram/Solarpaneel.zip/C-Programm/main.c

/*
060703	erstellt
Wenn's mal klemmt, debuggen mit
1. Konsolenfenster ("cmd" starten)
>msp430-gdbproxy.exe --port=3333 msp430
2. Konsolenfenster ("cmd" starten)
>msp430-gdb solar.elf
(gdb) target remote localhost:3333
(gdb) load solar.a43
(gdb) break funktionsname

*/
#include "solar.h"
#include <mspgcc/ringbuffer.h>	//Ringpuffer
#include <sys/cdefs.h>		//NULL
#include <stdio.h>		//puts()
#include "conio.h"

void delay(unsigned int d){
 int i;
 for (i = 0; i < d; i++) {
  nop();
  nop();
 }
}

typedef struct{	// Abgleichpunkt
 int ADvalue;	// A/D-Wert
 int DispValue;	// Festkommazahl
}TAbgleich,*PAbgleich;

typedef struct{
 TAbgleich a[2];
}TZweipunkt; // für Zweipunktabgleich

TZweipunkt InstrumentAbgleich[7]={	// überschreibbare Vorgabewerte
 {{{0xFFF<<3,0},{0x000,1881}}},	// U(Solar) 0 .. 18,81 V
 {{{0xFFF<<3,0},{0x000,3102}}},	// I(Solar) 0 .. 3,102 A
 {{{0xFFF<<3,0},{0x000,1881}}},	// U(Last)  0 .. 18,81 V
 {{{0xFFF<<3,0},{0x000,3102}}},	// I(Last)  0 .. 3,102 A
 {{{0xFFF<<3,0},{0x000,1881}}},	// U(Akku)  0 .. 18,81 V
 {{{0xC40<<3,0},{0x000,-2350}}},	// I(Akku)  .. 0 .. -2,350 A
 {{{0x4D3<<3,0},{0x680<<3,1000}}}};	// T(Chip)  .. 0 .. 100,0 .. °C

// Wie Win16-GDI, allerdings (noch) ohne Rundung
// Der Compiler verwendet automatisch den Hardware-Multiplizierer,
// benutzt jedoch eine ungünstige Divisionsroutine
// (ggf. in Assembler nachzuprogrammieren, s4:s2=s2 Rest s2)
int MulDiv(int f1, int f2, int n) {
 return (int)((long)f1*f2/n);
}

// Integerzahl <x> zwischen <u> und <o> eingrenzen
int Limit(int x, int u, int o) {
 if (x<u) x=u;
 if (x>o) x=o;
 return x;
}

//Lineare Transformation von int zu int, kein Problem bei a==e
// Zahl <x>, die (nicht unbedingt) zwischen Abgleichpunkten <a> und <e> liegt,
// in den Bildbereich mit den Abgleichwerten <A> und <E> transformieren;
// heraus kommt also <X> als das transformierte <x>.
int iitrafo(int x, int a, int e, int A, int E) {
 e-=a;
 if (!e) return 0;
 return MulDiv(x-a,E-A,e)+A;
}

// Zweipunktabgleichtabelle verwenden, Integer-Rechnung
int AD2Disp(int ADUval, const TZweipunkt*cal) {
 return iitrafo(ADUval,cal->a[0].ADvalue,cal->a[1].ADvalue,
   cal->a[0].DispValue,cal->a[1].DispValue);
}

typedef struct{
 int u,i,p;	// Spannung[mV], Strom[mA], Leistung[mW]
}TTriple, *PTriple;	// Anzeige-Tripel

typedef struct{
 int min, max;	// als Festkommawerte
 INT Val, Peak[2];	// als LED-Nummer (Großbuchstabe weil "Bild-Bereich")
 INT peakdelay[2];	// zum Halten des Maximums (vor Rücklauf)
}TBarControl, *PBarControl;

TBarControl BarControl[9]={	// Minima und Maxima vorinitialisieren
 {300,	1000},
 {0,	300},
 {0,	6000},
 {300,	2000},
 {0,	3000},
 {0,	6000},
 {880,	1440},
 {-1000,1000},
 {-1440,1440}};
 
#define BAR_EXPAND 1	// Minimum und Maximum an Messwert anpassen
#define BAR_FLASH 2	// Aufblitzen (durch größere Helligkeit)
#define PEAKDELAY 10
 
// In regelmäßigen Zeitabständen (ca. 10 Hz) aufrufen!
void BarUpdate(INT nBar,int val,INT flags){
 unsigned long bar;
 int Zero,Left,Right;	// LED-Positionen (0..31)
 TBarControl *pBar=BarControl+nBar;
 if (flags&BAR_EXPAND) {
  if (pBar->min>val) {pBar->min=val; flags|=BAR_FLASH;}
  if (pBar->max<val) {pBar->max=val; flags|=BAR_FLASH;}
 }
 Zero =Limit(iitrafo(0,  pBar->min,pBar->max,0,31),0,31);
 Right=Limit(iitrafo(val,pBar->min,pBar->max,0,31),0,31);
 Left=Zero; if (Right<Zero) {Left=Right; Right=Zero;}
 if (pBar->Peak[1]<=Right) {
  pBar->Peak[1]=Right; pBar->peakdelay[1]=PEAKDELAY;
 }else if (pBar->peakdelay[1]) {	// Zähler <>Null?
  --pBar->peakdelay[1];			// Peak stehen lassen
 }else if (pBar->Peak[1]>Zero) {	// sonst Peak zur Null laufen lassen
  pBar->Peak[1]--;
 }
 if (pBar->Peak[0]>=Left) {
  pBar->Peak[0]=Left;  pBar->peakdelay[0]=PEAKDELAY;
 }else if (pBar->peakdelay[0]) {	// Zähler <>Null?
  --pBar->peakdelay[0];			// Peak stehen lassen
 }else if (pBar->Peak[0]<Zero) {	// sonst Peak zur Null laufen lassen
  pBar->Peak[0]++;
 }
// zwei Peaks waren zunächst nicht vorgesehen, aber wirklich nützlich
 bar=TablCalcBargraf(Left,Right,pBar->Peak[1]);
 bar|=SHLD[pBar->Peak[0]];
 TablSetBargraf(nBar,bar);
 if (flags&BAR_FLASH) TablHiliteDigit[nBar]|=0xF0;
 else TablHiliteDigit[nBar]&=~0xF0;
}


// Zyklisch aufrufen!?
void HandleAllInstruments(void) {
 INT k,t;	// k=A/D-Kanal, t=Tableau-Nr.
 int u,i,uu,ii,pp;
 for (k=t=0; t<9; ) {
  u=ADU_Get(k);		// Spannung
  uu=AD2Disp(u,&InstrumentAbgleich[k]);
  k++;
  i=ADU_Get(k);		// Strom
  ii=AD2Disp(i,&InstrumentAbgleich[k]);
  k++;
  pp=MulDiv(uu,ii,1000);	// Leistung in 10-mW-Schritten
  
  TablDecimalOut(t,uu,2);	// in 10-mV-Schritten
  BarUpdate(t,uu,BAR_EXPAND);
  TermDrawValue(t,uu,2,TablGetBargraf(t),u);
  t++;
  TablDecimalOut(t,ii,3);	// in 1-mA-Schritten
  BarUpdate(t,ii,BAR_EXPAND);
  TermDrawValue(t,ii,3,TablGetBargraf(t),i);
  t++;
  TablDecimalOut(t,pp,2);	// in 10-mW-Schritten
  BarUpdate(t,pp,BAR_EXPAND);
  TermDrawValue(t,pp,2,TablGetBargraf(t),-1);
  t++;
 }
// hier sollte k==6 und t==9 sein
}
 

extern void Idle(void) {
// noch nichts tun, besser: schlafen!
}

RINGBUFFER_NEW(MsgQue,16);	// Nachrichtenwarteschlange
enum{
 MSG_KEYUP,	// Nokia Pfeil hoch
 MSG_KEYRECV,	// Nokia Hörer abnehmen (receiver = Hörer)
 MSG_KEYDOWN,	// Nokia Pfeil runter
 MSG_KEYHUP,	// Nokia Hörer auflegen (hang-up = auflegen)
 MSG_KEYLEFT,	// Nokia Pfeil links
 MSG_KEYOPTR,	// Nokia Option rechts
 MSG_KEYRIGHT,	// Nokia Pfeil rechts
 MSG_KEYOPTL,	// Nokia Option links
 MSG_SWL,	// Nockenschalter links
 MSG_SWM,	// Nockenschalter Mitte
 MSG_SWR,	// Nockenschalter rechts
 MSG_TICK,	// Timer-Tick (1ms)
};

// Tasten-Drück- und Loslass-Ereginisse generieren, VT100 aktualisieren
// Zyklisch (ca. 10 Hz) aufrufen!
static void Tastenabfrage(void) {
 static BYTE TastenVorher;
 BYTE k=Inp_Tasten()^TastenVorher;	// k = geänderte Zustände
 BYTE ta=TermTaster;		// Virtueller Tastenzustand
 if (k) {
  INT i;
  BYTE mask;
  if (k) for (i=0, mask=1; mask; i++, mask<<=1) {
   if (k&mask) {
    if (TastenVorher&mask) {	// loslassen
     ta&=~mask;
    }else{			// drücken
     ta|=mask;
     ringbuffer_put(&MsgQue,i);	// Kode 0..7 entsprechend obigem <enum>
    }
    TermSetTaster(ta);		// Virtuelle Tasterstellung setzen
   }
  }
  TastenVorher^=k;
 }
}

static void Schalterabfrage(void) {
 static BYTE SchalterVorher;	// Reale Nockenschalterstellung
 BYTE k=Inp_Schalter();
 if (k!=SchalterVorher) {
  SchalterVorher=k;
  ringbuffer_put(&MsgQue,MSG_SWL+k);
  TermSetSchalter(k);		// Virtuelle Nockenschalterstellung seten
 }
}

typedef struct _tagTimerEntry{
 struct _tagTimerEntry *next;
 unsigned StartAt;		// nichtperiodisch!
 void (*CallbackProc)(int);
 int CallbackValue;
}TTimerEntry,*PTimerEntry;

TTimerEntry TimerList[1];	// erst mal nur ein Timer

PTimerEntry SetTimer(unsigned ms, void (*CallbackProc)(int), int CallbackValue) {
 PTimerEntry pTE=TimerList;
// Jetzt müsste eine Einsortierung erfolgen!
 pTE->StartAt=GetTickCount()+ms;
 pTE->CallbackProc=CallbackProc;
 pTE->CallbackValue=CallbackValue;
 return pTE;
}

static void HandleTimer(void) {
 PTimerEntry pTE=TimerList;
 if (pTE->CallbackProc && (int)(GetTickCount()-pTE->StartAt)>=0) {
  pTE->CallbackProc(pTE->CallbackValue);
  pTE->CallbackProc=NULL;
 }
}

static void RemoveTaster(int i) {
 TermSetTaster(TermTaster&~SHL[i]);
}

// Eingabe von Hauptseite verarbeiten, liefert TRUE wenn Zeichen geschluckt
static bool HandleStdVt100Input(char c) {
 static WORD DauVal;
 int i=TermChar2KeyBit(c);
 if (i!=-1 && !(TermTaster&SHL[i])) {
  TermSetTaster(TermTaster|SHL[i]);
  ringbuffer_put(&MsgQue,i);
  SetTimer(100,RemoveTaster,i);
  return true;	// Zeichen verarbeitet!
 }
 switch (c) {
  case 0x10: {	// ^P = Pfeil hoch
   DauVal+=10;
   DAU_Put(0,DauVal);
   TermDrawDAU0();
  }return true;
  case 0x0E: {	// ^N = Pfeil runter
   DauVal-=10;
   DAU_Put(0,DauVal);
   TermDrawDAU0();
  }return true;
 }
 c&=~0x20;	// Großbuchstaben
 switch (c) {
  case 'L': c=0; goto x;
  case 'M': c=1; goto x;
  case 'R': c=2; x: {
   ringbuffer_put(&MsgQue,MSG_SWL+c);
   TermSetSchalter(c);
  }return true;

  case 'X':
  case 'Y':
  case 'Z': {
   c-='X';
   Rel_SetState(c,!Rel_GetState(c));
   TermDrawRelais(c);
  }return true;
 }
 return false;
}

int main(void) {
 unsigned int i,j;
// TTriple Solar/*,Last,Akku*/;
// int bar,peak=0,peakdelay=0;
 WDTCTL = WDTPW|WDTHOLD;
// Die folgenden Register habe ich nirgends in der Doku gefunden:
 BCSCTL1 &= ~XT2OFF;                   // XT2 = HF XTAL
 do {
  IFG1 &= ~OFIFG;                       // Clear OSCFault flag
  for (i = 0xFF; i > 0; i--);           // Time for flag to set
 }while ((IFG1 & OFIFG) != 0);          // OSCFault flag still set?                
 BCSCTL2 |= SELM1|SELS;                     // MCLK = XT2 (safe)
// FLL_CTL1*/ = 0x14;	// SMCLK = 8 MHz

 TablInit();	// Zuerst(!) wegen Timer
/*TEST*/
 TablLeds[0][4]=2;
 TablStrOut(0,"3.14");
 TablHiliteDigit[0]=0x10;	// probeweise Ziffer links HELL
// Tableau_Bargraf(4,0,31,32);
// Tableau_Bargraf(5,16,20,30);

 Ser0Init();
 ADU_Init();
 DAU_Init();
 _EINT();	// Globale Interrupts ein
 DispInit();	// benötigt Interrupts für Zeitüberschreitungserkennung
 TermInit();
 conio_init();
 if (DispFailed()) puts("Kein Display!");
 Rel_SetState(0,false); TermDrawRelais(0);
 Rel_SetState(1,true);  TermDrawRelais(1);
 Rel_SetState(2,false); TermDrawRelais(2);
 for(;;){
  AnsiSaveCursor();
  TermDrawDrehstrich();
  if (kbhit()) {
   HandleStdVt100Input(AnsiGetKey());
  }
  Tastenabfrage();
  Schalterabfrage();
  HandleTimer();
//  Tableau_Bargraf(8,0,i>>4,0);
  i=(i+1)&0x1FF;
  DispPrintf(14,3,"%3X",i);
  DispPrintf(14,4,"%02X",Inp_Tasten());
  HandleAllInstruments();
//  DAU_Put(0,/*((long)i*28836)>>16*/0x7FF);	// funktioniert?
//  Solar.u=((0xFFF-i)*301033UL)>>16;	// Spannung in 1-mV-Schritten
  // 301033UL = 3300(1mV)*5,7(Widerstandsteilerverhältnis)/4095(FullScale)
//  bar=Solar.u*30UL/12000;
//  if (peak<=bar){peak=bar; peakdelay=100;}
//  else {if (peakdelay) --peakdelay; else if (peak) --peak;}
//  Tableau_Bargraf(0,0,bar,peak);
/*
  DispPrintf(14,5,"%03X = %u,%03u V",i,Solar.u/1000,Solar.u%1000);
  i=0xFFF-ADU_Get(1);	// Strom der Solarzelle
  Solar.i=(i*59572UL)>>16;
  //3300(1mV)/4095(FullScale)*47kOhm/1MOhm/0,05(V/A)
  TablDecimalOut(1,Solar.i,3);
  Solar.p=((long)Solar.u*Solar.i)/1000;
  TablDecimalOut(2,Solar.p/10,2);
*/  
  i=ADU_Get(7);	// 3,55mV/K, 986mV @ 0°C
  j=((i*112703UL)>>16)-2777;
  // 2500(mV)/4095(FS)/3,55(mV/K)*10*65536
  // 986mV/3,55mV/K*10
  DispPrintf(14,6,"%03X = %u,%u °C",i,j/10,j%10);	// Chiptemperatur
//  Lampentest();
  AnsiRestoreCursor();
  Idle();
  delay(0xffff);
  if (DispFailed()) {
   DispInit();
   if (!DispFailed()) {
    puts("Display wieder vorhanden!");
   }
  }
 }
}
Detected encoding: ANSI (CP1252)4
Wrong umlauts? - Assume file is ANSI (CP1252) encoded