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

#include <msp430.h>
#include <string.h>	// memcpy
#include <stdint.h>
#include <stdbool.h>
#define _delay_ms(ms) __delay_cycles((unsigned long)F_CPU*ms/1000)

/****************************************************************
 * Piezo-Messgerät (Wobbel-Generator 100 kHz .. 20 MHz; 	*
 * Amplituden- und Phasenmessung, viele Analogsignalschalter)	*
 * MSP430F6636: 128 KByte Flash, 16+2 KByte RAM, ADU, DAU, USB	*
 * (vom Flash nur 32 KByte für Kode benutzbar = msp-gcc-Limit)	*
 * (Minimaler) Compiler: mspgcc 2012 oder msp430-gcc 2015	*
 * Taktfrequenz: 8 MHz (Quarz und CPU-Takt: konservativ)	*
 * heha, 151030							*
 ****************************************************************/
 
/* Angeschlossene Hardware
=== Amplituden- und Phasenvergleicher (AD8302) ===
P6.0	A0	97	Amplitudenvergleich
P6.1	A1	98	Referenzspannung
P6.2	A2	99	Phasenvergleich
=== Ausgangstreiber (VCA822) ===
P6.6	DAC0	3	Verstärkung
P6.7	DAC1	4	Offset
=== DDS-Generator (AD9834, mit 66 MHz gespeist) ===
P8.1	-	59	/FSYNC
P8.4	UCB1CLK	62	/SCLK
P8.5	UCB1SIMO 65	SDATA
=== LED ===
P8.6	-	66	rote LED (Anode)
P8.7	-	67	grüne LED (Anode)
=== Kopplung auf Ein/Ausgangsbuchse X4 ===
P1.0	-	34	mit 10 kΩ
P1.1	-	35	mit 2,2 kΩ
P1.2	-	36	mit 470 Ω
P1.3	-	37	mit 100 Ω
=== Vergleichsamplitude und -phase ===
P2.0	-	17	mit 2,2 kΩ (sonst 10 kΩ)
P2.1	-	18	mit 470 Ω
P2.2	-	19	mit 100 Ω
P2.3	-	20	direkt
P2.5	-	22	Terminierung (= Spannungsteiler) 50 Ω
P2.6	-	23	„Terminierung“ 100 pF (Phasendreher)
P2.7	-	24	„Terminierung“ 10 nF (Phasendreher)
=== Sonstige Analogsignalschalter ===
P1.4	-	38	legt Ausgangssignal auf X3 oder X4
P1.5	-	39	legt Ausgangssignal auf X2 (optional)
P1.6	-	40	Terminierung von X3 mit 50 Ω (wenn P1.7 ein)
P1.7	-	41	legt Ausgangssignal auf X3
P2.4	-	21	Terminierung 50 Ω an X4
=== USB ===
PU.0	DP	77	USB Data+
PU.1	DM	79	USB Data-
	PUR	78	Pull-Up-Widerstand 1,5 kΩ an Data+
P7.2	XT2IN	84	Quarz 8 MHz
P7.3	XT2OUT	85	Quarz 8 MHz
=== Debug ===
P5.0	VeREF+	9	Kontrolle Referenzspannung (2,5 V)
P5.1	VeREF-	10	Kontrolle Referenzspannung (0 V)
P5.6	ADC12CLK 16	Kontrolle A/D-Takt (4 MHz)
 P1.0	ACLK	34	Kontrolle Takt
P3.4	SMCLK	46	Kontrolle Takt
 P2.7	MCLK	24	Kontrolle Takt (via Port Mapping Controller)

Murks in der Schaltung:
	Quarz-Anschluss verwechselt!
	BSL-Jumper-Mittelpin 2 direkt an PUR,
	 Pin 1 weg von Masse, stattdessen 1 MΩ
	Stromversorgung für Analogsignalschalter abgetrennt
	Kondensator an VeREF+, Masse an VeREF-
	C44 raus!
Ein standby-konformer Schlafmodus kann von der Hardware nicht realisert werden.
Dazu müsste die 5P-Leitung abgetrennbar sein.
Stromaufnahme: 140 mA
*/

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

