Source file: /~heha/mb-iwp/Energiemessung/Netzteil/Firmware-mspgcc.zip/ng1.cpp

extern "C"{
#include <io.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
}
/****************************************************************
 * Symmetrisches Labornetzgerät, Spannungs- und Stromanzeige	*
 * auf Flüssigkristallanzeige 16 x 2 Zeichen			*
 * heha, 100930							*
 ****************************************************************/
 
/* Angeschlossene Hardware
P6.0	A0	59	Sollspannung, 0 .. 2,5 V (vom Potenziometer)
P6.1	A1	60	Sollstrom, 0 .. 2,5 V (vom Potenziometer)
P6.2	A2	61	neg. Istspannung, 0V=-16V, ca. Uref=0V
P6.3	A3	2	neg. Iststrom, 0 .. 2,5 V (von INA128P)
P6.6	A6	5	pos. Iststrom, 0 .. 2,5 V (von INA128P)
P6.7	A7	6	pos. Istspannung, 0 .. 2,5 V

P3.5	-	33	RS (0=Befehl / 1=Daten)
P3.6	-	34	R/W (0=Schreiben / 1=Lesen)
P3.7	-	35	E (Freigabe, Taktleitung, high-aktiv)
P4.0..P4.7	36..43	Datenbus (8 bit), standardmäßig auf EINGABE
*/


/*====================*
 *= Routinen für LCD =*
 *====================*/

static __attribute__ ((naked)) void delay(int n) {
//Zeit = (1/MCLK)*(8+(3*n))
 asm("lcdloop: dec %0\n  jnz lcdloop\n ret" :: "r" (n));
}
#define lcdDelay(ms) delay(1000*(ms));

// 1 Byte lesen (rs=0x40: Statusregister, rs=0x60: Datenbyte)
static char lcdRead(char rs) {
 P3OUT = rs;
 P3OUT |= 0x80;		// E high
 nop();
 char ret = P4IN;	// Datenbyte lesen
 P3OUT &=~0x80;		// E low
 return ret;
}

static void lcdBusyWait() {
 while (lcdRead(0x40)&0x80);
}

//static char LcdRead(char rs) {
// lcdBusyWait();
// return lcdRead(rs);
//}
static inline unsigned swpb(unsigned x) {return x>>8|x<<8;}

// 1 Byte schreiben (HIBYTE: 0x00: Befehl, 0x20: Datenbyte)
static void lcdWrite(unsigned c) {
 P4OUT = c;		// Byte ausgeben
 P4DIR = 0xFF;
 lcdRead(swpb(c));		// gleiches Signalspiel, gelesenes Byte verwerfen
 P4DIR = 0;
}

static void LcdWrite(unsigned c) {
 lcdBusyWait();
 lcdWrite(c);
}

static char charmap[]={
 0x00,0x00,0x0F,0x11,0x11,0x0F,0x01,0x0E,	// g mit Unterlänge
 0x00,0x00,0x1E,0x11,0x11,0x1E,0x10,0x10,	// p mit Unterlänge
};

static void LcdInit() {
 lcdDelay(30);		//wait more than 30ms
 lcdWrite(0x30);	//set 8 bit interface
 lcdDelay(1);
 lcdWrite(0x30);
 lcdDelay(1);
 lcdWrite(0x30);
 lcdDelay(1);
 LcdWrite(0x38);	// 2-zeilige Anzeige
 LcdWrite(0x01);	// Anzeige löschen
 LcdWrite(0x06);	// Auto-Adressinkrement
 LcdWrite(0x0C);	// Display EIN, Cursor AUS
 LcdWrite(0x40);	// Zeichengenerator-RAM-Adresse setzen
 for (size_t i=0; i<sizeof(charmap); i++) LcdWrite((unsigned char)charmap[i]|0x2000);
}

