Source file: /~heha/mb-iwp/NA/Firmware.zip/pm1/pm1.cpp

extern "C"{
#include <io.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <mspgcc/util.h>	// delay()
#include <signal.h>		// interrupt
}
/****************************************************************
 * Piezo-Messgerät (Wobbel-Generator 100 .. 200 kHz; 		*
 * Amplituden- und Phasenmessung, Hochspannungs-Ausgang)	*
 * 32 KByte Flash, 5 KByte RAM					*
 * Flüssigkristallanzeige 16 x 2 Zeichen			*
 * heha, 100930							*
 ****************************************************************/
 
/* Angeschlossene Hardware
=== Amplituden- und Phasenvergleicher (AD8302) ===
P6.0	A0	59	Amplitudenvergleich
P6.1	A1	60	Referenzspannung
P6.2	A2	61	Phasenvergleich
=== Gesteuerter Gleichrichter (wahrscheinlich Murks) ===
P4.0	-	36	Multiplexer U oder I
P6.4	A4	3	Gleichgerichtete Spannung
=== Niederspannungsausgangstreiber (VCA822) ===
P6.5	DAC1	4	Offset
P6.6	DAC0	5	Verstärkung
=== Hochspannungsgenerator (max. 100 V) ===
P6.3	A3	2	Istspannung (geteilt)
P2.4	TA2	24	Basisansteuerung
=== Hochspannungsverstärker (OPA454) ===
P5.6	-	50	Status Flag
P5.7	-	51	E/D
=== Bedienelemente ===
P1.2	-	14	S4 (low-aktiv)
P1.3	-	15	Inkrementalgeber (low-aktiv)
P1.4	-	16	Inkrementalgeber (low-aktiv)
P1.5	-	17	Inkrementalgeber-Schaltkontakt (low-aktiv)
P1.6	-	18	S2 (low-aktiv)
P1.7	-	19	S1 (low-aktiv)
=== Filtereinrichtung ===
P2.0	-	20	H = π-Filter (Rekonstruktionsfilter) aktivieren
P2.1	-	21	L = Rechteck, H = Sinus auswählen
=== DDS-Generator (AD9834, mit 66 MHz gespeist) ===
P2.6	-	26	FSEL
P2.7	-	27	PSEL
P3.0	-	28	RESET
P3.1	0SIMO	29	SDATA
P3.2	-	30	/FSYNC
P3.3	0CLK	31	/SCLK
=== Serielle Schnittstelle ===
P1.1	-	13	(mit TxD verbunden für Bootloader)
P2.2	-	22	(mit RxD verbunden für Bootloader)
P3.4	-	32	!CTS (Ausgang)
P3.5	-	33	!RTS (Eingang), H→L startet PnP-Sequenz @ 1200,7,n,1
P3.6	1TxD	34	TxD regulär 115200,n,8,1
P3.7	1RxD	35	RxD
=== LCD ===
P4.1	-	37	RS (0=Befehl / 1=Daten)
P4.2	-	38	R/W (0=Schreiben / 1=Lesen)
P4.3	-	39	E (Freigabe, Taktleitung, high-aktiv)
P4.4..P4.7	40..43	Datenbus (4 bit), standardmäßig auf EINGABE
=== Externe Bedieneinrichtung (µMatch-8) ===
P5.0..P5.5	44..49	nicht definiert

Murks in der Schaltung:
	UART0 und SPI beißt sich!
	Bootloader-Reset nötigt zum Stecker-Ziehen
	Pull-Ups an Reset und den Tastern vergessen (µC hat KEINE Pullups!!)
*/

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

#define DELAY_MS(ms) delay(1000*(ms));

typedef uint8_t BYTE;
#define elemof(x) (sizeof(x)/sizeof(*(x)))

// Port-Bits an P4
#define RS 0x02
#define RW 0x04
#define EN 0x08
#define DMASK 0xF0

// 1 Nibble ausgeben oder lesen (High-Nibble von c)
// Das Low-Nibble von c wird unverändert durchgereicht
static BYTE lcdNibble(BYTE c) {
 P4OUT = P4OUT&~DMASK | c&DMASK;
 P4OUT |= EN;			// E high
 c &= ~DMASK;
 c |= P4IN&DMASK;		// Nibble lesen
 P4OUT &=~EN;			// E low
 return c;
}

