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