// Persistente Einstellungen
struct FEATURE {
 int g,o;	// Verstärkung (Gain) und Offset
 long fu,fo,fs;	// Frequenzbereich des Wobbelgenerators und Schrittweite
 unsigned switches;// 16 Analogsignalschalter
}efeature __attribute__((section(".infomem")))={
 0,0,
 FVAL(100000),	// 100 kHz, >402653
 FVAL(140000),	// 140 kHz, >603979
 FVAL(50),	// Schrittweite 50 Hz -> 801 Schritte
 0x3190};	// 90: VCA-Ausgang auf X3
		// 31: X4 terminieren, Vergleichseingang terminieren, 0 dB = 2,2 kΩ


static struct{
 char reportid;	// == 2
 bool enab;	// Ausgabe der Messdaten
 FEATURE f;	// im RAM liegende Kopie von <efeature> 
}feature;

static struct{
 char reportid;	// == 3
 char cntr;	// umlaufender Interruptzähler
 unsigned ref;	// Referenzspannung
 unsigned temp;	// Temperatur
}adcmean;	// Mittelwerte aufheben (immer Summe aus 16 Samples)

static void FlashUpdate() {
 if (!(FCTL4&0x80)
 && memcmp(&efeature,&feature.f,sizeof(efeature))) {
  FCTL3 = 0xA500;
  FCTL1 = 0xA502;		// ERASE setzen
  efeature.g=0xFFFF;		// Segment löschen (≈ 10 ms)
  FCTL1 = 0xA5C0;		// Schreiben freigeben
  efeature=feature.f;		// schreibt byteweise in den Flash-Bereich (≈ 1,2 ms)
 }
}

long fc;	// Momentane Frequenz

#define DDS_SPI

static void dds_write(unsigned w) {
 P8OUT&=~0x02;		// !FSYNC LOW
#ifdef DDS_SPI
 UCB1TXBUF = w>>8;	// High-Datenbyte senden (fällt zum Sendeschieberegister durch)
 while (!(UCB1IFG&2));	// warten bis Sendehalteregister leer
 UCB1TXBUF = w;		// Low-Datenbyte senden (kommt alsbald ins Sendeschieberegister)
 while (!(UCB1IFG&2));	// warten bis Sendehalteregister leer
 while (UCB1STAT&1);	// warten bis alle Bits raus sind
#else
 for (int i=0; i<16; i++) {
  if (w&0x8000) P8OUT|=0x20; else P8OUT&=~0x20;
  P8OUT&=~0x10;
  P8OUT|= 0x10;
  w<<=1;
 }
#endif
 P8OUT|= 0x02;		// !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.f.fu;
#ifdef DDS_SPI
 UCB1CTLW0 = 0xE980;	// SCK inaktiv HIGH, Taktquelle = SMCLK, kein STE
 UCB1BRW = 0;		// nicht dividieren
 P8SEL |= 0x30;		// Portpins P8.4 und P8.5 umfunktionieren
#endif
 dds_writefc0();
}

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

/*=========================*
 *= USB-HID-Schnittstelle =*
 *=========================*/
typedef union {
 struct{
  uint8_t bmRequestType;
  uint8_t bRequest;
  uint16_t wValue;
  uint16_t wIndex;
  uint16_t wLength;
 };
 struct{
  uint16_t wRequest;
  uint8_t wValueL;
  uint8_t wValueH;
  uint8_t wIndexL;
  uint8_t wIndexH;
  uint8_t wLengthL;
  uint8_t wLengthH;
 };
} __attribute__((packed)) USB_DEVICE_REQUEST;

static uint8_t usb_configuration;
static uint16_t zero;		// Antwort für GET_STATUS
static uint8_t*ep0_ptr;
static int ep0_len;
static uint8_t usb_free_memory_ptr;