// Nibbles tauschen
static BYTE SwapNibbles(BYTE b) {
 return b<<4 | b>>4; 
}
/* ASM besser: 4x
	rla.b	r
	adc.b	r
*/

#define NIBBLE  0x01
#define NO_ACK	0x08
// 1 Byte lesen (rs=0x05: Statusregister, rs=0x07: Datenbyte)
// cmd: Kommandobyte
//  Bit 0: Halbbyte lesen/schreiben (sonst ganzes Byte)
//  Bit 1: Datenbyte (sonst Befehlsbyte)
//  Bit 2: Lesen (sonst Schreiben)
//  Bit 3: Nicht vorher auf ACK-Bit warten
static BYTE lcdCycle(BYTE cmd, BYTE data) {
 if (!(cmd&NO_ACK)) do; while (lcdCycle(NO_ACK|RW,0)&0x80);
 P4OUT = P4OUT&~(RS|RW|EN) | cmd&(RS|RW);	// setze RS und R/W, lösche E
 if (!(cmd&RW)) P4DIR |= DMASK;	// Schreibzyklus: Treiber aktivieren
 data=lcdNibble(data);		// High-Nibble prozessieren
 if (!(cmd&NIBBLE)) {
  data=SwapNibbles(data);
  data=lcdNibble(data);		// Low-Nibble prozessieren
  data=SwapNibbles(data);	// Nibbles rücktauschen: Byte fertig
 }
 P4DIR &= ~DMASK;
 return data;
}

static BYTE LcdWrite(unsigned data) {
 P5OUT=P5OUT^2;
 return lcdCycle(data>>8,data);
}

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
 0x00,0x00,0x00,0x00,0x00,0x0C,0x04,0x08,	// Komma mit Unterlänge
};

static void LcdInit() {
 DELAY_MS(15);		// wait > 15 ms
 LcdWrite(0x930);	// set 8 bit interface
 DELAY_MS(5);		// wait > 4.1 ms
 LcdWrite(0x930);	// set 8 bit interface
 DELAY_MS(1);		// wait > 0.1 ms
 LcdWrite(0x832);	// set 8 bit, then 4 bit interface
 LcdWrite(0x28);	// 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++) lcdCycle(RS,charmap[i]);
}

// String ausgeben
static void LcdWrite(const char*s) {
 for(;;) {
  char c=*s++;
  if (!c) break;
  lcdCycle(RS,c);
 }
}

// String ab Speicherposition (Kursorposition) 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=012;
  s=e;		// auf das Ende
 }
// 4. Leerzeichen auffüllen
 while (s-a<len) *s++=' ';
// 5. terminieren
 *s=0;
 return s-a;
}

/*=================*
 *= DDS-Generator =*
 *=================*/
#define MCLK 66666666LL	// Hz
#define FVAL(x) (long)((((long long)(x)<<28)+MCLK/2)/MCLK)
#define HVVAL(x) ((x)*65536/118)

struct FEATURE {
 int g,o;	// Verstärkung (Gain) und Offset
 long fu,fo,fs;	// Frequenzbereich des Wobbelgenerators und Schrittweite
 unsigned hv;	// Sollspannung (100V) als A/D-Wert
 unsigned flags;// unbenutzt (Sinus/Dreieck/Rechteck-Auswahl usw.)
}efeature __attribute__((section(".infomem")))={
 0,0,
 FVAL(100000),	// 100 kHz, >402653
 FVAL(140000),	// 200 kHz, >603979
 FVAL(50),	// 805 Schrittweite 0,2 kHz -> 251 Schritte
 HVVAL(80),	// Soll-Hochspannung (Regelziel)
 0};
 
FEATURE feature;

long fc;	// Momentane Frequenz

#define DDS_SPI

// Erst mal ohne serielle Schnittstelle
static void dds_write(unsigned w) {
 P3OUT &= ~0x04;	// !FSYNC LOW
#ifdef DDS_SPI
 U0TXBUF = w>>8;	// High-Datenbyte senden (fällt zum Sendeschieberegister durch)
 U0TXBUF = w;		// Low-Datenbyte senden (kommt alsbald ins Sendeschieberegister)
 while (!(U0TCTL&1));	// warten bis alle Bits raus sind
#else
 for (int i=0; i<16; i++) {
  if (w&0x8000) P3OUT |= 0x02; else P3OUT &=~0x02;
  P3OUT &=~0x08;
  P3OUT |= 0x08;
  w<<=1;
 }
#endif
 P3OUT |= 0x04;		// !FSYNC HIGH
}

