git-svn-id: https://bucket.mit.edu/svn/nilm/zoom@6896 ddd99763-3ecb-0310-9145-efcb8ce7c51ftags/zoom-1.0
@@ -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; | |||
} |
@@ -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 |
@@ -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 */ | |||
@@ -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") | |||
@@ -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; | |||
} |
@@ -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; | |||
} |