#define DW(w) (w)&0xFF,(w)>>8
static const uint8_t report_desc[]={
 0x06, DW(0xFF00),	// Usage Page (Vendor Defined)
 0x09, 0x01,	// Usage Page (Vendor Defined)
 0xA1, 0x01,	// COLLECTION (Application)
 0x85, 1,	// Report ID (1)
 0x95, 63,	// Report Count
 0x75, 8,	// Report Size: Bytes
 0x09, 1,	// Usage
 0x81, 0x02,	// Input (Data,Var,Abs)
 0x85, 2,	// Report ID (2)
 0x95, sizeof feature-1,	// Report Count
 0x09, 2,	// Usage
 0xB1, 0x02,	// Feature (Data,Var,Abs)
 0x85, 3,	// Report ID (3)
 0x95, sizeof adcmean-1,	// Report Count
 0x09, 3,	// Usage
 0xB1, 0x02,	// Feature (Data,Var,Abs)
 0xc0};		// end Collection

static const uint8_t device_desc[18] = {
 18,		// sizeof(USB_DEVICE_DESCR)
 1,		// USB_DESCR_DEVICE
 DW(0x0101),	// USB-Version
 0,		// Klasse: -
 0,		// Unterklasse: -
 0,		// Protokoll: -
 8,		// maximale Paketgröße EP0 (8 durch MSP430 festgelegt)
 DW(0x16C0),	// VENDOR,
 DW(0x27D9),	// PRODUCT,
 DW(0x0050),	// bcdVERSION,
 1,		// iManufacturer (erscheint unter Linux)
 2,		// iProduct (erscheint unter Windows und Linux)
 3,		// iSerialNumber
 1};		// Anzahl Konfigurationen

static const uint8_t config_desc[9+9+9+7] = {
 9,		// sizeof(USB_CONFIG_DESCR)
 2,		// USB_DESCR_CONFIG
 DW(sizeof config_desc),	// wTotalLength
 1,		// bNumInterfaces
 1,		// bConfigurationValue
 0,		// iConfiguration
 0x80,		// bmAttributes: busgespeist
 150/2,		// bMaxPower: viel!!

 9,		// sizeof(USB_INTERFACE_DESCR)
 4,		// USB_DESCR_INTERFACE
 0,		// bInterfaceNumber
 0,		// bAlternateSetting
 1,		// bNumEndpoints
 3,		// USB_DEVICE_CLASS
 0,		// USB_DEVICE_SUBCLASS
 0,		// USB_DEVICE_PROTOCOL
 0,		// iInterface

 9,		// bLength of HID descriptor
 0x21,		// HID Descriptor Type
 DW(0x0101),	// HID Revision number 1.01
 0,		// Target country, nothing specified
 1,		// Number of HID descriptors to follow
 0x22,		// Report descriptor type
 DW(sizeof report_desc),  // Total length of report descriptor

 7,		// sizeof(USB_ENDPOINT_DESCR)
 5,		// USB_DESCR_ENDPOINT
 0x81,		// bEndpointAddress: IN EP1
 3,		// bmAttributes: INTERRUPT
 DW(64),	// wMaxPacketSize
 1}; 		// bInterval

//const void *const usb_config_descr[USB_CONFIG_COUNT] = {&usb_config1_descr};

static const uint8_t*string_descr(int idx) {
 if ((unsigned)idx>3) return 0;
 static const wchar_t strings[]=
	L"\x0304" L"\x0407"
	L"\x0324" L"haftmann#software"	//*
	L"\x0326" L"Netzwerkanalysator"
	L"\x033E" L"henrik.haftmann@gmail.com:0050";
//* "\x0324": Die 24 ist die Anzahl der Buchstaben von "haftmann#software"
// (=17) mal zwei (=34) plus zwei (=36), in hexadezimal (=24).
// Leider gibt es kein C-Makro, was das automatisch bewerkstelligen könnte.
// Die maximale Stringlänge beträgt 126 Zeichen.
 const uint8_t*p=reinterpret_cast<const uint8_t*>(strings);
 if (idx) do p+=*p; while(--idx);
 return p;
}