// Frequenz setzen, w=0x4000 für FREQ0, w=0x8000 für FREQ1
static void dds_writef(long f, unsigned w) {
 dds_write(f&0x3FFF | w);
 dds_write(f>>14 | w);
}

static void dds_writefc0(){
 dds_write(0x2020);
 dds_writef(fc,0x4000);
}

static void dds_init() {
 fc=feature.fu;
#ifdef DDS_SPI
 U0CTL |= 1;	// anhalten
 U0TCTL = 0xE2;	// SCK inaktiv HIGH, Taktquelle = SMCLK, ohne STE
 U0BR0  = 0x02;	// maximale Geschwindigkeit
 U0BR1  = 0x00;
 U0MCTL = 0;	// keine Modulation
 U0CTL  = 0x1F;	// 8 bit, SPI Master, Loopback-Modus
 ME1   |= 0x40;	// aktivieren
 U0CTL &=~1;	// aktivieren
 P3SEL |= 0x0A;	// Portpins P3.1 und P3.3 umfunktionieren
#endif
 dds_writefc0();
}

// Sweep von 100 bis 150 kHz ausführen
static bool dds_nextf() {
 bool ret=true;
 fc+=feature.fs;
 if (fc>feature.fo) fc=feature.fu, ret=false;
 dds_writefc0();
 return ret;
}

/*==========================*
 *= Serielle Schnittstelle =*
 *==========================*/
#define SERINT

// Ermittelte Taktfrequenz: 40*115200 = 4,6 MHz
#ifdef SERINT
static volatile char recvbuf[256];
static volatile unsigned char recvwr, recvrd;	// Lese- und Schreibzeiger

// Interruptroutinen (noch) ohne (Hardware-)Handshake
//interrupt(UART1TX_VECTOR) isr_uart1tx() {
// U1TXBUF=sendbuf[sendrd++];
// if (sendrd==sendwr) IE2 &=~UTXIE1;
//}

interrupt(UART1RX_VECTOR) isr_uart1rx() {
 recvbuf[recvwr++]=U1RXBUF;
// if (recvwr==recvrd) recvrd++;	// älteste Daten verfallen
}
#endif

// Serielle Schnittstelle für 1200-7-n-1 initialisieren
static void com_initpnp() {
 U1CTL |= 1;	// anhalten
#ifdef SERINT
 /*sendwr=sendrd=*/recvwr=recvrd=0;
#endif
 U1BR0 = 3840 & 0xFF; 
 U1BR1 = 3840 >> 8;
 U1CTL = 0;	// 7-n-1
 U1CTL &=~1;	// aktivieren
}
 
static void com_init() {
 U1CTL |= 1;	// anhalten
 U1TCTL = 0x20;	// MCLK
 U1BR0  = 40;	// 115200 Baud (was der Standard-PC kann)
 U1BR1  = 0;
 U1MCTL = 0;
 U1CTL  = 0x10;	// 8-n-1
 ME2   |= 0x30;	// aktivieren
 U1CTL &=~1;	// aktivieren
 P3SEL |= 0xC0;	// Portpins P3.6 und P3.7 umfunktionieren
#ifdef SERINT
 IE2   |= URXIE1;	// Interruptfreigabe für Empfang
#endif
 DMACTL0 = 0x000A;	// DMA-Kanal 0 dem TxD zuordnen
 DMA0DA  = (unsigned)&U1TXBUF;
}

// Einzelbyte-Version, blockierend
//static void com_send(char c) {
//#ifdef SERINT
// unsigned char swr=sendwr;
// sendbuf[swr++]=c;
// while (sendrd==swr);	// warten bis Platz
// sendwr=swr;
// IE2 |= UTXIE1;		// Interrupts freigeben (falls gesperrt gewesen)
//#else
// while (!(IFG2&UTXIFG1));	// warten
// U1TXBUF=c;			// senden
//#endif
//}

