From c5c949c50918030c3788d212edfd3cb18ac80af3 Mon Sep 17 00:00:00 2001 From: jim Date: Tue, 23 Sep 2008 23:27:40 +0000 Subject: [PATCH] All the changes I made on Friday before my computer blew up git-svn-id: https://bucket.mit.edu/svn/nilm/zoom@6896 ddd99763-3ecb-0310-9145-efcb8ce7c51f --- firmware/adc.c | 112 +++++++------------------- firmware/adc.h | 13 +-- firmware/adcext.c | 29 ++++--- firmware/config.h | 5 ++ firmware/dac.c | 13 +-- firmware/zoom.c | 196 +++++++++++++++++++++------------------------- 6 files changed, 150 insertions(+), 218 deletions(-) diff --git a/firmware/adc.c b/firmware/adc.c index b2579da..99f3bda 100644 --- a/firmware/adc.c +++ b/firmware/adc.c @@ -2,93 +2,39 @@ #include "adc.h" #include "timer.h" -/* Layout of buffer: - adc_dmabuf[x] contains 16 samples of channel AN0 -*/ -uint16_t adc_dmabuf[16] __attribute__((space(dma))); +/* Currently using software SPI because it's easy. + Consider using hardware SPI with DMA on a timer, though. */ -/* Stores the offset of the most recently written DMA sample */ -int adc_offset = 0; +#define TRIS_SDATA TRISFbits.TRISF7 +#define R_SDATA PORTFbits.RF7 +#define TRIS_SCLK TRISFbits.TRISF6 +#define LAT_SCLK LATFbits.LATF6 +#define TRIS_CS TRISBbits.TRISB2 +#define LAT_CS LATBbits.LATB2 -void (*adc_adc_callback)(void) = 0; -void (*adc_dma_callback)(void) = 0; - -/* ADC1 interrupt after each sample */ -void __attribute__((__interrupt__,auto_psv)) _ADC1Interrupt(void) -{ - static int next_adc_offset = 0; - - adc_offset = next_adc_offset; - - /* DMA0STA points to the next sample that will be written. - Convert that into a buffer offset that we'll for next time */ - next_adc_offset = (DMA0STA - __builtin_dmaoffset(adc_dmabuf)) >> 1; - - IFS0bits.AD1IF = 0; - if (adc_adc_callback) - (*adc_adc_callback)(); -} - -/* DMA0 interrupt after 16 samples */ -void __attribute__((__interrupt__,auto_psv)) _DMA0Interrupt(void) +void adc_init(void) { - IFS0bits.DMA0IF = 0; - if (adc_dma_callback) - (*adc_dma_callback)(); + TRIS_SDATA = 1; + LAT_CS = IO_HIGH; + TRIS_CS = 0; + LAT_SCLK = IO_HIGH; + TRIS_SCLK = 0; } -/* Initialize ADC1 to constantly DMA AN0. */ -void adc_init(void) +uint16_t adc_get(void) { - /* AD1CON1 */ - AD1CON1bits.ADDMABM = 1; /* Scatter-gather DMA */ - AD1CON1bits.AD12B = 1; /* 12-bit, 1 channel */ - AD1CON1bits.FORM = 0; /* Integer output 0000dddddddddddd */ - AD1CON1bits.SSRC = 2; /* Convert on Timer3 */ - AD1CON1bits.ASAM = 1; /* Automatically start sampling */ - AD1CON1bits.SIMSAM = 0; /* Simul sampling, N/A when AD12B=1 */ - - /* AD1CON2 */ - AD1CON2bits.VCFG = 0; /* Ref: AVDD / AVSS */ -// AD1CON2bits.VCFG = 3; /* Ref: VREF+ / VREF- */ - AD1CON2bits.CSCNA = 0; /* Do not scan inputs on Mux A */ - AD1CON2bits.CHPS = 0; /* Convert CH0 only, N/A when AD12B=1 */ - AD1CON2bits.SMPI = 0; /* Increase DMA address at each sample */ - AD1CON2bits.BUFM = 0; /* Fill buffer from start */ - AD1CON2bits.ALTS = 0; /* Always use Mux A settings */ - - /* AD1CON3 */ - AD1CON3bits.ADRC = 0; /* Use system clock, not internal RC */ - AD1CON3bits.SAMC = 0; /* Sample time, N/A when ADRC = 0 */ - AD1CON3bits.ADCS = 0x7; /* Tad = 8 * Tcy */ - - /* AD1CON4 */ - AD1CON4bits.DMABL = 4; /* Each input gets 16 words in DMA buf */ - - /* Channel 0 setup */ - AD1CHS0bits.CH0NA = 0; /* CH0-: Vrefl */ - AD1CHS0bits.CH0SA = 0; /* CH0+: AN0 */ - - /* Set Timer3 to trigger conversion at 128KHz */ - timer_setup_16bit(3, 128000, 0); - - /* Turn it on */ - AD1PCFGL = 0xfffe; /* Analog = AN0 */ - AD1PCFGH = 0xffff; - AD2PCFGL = 0xffff; - AD1CON1bits.ADON = 1; - - /* DMA0 setup */ - DMA0CONbits.AMODE = 0; /* Register indirect, post-increment */ - DMA0CONbits.MODE = 0; /* Continuous, no ping-pong */ - DMA0PAD = (int)&ADC1BUF0; /* Get addresses from ADC1 */ - DMA0CNT = 15; /* 16 transfers before wraparound */ - DMA0STA = __builtin_dmaoffset(adc_dmabuf); - DMA0REQbits.IRQSEL = 13; /* Triggered by ADC1 */ - IFS0bits.DMA0IF = 0; - IEC0bits.DMA0IE = 1; /* DMA interrupt (every 16 samples) */ - DMA0CONbits.CHEN = 1; /* enable this DMA channel */ - - IFS0bits.AD1IF = 0; - IEC0bits.AD1IE = 1; /* ADC interrupt (every sample) */ + uint16_t v = 0; + int i; + + LAT_CS = 0; + for (i = 0; i < 16; i++) { + if (R_SDATA) + v |= 1; + v <<= 1; + LAT_SCLK = 0; + nop(); + LAT_SCLK = 1; + } + LAT_CS = 1; + return v; } diff --git a/firmware/adc.h b/firmware/adc.h index cdede71..1f69b8e 100644 --- a/firmware/adc.h +++ b/firmware/adc.h @@ -1,17 +1,12 @@ #ifndef ADC_H #define ADC_H -/* adc_dmabuf[x] contains 16 samples of channel AN0 */ -extern uint16_t adc_dmabuf[16]; +#include "config.h" -/* Stores the offset of the most recently written DMA sample */ -extern int adc_offset; - -/* Initialize internal ADC */ +/* Initialize external 12-bit ADC (AD7450R) */ void adc_init(void); -/* Callback functions */ -extern void (*adc_adc_callback)(void); -extern void (*adc_dma_callback)(void); +/* Trigger conversion and return it */ +uint16_t adc_get(void); #endif diff --git a/firmware/adcext.c b/firmware/adcext.c index 6da2f4d..7dd242e 100644 --- a/firmware/adcext.c +++ b/firmware/adcext.c @@ -11,15 +11,12 @@ tie ADC SDI for rate selection, tie /CS low, use BUSY for interrupt notification if desired */ -/* Since the ADC is 5v, ADC SDO must be on a digital-only pin. - All exposed pins on B D and E are analog, so use SPI pins: - ADC SDO = PIC SDI1 = RF7 - ADC SCK = PIC SCK1 = RF6 -*/ -#define TRIS_SDO TRISFbits.TRISF7 -#define R_SDO PORTFbits.RF7 -#define TRIS_SCK TRISFbits.TRISF6 -#define LAT_SCK LATFbits.LATF6 +#define TRIS_SDO TRISBbits.TRISB5 +#define R_SDO PORTBbits.RB5 +#define TRIS_SCK TRISBbits.TRISB4 +#define LAT_SCK LATBbits.LATB4 +#define TRIS_SCS TRISBbits.TRISB3 +#define LAT_SCS LATBbits.LATB3 /* Short delays */ #define wait_200ns() do { \ @@ -33,8 +30,10 @@ void adcext_init(void) int32_t i; TRIS_SDO = 1; - LAT_SCK = 0; + LAT_SCK = IO_HIGH; TRIS_SCK = 0; + LAT_SCS = IO_LOW; + TRIS_SCS = 0; /* Startup delay CS down to SCK high t4 = 5000ns */ for (i = 0; i < 5000 / 25; i++) @@ -60,13 +59,13 @@ uint32_t adcext_read(void) int i; /* Start conversion by completing previous read */ - LAT_SCK = 0; + LAT_SCK = IO_LOW; /* Wait tKQMAX for SCK down to SDO valid */ wait_200ns(); /* Wait for conversion to finish */ - while (R_SDO == 1) + while (R_SDO == IO_HIGH) continue; /* Read it out */ @@ -74,7 +73,7 @@ uint32_t adcext_read(void) for (i = 0; i < 32; i++) { /* SCK low tLESCK = 25ns */ wait_25ns(); - LAT_SCK = 1; + LAT_SCK = IO_HIGH; /* SCK high tHESCK = 25ns, but we also have SCK down to SDO valid tKQMAX = 200ns? @@ -82,11 +81,11 @@ uint32_t adcext_read(void) wait_200ns(); val <<= 1; - if (R_SDO) + if (R_SDO == IO_HIGH) val |= 1; /* Leave SCK high on final bit to delay new conversion */ if (i < 31) - LAT_SCK = 0; + LAT_SCK = IO_LOW; } /* Done */ diff --git a/firmware/config.h b/firmware/config.h index 38a7652..6e5feaa 100644 --- a/firmware/config.h +++ b/firmware/config.h @@ -14,6 +14,11 @@ typedef unsigned long long uint64_t; #define FCY 40000000 +/* define as 0/1 to invert I/O polarity for optocouplers + define as 1/0 for normal polarity */ +#define IO_HIGH 0 +#define IO_LOW 1 + void config_init(void); #define nop() __asm("nop") diff --git a/firmware/dac.c b/firmware/dac.c index 635f9fd..d15c991 100644 --- a/firmware/dac.c +++ b/firmware/dac.c @@ -13,7 +13,7 @@ void dac_init(void) SPI2CON1bits.SMP = 0; SPI2CON1bits.CKE = 0; SPI2CON1bits.SSEN = 0; - SPI2CON1bits.CKP = 1; + SPI2CON1bits.CKP = IO_HIGH; SPI2CON1bits.MSTEN = 1; SPI2CON1bits.SPRE = 4; SPI2CON1bits.PPRE = 3; @@ -21,7 +21,7 @@ void dac_init(void) /* There's no framed mode that does the normal "chip select" behavior, so control /SS2 manually */ SPI2CON2bits.FRMEN = 0; - LATGbits.LATG9 = 1; + LATGbits.LATG9 = IO_HIGH; TRISGbits.TRISG9 = 0; SPI2STATbits.SPISIDL = 0; @@ -37,10 +37,13 @@ void dac_init(void) */ void dac_write(uint16_t val) { - LATGbits.LATG9 = 0; - SPI2BUF = val; + LATGbits.LATG9 = IO_LOW; + if (IO_HIGH == 1) + SPI2BUF = val; + else + SPI2BUF = ~val; while (!SPI2STATbits.SPIRBF) continue; (void) SPI2BUF; - LATGbits.LATG9 = 1; + LATGbits.LATG9 = IO_HIGH; } diff --git a/firmware/zoom.c b/firmware/zoom.c index 5199309..593b895 100644 --- a/firmware/zoom.c +++ b/firmware/zoom.c @@ -22,66 +22,41 @@ uint16_t send_adc, send_dac; uint16_t dac_cmd = ((uint32_t)DAC_MAX + DAC_MIN) / 2; -/* 128 KHz, after each ADC sample */ -void fast_callback(void) +/* 64 KHz */ +void timer(void) { - /* Run at 64 KHz */ static int count = 0; - if (count++ < 1) return; - count = 0; - - /* Get most recent sample. */ - uint16_t v = adc_dmabuf[adc_offset]; + uint16_t v; + int32_t adjustment; + int32_t newdac; + + /* Get most recent sample from 12-bit ADC. */ + v = adc_get(); + + /* Send data to PC at 8 KHz */ + if (count++ >= 8) { + count = 0; + + /* Send most recent sample and old DAC value */ + send_adc = v; + send_dac = dac_cmd; + send_data = 1; + } -#if 1 /* Update DAC. Add whatever is necessary to cause the ADC to change by 150% towards the center of 0x0800 (intentional overshoot to increase slew rate capability) */ - { - int32_t adjustment = ((int32_t) v - 0x0800) * 1.50 * ADC_DAC_SCALING; - int32_t newdac = dac_cmd + adjustment; - //if (adjustment) { - if (newdac < DAC_MIN) - dac_cmd = DAC_MIN; - else if (newdac > DAC_MAX) - dac_cmd = DAC_MAX; - else - dac_cmd = newdac; - dac_write(dac_cmd); - //} + adjustment = ((int32_t) v - 0x0800) * 1.50 * ADC_DAC_SCALING; + newdac = dac_cmd + adjustment; + if (1 || adjustment) { + if (newdac < DAC_MIN) + dac_cmd = DAC_MIN; + else if (newdac > DAC_MAX) + dac_cmd = DAC_MAX; + else + dac_cmd = newdac; + dac_write(dac_cmd); } -#endif -#if 0 - /* Update DAC if necessary */ - if (v < ADC_BIGMIN && dac_cmd < (DAC_MAX - DAC_BIGSTEP)) - dac_cmd += DAC_BIGSTEP; - else if (v < ADC_MIN && dac_cmd < (DAC_MAX - DAC_STEP)) - dac_cmd += DAC_STEP; - else if (v > ADC_BIGMAX && dac_cmd > (DAC_MIN + DAC_BIGSTEP)) - dac_cmd -= DAC_BIGSTEP; - else if (v > ADC_MAX && dac_cmd > (DAC_MIN + DAC_STEP)) - dac_cmd -= DAC_STEP; - else - return; - dac_write(dac_cmd); -#endif -} - -/* 8 KHz, after DMA buffer filled */ -void slow_callback(void) -{ - /* Get most recent sample. */ - uint16_t v = adc_dmabuf[adc_offset]; - - /* Send most recent sample to PC */ - send_adc = v; - send_dac = dac_cmd; - send_data = 1; -} - -void debug_callback(void) -{ - send_adc = adc_dmabuf[adc_offset]; } void send_to_pc(void) @@ -97,65 +72,74 @@ void send_to_pc(void) uart1_put((send_dac & 0x007F)); } +void run_debug(void) +{ + uint16_t dac = 32768; + uart1_init(115200); + + while (1) { + dac_write(dac); + uart1_put_dec(dac); + uart1_put(' '); + uart1_put_hex16(adc_get()); + uart1_crlf(); + switch (uart1_get()) { + case '[': + dac--; + break; + case ']': + dac++; + break; + case '-': + dac -= 16; + break; + case '+': + case '=': + dac += 16; + break; + case ',': + case '<': + dac -= 1024; + break; + case '.': + case '>': + dac += 1024; + break; + case '0': + dac = 32768; + break; + } + } +} + +void run_normal(void) +{ + uart1_init(500000); + + while(1) { + if (send_data) { + send_to_pc(); + send_data = 0; + } + } +} + int main(void) { - int debug = 0; - int i; - config_init(); adcext_init(); dac_init(); adc_init(); - if (debug) { - uart1_init(115200); - adc_dma_callback = debug_callback; - - uint16_t dac = 32768; - while (1) { - dac_write(dac); - uart1_put_dec(dac); - uart1_put(' '); - uart1_put_dec(send_adc); - uart1_crlf(); - switch (uart1_get()) { - case '[': - dac--; - break; - case ']': - dac++; - break; - case '-': - dac -= 16; - break; - case '+': - case '=': - dac += 16; - break; - case ',': - case '<': - dac -= 1024; - break; - case '.': - case '>': - dac += 1024; - break; - case '0': - dac = 32768; - break; - } - } - } else { - uart1_init(500000); - - adc_adc_callback = fast_callback; - adc_dma_callback = slow_callback; + /* Pull PGD high internally. If it is externally tied + to ground, run in special debug mode. */ + TRISCbits.TRISC13 = 1; + CNPU1bits.CN1PUE = 1; + if (PORTCbits.RC13 == 0) + run_debug(); + else + run_normal(); - while(1) { - if (send_data) { - send_to_pc(); - send_data = 0; - } - } - } + for (;;) + continue; }