// MSP430-Endpoint-Register, in eine Struktur gepackt
typedef struct{
 volatile uint8_t CNF;
 volatile uint8_t BAX;
 volatile uint8_t CTX;
 uint8_t reserved[2];
 volatile uint8_t BAY;
 volatile uint8_t CTY;
 volatile uint8_t SIZXY;
} __attribute__((packed)) USB_EPBD;

volatile USB_EPBD *const usb_in_epbd = ((USB_EPBD*)USBIEPCNF_1_) - 1;
volatile USB_EPBD *const usb_out_epbd = ((USB_EPBD*)USBOEPCNF_1_) - 1;

static void epDisable(uint8_t ep) {
 volatile USB_EPBD *epbd;
 if (ep & 0x80) {
  ep &= ~0x80;
  epbd = &usb_in_epbd[ep];
  USBIEPIE &= ~(1 << ep);
 } else {
  epbd = &usb_out_epbd[ep];
  USBOEPIE &= ~(1 << ep);
 }
 if (!ep) return;
 epbd->CNF = 0;
 epbd->BAX = 0;
 epbd->BAY = 0;
 epbd->CTX = NAK;
 epbd->CTY = NAK;
 epbd->SIZXY = 0;
}

static void usbDisableEndpoints() {
 feature.enab=false;
 int i;
 for (i = 1; i < 8; i++) {
  epDisable(i | 0x80);
  epDisable(i);
 }
 usb_free_memory_ptr = 0;
}

static void usbReset() {
 USBCTL = 0;
 USBFUNADR = 0;
 ep0_len = 0;
 usb_configuration = 0;

 USBOEPIE = 0;
 USBIEPIE = 0;
 USBOEPCNT_0 = NAK;
 USBOEPCNF_0 = UBME | USBIIE | STALL;
 USBIEPCNT_0 = NAK;
 USBIEPCNF_0 = UBME | USBIIE | STALL;
 usbDisableEndpoints();
 USBOEPIE = BIT0;
 USBIEPIE = BIT0;

 P8OUT &= ~BIT7;

 USBCTL = FEN;
 USBIFG = 0;
 USBIE = SETUPIE | RSTRIE | SUSRIE;
}

static void usbEnablePll() {
 USBPLLDIVB=USBPLL_SETCLK_8_0;	// für 8-MHz-Quarz, DIVQ=3, DIVM=18
 USBPLLCTL=UPFDEN|UPLLEN;	// PLL und Diskriminator aktivieren
 do {
  USBPLLIR = 0;
  _delay_ms(1);		// warten
 }while(USBPLLIR);	// warten bis PLL eingerastet
}

static void usbInit() {
 USBKEYPID = USBKEY;
 USBCNF = 0;
 USBPHYCTL =PUSEL;		// Port U umschalten
 USBPWRCTL = SLDOEN|VUSBEN;
 while(!(USBPWRCTL&USBBGVBV));
 usbEnablePll();
 USBCNF|=USB_EN;		// USB einschalten 
 usbReset();
 USBCNF|=PUR_EN;
}

static void usbSuspend() {
 USBCTL|=FRSTE;
 USBIFG&=~SUSRIFG;
 USBPLLCTL&=~UPLLEN;
 USBIE=RESRIE;
}

static void usbResume() {
 usbEnablePll();
 USBIFG&=~(RESRIFG|SUSRIFG);
 USBIE=SETUPIE|RSTRIE|SUSRIE;
}

static void ep0SendChunk() {
 int len=ep0_len;
 if (len>8) len = 8;
 memcpy((void*)&USBIEP0BUF,ep0_ptr,len);
 ep0_ptr+=len;
 ep0_len-=len;
 USBIEPCNT_0=len;
}

static void ep0Recv(void*buffer, unsigned len) {
 USB_DEVICE_REQUEST*rq = (USB_DEVICE_REQUEST*)&USBSUBLK;
 if (len>rq->wLength) len = rq->wLength;
 ep0_ptr=(uint8_t*)buffer;
 ep0_len=len;
}