static int com_recv() {
 unsigned to=0;
 do if (!--to) return -1; 
#ifdef SERINT
 while (recvrd==recvwr);	// warten
 return (unsigned char)recvbuf[recvrd++];	// Zeichen lesen
#else
 while (!(IFG2&URXIFG1));	// warten
 return U1RXBUF;		// empfangen
#endif
}

// Datenblock-Version, blockierend
//static void com_send(const char *data, int len) {
// if (len) do com_send(*data++); while (--len);
//}

// PROBLEM: Datenempfang zu langsam, ISR kommt anscheinend nicht hinterher
static int com_recv(void *data, int len) {
 char *d=(char*)data;
 int ret=0;
 if (len) do{
  int c=com_recv();
  if (c==-1) break;
  *d++=c;
  ret++;
 }while (--len);
 return ret;
}

// Statusabfrage; liefert Anzahl freier Bytes im Sendepuffer
//static int com_txspace() {
//#ifdef SERINT
// return (unsigned char)(sendrd-sendwr-1);	// 0..255
//#else
// return IFG2&UTXIFG1 ? 1 : 0;
//#endif
//}

// Statusabfrage; liefert Anzahl vorhandener Bytes im Empfangspuffer
//static unsigned com_rxavail() {
//#ifdef SERINT
// return (unsigned char)(recvwr-recvrd);	// 0..255
//#else
// return IFG2&URXIFG1 ? 1 : 0;
//#endif
//}

static void SendSerialPnpId() {
 static const char serpnpid[]={
   '(',
   0x01,0x24,
   'M','D','C',
   '0','2','8','8',
   '\\','0','0','3','1','4','1','5','9',
   '\\','M','O','D','E','M',
   '\\','M','D','C','0','1','4','4',
   ',','A','T','M','0','0','9','6',
   '\\','Z','I','P',' ','2','8','8',
   'C','4',
   ')'};
 DMA0SA = (unsigned)serpnpid;
 DMA0SZ = sizeof(serpnpid);
 DMA0CTL= 0x03F0;	// DMA-Transfer-Start
 while(!(DMA0CTL&DMAIFG));
 delay(0xFFFF);		// warten
 com_init();		// auf 8 bit schalten
}

/*=======*
 *= ADC =*
 *=======*/
static unsigned adcadd[5];	// Mittelwerte aufsammeln, Indizes:
// 0: Amplidudenvergleich, 1: Phasenvergleich (4 Blöcke à 4 Samples)
// 2: Hochspannung (8 Blöcke à 2 Samples)
// 3: Referenzspannung, 4: Temperatur (16 Blöcke à 1 Sample)
static struct{
 unsigned hv;	// Hochspannung (nur zum Report)
 unsigned ref;	// Referenzspannung
 unsigned temp;	// Temperatur
}adcmean;	// Mittelwerte aufheben (immer Summe aus 16 Samples)
// Alle anderen Mittelwerte werden sofort verarbeitet

static volatile char bits;	// Steuerbits, Bit-Zuordnung
// 0: Wobbel-Zyklus aktiv
// 1: Ausgabe der Messdaten auf serielle Schnittstelle (sonst verwerfen oder RAM?)
// High-Nibble = umlaufender Interruptzähler

static volatile char dmatxbuf[3];	// 3 Bytes pro Block

static unsigned Ub;	// Betriebsspannung (nach Reset vor Start des HV-Generators)
static unsigned sut=1600;	// Start-Up-Timer nach Reset (1 s)

static volatile struct{
 long x;	// Frequenzwert
 unsigned y;	// Minimum oder Maximum
}minmax[2];

