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-8 | 0
|