// String ausgeben
static void LcdWrite(const char*s) {
 for(;;) {
  char c=*s++;
  if (!c) break;
  LcdWrite((unsigned char)c|0x2000);
 }
}

// String ab Speicherposition ausgeben
static void LcdWrite(unsigned at, const char*s) {
 LcdWrite(at);
 LcdWrite(s);
}

/*=============================*
 *= Formatierte Zahlenausgabe =*
 *=============================*/

// Dezimalzahl mit Zwangs-Vorzeichen formatieren
// linksbündig, mit Leerzeichen aufgefüllt
// mit Komma bei <Nachkommastelle> wenn !=0
static int Dezimal(int v, char*s, int len, int nk) {
 char*a=s;	// String-Anfang
// 1. Zahl (Platz muss reichen!)
 s+=sprintf(s,"%+0*d",nk+2,v);
// 2. Vorzeichen korrigieren wenn Null
 if (v==0) *a=' ';
// 3. Komma einbasteln
 if (nk) {
  char*e=s+1;
  do{
   char*d=s;
   *d=*--s;
  }while(--nk);
  *s=',';
  s=e;		// auf das Ende
 }
// 4. Leerzeichen auffüllen
 while (s-a<len) *s++=' ';
// 5. terminieren
 *s=0;
 return s-a;
}

// Spannung (in mV, -16000 .. +16000) formatieren
static int Spannung(int v, char*s) {
 if (abs(v)>=10000) return Dezimal(v/10,s,6,2);	// 10-mV-Schritte
 else return Dezimal(v,s,6,3);			// 1-mV-Schritte
}

// Strom (in 0,1 mA, -12000 .. +12000) formatieren
static int Strom(int v, char*s) {
 if (abs(v)>=1000) return Dezimal(v/10,s,5,0);	// ganze mA
 else return Dezimal(v,s,5,1);			// 100-µA-Schritte
}

// Messwert skalieren (mit Faktor <f>/4096) und verschieben (mit Offset <o>)
static int Scale(int v, int f, int o) {
 return int((long)v*f>>12)+o;
}

static char dispstate;
static char dispdelay;

static void ShowState0() {
 LcdWrite(0x80,"+ ??? V  - ??? V");
 LcdWrite(0xC0,"+ ?? mA  - ?? mA");	// zweite Zeile (0,1)
}

static void ShowState1() {
 LcdWrite(0x80," S\011annun\010sbe\010r.:");
 LcdWrite(0xC0," ca.    ??   V  ");
}

static void ShowState2() {
 LcdWrite(0x80,"Strombe\010renzun\010:");
 LcdWrite(0xC0," ca.    ??  mA  ");
}

static void ShowState3() {
 LcdWrite(0x80,"   Tem\011eratur   ");
 LcdWrite(0xC0," ca.  + ??  \337C  ");
}

static void setstate(char state) {
 dispdelay=10;
 if (dispstate==state) return;
 dispstate=state;
 switch (state) {
  case 0: ShowState0(); break;
  case 1: ShowState1(); break;
  case 2: ShowState2(); break;
  case 3: ShowState3(); break;
 }
}

// Sind zwei Abtastwerte nahe beieinander?
// Bei großer zulässiger Differenz auch die Absolutwerte einkalkulieren
static int near(int v1, int v2, int diff=50) {
 if (diff>50) diff+=(v1+v2)>>6;
 return abs(v1-v2)<diff;
}

