Seems to work well. Next step is to process ADC data at 128 KHz to determine DAC stepping, and send DAC + ADC to PC at 8 KHz. git-svn-id: https://bucket.mit.edu/svn/nilm/zoom@5931 ddd99763-3ecb-0310-9145-efcb8ce7c51ftags/zoom-1.0
@@ -0,0 +1,83 @@ | |||
#include "config.h" | |||
#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))); | |||
void (*adc_adc_callback)(void) = 0; | |||
void (*adc_dma_callback)(void) = 0; | |||
/* ADC1 interrupt after each sample (if enabled) */ | |||
void __attribute__((__interrupt__,auto_psv)) _ADC1Interrupt(void) | |||
{ | |||
if (adc_adc_callback) | |||
(*adc_adc_callback)(); | |||
IFS0bits.AD1IF = 0; | |||
} | |||
/* DMA0 interrupt after 16 samples */ | |||
void __attribute__((__interrupt__,auto_psv)) _DMA0Interrupt(void) | |||
{ | |||
if (adc_dma_callback) | |||
(*adc_dma_callback)(); | |||
IFS0bits.DMA0IF = 0; | |||
} | |||
/* Initialize ADC1 to constantly DMA AN0. */ | |||
void adc_init(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 = 0; /* No ADC interrupt (every sample) */ | |||
} |
@@ -0,0 +1,14 @@ | |||
#ifndef ADC_H | |||
#define ADC_H | |||
/* DMA region for the ADC */ | |||
extern uint16_t adc_dmabuf[16]; | |||
/* Initialize internal ADC */ | |||
void adc_init(void); | |||
/* Callback functions */ | |||
extern void (*adc_adc_callback)(void); | |||
extern void (*adc_dma_callback)(void); | |||
#endif |
@@ -12,7 +12,7 @@ | |||
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 digital, so use SPI pins: | |||
All exposed pins on B D and E are analog, so use SPI pins: | |||
ADC SDO = PIC SDI1 = RF7 | |||
ADC SCK = PIC SCK1 = RF6 | |||
*/ | |||
@@ -30,16 +30,22 @@ | |||
/* Initialize ADC */ | |||
void adcext_init(void) | |||
{ | |||
int i; | |||
int32_t i; | |||
TRIS_SDO = 1; | |||
LAT_SCK = 0; | |||
TRIS_SCK = 0; | |||
/* Startup delay CS down to SCK high t4 = 5000ns */ | |||
for (i = 0; i < 5000 / 25; i++) | |||
wait_25ns(); | |||
/* We need to monitor BUSY, I think. With the 2-wire | |||
interface, we never know if we start in the middle of | |||
a data output phase. For now, consider reading broken.. | |||
just return early. XXXX */ | |||
return; | |||
/* Trigger a dummy read so we're prepared for the | |||
next conversion */ | |||
(void) adcext_read(); | |||
@@ -118,7 +124,7 @@ void adcext_start_conversion(void) | |||
LAT_SCK = 0; | |||
/* Wait tKQMAX for SCK down to SDO valid in case we | |||
call adc_is_conversion_finished right away. */ | |||
call adcext_is_conversion_ready right away. */ | |||
wait_200ns(); | |||
} | |||
@@ -1,5 +1,5 @@ | |||
#ifndef ADC_H | |||
#define ADC_H | |||
#ifndef ADCEXT_H | |||
#define ADCEXT_H | |||
/* Initialize ADC */ | |||
void adcext_init(void); | |||
@@ -13,7 +13,6 @@ typedef signed long long int64_t; | |||
typedef unsigned long long uint64_t; | |||
#define FCY 40000000 | |||
#define TMR1_RATE 8000 | |||
void config_init(void); | |||
@@ -12,7 +12,7 @@ void uart_init(int uart, int32_t rate) | |||
if (uart == 1) { | |||
U1MODE = 0; | |||
U1MODEbits.BRGH = 0; | |||
U1MODEbits.BRGH = 0; // errata: BRGH=1 is broken | |||
U1BRG = brg; | |||
U1STA = 0; | |||
U1MODEbits.UARTEN = 1; | |||
@@ -1,39 +1,37 @@ | |||
#include "config.h" | |||
#include "adc.h" | |||
#include "adcext.h" | |||
#include "dac.h" | |||
#include "uart.h" | |||
#include "timer.h" | |||
#include <stdio.h> | |||
#include <math.h> | |||
int32_t tmr1_ms = 0; | |||
void TISR_HANDLER(1) | |||
int new_data = 0; | |||
uint16_t data; | |||
void callback(void) | |||
{ | |||
/* Send most recent sample to PC */ | |||
data = adc_dmabuf[15]; | |||
new_data = 1; | |||
} | |||
int main(void) | |||
{ | |||
config_init(); | |||
uart1_init(1000000); | |||
uart1_init(500000); | |||
adcext_init(); | |||
dac_init(); | |||
timer_setup_16bit(1, 1000, 1); | |||
TRISDbits.TRISD0 = 0; | |||
TRISDbits.TRISD1 = 1; | |||
TRISDbits.TRISD2 = 1; | |||
adc_init(); | |||
adc_dma_callback = callback; | |||
while(1) { | |||
/* these are ints, so it should be atomic */ | |||
/* | |||
tmp = output_count; | |||
tmp2 = dacval; | |||
uart1_put_hex((tmp & 0x7F) | (LATDbits.LATD0 ? 0x80 : 0x00)); | |||
uart1_put_hex((tmp2 & 0xFF00) >> 8); | |||
uart1_put_hex((tmp2 & 0x00FF)); | |||
uart1_put('\r'); | |||
*/ | |||
if (new_data) { | |||
uart1_put_hex16(data); | |||
uart1_crlf(); | |||
new_data = 0; | |||
} | |||
} | |||
for(;;) continue; | |||
@@ -31,6 +31,8 @@ file_010=no | |||
file_011=no | |||
file_012=no | |||
file_013=no | |||
file_014=no | |||
file_015=no | |||
[FILE_INFO] | |||
file_000=zoom.c | |||
file_001=config.c | |||
@@ -39,13 +41,15 @@ file_003=util.c | |||
file_004=dac.c | |||
file_005=timer.c | |||
file_006=adcext.c | |||
file_007=config.h | |||
file_008=uart.h | |||
file_009=util.h | |||
file_010=dac.h | |||
file_011=timer.h | |||
file_012=adcext.h | |||
file_013=p33fj256gp710.gld | |||
file_007=adc.c | |||
file_008=config.h | |||
file_009=uart.h | |||
file_010=util.h | |||
file_011=dac.h | |||
file_012=timer.h | |||
file_013=adcext.h | |||
file_014=adc.h | |||
file_015=p33fj256gp710.gld | |||
[SUITE_INFO] | |||
suite_guid={479DDE59-4D56-455E-855E-FFF59A3DB57E} | |||
suite_state= | |||