static void ep0Send(const void*buffer, unsigned len) {
 ep0Recv((void*)buffer,len);
 ep0SendChunk();	// den ersten Block sofort absetzen; bei kurzen <len> wird <buffer> später ungültig
}

static void ep0SendNull() {
 ep0Send(0,0);
}

static void ep0Stall() {
 USBIEPCNF_0|=STALL;
}

static void ep0SendDesc(const uint8_t*desc) {
 if (!desc) ep0Stall();
 else ep0Send(desc,*desc);
}

/*
static void ep0EnableRecv() {
	USBOEPCNT_0 = 0;
}
*/
static void epSetup(uint8_t ep, uint8_t bufferSize, bool doubleBuf) {
 volatile USB_EPBD *epbd;
 int in;
 if (ep & 0x80) {
  ep &= ~0x80;
  epbd = &usb_in_epbd[ep];
  in = 1;
 }else{
  epbd = &usb_out_epbd[ep];
  in = 0;
 }
 if (!ep) return;
 epbd->CNF = 0;
 epbd->BAX = usb_free_memory_ptr;
 usb_free_memory_ptr += (bufferSize + 7) >> 3;
 if (doubleBuf) {
  epbd->BAY = usb_free_memory_ptr;
  usb_free_memory_ptr += (bufferSize + 7) >> 3;
  epbd->CNF |= DBUF;
 }
 epbd->SIZXY = bufferSize;
 epbd->CTX = NAK;
 epbd->CTY = NAK;
 epbd->CNF |= UBME | USBIIE;
 if (in) USBIEPIE |= 1 << ep;
 else USBOEPIE |= 1 << ep;
}

static void usbSetConfigurationHandler() {
 switch (usb_configuration) {
  case 1: {
   P8OUT|=BIT7;
   epSetup(0x81,64,true);
   ((char*)&USBSTABUFF)[1]=0;
   ((char*)&USBSTABUFF)[65]=0;
   feature.enab=true;
  }break;
  case 0: {
   usbDisableEndpoints();
   P8OUT&=~BIT7;
  }break;
 }
}