// Aufruf alle 76 µs, 13 kHz
// Zur Lastverteilung erfolgt die Datenverarbeitung in folgenden Zeitschlitzen:
//		0   1	2   3	4   5	6   7	8   9	10  11	12  13	14   15
// Amp+Pha	x		x		x		x
// Hochsp.		x				x
// Referenzsp.				x
// Temperatur								x
interrupt(ADC12_VECTOR) AdcComplete() {
 P5OUT^=4;
 unsigned a,b;
 char c;
// Teil 1: Amp&Pha
 a=ADC12MEM[0]+ADC12MEM[3]+ADC12MEM[6]+ADC12MEM[9] +adcadd[0];
 b=ADC12MEM[1]+ADC12MEM[4]+ADC12MEM[7]+ADC12MEM[10]+adcadd[1];
 c=bits+=0x10;
 if (!(c&0x30)) {		// alle 4 Zyklen, 303 µs, 3,3 kHz
  if (c&1) {
   P5OUT^=2;
   if (c&2) {
    a>>=4;
    dmatxbuf[0]=(char)a;
    dmatxbuf[1]=(char)(a>>8|b&0xF0);
    dmatxbuf[2]=(char)(b>>8);
    DMA0CTL=0x03F0;	// DMA-Transfer-Start
   }else{
    if (minmax[0].y>a) {
     minmax[0].y=a;
     minmax[0].x=fc;
    }
    if (minmax[1].y<a) {
     minmax[1].y=a;
     minmax[1].x=fc;
    }
   }
  }
  a=b=0;
  if (c&1) {
   if (!dds_nextf()) bits&=~3;
  }
 }
 adcadd[0]=a;
 adcadd[1]=b;
// Teil 2: Hochspannung
 a=ADC12MEM[2]+ADC12MEM[8]+adcadd[2];	// Hochspannung (1,6 kHz)
 if ((c&0x70)==0x20) {		// TODO: Hochspannung regeln
  adcmean.hv=a;
  b=sut;
  if (b) {
   sut=--b;
   if (!b) Ub=a;		// als Speisespannung abspeichern
  }else{
#define PWMMIN 8		// minimaler PWM-Wert (außer bei Überspannung)
#define PWMMAX 220		// maximaler PWM-Wert (zusätzlich begrenzt durch Tastverhältnis-Limit)
// Tastverhältnis für dreieckförmigen Stromverlauf in Spule:
// TV = (U(hv)-Ub) / Ub (oder U(hv)/Ub - 1)
// bspw. U(hv)=100, Ub=10 → TV = 9 (also 9:1)
// PWM-Wert = TV*256/(TV+1), bspw. TV=9 → 230
// Annäherung an Regelziel: TV = (soll-ist)*Faktor
   b = a/(Ub>>4);
   b = b>=16 ? b-16 : 0;	// 16 = Tastverhältnis 1:1
  unsigned c = feature.hv>a ? (feature.hv-a)>>3 : 0;
  if (b>c) b=c;			// Kleineres beider Tastverhältnisse
//  if (feature.hv<=a) b=0;
  b = b<<8/(b+16);
  if (b<PWMMIN) b=PWMMIN;
  if (b>PWMMAX) b=PWMMAX;
  TACCR2 = b;
  }
  a=0;
 }
 adcadd[2]=a;
// Teil 3: Referenzspannung und Temperatur
 a=ADC12MEM[5]+adcadd[3];	// Referenzspannung (1,8 V = 0x0B84?)
 b=ADC12MEM[11]+adcadd[4];	// Temperatursensor (inzwischen aktualisiert)
 if ((c&0xF0)==0x60) {
  adcmean.ref=a;
  adcmean.temp=b;
  a=b=0;
 }
 adcadd[3]=a;
 adcadd[4]=b;
}

// Start eines Wobbelzyklus' mit Ausgabe der Amplituden- und Phasenwerte
// über die serielle Schnittstelle mit DMA
static void MessenStart(char c) {
 minmax[0].y=0xFFFF;		// Extremwertsuche vorbereiten
 minmax[1].y=0;
 DMA0SA  = (unsigned)dmatxbuf;	// DMA vorbereiten
 DMA0SZ  = 3;
 bits|=c;	// loslegen (ADU-ISR macht den Rest!)
}

// Messen und gleichzeitig Daten ausgeben
static void Messen() {
 P5OUT|=1;	// H = Samplezeit
 MessenStart(3);
 while (bits&1);
 P5OUT&=~1;	// L = Pausenzeit
}

// Datenblock von serieller Schnittstelle setzt D/A-Wandler und Wobbel-Bereich
static void SetFeature() {
 int rl=com_recv(&feature,sizeof(feature));
 if (rl!=sizeof(feature)) {
  char s[16];
  sprintf(s,"B: TimeOut %d.",rl);
  LcdWrite(0xC0,s);
  feature=efeature;
  return;
 }
 DAC12_0DAT=feature.g;		// D/A-Wandler nachführen
 DAC12_1DAT=-feature.o;
 fc=feature.fu;			// DDS-Generator nachführen
 if (feature.fu>feature.fo) {
  memcpy(&feature.fu,&efeature.fu,12);
  LcdWrite(0xC0,"B: Fehler!");
 }
 dds_writefc0();
 LcdWrite(0xC0,"B: OK     ");
}