int main(void) {
 WDTCTL = WDTPW|WDTHOLD;	// Watchdog beruhigen
 SVSCTL = 0x18;			// Bei Unterschreiten der Betriebsspannung unter 3 V Reset auslösen lassen
 P1OUT = 0; P1DIR = 0xFF;	// Unbeschaltete Pins festnageln
 P2OUT = 0; P2DIR = 0xFF;
 P3OUT = 0; P3DIR = 0xFF;
 P4OUT = 0; P4DIR = 0xFF;
 P5OUT = 0; P5DIR = 0xFF;
 P6OUT = 0; P6SEL = 0xFF;	// Port 6 rein analog
 ADC12CTL0 = 0x44F0;		// 2,5 V Referenzspannung
 ADC12CTL1 = 0x02F6;		// langsam arbeiten
 ADC12MCTL0 = 0x10;		// Usoll
 ADC12MCTL1 = 0x17;		// +Uist
 ADC12MCTL2 = 0x12;		// -Uist
 ADC12MCTL3 = 0x11;		// Isoll
 ADC12MCTL4 = 0x16;		// +Iist
 ADC12MCTL5 = 0x13;		// -Iist
 ADC12MCTL6 = 0x9A;		// Chiptemperatur
 LcdInit();
 setstate(3);

 static char s[12];
 static int Usoll,UistP,UistN,Isoll,IistP,IistN,Temp;
 static int Usoll_, Isoll_;	// vergangene Werte
// Spannungen in mV, Ströme in 0,1 mA, Temperatur in 0,1 °C
 ADC12CTL0|=3;			// ADU freigeben und starten

 for(;;) {
  while (!(ADC12IFG&1<<5));	// warten bis fertig

// Werte auslesen und skalieren (Werte experimentell gefunden)
  Usoll=Scale(ADC12MEM0,18600,0);
  UistP=Scale(ADC12MEM1,16113,-3);
  UistN=Scale(ADC12MEM2,18599,-16060);
  Isoll=Scale(ADC12MEM3,13000,0);
  IistP=Scale(ADC12MEM4,13013,0);
  IistN=Scale(ADC12MEM5,-14508,Scale(UistN,-54,0));
  Temp =Scale(ADC12MEM6,2380,-800);

// Übertemperatur detektieren und Anzeige umschalten,
// bei permanenter Übertemperatur wechselt die Anzeige zwischen 0 und 3
  if (Temp>600 && !dispdelay) setstate(3);

// Wertänderungen an Usoll detektieren und Anzeige umschalten
  if (!near(Usoll,Usoll_)	// am U-Knopf gedreht?
  &&  near(Isoll,IistP,100)	// UND Strombegrenzung an Ausgang+?
  &&  near(Isoll,-IistN,100)	// UND Strombegrenzung an Ausgang-?
  &&  dispstate!=3) setstate(1);	// Anzeige umschalten
  Usoll_=Usoll;

// Wertänderungen an Isoll detektieren und Anzeige umschalten
  if (!near(Isoll,Isoll_)	// am I-Knopf gedreht?
  &&  !near(Isoll,IistP,100)	// UND keine Strombegrenzung an Ausgang+?
  &&  !near(Isoll,-IistN,100)	// UND keine Strombegrenzung an Ausgang-?
  &&  dispstate!=3) setstate(2);	// Anzeige umschalten
  Isoll_=Isoll;

// Veränderliche Positionen aktualisieren  
  switch (dispstate) {
   case 0: {
    Spannung(UistP,s);  LcdWrite(0x80,s);	// Cursor nach (0,0)
    Spannung(UistN,s);  LcdWrite(0x89,s);	// Cursor nach (9,0)
    Strom(IistP,s);  LcdWrite(0xC0,s);		// Cursor nach (0,1)
    Strom(IistN,s);  LcdWrite(0xC9,s);		// Cursor nach (9,1)
   }break;
   
   case 1: {
    Spannung(Usoll,s); LcdWrite(0xC7,s+1);	// ohne Vorzeichen ausgeben
   }break;

   case 2: {
    Strom(Isoll,s); LcdWrite(0xC7,s+1);		// ohne Vorzeichen ausgeben
   }break;

   case 3: {
    Dezimal(Temp,s,6,1); LcdWrite(0xC6,s);
   }break;
  }

  delay(0);			// maximal warten
  if (dispdelay && !(--dispdelay) && dispstate) setstate(0);
 }
}
Detected encoding: UTF-80