static void usbHandleSetupRequests() {
 USB_DEVICE_REQUEST*rq = (USB_DEVICE_REQUEST*)&USBSUBLK;
 if (rq->bmRequestType&0x80) USBCTL|=DIR;
 else USBCTL&=~DIR;
 switch (rq->wRequest) {
  case 0x0080:				// GET_STATUS,IN,Standard,Device
  case 0x0081: {			// GET_STATUS,IN,Standard,Interface
   ep0Send(&zero,2);
  }break;

  case 0x0082: {			// GET_STATUS,IN,Standard,Endpoint
   if (rq->wIndex!=0x81) goto stall;	// EP1IN?
   unsigned t=USBIEPCNF_1&STALL?1:0;
   ep0Send(&t,2);
  }break;

  case 0x0100:				// CLEAR_FEATURE,OUT,Standard,Device
  case 0x0101:				// CLEAR_FEATURE,OUT,Standard,Interface
  case 0x0300: 				// SET_FEATURE,OUT,Standard,Device
  case 0x0301: {			// SET_FEATURE,OUT,Standard,Interface
   ep0SendNull();
  }break;

  case 0x0500: {			// SET_ADDRESS,OUT,Standard,Device
   USBFUNADR=rq->wValueL;
   ep0SendNull();
  }break;
  
  case 0x0680:				// GET_DESCRIPTOR,IN,Standard,Device
  case 0x0681: switch (rq->wValueH) {	//GET_DESCRIPTOR,IN,Standard,Interface
   case 1: ep0SendDesc(device_desc); break;			// DEVICE
   case 2: ep0Send(config_desc,sizeof config_desc); break;	// CONFIGURATION
   case 3: ep0SendDesc(string_descr(rq->wValueL)); break;	// STRING
   case 0x22: ep0Send(report_desc,sizeof report_desc); break;	// HID Report
   default: goto stall;
  }break;

  case 0x0880: {			// GET_CONFIGURATION,IN,Standard,Device
   ep0Send(&usb_configuration,1);
  }break;
  
  case 0x0900: {			// SET_CONFIGURATION,OUT,Standard,Device
   usbDisableEndpoints();
   usb_configuration=rq->wValue;
   ep0SendNull();
   usbSetConfigurationHandler();
  }break;
  
  case 0x01A1: switch (rq->wValue) {	// GET_REPORT,IN,Class,Interface
//   case 0x0302: ep0Send(&feature,sizeof feature); break;	// type==Feature && ID==2
   case 0x0303: ep0Send(&adcmean,sizeof adcmean); break;	// ID==3
//   default: goto stall;
   default: ep0Send(&feature,sizeof feature); break;	// type==Feature && ID==2
  }break;
  
  case 0x0921: switch (rq->wValue) {	// SET_REPORT,OUT,Class,Interface
   case 0x0302: ep0Recv(&feature,sizeof feature); break;	// type==Feature && ID==2
   default: goto stall;
  }
  
  default: stall: ep0Stall();
 }
}
/*
#define USB_BUFFER(base_addr) ((void*)(((base_addr) << 3) + USBSTABUFF_))

static void epSendData(uint8_t ep, const void *data, uint8_t len) {
	if (ep & 0x80) {
		ep &= ~0x80;
		if (ep == 0) {
			ep0Send(data, len);
		} else {
			if ((usb_in_epbd[ep].CNF & DBUF) && (usb_in_epbd[ep].CNF & TOGGLE)) {
				memcpy(USB_BUFFER(usb_in_epbd[ep].BAY), data, len);
				usb_in_epbd[ep].CTY = len;
			} else {
				memcpy(USB_BUFFER(usb_in_epbd[ep].BAX), data, len);
				usb_in_epbd[ep].CTX = len;
			}
		}
	}
}

static void epStall(uint8_t ep) {
 if (!(ep&0x80)) return;
 ep&=~0x80;
 if (!ep) ep0Stall();
 else usb_in_epbd[ep].CNF|=STALL;
}

static void epEnableRecv(uint8_t ep) {
 if (ep&0x80) return;
 if (!ep) ep0EnableRecv();
 else if ((usb_out_epbd[ep].CNF&DBUF) && (usb_out_epbd[ep].CNF&TOGGLE)) 
   usb_out_epbd[ep].CTY = 0;
 else usb_out_epbd[ep].CTX = 0;
}

static bool epBusy(uint8_t ep) {
 if (!(ep&0x80)) return false;
 ep &= ~0x80;
 if (!ep) return false;
 if ((usb_in_epbd[ep].CNF&DBUF) && (usb_in_epbd[ep].CNF & TOGGLE))
   return (usb_in_epbd[ep].CTY != NAK);
 else return (usb_in_epbd[ep].CTX != NAK);
}
*/

static void usbEndpointInHandler(uint8_t ep) {
}

static void FeatureChanged() {
 PAOUT = feature.f.switches;
 DAC12_0DAT = feature.f.g;
 DAC12_1DAT = -feature.f.o;
 FlashUpdate();
}

static void ep0RecvChunk() {
 int l=USBOEPCNT_0&~NAK;
 if (l>ep0_len) l=ep0_len;
 memcpy(ep0_ptr,(void*)&USBOEP0BUF,l);
 ep0_ptr+=l;
 ep0_len-=l;
 USBOEPCNT_0=0;
 if (!ep0_len) FeatureChanged();
}