// Liefert die per SetFeature() gesetzten Parameter zurück
static void GetFeature() {
 DMA0SA = (unsigned)&feature;
 DMA0SZ = sizeof(feature);	// 20
 DMA0CTL= 0x03F0;	// DMA-Transfer-Start
}

// Liefert die (gemittelten) A/D-Wandler-Werte:
// int Hochspannung
// int Referenzspannung
// int Temperatur
static void GetAdcValues() {
 DMA0SA = (unsigned)&adcmean;
 DMA0SZ = sizeof(adcmean);	// 6
 DMA0CTL= 0x03F0;	// DMA-Transfer-Start
}

/********************************/

static int Scale(long v, long f) {
 return int(v*f>>16);
}
// Messwert skalieren (mit Faktor <f>/65536) und verschieben (mit Offset <o>)
static int Scale(unsigned v, unsigned f, int o=0) {
 return Scale((long)v,(long)f)+o;
}

static char dispstate;

static void ShowState0() {
 LcdWrite(0x80,"+??,??kHz ???,?V");
 LcdWrite(0xC0,"+?,??? V  ??,?\337C");	// zweite Zeile
}

static void ShowState1() {
 LcdWrite(0x80,"  PC-Steuerun\010  ");
 LcdWrite(0xC0,"                ");
}

static void ShowState2() {
 LcdWrite(0x80,"  Reset         ");
 LcdWrite(0xC0,"  PnP-Abfra\010e?  ");
}

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

