You did not mentioned the exact dsPIC model so I am assuming it is dsPIC30-series and the documentation you are referring to is this one: http://ww1.microchip.com/downloads/en/devicedoc/70064e.pdf
Your assumptions are correct. I don't think there's a way to setup this ADC to automatically stop after certain number of sample / conversions. However, you can manually disable the whole module in the ISR occurring after 16th conversion. This will abort 17th sample / conversion and ADCBUF0 will not be overwritten. You can then later re-enable the ADC when you have processed the buffer.
volatile uint8_t newValues = 0;
void __attribute__((interrupt, no_auto_psv)) _ADC1Interrupt(void)
{
AD1CON1bits.ADON = 0; // this must be executed before the end of 17th conversion
IFS0bits.AD1IF = 0;
newValues = 1;
}
int main(void)
{
AD1CON1bits.SSRC = 0b111; // auto-convert
AD1CON1bits.ASAM = 1; // auto-sample
AD1CON2bits.SMPI = (16-1); // interrupt after 16th sample/conversion
AD1CON3bits.SAMC = 31; // sample time
AD1CON3bits.ADCS = 0b111111;// clock setup
IFS0bits.AD1IF = 0; // clear ADC int. flag
IEC0bits.AD1IE = 1; // enable ADC int.
AD1CON1bits.ADON = 1; // enable ADC -> starts sequence of 16 sample/conversions
while(1)
{
if(newValues)
{
newValues = 0;
// read and process ADCBUF0...F
AD1CON1bits.ADON = 1; // start next sequence
}
}
return 0;
}
However, I think a better way is to utilize alternating buffer filling mode, described in chapter 17.14.3. In this mode, interrupt will occur twice per full buffer update. Software should have plenty of time (8 * sample / conversion time) to copy ADCBUF values to some other buffer for later processing.
#define ADC_RINGBUF_SIZE 32 // must be multiple of 8
volatile uint16_t adcRingBuf[ADC_RINGBUF_SIZE];
volatile uint8_t adcRingBufWr, adcRingBufRd;
void __attribute__((interrupt, no_auto_psv)) _ADC1Interrupt(void)
{
IFS0bits.AD1IF = 0;
if(adcRingBufWr >= ADC_RINGBUF_SIZE)
{
adcRingBufWr = 0;
}
if(AD1CON2bits.BUFS)
{
adcRingBuf[adcRingBufWr++] = ADCBUF0;
adcRingBuf[adcRingBufWr++] = ADCBUF1;
adcRingBuf[adcRingBufWr++] = ADCBUF2;
adcRingBuf[adcRingBufWr++] = ADCBUF3;
adcRingBuf[adcRingBufWr++] = ADCBUF4;
adcRingBuf[adcRingBufWr++] = ADCBUF5;
adcRingBuf[adcRingBufWr++] = ADCBUF6;
adcRingBuf[adcRingBufWr++] = ADCBUF7;
}
else
{
adcRingBuf[adcRingBufWr++] = ADCBUF8;
adcRingBuf[adcRingBufWr++] = ADCBUF9;
adcRingBuf[adcRingBufWr++] = ADCBUFA;
adcRingBuf[adcRingBufWr++] = ADCBUFB;
adcRingBuf[adcRingBufWr++] = ADCBUFC;
adcRingBuf[adcRingBufWr++] = ADCBUFD;
adcRingBuf[adcRingBufWr++] = ADCBUFE;
adcRingBuf[adcRingBufWr++] = ADCBUFF;
}
}
int main(void)
{
AD1CON1bits.SSRC = 0b111; // auto-convert
AD1CON1bits.ASAM = 1; // auto-sample
AD1CON2bits.SMPI = (8-1); // interrupt after 8th sample/conversion
AD1CON2bits.BUFM = 1; // alternate buffer half on each interrupt
AD1CON3bits.SAMC = 31; // sample time
AD1CON3bits.ADCS = 0b111111;// clock setup
IFS0bits.AD1IF = 0; // clear ADC int. flag
IEC0bits.AD1IE = 1; // enable ADC int.
AD1CON1bits.ADON = 1; // enable ADC
while(1)
{
if(adcRingBufWr > adcRingBufRd)
{
if((adcRingBufWr - adcRingBufRd) >= 16) // how many values we want to process at a time
{
// process adcRingBuf[adcRingBufRd] ... adcRingBuf[adcRingBufRd+15]
adcRingBufRd += 16;
}
}
else if(adcRingBufWr < adcRingBufRd)
{
if(adcRingBufRd >= ADC_RINGBUF_SIZE)
{
adcRingBufRd = 0;
}
}
}
return 0;
}
Note: This is very naive ring buffer implementation (just to show idea).