#pragma vector=USB_UBM_VECTOR
static __interrupt void USB_UBM_ISR(void) {
 if (USBIFG & SETUPIFG) {
  USBCTL |= FRSTE;
  usbHandleSetupRequests();
  USBIFG &= ~SETUPIFG;
 }
 int vector = USBVECINT;
 switch (vector) {
  case USBVECINT_RSTR: usbReset(); break;
  case USBVECINT_SUSR: usbSuspend(); break;
  case USBVECINT_RESR: usbResume(); break;
  case USBVECINT_INPUT_ENDPOINT0: {
   USBOEPCNT_0 = 0;
   if (ep0_len) ep0SendChunk();
   else ep0Stall();
  }break;
  case USBVECINT_OUTPUT_ENDPOINT0: {
   if (ep0_len) ep0RecvChunk();
   else USBOEPCNF_0 |= STALL;
  }break;
   
  case USBVECINT_SETUP_PACKET_RECEIVED: usbHandleSetupRequests(); break;
	
  default:
	if ((vector >= USBVECINT_INPUT_ENDPOINT1) && (vector <= USBVECINT_INPUT_ENDPOINT7)) {
		uint8_t ep = ((vector - USBVECINT_INPUT_ENDPOINT1) >> 1) + 1;
		usbEndpointInHandler(ep);
	} else if ((vector >= USBVECINT_OUTPUT_ENDPOINT1) && (vector <= USBVECINT_OUTPUT_ENDPOINT7)) {
		uint8_t ep = ((vector - USBVECINT_OUTPUT_ENDPOINT1) >> 1) + 1;
		usb_out_epbd[ep].CNF |= STALL;
	}
 }
}

static void setupClock() {
 P7SEL|=0x04;
 UCSCTL6=0x40CD;		// Quarz an XT2 aktivieren
 __delay_cycles(1000);
 do {
  UCSCTL7=0;
  __delay_cycles(100);
 }while (UCSCTL7&8);		// warten bis fehlerfrei
 SFRIFG1=0;			// sonst geht nichts!
 UCSCTL4=0x0055;		// MCLK = SMCLK = 8 MHz hochschalten
}

/*=======*
 *= ADC =*
 *=======*/
static unsigned adcadd[2];	// Mittelwerte aufsammeln, Indizes:
// 0: Referenzspannung, 1: Temperatur
// Alle anderen Mittelwerte werden sofort verarbeitet

static char*ep1base() {
 return (char*)&USBSTABUFF+(USBIEPCNF_1&TOGGLE?64:0);
}

static void ep1Commit() {
 USBIEPBCTX_1=64;	// ganzen Report abschicken
 ep1base()[1]=0;	// Zähler nullsetzen
}

static void ep1Put(unsigned a, unsigned b) {
 char*p=ep1base()+1;
 char*q=p+*p;	// Schreibzeiger
 *++q=(char)a;
 *++q=(char)(a>>8|b&0xF0);
 *++q=(char)(b>>8);
 *p+=3;		// 3 Bytes mehr
 if (*p>59) ep1Commit();	// kein Platz mehr? (Max. 63 Bytes pro Report)
}

// Aufruf alle 43,5 µs, 23 kHz (!)
// Der A/D-Wandler hat dieses Abarbeitungsregime:
//		0   1	2   3	4   5	6   7	8   9
// Amplitude	x	x	    x	    x
// Phase	    x	    x		x	x
// Referenzsp.			x
// Temperatur					    x
#pragma vector=ADC12_VECTOR
static __interrupt void AdcComplete(void) {
 P8OUT^=0x40;
// Teil 1: Amp&Pha
 unsigned a=ADC12MEM[0]+ADC12MEM[2]+ADC12MEM[5]+ADC12MEM[7];
 unsigned b=ADC12MEM[1]+ADC12MEM[3]+ADC12MEM[6]+ADC12MEM[8];
 if (feature.enab) {
  a>>=2;
  b<<=2;
  ep1Put(a,b);
 }
 if (dds_nextf() && feature.enab) ep1Commit();
// Teil 2: Referenzspannung und Temperatur
 a=ADC12MEM[4]+adcadd[0];	// Referenzspannung (1,8 V = 0x0B84?)
 b=ADC12MEM[9]+adcadd[1];	// Temperatursensor (inzwischen aktualisiert)
 if (!(++adcmean.cntr&15)) {	// nach insgesamt 16 Additionen
  adcmean.ref=a;		// … abspeichern
  adcmean.temp=b;
  a=b=0;
 }
 adcadd[0]=a;
 adcadd[1]=b;
}

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