int main(void) {
// WDTCTL = WDTPW|WDTHOLD;	// Watchdog beruhigen (macht __low_level_init)
 DCOCTL = 0xE0;			// schneller
 BCSCTL1= 0x87;			// MCLK = SMCLK = 4,6 MHz
 SVSCTL = 0x18;			// Bei Unterschreiten der Betriebsspannung unter 3 V Reset auslösen lassen
 FCTL2 = 0xA549;		// Flash-Takt = 4,6 MHz / 10 = 460 kHz
 P1OUT = 0x00; P1DIR = 0x01;	// Unbeschaltete Pins festnageln
 P2OUT = 0x03; P2DIR = 0xFB;	// Sinus zm VCA
 P3OUT = 0x5C; P3DIR = 0x5F;	// !SCLK und !FSYNC auf HIGH
 P4OUT = 0x00; P4DIR = 0x0F;
 P5OUT = 0x00; P5DIR = 0xBF;

 P6OUT = 0; P6SEL = 0xFF;	// Port 6 rein analog
 feature=efeature;
/*##################
# A/D-Schema für 12 AD12MCTL-Plätze:
# 0-2-3	(100V)
# 0-2-1	(Referenz)
# 0-2-3	(100V)
# 0-2-A	(Chiptemperatur) - Interrupt bei Speicherplatz 10(!)
###################*/
 static const char DefAdc12Mctl[]={
  0x10,0x12,0x13,	// alles zwischen 0V und 2,5V
  0x10,0x12,0x11,
  0x10,0x12,0x13,
  0x10,0x12,0x9A};
 memcpy(ADC12MCTL,DefAdc12Mctl,sizeof(DefAdc12Mctl));
  
 ADC12IE = 1<<10;		// Interrupt bei Speicherplatz 5 und 10
 ADC12CTL1 = 0x0216;		// MCLK, alle Kanäle, durchlaufend
 ADC12CTL0 = 0x22F3;		// 2,5 V Referenzspannung, Sample-Timer 16 Takte, Start
/*##################
# Zeitbetrachtungen (MCLK = 4,6 MHz):
# Sample-Zeit:	16 MCLK-Takte	3,4 µs	zu kurz für Temperatursensor, min. 30 µs
# Umsetz-Zeit:	13 -"-		2,8 µs
# Umsetzrate:	29 -"-		6,3 µs	160 kSa/s
# Block-Rate:	348 -"-		76 µs	13 kSa/s	Interruptrate
# Vier Blöcke:	1392 -"-	303 µs	3,3 kSa/s	Maximal 4 für Summation in 16 Bit
# Zum Vergleich: Serielle Schnittstelle @ 115200 Baud
# Vier Bytes:	40×10×4 -"-	348 µs	2,8 kSa/s	ungepackte 16-Bit-Übertragung
# Drei Bytes:	40×10×3 -"-	260 µs	3,8 kSa/s	gepackte 12-Bit-Übertragung
# Die Schnittstelle muss ein wenig langsamer sein als der A/D-Wandler!
###################*/
 
 DAC12_0CTL = 0x03B2;		// D/A-Wandler aktivieren
 DAC12_1CTL = 0x03B2;		// … kalibrieren lassen
 DAC12_0DAT = feature.g;
 DAC12_1DAT = -feature.o;
 
 TACCR0 = 0x00FF;
 TACTL  = 0x0210;		// als 8-bit-Zähler, generiert 18 kHz
 TACCTL2= 0x00E0;		// Set/Reset-Modus
 P2SEL |= 0x10;			// Port P2.4 umfunktionieren

 LcdInit();
 com_init();
 if (P3IN&0x20) com_initpnp();

 setstate(2);			// PnP-Abfrage?

 static char s[12];
 static int DeltaF;		// Resonanzfrequenzdifferenz bei ∆U ≈ 4 V, in Hz
 static int Uhoch,/*Ugr,*/Temp;
// Spannungen in mV, Temperatur in 0,1 °C

 dds_init();
 _EINT();

 for(;;) {
  if (U1CTL&CHAR) {		// 8-Bit-Modus
// warte auf Kommandozeichen
   int c=com_recv();
   switch (c) {
    case 'A': setstate(1); Messen(); break;
    case 'B': setstate(1); SetFeature(); break;
    case 'C': setstate(1); GetFeature(); break;
    case 'D': setstate(1); GetAdcValues(); break;
    case -1:  {			// bei TimeOut
     if (dispstate==1 && memcmp(&efeature,&feature,sizeof(efeature))) {
      FCTL3 = 0xA500;		// LOCK entfernen
      FCTL1 = 0xA502;		// ERASE setzen
      efeature.g=0xFFFF;	// Segment löschen (≈ 10 ms)
      FCTL1 = 0xA540;		// Schreiben freigeben
      efeature=feature;		// schreibt byteweise in den Flash-Bereich (≈ 1,2 ms)
      FCTL1 = 0xA500;		// Schreiben sperren
      FCTL3 = 0xA510;		// LOCK setzen
     }
     setstate(0);
     DAC12_1DAT=(unsigned)-0x7FE;	// maximal positiver Offset
     delay(1024);
     MessenStart(1);		// Extremwertsuche
     while (bits&1);
     long diffmin=minmax[1].x;	// hier: nur Minima verwenden
     DAC12_1DAT=+0x7FE;		// maximal negativer Offset
     delay(1024);
     MessenStart(1);		// Extremwertsuche
     while (bits&1);
     DAC12_1DAT=-feature.o;	// Offset zurück
     diffmin-=minmax[1].x;
     DeltaF=Scale(diffmin,(MCLK>>12)/10/*1628*/);
    }break;
   }
  }else if (!(P3IN&0x20)) {	// RTS aktiviert: String senden!
   LcdWrite(0xCD,"!");		// Fragezeichen ersetzen
   SendSerialPnpId();
  }

// Veränderliche Positionen aktualisieren  
  switch (dispstate) {
   case 0: {
    Dezimal(DeltaF,s,6,2); LcdWrite(0x80,s);	// Differenzfrequenz in kHz ausgeben
    Uhoch=Scale(adcmean.hv,1180);		// Hochspannung in 1/10 V
    Dezimal(Uhoch,s,6,1); LcdWrite(0x89,s);	// in V ausgeben
    int Uref=Scale(adcmean.ref,2540);		// Referenzspannung in 10 mV
    Dezimal(Uref,s,6,3); LcdWrite(0xC0,s);	// in V ausgeben
    Temp =Scale(adcmean.temp,2380,-850);	// Temperatur in 1/10 K
    Dezimal(Temp,s,5,1); LcdWrite(0xC9,s);	// in °C ausgeben
    delay(0xFFFF);				// warten (ca. 40 ms)
   }break;
  }

 }
}
Detected encoding: UTF-80