int main(void) {
 WDTCTL = WDTPW|WDTHOLD;
 setupClock();
// Hardware initialiseren
//DEBUG
//   P1SEL|=1;		//ACLK (8 MHz?)
//   P2SEL|=0x80;
//   PMAPKEYID=0x2D52;
//   PMAPCTL=2;
//   P2MAP7=PM_MCLK;
   P3SEL|=0x10;		//SMCLK
   P3DIR|=0x10;
 PADIR = 0xFFFF;	// P1 und P2
 PBREN = 0xFFFF;	// P3 und P4
 P5REN = 0xBC;
 P5DIR = 0x40;
 P5SEL = 0x43;	//ADC12CLK zur Kontrolle an Pin 16
 P6SEL = 0xFF;	// Port 6 rein analog
 P7REN = 0xFF;
 P8OUT = 0x32; P8DIR = 0xFF;	// !SCLK und !FSYNC auf HIGH
 P9REN = 0xFF;
 USBPHYCTL=0x20;	// USB-Bus-Reset erzwingen
 _delay_ms(50);
 USBPHYCTL=0x00;
 ((char*)&USBSTABUFF)[0]=1;
 ((char*)&USBSTABUFF)[64]=1;
 feature.reportid=2;
 feature.f=efeature;
 adcmean.reportid=3;
/*##################
# A/D-Schema für 12 AD12MCTL-Plätze:
# 0-2-1	(Referenz)
# 0-2-A	(Chiptemperatur) - Interrupt bei Speicherplatz 10(!)
###################*/
 while (REFCTL0&1<<10);	// warten bis Referenzspannung bereit
 REFCTL0=0x00A3;		// Referenzspannung 2,5 V einschalten
 while (REFCTL0&1<<10);	// warten bis bereit
// ADC12CTL0=0;
 static const char DefAdc12Mctl[]={
  0x10,0x12,		// alles zwischen 0V und 2,5V
  0x10,0x12,0x11,
  0x10,0x12,
  0x10,0x12,0x9A};
 memcpy((void*)ADC12MCTL,DefAdc12Mctl,sizeof(DefAdc12Mctl));
// Maximale ADC-Taktfrequenz = 4,8 MHz!
 ADC12IE = 1<<8;		// Interrupt bei Speicherplatz 8
 ADC12CTL2 = 0x0022;		// 12 bit, vorzeichenlos, rechts ausgerichtet, Referenzausgang EIN
 ADC12CTL1 = 0x023E;		// SMCLK, alle Kanäle, durchlaufend
 ADC12CTL0 = 0x61F3;		// 2,5 V Referenzspannung, Sample-Timer 8 Takte, Start
/*##################
# Zeitbetrachtungen (SMCLK = 8 MHz):
# Sample-Zeit:	16 SMCLK-Takte	2,0 µs	zu kurz für Temperatursensor, min. 30 µs
# Sample-Zeit für Temp.: 256-"-	32 µs
# Umsetz-Zeit:	26 -"-		3,2 µs
# Umsetzrate:	16+26 = 42 -"-	5,2 µs	190 kSa/s
# Block-Rate:	42*12 = 504 -"-	93 µs	16 kSa/s	Interruptrate, DDS-Rate
# Zum Vergleich: HID mit Interrupt-Report 64 Byte à 1 ms (64 kByte/s) und Längenbyte
# Vier Bytes:	15 Samples	66 µs	15 kSa/s	ungepackte 16-Bit-Übertragung
# Drei Bytes:	20 Samples	47 µs	20 kSa/s	gepackte 12-Bit-Übertragung
###################*/
 
 DAC12_0CTL0 = 0x02F0;		// D/A-Wandler aktivieren
 while (DAC12_0CTL0&1<<9);
 DAC12_1CTL0 = 0x02F0;		// … kalibrieren lassen
 while (DAC12_1CTL0&1<<9);	// abwarten
 FeatureChanged();
 
 dds_init();
 usbInit();
 _EINT();
 for(;;);
}
Detected encoding: UTF-80